From ca452c47d8479579672c286557b2dfbc174b0588 Mon Sep 17 00:00:00 2001 From: Guillaume Lours <705411+glours@users.noreply.github.com> Date: Wed, 17 Jul 2024 16:57:39 +0200 Subject: [PATCH] bump compose-go to v2.1.4 Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 +- .../compose-go/v2/loader/environment.go | 59 +++++- .../compose-go/v2/loader/extends.go | 88 ++++++-- .../compose-go/v2/loader/include.go | 27 ++- .../compose-go/v2/loader/loader.go | 193 +++++++++--------- .../compose-go/v2/loader/normalize.go | 13 +- .../compose-go/v2/paths/resolve.go | 2 +- .../compose-spec/compose-go/v2/paths/unix.go | 14 ++ .../compose-go/v2/schema/compose-spec.json | 165 +++++++-------- .../compose-go/v2/schema/using-variables.yaml | 123 +++++++++++ .../compose-go/v2/transform/defaults.go | 1 + .../compose-go/v2/transform/ports.go | 15 ++ .../compose-go/v2/types/derived.gen.go | 7 +- .../compose-spec/compose-go/v2/types/types.go | 32 +++ .../compose-go/v2/utils/pathutils.go | 92 +++++++++ .../compose-go/v2/utils/stringutils.go | 6 +- vendor/modules.txt | 2 +- 18 files changed, 625 insertions(+), 220 deletions(-) create mode 100644 vendor/github.com/compose-spec/compose-go/v2/schema/using-variables.yaml create mode 100644 vendor/github.com/compose-spec/compose-go/v2/utils/pathutils.go diff --git a/go.mod b/go.mod index 9172f00d..a8d485f8 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/Masterminds/semver/v3 v3.2.1 github.com/Microsoft/go-winio v0.6.2 github.com/aws/aws-sdk-go-v2/config v1.26.6 - github.com/compose-spec/compose-go/v2 v2.1.3 + github.com/compose-spec/compose-go/v2 v2.1.4 github.com/containerd/console v1.0.4 github.com/containerd/containerd v1.7.19 github.com/containerd/continuity v0.4.3 diff --git a/go.sum b/go.sum index a2ed9d54..b2c109b7 100644 --- a/go.sum +++ b/go.sum @@ -84,8 +84,8 @@ github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+g github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= -github.com/compose-spec/compose-go/v2 v2.1.3 h1:bD67uqLuL/XgkAK6ir3xZvNLFPxPScEi1KW7R5esrLE= -github.com/compose-spec/compose-go/v2 v2.1.3/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc= +github.com/compose-spec/compose-go/v2 v2.1.4 h1:+1UKMvbBJo22Bpulgb9KAeZwRT99hANf3tDQVeG6ZJo= +github.com/compose-spec/compose-go/v2 v2.1.4/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= diff --git a/vendor/github.com/compose-spec/compose-go/v2/loader/environment.go b/vendor/github.com/compose-spec/compose-go/v2/loader/environment.go index e6983bb5..1906d2d9 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/loader/environment.go +++ b/vendor/github.com/compose-spec/compose-go/v2/loader/environment.go @@ -22,9 +22,14 @@ import ( "github.com/compose-spec/compose-go/v2/types" ) -// Will update the environment variables for the format {- VAR} (without interpolation) -// This function should resolve context environment vars for include (passed in env_file) -func resolveServicesEnvironment(dict map[string]any, config types.ConfigDetails) { +// ResolveEnvironment update the environment variables for the format {- VAR} (without interpolation) +func ResolveEnvironment(dict map[string]any, environment types.Mapping) { + resolveServicesEnvironment(dict, environment) + resolveSecretsEnvironment(dict, environment) + resolveConfigsEnvironment(dict, environment) +} + +func resolveServicesEnvironment(dict map[string]any, environment types.Mapping) { services, ok := dict["services"].(map[string]any) if !ok { return @@ -45,7 +50,7 @@ func resolveServicesEnvironment(dict map[string]any, config types.ConfigDetails) if !ok { continue } - if found, ok := config.Environment[varEnv]; ok { + if found, ok := environment[varEnv]; ok { envs = append(envs, fmt.Sprintf("%s=%s", varEnv, found)) } else { // either does not exist or it was already resolved in interpolation @@ -57,3 +62,49 @@ func resolveServicesEnvironment(dict map[string]any, config types.ConfigDetails) } dict["services"] = services } + +func resolveSecretsEnvironment(dict map[string]any, environment types.Mapping) { + secrets, ok := dict["secrets"].(map[string]any) + if !ok { + return + } + + for name, cfg := range secrets { + secret, ok := cfg.(map[string]any) + if !ok { + continue + } + env, ok := secret["environment"].(string) + if !ok { + continue + } + if found, ok := environment[env]; ok { + secret["content"] = found + } + secrets[name] = secret + } + dict["secrets"] = secrets +} + +func resolveConfigsEnvironment(dict map[string]any, environment types.Mapping) { + configs, ok := dict["configs"].(map[string]any) + if !ok { + return + } + + for name, cfg := range configs { + config, ok := cfg.(map[string]any) + if !ok { + continue + } + env, ok := config["environment"].(string) + if !ok { + continue + } + if found, ok := environment[env]; ok { + config["content"] = found + } + configs[name] = config + } + dict["configs"] = configs +} diff --git a/vendor/github.com/compose-spec/compose-go/v2/loader/extends.go b/vendor/github.com/compose-spec/compose-go/v2/loader/extends.go index cdfed509..21730f10 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/loader/extends.go +++ b/vendor/github.com/compose-spec/compose-go/v2/loader/extends.go @@ -22,7 +22,11 @@ import ( "path/filepath" "github.com/compose-spec/compose-go/v2/consts" + "github.com/compose-spec/compose-go/v2/interpolation" "github.com/compose-spec/compose-go/v2/override" + "github.com/compose-spec/compose-go/v2/paths" + "github.com/compose-spec/compose-go/v2/template" + "github.com/compose-spec/compose-go/v2/transform" "github.com/compose-spec/compose-go/v2/types" ) @@ -67,25 +71,43 @@ func applyServiceExtends(ctx context.Context, name string, services map[string]a ) switch v := extends.(type) { case map[string]any: + if opts.Interpolate != nil { + v, err = interpolation.Interpolate(v, *opts.Interpolate) + if err != nil { + return nil, err + } + } ref = v["service"].(string) file = v["file"] opts.ProcessEvent("extends", v) case string: + if opts.Interpolate != nil { + v, err = opts.Interpolate.Substitute(v, template.Mapping(opts.Interpolate.LookupValue)) + if err != nil { + return nil, err + } + } ref = v opts.ProcessEvent("extends", map[string]any{"service": ref}) } - var base any + var ( + base any + processor PostProcessor + ) + if file != nil { - filename = file.(string) - services, err = getExtendsBaseFromFile(ctx, ref, filename, opts, tracker) + refFilename := file.(string) + services, processor, err = getExtendsBaseFromFile(ctx, name, ref, filename, refFilename, opts, tracker) + post = append(post, processor) if err != nil { return nil, err } + filename = refFilename } else { _, ok := services[ref] if !ok { - return nil, fmt.Errorf("cannot extend service %q in %s: service not found", name, filename) + return nil, fmt.Errorf("cannot extend service %q in %s: service %q not found", name, filename, ref) } } @@ -121,47 +143,71 @@ func applyServiceExtends(ctx context.Context, name string, services map[string]a return merged, nil } -func getExtendsBaseFromFile(ctx context.Context, name string, path string, opts *Options, ct *cycleTracker) (map[string]any, error) { +func getExtendsBaseFromFile( + ctx context.Context, + name, ref string, + path, refPath string, + opts *Options, + ct *cycleTracker, +) (map[string]any, PostProcessor, error) { for _, loader := range opts.ResourceLoaders { - if !loader.Accept(path) { + if !loader.Accept(refPath) { continue } - local, err := loader.Load(ctx, path) + local, err := loader.Load(ctx, refPath) if err != nil { - return nil, err + return nil, nil, err } localdir := filepath.Dir(local) - relworkingdir := loader.Dir(path) + relworkingdir := loader.Dir(refPath) extendsOpts := opts.clone() // replace localResourceLoader with a new flavour, using extended file base path extendsOpts.ResourceLoaders = append(opts.RemoteResourceLoaders(), localResourceLoader{ WorkingDir: localdir, }) - extendsOpts.ResolvePaths = true + extendsOpts.ResolvePaths = false // we do relative path resolution after file has been loaded extendsOpts.SkipNormalization = true extendsOpts.SkipConsistencyCheck = true extendsOpts.SkipInclude = true extendsOpts.SkipExtends = true // we manage extends recursively based on raw service definition extendsOpts.SkipValidation = true // we validate the merge result extendsOpts.SkipDefaultValues = true - source, err := loadYamlModel(ctx, types.ConfigDetails{ - WorkingDir: relworkingdir, - ConfigFiles: []types.ConfigFile{ - {Filename: local}, - }, - }, extendsOpts, ct, nil) + source, processor, err := loadYamlFile(ctx, types.ConfigFile{Filename: local}, + extendsOpts, relworkingdir, nil, ct, map[string]any{}, nil) if err != nil { - return nil, err + return nil, nil, err } services := source["services"].(map[string]any) - _, ok := services[name] + _, ok := services[ref] if !ok { - return nil, fmt.Errorf("cannot extend service %q in %s: service not found", name, path) + return nil, nil, fmt.Errorf( + "cannot extend service %q in %s: service %q not found in %s", + name, + path, + ref, + refPath, + ) } - return services, nil + + // Attempt to make a canonical model so ResolveRelativePaths can operate on source:target short syntaxes + source, err = transform.Canonical(source, true) + if err != nil { + return nil, nil, err + } + + var remotes []paths.RemoteResource + for _, loader := range opts.RemoteResourceLoaders() { + remotes = append(remotes, loader.Accept) + } + err = paths.ResolveRelativePaths(source, relworkingdir, remotes) + if err != nil { + return nil, nil, err + } + + return services, processor, nil } - return nil, fmt.Errorf("cannot read %s", path) + return nil, nil, fmt.Errorf("cannot read %s", refPath) } func deepClone(value any) any { diff --git a/vendor/github.com/compose-spec/compose-go/v2/loader/include.go b/vendor/github.com/compose-spec/compose-go/v2/loader/include.go index d40b24ec..924bb654 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/loader/include.go +++ b/vendor/github.com/compose-spec/compose-go/v2/loader/include.go @@ -30,7 +30,7 @@ import ( ) // loadIncludeConfig parse the required config from raw yaml -func loadIncludeConfig(source any) ([]types.IncludeConfig, error) { +func loadIncludeConfig(source any, options *Options) ([]types.IncludeConfig, error) { if source == nil { return nil, nil } @@ -45,21 +45,32 @@ func loadIncludeConfig(source any) ([]types.IncludeConfig, error) { } } } + if options.Interpolate != nil { + for i, config := range configs { + interpolated, err := interp.Interpolate(config.(map[string]any), *options.Interpolate) + if err != nil { + return nil, err + } + configs[i] = interpolated + } + } + var requires []types.IncludeConfig err := Transform(source, &requires) return requires, err } -func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model map[string]any, options *Options, included []string) error { - includeConfig, err := loadIncludeConfig(model["include"]) +func ApplyInclude(ctx context.Context, workingDir string, environment types.Mapping, model map[string]any, options *Options, included []string) error { + includeConfig, err := loadIncludeConfig(model["include"], options) if err != nil { return err } + for _, r := range includeConfig { for _, listener := range options.Listeners { listener("include", map[string]any{ "path": r.Path, - "workingdir": configDetails.WorkingDir, + "workingdir": workingDir, }) } @@ -83,7 +94,7 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model r.ProjectDirectory = filepath.Dir(path) case !filepath.IsAbs(r.ProjectDirectory): relworkingdir = loader.Dir(r.ProjectDirectory) - r.ProjectDirectory = filepath.Join(configDetails.WorkingDir, r.ProjectDirectory) + r.ProjectDirectory = filepath.Join(workingDir, r.ProjectDirectory) default: relworkingdir = r.ProjectDirectory @@ -117,7 +128,7 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model envFile := []string{} for _, f := range r.EnvFile { if !filepath.IsAbs(f) { - f = filepath.Join(configDetails.WorkingDir, f) + f = filepath.Join(workingDir, f) s, err := os.Stat(f) if err != nil { return err @@ -131,7 +142,7 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model r.EnvFile = envFile } - envFromFile, err := dotenv.GetEnvFromFile(configDetails.Environment, r.EnvFile) + envFromFile, err := dotenv.GetEnvFromFile(environment, r.EnvFile) if err != nil { return err } @@ -139,7 +150,7 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model config := types.ConfigDetails{ WorkingDir: relworkingdir, ConfigFiles: types.ToConfigFiles(r.Path), - Environment: configDetails.Environment.Clone().Merge(envFromFile), + Environment: environment.Clone().Merge(envFromFile), } loadOptions.Interpolate = &interp.Options{ Substitute: options.Interpolate.Substitute, diff --git a/vendor/github.com/compose-spec/compose-go/v2/loader/loader.go b/vendor/github.com/compose-spec/compose-go/v2/loader/loader.go index 25565689..fc231598 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/loader/loader.go +++ b/vendor/github.com/compose-spec/compose-go/v2/loader/loader.go @@ -89,7 +89,7 @@ var versionWarning []string func (o *Options) warnObsoleteVersion(file string) { if !slices.Contains(versionWarning, file) { - logrus.Warning(fmt.Sprintf("%s: `version` is obsolete", file)) + logrus.Warning(fmt.Sprintf("%s: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion", file)) } versionWarning = append(versionWarning, file) } @@ -358,100 +358,19 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option dict = map[string]interface{}{} err error ) + workingDir, environment := config.WorkingDir, config.Environment + for _, file := range config.ConfigFiles { - fctx := context.WithValue(ctx, consts.ComposeFileKey{}, file.Filename) - if file.Content == nil && file.Config == nil { - content, err := os.ReadFile(file.Filename) - if err != nil { - return nil, err - } - file.Content = content + dict, _, err = loadYamlFile(ctx, file, opts, workingDir, environment, ct, dict, included) + if err != nil { + return nil, err } + } - processRawYaml := func(raw interface{}, processors ...PostProcessor) error { - converted, err := convertToStringKeysRecursive(raw, "") - if err != nil { - return err - } - cfg, ok := converted.(map[string]interface{}) - if !ok { - return errors.New("Top-level object must be a mapping") - } - - if opts.Interpolate != nil && !opts.SkipInterpolation { - cfg, err = interp.Interpolate(cfg, *opts.Interpolate) - if err != nil { - return err - } - } - - fixEmptyNotNull(cfg) - - if !opts.SkipExtends { - err = ApplyExtends(fctx, cfg, opts, ct, processors...) - if err != nil { - return err - } - } - - for _, processor := range processors { - if err := processor.Apply(dict); err != nil { - return err - } - } - - if !opts.SkipInclude { - included = append(included, config.ConfigFiles[0].Filename) - err = ApplyInclude(ctx, config, cfg, opts, included) - if err != nil { - return err - } - } - - dict, err = override.Merge(dict, cfg) - if err != nil { - return err - } - - dict, err = override.EnforceUnicity(dict) - if err != nil { - return err - } - - if !opts.SkipValidation { - if err := schema.Validate(dict); err != nil { - return fmt.Errorf("validating %s: %w", file.Filename, err) - } - if _, ok := dict["version"]; ok { - opts.warnObsoleteVersion(file.Filename) - delete(dict, "version") - } - } - - return err - } - - if file.Config == nil { - r := bytes.NewReader(file.Content) - decoder := yaml.NewDecoder(r) - for { - var raw interface{} - processor := &ResetProcessor{target: &raw} - err := decoder.Decode(processor) - if err != nil && errors.Is(err, io.EOF) { - break - } - if err != nil { - return nil, err - } - if err := processRawYaml(raw, processor); err != nil { - return nil, err - } - } - } else { - if err := processRawYaml(file.Config); err != nil { - return nil, err - } + if opts.Interpolate != nil && !opts.SkipInterpolation { + dict, err = interp.Interpolate(dict, *opts.Interpolate) + if err != nil { + return nil, err } } @@ -460,7 +379,6 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option return nil, err } - // Canonical transformation can reveal duplicates, typically as ports can be a range and conflict with an override dict, err = override.EnforceUnicity(dict) if err != nil { return nil, err @@ -489,11 +407,98 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option return nil, err } } - resolveServicesEnvironment(dict, config) + ResolveEnvironment(dict, config.Environment) return dict, nil } +func loadYamlFile(ctx context.Context, file types.ConfigFile, opts *Options, workingDir string, environment types.Mapping, ct *cycleTracker, dict map[string]interface{}, included []string) (map[string]interface{}, PostProcessor, error) { + ctx = context.WithValue(ctx, consts.ComposeFileKey{}, file.Filename) + if file.Content == nil && file.Config == nil { + content, err := os.ReadFile(file.Filename) + if err != nil { + return nil, nil, err + } + file.Content = content + } + + processRawYaml := func(raw interface{}, processors ...PostProcessor) error { + converted, err := convertToStringKeysRecursive(raw, "") + if err != nil { + return err + } + cfg, ok := converted.(map[string]interface{}) + if !ok { + return errors.New("Top-level object must be a mapping") + } + + fixEmptyNotNull(cfg) + + if !opts.SkipExtends { + err = ApplyExtends(ctx, cfg, opts, ct, processors...) + if err != nil { + return err + } + } + + for _, processor := range processors { + if err := processor.Apply(dict); err != nil { + return err + } + } + + if !opts.SkipInclude { + included = append(included, file.Filename) + err = ApplyInclude(ctx, workingDir, environment, cfg, opts, included) + if err != nil { + return err + } + } + + dict, err = override.Merge(dict, cfg) + if err != nil { + return err + } + + if !opts.SkipValidation { + if err := schema.Validate(dict); err != nil { + return fmt.Errorf("validating %s: %w", file.Filename, err) + } + if _, ok := dict["version"]; ok { + opts.warnObsoleteVersion(file.Filename) + delete(dict, "version") + } + } + return nil + } + + var processor PostProcessor + if file.Config == nil { + r := bytes.NewReader(file.Content) + decoder := yaml.NewDecoder(r) + for { + var raw interface{} + reset := &ResetProcessor{target: &raw} + err := decoder.Decode(reset) + if err != nil && errors.Is(err, io.EOF) { + break + } + if err != nil { + return nil, nil, err + } + processor = reset + if err := processRawYaml(raw, processor); err != nil { + return nil, nil, err + } + } + } else { + if err := processRawYaml(file.Config); err != nil { + return nil, nil, err + } + } + return dict, processor, nil +} + func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options, loaded []string) (map[string]interface{}, error) { mainFile := configDetails.ConfigFiles[0].Filename for _, f := range loaded { diff --git a/vendor/github.com/compose-spec/compose-go/v2/loader/normalize.go b/vendor/github.com/compose-spec/compose-go/v2/loader/normalize.go index 5f7c8e85..e99b61b5 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/loader/normalize.go +++ b/vendor/github.com/compose-spec/compose-go/v2/loader/normalize.go @@ -52,14 +52,14 @@ func Normalize(dict map[string]any, env types.Mapping) (map[string]any, error) { } if a, ok := build["args"]; ok { - build["args"], _ = resolve(a, fn) + build["args"], _ = resolve(a, fn, false) } service["build"] = build } if e, ok := service["environment"]; ok { - service["environment"], _ = resolve(e, fn) + service["environment"], _ = resolve(e, fn, true) } var dependsOn map[string]any @@ -178,12 +178,12 @@ func normalizeNetworks(dict map[string]any) { } } -func resolve(a any, fn func(s string) (string, bool)) (any, bool) { +func resolve(a any, fn func(s string) (string, bool), keepEmpty bool) (any, bool) { switch v := a.(type) { case []any: var resolved []any for _, val := range v { - if r, ok := resolve(val, fn); ok { + if r, ok := resolve(val, fn, keepEmpty); ok { resolved = append(resolved, r) } } @@ -197,6 +197,8 @@ func resolve(a any, fn func(s string) (string, bool)) (any, bool) { } if s, ok := fn(key); ok { resolved[key] = s + } else if keepEmpty { + resolved[key] = nil } } return resolved, true @@ -205,6 +207,9 @@ func resolve(a any, fn func(s string) (string, bool)) (any, bool) { if val, ok := fn(v); ok { return fmt.Sprintf("%s=%s", v, val), true } + if keepEmpty { + return v, true + } return "", false } return v, true diff --git a/vendor/github.com/compose-spec/compose-go/v2/paths/resolve.go b/vendor/github.com/compose-spec/compose-go/v2/paths/resolve.go index ecfa0e9b..3bb97f8c 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/paths/resolve.go +++ b/vendor/github.com/compose-spec/compose-go/v2/paths/resolve.go @@ -38,7 +38,7 @@ func ResolveRelativePaths(project map[string]any, base string, remotes []RemoteR "services.*.build.additional_contexts.*": r.absContextPath, "services.*.env_file.*.path": r.absPath, "services.*.extends.file": r.absExtendsPath, - "services.*.develop.watch.*.path": r.absPath, + "services.*.develop.watch.*.path": r.absSymbolicLink, "services.*.volumes.*": r.absVolumeMount, "configs.*.file": r.maybeUnixPath, "secrets.*.file": r.maybeUnixPath, diff --git a/vendor/github.com/compose-spec/compose-go/v2/paths/unix.go b/vendor/github.com/compose-spec/compose-go/v2/paths/unix.go index dc1c68b6..5ab2616e 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/paths/unix.go +++ b/vendor/github.com/compose-spec/compose-go/v2/paths/unix.go @@ -19,6 +19,8 @@ package paths import ( "path" "path/filepath" + + "github.com/compose-spec/compose-go/v2/utils" ) func (r *relativePathsResolver) maybeUnixPath(a any) (any, error) { @@ -38,3 +40,15 @@ func (r *relativePathsResolver) maybeUnixPath(a any) (any, error) { } return p, nil } + +func (r *relativePathsResolver) absSymbolicLink(value any) (any, error) { + abs, err := r.absPath(value) + if err != nil { + return nil, err + } + str, ok := abs.(string) + if !ok { + return abs, nil + } + return utils.ResolveSymbolicLink(str) +} diff --git a/vendor/github.com/compose-spec/compose-go/v2/schema/compose-spec.json b/vendor/github.com/compose-spec/compose-go/v2/schema/compose-spec.json index 33a79e9b..11a733b4 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/schema/compose-spec.json +++ b/vendor/github.com/compose-spec/compose-go/v2/schema/compose-spec.json @@ -13,7 +13,6 @@ "name": { "type": "string", - "pattern": "^[a-z0-9][a-z0-9_-]*$", "description": "define the Compose project name, until user defines one explicitly." }, @@ -94,7 +93,7 @@ "develop": {"$ref": "#/definitions/development"}, "deploy": {"$ref": "#/definitions/deployment"}, "annotations": {"$ref": "#/definitions/list_or_dict"}, - "attach": {"type": "boolean"}, + "attach": {"type": ["boolean", "string"]}, "build": { "oneOf": [ {"type": "string"}, @@ -110,15 +109,15 @@ "labels": {"$ref": "#/definitions/list_or_dict"}, "cache_from": {"type": "array", "items": {"type": "string"}}, "cache_to": {"type": "array", "items": {"type": "string"}}, - "no_cache": {"type": "boolean"}, + "no_cache": {"type": ["boolean", "string"]}, "additional_contexts": {"$ref": "#/definitions/list_or_dict"}, "network": {"type": "string"}, - "pull": {"type": "boolean"}, + "pull": {"type": ["boolean", "string"]}, "target": {"type": "string"}, "shm_size": {"type": ["integer", "string"]}, "extra_hosts": {"$ref": "#/definitions/list_or_dict"}, "isolation": {"type": "string"}, - "privileged": {"type": "boolean"}, + "privileged": {"type": ["boolean", "string"]}, "secrets": {"$ref": "#/definitions/service_config_or_secret"}, "tags": {"type": "array", "items": {"type": "string"}}, "ulimits": {"$ref": "#/definitions/ulimits"}, @@ -148,7 +147,7 @@ "type": "array", "items": {"$ref": "#/definitions/blkio_limit"} }, - "weight": {"type": "integer"}, + "weight": {"type": ["integer", "string"]}, "weight_device": { "type": "array", "items": {"$ref": "#/definitions/blkio_weight"} @@ -156,15 +155,21 @@ }, "additionalProperties": false }, - "cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, - "cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "cap_add": {"type": "array", "items": {"type": "string"}}, + "cap_drop": {"type": "array", "items": {"type": "string"}}, "cgroup": {"type": "string", "enum": ["host", "private"]}, "cgroup_parent": {"type": "string"}, "command": {"$ref": "#/definitions/command"}, "configs": {"$ref": "#/definitions/service_config_or_secret"}, "container_name": {"type": "string"}, - "cpu_count": {"type": "integer", "minimum": 0}, - "cpu_percent": {"type": "integer", "minimum": 0, "maximum": 100}, + "cpu_count": {"oneOf": [ + {"type": "string"}, + {"type": "integer", "minimum": 0} + ]}, + "cpu_percent": {"oneOf": [ + {"type": "string"}, + {"type": "integer", "minimum": 0, "maximum": 100} + ]}, "cpu_shares": {"type": ["number", "string"]}, "cpu_quota": {"type": ["number", "string"]}, "cpu_period": {"type": ["number", "string"]}, @@ -192,8 +197,9 @@ "^[a-zA-Z0-9._-]+$": { "type": "object", "additionalProperties": false, + "patternProperties": {"^x-": {}}, "properties": { - "restart": {"type": "boolean"}, + "restart": {"type": ["boolean", "string"]}, "required": { "type": "boolean", "default": true @@ -210,9 +216,9 @@ ] }, "device_cgroup_rules": {"$ref": "#/definitions/list_of_strings"}, - "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "devices": {"type": "array", "items": {"type": "string"}}, "dns": {"$ref": "#/definitions/string_or_list"}, - "dns_opt": {"type": "array","items": {"type": "string"}, "uniqueItems": true}, + "dns_opt": {"type": "array","items": {"type": "string"}}, "dns_search": {"$ref": "#/definitions/string_or_list"}, "domainname": {"type": "string"}, "entrypoint": {"$ref": "#/definitions/command"}, @@ -224,8 +230,7 @@ "items": { "type": ["string", "number"], "format": "expose" - }, - "uniqueItems": true + } }, "extends": { "oneOf": [ @@ -242,23 +247,22 @@ } ] }, - "external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "external_links": {"type": "array", "items": {"type": "string"}}, "extra_hosts": {"$ref": "#/definitions/list_or_dict"}, "group_add": { "type": "array", "items": { "type": ["string", "number"] - }, - "uniqueItems": true + } }, "healthcheck": {"$ref": "#/definitions/healthcheck"}, "hostname": {"type": "string"}, "image": {"type": "string"}, - "init": {"type": "boolean"}, + "init": {"type": ["boolean", "string"]}, "ipc": {"type": "string"}, "isolation": {"type": "string"}, "labels": {"$ref": "#/definitions/list_or_dict"}, - "links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "links": {"type": "array", "items": {"type": "string"}}, "logging": { "type": "object", @@ -277,7 +281,7 @@ "mac_address": {"type": "string"}, "mem_limit": {"type": ["number", "string"]}, "mem_reservation": {"type": ["string", "integer"]}, - "mem_swappiness": {"type": "integer"}, + "mem_swappiness": {"type": ["integer", "string"]}, "memswap_limit": {"type": ["number", "string"]}, "network_mode": {"type": "string"}, "networks": { @@ -315,8 +319,11 @@ } ] }, - "oom_kill_disable": {"type": "boolean"}, - "oom_score_adj": {"type": "integer", "minimum": -1000, "maximum": 1000}, + "oom_kill_disable": {"type": ["boolean", "string"]}, + "oom_score_adj": {"oneOf": [ + {"type": "string"}, + {"type": "integer", "minimum": -1000, "maximum": 1000} + ]}, "pid": {"type": ["string", "null"]}, "pids_limit": {"type": ["number", "string"]}, "platform": {"type": "string"}, @@ -324,15 +331,15 @@ "type": "array", "items": { "oneOf": [ - {"type": "number", "format": "ports"}, - {"type": "string", "format": "ports"}, + {"type": "number"}, + {"type": "string"}, { "type": "object", "properties": { "name": {"type": "string"}, "mode": {"type": "string"}, "host_ip": {"type": "string"}, - "target": {"type": "integer"}, + "target": {"type": ["integer", "string"]}, "published": {"type": ["string", "integer"]}, "protocol": {"type": "string"}, "app_protocol": {"type": "string"} @@ -341,32 +348,31 @@ "patternProperties": {"^x-": {}} } ] - }, - "uniqueItems": true + } }, - "privileged": {"type": "boolean"}, + "privileged": {"type": ["boolean", "string"]}, "profiles": {"$ref": "#/definitions/list_of_strings"}, "pull_policy": {"type": "string", "enum": [ "always", "never", "if_not_present", "build", "missing" ]}, - "read_only": {"type": "boolean"}, + "read_only": {"type": ["boolean", "string"]}, "restart": {"type": "string"}, "runtime": { "type": "string" }, "scale": { - "type": "integer" + "type": ["integer", "string"] }, - "security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "security_opt": {"type": "array", "items": {"type": "string"}}, "shm_size": {"type": ["number", "string"]}, "secrets": {"$ref": "#/definitions/service_config_or_secret"}, "sysctls": {"$ref": "#/definitions/list_or_dict"}, - "stdin_open": {"type": "boolean"}, - "stop_grace_period": {"type": "string", "format": "duration"}, + "stdin_open": {"type": ["boolean", "string"]}, + "stop_grace_period": {"type": "string"}, "stop_signal": {"type": "string"}, "storage_opt": {"type": "object"}, "tmpfs": {"$ref": "#/definitions/string_or_list"}, - "tty": {"type": "boolean"}, + "tty": {"type": ["boolean", "string"]}, "ulimits": {"$ref": "#/definitions/ulimits"}, "user": {"type": "string"}, "uts": {"type": "string"}, @@ -383,13 +389,13 @@ "type": {"type": "string"}, "source": {"type": "string"}, "target": {"type": "string"}, - "read_only": {"type": "boolean"}, + "read_only": {"type": ["boolean", "string"]}, "consistency": {"type": "string"}, "bind": { "type": "object", "properties": { "propagation": {"type": "string"}, - "create_host_path": {"type": "boolean"}, + "create_host_path": {"type": ["boolean", "string"]}, "selinux": {"type": "string", "enum": ["z", "Z"]} }, "additionalProperties": false, @@ -398,7 +404,7 @@ "volume": { "type": "object", "properties": { - "nocopy": {"type": "boolean"}, + "nocopy": {"type": ["boolean", "string"]}, "subpath": {"type": "string"} }, "additionalProperties": false, @@ -413,7 +419,7 @@ {"type": "string"} ] }, - "mode": {"type": "number"} + "mode": {"type": ["number", "string"]} }, "additionalProperties": false, "patternProperties": {"^x-": {}} @@ -423,13 +429,11 @@ "patternProperties": {"^x-": {}} } ] - }, - "uniqueItems": true + } }, "volumes_from": { "type": "array", - "items": {"type": "string"}, - "uniqueItems": true + "items": {"type": "string"} }, "working_dir": {"type": "string"} }, @@ -441,18 +445,18 @@ "id": "#/definitions/healthcheck", "type": "object", "properties": { - "disable": {"type": "boolean"}, - "interval": {"type": "string", "format": "duration"}, - "retries": {"type": "number"}, + "disable": {"type": ["boolean", "string"]}, + "interval": {"type": "string"}, + "retries": {"type": ["number", "string"]}, "test": { "oneOf": [ {"type": "string"}, {"type": "array", "items": {"type": "string"}} ] }, - "timeout": {"type": "string", "format": "duration"}, - "start_period": {"type": "string", "format": "duration"}, - "start_interval": {"type": "string", "format": "duration"} + "timeout": {"type": "string"}, + "start_period": {"type": "string"}, + "start_interval": {"type": "string"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} @@ -484,16 +488,16 @@ "properties": { "mode": {"type": "string"}, "endpoint_mode": {"type": "string"}, - "replicas": {"type": "integer"}, + "replicas": {"type": ["integer", "string"]}, "labels": {"$ref": "#/definitions/list_or_dict"}, "rollback_config": { "type": "object", "properties": { - "parallelism": {"type": "integer"}, - "delay": {"type": "string", "format": "duration"}, + "parallelism": {"type": ["integer", "string"]}, + "delay": {"type": "string"}, "failure_action": {"type": "string"}, - "monitor": {"type": "string", "format": "duration"}, - "max_failure_ratio": {"type": "number"}, + "monitor": {"type": "string"}, + "max_failure_ratio": {"type": ["number", "string"]}, "order": {"type": "string", "enum": [ "start-first", "stop-first" ]} @@ -504,11 +508,11 @@ "update_config": { "type": "object", "properties": { - "parallelism": {"type": "integer"}, - "delay": {"type": "string", "format": "duration"}, + "parallelism": {"type": ["integer", "string"]}, + "delay": {"type": "string"}, "failure_action": {"type": "string"}, - "monitor": {"type": "string", "format": "duration"}, - "max_failure_ratio": {"type": "number"}, + "monitor": {"type": "string"}, + "max_failure_ratio": {"type": ["number", "string"]}, "order": {"type": "string", "enum": [ "start-first", "stop-first" ]} @@ -524,7 +528,7 @@ "properties": { "cpus": {"type": ["number", "string"]}, "memory": {"type": "string"}, - "pids": {"type": "integer"} + "pids": {"type": ["integer", "string"]} }, "additionalProperties": false, "patternProperties": {"^x-": {}} @@ -548,9 +552,9 @@ "type": "object", "properties": { "condition": {"type": "string"}, - "delay": {"type": "string", "format": "duration"}, - "max_attempts": {"type": "integer"}, - "window": {"type": "string", "format": "duration"} + "delay": {"type": "string"}, + "max_attempts": {"type": ["integer", "string"]}, + "window": {"type": "string"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} @@ -570,7 +574,7 @@ "patternProperties": {"^x-": {}} } }, - "max_replicas_per_node": {"type": "integer"} + "max_replicas_per_node": {"type": ["integer", "string"]} }, "additionalProperties": false, "patternProperties": {"^x-": {}} @@ -590,7 +594,7 @@ "type": "object", "properties": { "kind": {"type": "string"}, - "value": {"type": "number"} + "value": {"type": ["number", "string"]} }, "additionalProperties": false, "patternProperties": {"^x-": {}} @@ -655,7 +659,7 @@ "items": { "type": "object", "properties": { - "subnet": {"type": "string", "format": "subnet_ip_address"}, + "subnet": {"type": "string"}, "ip_range": {"type": "string"}, "gateway": {"type": "string"}, "aux_addresses": { @@ -678,7 +682,7 @@ "patternProperties": {"^x-": {}} }, "external": { - "type": ["boolean", "object"], + "type": ["boolean", "string", "object"], "properties": { "name": { "deprecated": true, @@ -688,9 +692,9 @@ "additionalProperties": false, "patternProperties": {"^x-": {}} }, - "internal": {"type": "boolean"}, - "enable_ipv6": {"type": "boolean"}, - "attachable": {"type": "boolean"}, + "internal": {"type": ["boolean", "string"]}, + "enable_ipv6": {"type": ["boolean", "string"]}, + "attachable": {"type": ["boolean", "string"]}, "labels": {"$ref": "#/definitions/list_or_dict"} }, "additionalProperties": false, @@ -710,7 +714,7 @@ } }, "external": { - "type": ["boolean", "object"], + "type": ["boolean", "string", "object"], "properties": { "name": { "deprecated": true, @@ -734,7 +738,7 @@ "environment": {"type": "string"}, "file": {"type": "string"}, "external": { - "type": ["boolean", "object"], + "type": ["boolean", "string", "object"], "properties": { "name": {"type": "string"} } @@ -762,7 +766,7 @@ "environment": {"type": "string"}, "file": {"type": "string"}, "external": { - "type": ["boolean", "object"], + "type": ["boolean", "string", "object"], "properties": { "name": { "deprecated": true, @@ -801,7 +805,7 @@ "type": "string" }, "required": { - "type": "boolean", + "type": ["boolean", "string"], "default": true } }, @@ -824,8 +828,7 @@ "list_of_strings": { "type": "array", - "items": {"type": "string"}, - "uniqueItems": true + "items": {"type": "string"} }, "list_or_dict": { @@ -839,7 +842,7 @@ }, "additionalProperties": false }, - {"type": "array", "items": {"type": "string"}, "uniqueItems": true} + {"type": "array", "items": {"type": "string"}} ] }, @@ -855,7 +858,7 @@ "type": "object", "properties": { "path": {"type": "string"}, - "weight": {"type": "integer"} + "weight": {"type": ["integer", "string"]} }, "additionalProperties": false }, @@ -871,7 +874,7 @@ "target": {"type": "string"}, "uid": {"type": "string"}, "gid": {"type": "string"}, - "mode": {"type": "number"} + "mode": {"type": ["number", "string"]} }, "additionalProperties": false, "patternProperties": {"^x-": {}} @@ -884,12 +887,12 @@ "patternProperties": { "^[a-z]+$": { "oneOf": [ - {"type": "integer"}, + {"type": ["integer", "string"]}, { "type": "object", "properties": { - "hard": {"type": "integer"}, - "soft": {"type": "integer"} + "hard": {"type": ["integer", "string"]}, + "soft": {"type": ["integer", "string"]} }, "required": ["soft", "hard"], "additionalProperties": false, diff --git a/vendor/github.com/compose-spec/compose-go/v2/schema/using-variables.yaml b/vendor/github.com/compose-spec/compose-go/v2/schema/using-variables.yaml new file mode 100644 index 00000000..3f302cd6 --- /dev/null +++ b/vendor/github.com/compose-spec/compose-go/v2/schema/using-variables.yaml @@ -0,0 +1,123 @@ +name: ${VARIABLE} +services: + foo: + deploy: + mode: ${VARIABLE} + replicas: ${VARIABLE} + rollback_config: + parallelism: ${VARIABLE} + delay: ${VARIABLE} + failure_action: ${VARIABLE} + monitor: ${VARIABLE} + max_failure_ratio: ${VARIABLE} + update_config: + parallelism: ${VARIABLE} + delay: ${VARIABLE} + failure_action: ${VARIABLE} + monitor: ${VARIABLE} + max_failure_ratio: ${VARIABLE} + resources: + limits: + memory: ${VARIABLE} + reservations: + memory: ${VARIABLE} + generic_resources: + - discrete_resource_spec: + kind: ${VARIABLE} + value: ${VARIABLE} + - discrete_resource_spec: + kind: ${VARIABLE} + value: ${VARIABLE} + restart_policy: + condition: ${VARIABLE} + delay: ${VARIABLE} + max_attempts: ${VARIABLE} + window: ${VARIABLE} + placement: + max_replicas_per_node: ${VARIABLE} + preferences: + - spread: ${VARIABLE} + endpoint_mode: ${VARIABLE} + expose: + - ${VARIABLE} + external_links: + - ${VARIABLE} + extra_hosts: + - ${VARIABLE} + hostname: ${VARIABLE} + + healthcheck: + test: ${VARIABLE} + interval: ${VARIABLE} + timeout: ${VARIABLE} + retries: ${VARIABLE} + start_period: ${VARIABLE} + start_interval: ${VARIABLE} + image: ${VARIABLE} + mac_address: ${VARIABLE} + networks: + some-network: + aliases: + - ${VARIABLE} + other-network: + ipv4_address: ${VARIABLE} + ipv6_address: ${VARIABLE} + mac_address: ${VARIABLE} + ports: + - ${VARIABLE} + privileged: ${VARIABLE} + read_only: ${VARIABLE} + restart: ${VARIABLE} + secrets: + - source: ${VARIABLE} + target: ${VARIABLE} + uid: ${VARIABLE} + gid: ${VARIABLE} + mode: ${VARIABLE} + stdin_open: ${VARIABLE} + stop_grace_period: ${VARIABLE} + stop_signal: ${VARIABLE} + storage_opt: + size: ${VARIABLE} + sysctls: + net.core.somaxconn: ${VARIABLE} + tmpfs: + - ${VARIABLE} + tty: ${VARIABLE} + ulimits: + nproc: ${VARIABLE} + nofile: + soft: ${VARIABLE} + hard: ${VARIABLE} + user: ${VARIABLE} + volumes: + - ${VARIABLE}:${VARIABLE} + - type: tmpfs + target: ${VARIABLE} + tmpfs: + size: ${VARIABLE} + +networks: + network: + ipam: + driver: ${VARIABLE} + config: + - subnet: ${VARIABLE} + ip_range: ${VARIABLE} + gateway: ${VARIABLE} + aux_addresses: + host1: ${VARIABLE} + external-network: + external: ${VARIABLE} + +volumes: + external-volume: + external: ${VARIABLE} + +configs: + config1: + external: ${VARIABLE} + +secrets: + secret1: + external: ${VARIABLE} diff --git a/vendor/github.com/compose-spec/compose-go/v2/transform/defaults.go b/vendor/github.com/compose-spec/compose-go/v2/transform/defaults.go index 276b1370..96693c65 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/transform/defaults.go +++ b/vendor/github.com/compose-spec/compose-go/v2/transform/defaults.go @@ -25,6 +25,7 @@ var defaultValues = map[tree.Path]transformFunc{} func init() { defaultValues["services.*.build"] = defaultBuildContext defaultValues["services.*.secrets.*"] = defaultSecretMount + defaultValues["services.*.ports.*"] = portDefaults } // SetDefaultValues transforms a compose model to set default values to missing attributes diff --git a/vendor/github.com/compose-spec/compose-go/v2/transform/ports.go b/vendor/github.com/compose-spec/compose-go/v2/transform/ports.go index 7bb8e724..68e26f3d 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/transform/ports.go +++ b/vendor/github.com/compose-spec/compose-go/v2/transform/ports.go @@ -87,3 +87,18 @@ func encode(v any) (map[string]any, error) { err = decoder.Decode(v) return m, err } + +func portDefaults(data any, _ tree.Path, _ bool) (any, error) { + switch v := data.(type) { + case map[string]any: + if _, ok := v["protocol"]; !ok { + v["protocol"] = "tcp" + } + if _, ok := v["mode"]; !ok { + v["mode"] = "ingress" + } + return v, nil + default: + return data, nil + } +} diff --git a/vendor/github.com/compose-spec/compose-go/v2/types/derived.gen.go b/vendor/github.com/compose-spec/compose-go/v2/types/derived.gen.go index 6c50028a..dd1ea8a6 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/types/derived.gen.go +++ b/vendor/github.com/compose-spec/compose-go/v2/types/derived.gen.go @@ -1341,7 +1341,12 @@ func deriveDeepCopy_21(dst, src *NetworkConfig) { } else { dst.Labels = nil } - dst.EnableIPv6 = src.EnableIPv6 + if src.EnableIPv6 == nil { + dst.EnableIPv6 = nil + } else { + dst.EnableIPv6 = new(bool) + *dst.EnableIPv6 = *src.EnableIPv6 + } if src.Extensions != nil { dst.Extensions = make(map[string]any, len(src.Extensions)) src.Extensions.DeepCopy(dst.Extensions) diff --git a/vendor/github.com/compose-spec/compose-go/v2/types/types.go b/vendor/github.com/compose-spec/compose-go/v2/types/types.go index 56af93e5..b0f97bf9 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/types/types.go +++ b/vendor/github.com/compose-spec/compose-go/v2/types/types.go @@ -782,9 +782,41 @@ type ExtendsConfig struct { // SecretConfig for a secret type SecretConfig FileObjectConfig +// MarshalYAML makes SecretConfig implement yaml.Marshaller +func (s SecretConfig) MarshalYAML() (interface{}, error) { + // secret content is set while loading model. Never marshall it + s.Content = "" + return FileObjectConfig(s), nil +} + +// MarshalJSON makes SecretConfig implement json.Marshaller +func (s SecretConfig) MarshalJSON() ([]byte, error) { + // secret content is set while loading model. Never marshall it + s.Content = "" + return json.Marshal(FileObjectConfig(s)) +} + // ConfigObjConfig is the config for the swarm "Config" object type ConfigObjConfig FileObjectConfig +// MarshalYAML makes ConfigObjConfig implement yaml.Marshaller +func (s ConfigObjConfig) MarshalYAML() (interface{}, error) { + // config content may have been set from environment while loading model. Marshall actual source + if s.Environment != "" { + s.Content = "" + } + return FileObjectConfig(s), nil +} + +// MarshalJSON makes ConfigObjConfig implement json.Marshaller +func (s ConfigObjConfig) MarshalJSON() ([]byte, error) { + // config content may have been set from environment while loading model. Marshall actual source + if s.Environment != "" { + s.Content = "" + } + return json.Marshal(FileObjectConfig(s)) +} + type IncludeConfig struct { Path StringList `yaml:"path,omitempty" json:"path,omitempty"` ProjectDirectory string `yaml:"project_directory,omitempty" json:"project_directory,omitempty"` diff --git a/vendor/github.com/compose-spec/compose-go/v2/utils/pathutils.go b/vendor/github.com/compose-spec/compose-go/v2/utils/pathutils.go new file mode 100644 index 00000000..fd2a635e --- /dev/null +++ b/vendor/github.com/compose-spec/compose-go/v2/utils/pathutils.go @@ -0,0 +1,92 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package utils + +import ( + "os" + "path/filepath" + "strings" +) + +// ResolveSymbolicLink converts the section of an absolute path if it is a +// symbolic link +// +// Parameters: +// - path: an absolute path +// +// Returns: +// - converted path if it has a symbolic link or the same path if there is +// no symbolic link +func ResolveSymbolicLink(path string) (string, error) { + sym, part, err := getSymbolinkLink(path) + if err != nil { + return "", err + } + if sym == "" && part == "" { + // no symbolic link detected + return path, nil + } + return strings.Replace(path, part, sym, 1), nil + +} + +// getSymbolinkLink parses all parts of the path and returns the +// the symbolic link part as well as the correspondent original part +// Parameters: +// - path: an absolute path +// +// Returns: +// - string section of the path that is a symbolic link +// - string correspondent path section of the symbolic link +// - An error +func getSymbolinkLink(path string) (string, string, error) { + parts := strings.Split(path, string(os.PathSeparator)) + + // Reconstruct the path step by step, checking each component + var currentPath string + if filepath.IsAbs(path) { + currentPath = string(os.PathSeparator) + } + + for _, part := range parts { + if part == "" { + continue + } + currentPath = filepath.Join(currentPath, part) + + if isSymLink := isSymbolicLink(currentPath); isSymLink { + // return symbolic link, and correspondent part + target, err := filepath.EvalSymlinks(currentPath) + if err != nil { + return "", "", err + } + return target, currentPath, nil + } + } + return "", "", nil // no symbolic link +} + +// isSymbolicLink validates if the path is a symbolic link +func isSymbolicLink(path string) bool { + info, err := os.Lstat(path) + if err != nil { + return false + } + + // Check if the file mode indicates a symbolic link + return info.Mode()&os.ModeSymlink != 0 +} diff --git a/vendor/github.com/compose-spec/compose-go/v2/utils/stringutils.go b/vendor/github.com/compose-spec/compose-go/v2/utils/stringutils.go index dfabf6c9..fc6b2035 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/utils/stringutils.go +++ b/vendor/github.com/compose-spec/compose-go/v2/utils/stringutils.go @@ -32,8 +32,10 @@ func StringToBool(s string) bool { func GetAsEqualsMap(em []string) map[string]string { m := make(map[string]string) for _, v := range em { - kv := strings.SplitN(v, "=", 2) - m[kv[0]] = kv[1] + key, val, found := strings.Cut(v, "=") + if found { + m[key] = val + } } return m } diff --git a/vendor/modules.txt b/vendor/modules.txt index fd91c2ff..c8136043 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -128,7 +128,7 @@ github.com/cenkalti/backoff/v4 # github.com/cespare/xxhash/v2 v2.2.0 ## explicit; go 1.11 github.com/cespare/xxhash/v2 -# github.com/compose-spec/compose-go/v2 v2.1.3 +# github.com/compose-spec/compose-go/v2 v2.1.4 ## explicit; go 1.21 github.com/compose-spec/compose-go/v2/cli github.com/compose-spec/compose-go/v2/consts