diff --git a/go.mod b/go.mod index 5e519d6..7b65576 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/gofrs/uuid v4.3.0+incompatible github.com/gorilla/websocket v1.5.0 github.com/iancoleman/strcase v0.2.0 - github.com/koyeb/koyeb-api-client-go v0.0.0-20241129081540-9cecbc45397f + github.com/koyeb/koyeb-api-client-go v0.0.0-20241206143543-39fbd0d0bf5e github.com/logrusorgru/aurora v2.0.3+incompatible github.com/manifoldco/promptui v0.9.0 github.com/mitchellh/go-homedir v1.1.0 diff --git a/go.sum b/go.sum index 5294835..2af6b0c 100644 --- a/go.sum +++ b/go.sum @@ -168,10 +168,12 @@ github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLf github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/koyeb/koyeb-api-client-go v0.0.0-20241127113640-742236b17b87 h1:VkwuM05Wszx/dwLrJm48AkTg13Pmq7TFzwUpiFBOgxo= -github.com/koyeb/koyeb-api-client-go v0.0.0-20241127113640-742236b17b87/go.mod h1:+oQfFj2WL3gi9Pb+UHbob4D7xaT52mPfKyH1UvWa4PQ= github.com/koyeb/koyeb-api-client-go v0.0.0-20241129081540-9cecbc45397f h1:jzyPhhyZgLKA3FXMNxyEZiETROBEde4uI4qwHsliBrk= github.com/koyeb/koyeb-api-client-go v0.0.0-20241129081540-9cecbc45397f/go.mod h1:+oQfFj2WL3gi9Pb+UHbob4D7xaT52mPfKyH1UvWa4PQ= +github.com/koyeb/koyeb-api-client-go v0.0.0-20241206143120-7817d20778fd h1:krjNLKRYbBEExAIEI1maqyo9P3oF1cKevQXiVedTX18= +github.com/koyeb/koyeb-api-client-go v0.0.0-20241206143120-7817d20778fd/go.mod h1:+oQfFj2WL3gi9Pb+UHbob4D7xaT52mPfKyH1UvWa4PQ= +github.com/koyeb/koyeb-api-client-go v0.0.0-20241206143543-39fbd0d0bf5e h1:/pSEDHiuCglPMooi0kcQay0ZmJ1fZys8qre6Jz2iQ7Q= +github.com/koyeb/koyeb-api-client-go v0.0.0-20241206143543-39fbd0d0bf5e/go.mod h1:+oQfFj2WL3gi9Pb+UHbob4D7xaT52mPfKyH1UvWa4PQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= diff --git a/pkg/koyeb/flags_list/file_mounts.go b/pkg/koyeb/flags_list/file_mounts.go index 356f61e..2ab02db 100644 --- a/pkg/koyeb/flags_list/file_mounts.go +++ b/pkg/koyeb/flags_list/file_mounts.go @@ -1,7 +1,6 @@ package flags_list import ( - "encoding/base64" "errors" "fmt" "os" @@ -13,17 +12,14 @@ import ( type FlagFileMount struct { BaseFlag - raw *string - secret *string path string permissions string + content string } -// Parse the list of values in the form path:secret:name or path:file: -// The function is wrapped in another function to allow the caller to provide a function to resolve the volume ID from its name. -func GetNewFileMountListFromFlags() func(values []string) ([]Flag[koyeb.DeploymentFileMount], error) { - return func(values []string) ([]Flag[koyeb.DeploymentFileMount], error) { - ret := make([]Flag[koyeb.DeploymentFileMount], 0, len(values)) +func GetNewFileMountListFromFlags() func(values []string) ([]Flag[koyeb.FileMount], error) { + return func(values []string) ([]Flag[koyeb.FileMount], error) { + ret := make([]Flag[koyeb.FileMount], 0, len(values)) for _, value := range values { hc := &FlagFileMount{BaseFlag: BaseFlag{cliValue: value}} @@ -35,7 +31,7 @@ func GetNewFileMountListFromFlags() func(values []string) ([]Flag[koyeb.Deployme What: "Error while configuring the service", Why: fmt.Sprintf("unable to parse the file mount\"%s\"", hc.cliValue), Additional: []string{ - "To remove a file mount from the service, prefix the path with '!', e.g. '!path'", + "To remove a mounted file from the service, prefix the path with '!', e.g. '!path'", "The source should not be specified to remove it from the service", }, Orig: nil, @@ -45,62 +41,57 @@ func GetNewFileMountListFromFlags() func(values []string) ([]Flag[koyeb.Deployme hc.markedForDeletion = true hc.path = components[0][1:] } else { - if len(components) != 4 { + if len(components) != 2 && len(components) != 3 { return nil, &kerrors.CLIError{ What: "Error while configuring the service", Why: fmt.Sprintf("unable to parse the file mount\"%s\"", hc.cliValue), Additional: []string{ - "File mounts must be specified as PATH:SOURCE:DATA:PERMISSIONS", - "To remove a file mount from the service, prefix it with '!', e.g. '!path'", + "File mount must be specified as SOURCE:PATH[:PERMISSIONS]", + "To remove a mounted file from the service, prefix the path with '!', e.g. '!path'", }, Orig: nil, Solution: "Fix the file mount and try again", } } - hc.path = components[0] - source := components[1] - source = strings.ToLower(source) - switch source { - case "secret": - hc.secret = &components[2] - case "file": - path := components[2] - if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) { - return nil, &kerrors.CLIError{ - What: "Error while configuring the service", - Why: fmt.Sprintf("unable to parse the file mount\"%s\"", hc.cliValue), - Additional: []string{ - "File mounts must be specified as PATH:SOURCE:DATA:PERMISSIONS", - "To remove a file mount from the service, prefix it with '!', e.g. '!path'", - }, - Orig: nil, - Solution: "Fix the file mount and try again", - } + hc.path = components[1] + path := components[0] + if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) { + return nil, &kerrors.CLIError{ + What: "Error while configuring the service", + Why: fmt.Sprintf("unable to parse the file mount \"%s\"", hc.cliValue), + Additional: []string{ + "File mount must be specified as SOURCE:PATH[:PERMISSIONS]", + "To remove a file mount from the service, prefix it with '!', e.g. '!path'", + }, + Orig: nil, + Solution: "Fix the file mount and try again", } - data, err := os.ReadFile(components[2]) - if err != nil { - return nil, &kerrors.CLIError{ - What: "Error while configuring the service", - Why: fmt.Sprintf("unable to parse the file mount\"%s\"", hc.cliValue), - Additional: []string{ - "File mounts must be specified as PATH:SOURCE:DATA:PERMISSIONS", - "To remove a file mount from the service, prefix it with '!', e.g. '!path'", - }, - Orig: nil, - Solution: "Fix the file mount and try again", - } + } + data, err := os.ReadFile(path) + if err != nil { + return nil, &kerrors.CLIError{ + What: "Error while configuring the service", + Why: fmt.Sprintf("unable to read the file mount\"%s\"", hc.cliValue), + Additional: []string{ + "File mount must be specified as SOURCE:PATH[:PERMISSIONS]", + "To remove a file mount from the service, prefix it with '!', e.g. '!path'", + }, + Orig: nil, + Solution: "Fix the file mount and try again", } - encoded := base64.URLEncoding.EncodeToString([]byte(data)) - hc.raw = &encoded + } + hc.content = string(data) + permissions := "0644" + if len(components) == 3 { + permissions = components[2] } - permissions := components[3] if len(permissions) != 4 { return nil, &kerrors.CLIError{ What: "Error while configuring the service", Why: fmt.Sprintf("unable to parse the file mount\"%s\"", hc.cliValue), Additional: []string{ - "File mounts must be specified as PATH:SOURCE:DATA:PERMISSIONS", + "File mount permission must be specified as SOURCE:PATH:PERMISSIONS", "To remove a file mount from the service, prefix it with '!', e.g. '!path'", }, Orig: nil, @@ -108,30 +99,25 @@ func GetNewFileMountListFromFlags() func(values []string) ([]Flag[koyeb.Deployme } } hc.permissions = permissions - ret = append(ret, hc) } + ret = append(ret, hc) } return ret, nil } } -func (f *FlagFileMount) IsEqualTo(hc koyeb.DeploymentFileMount) bool { +func (f *FlagFileMount) IsEqualTo(hc koyeb.FileMount) bool { return hc.GetPath() == f.path } -func (f *FlagFileMount) UpdateItem(hc *koyeb.DeploymentFileMount) { - if f.secret != nil { - hc.Secret = &koyeb.SecretSource{Name: f.secret} - } - if f.raw != nil { - hc.Raw = &koyeb.RawSource{Content: f.raw} - } +func (f *FlagFileMount) UpdateItem(hc *koyeb.FileMount) { + hc.Content = &f.content hc.Path = &f.path hc.Permissions = &f.permissions } -func (f *FlagFileMount) CreateNewItem() *koyeb.DeploymentFileMount { - item := koyeb.NewDeploymentFileMountWithDefaults() +func (f *FlagFileMount) CreateNewItem() *koyeb.FileMount { + item := koyeb.NewFileMountWithDefaults() f.UpdateItem(item) return item } diff --git a/pkg/koyeb/services.go b/pkg/koyeb/services.go index f6a4bae..4c8048c 100644 --- a/pkg/koyeb/services.go +++ b/pkg/koyeb/services.go @@ -378,10 +378,10 @@ func (h *ServiceHandler) addServiceDefinitionFlagsForAllSources(flags *pflag.Fla "To delete a volume, use !VOLUME, for example --volume '!myvolume'\n", ) flags.StringSlice( - "file-mounts", + "mount-config-file", nil, - "Mount a secret or file in the service container using the format PATH:SECRET:NAME, PATH:FILE:, for example --file-mount /data:secret:mysecret.\n"+ - "To delete a file mount, use !PATH, for example\n", + "Mount a copy of local file as a file in the service container using the format LOCAL_FILE:PATH:[PERMISSIONS] for example --mount-file /etc/data.yaml:/etc/data.yaml:0644\n"+ + "To delete a file mount, use !PATH, for example --mount-file !/etc/data.yaml\n", ) // Configure aliases: for example, allow user to use --port instead of --ports @@ -414,7 +414,6 @@ func (h *ServiceHandler) addServiceDefinitionFlagsForAllSources(flags *pflag.Fla "git-docker-arg": "git-docker-args", "docker-arg": "docker-args", "archive-docker-arg": "archive-docker-args", - "file-mount": "file-mounts", } alias, exists := aliases[name] if exists { @@ -1755,9 +1754,9 @@ func (h *ServiceHandler) parseVolumes(ctx *CLIContext, flags *pflag.FlagSet, cur return parseListFlags("volumes", flags_list.GetNewVolumeListFromFlags(wrappedResolveVolumeId), flags, currentVolumes) } -// Parse --file-mounts -func (h *ServiceHandler) parseFileMounts(ctx *CLIContext, flags *pflag.FlagSet, currentFileMounts []koyeb.DeploymentFileMount) ([]koyeb.DeploymentFileMount, error) { - return parseListFlags("file-mounts", flags_list.GetNewFileMountListFromFlags(), flags, currentFileMounts) +// Parse --mount-config-file +func (h *ServiceHandler) parseFileMounts(ctx *CLIContext, flags *pflag.FlagSet, currentFileMounts []koyeb.FileMount) ([]koyeb.FileMount, error) { + return parseListFlags("mount-config-file", flags_list.GetNewFileMountListFromFlags(), flags, currentFileMounts) } // DeploymentStrategy is a type alias for koyeb.DeploymentStrategyType which implements the pflag.Value interface.