diff --git a/go.mod b/go.mod index c158d9e0400..2133fd242c4 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.4.5 + github.com/compose-spec/compose-go/v2 v2.4.6 github.com/containerd/console v1.0.4 github.com/containerd/containerd v1.7.24 github.com/containerd/continuity v0.4.5 diff --git a/go.sum b/go.sum index be61c724eb2..ba8ef454d11 100644 --- a/go.sum +++ b/go.sum @@ -83,8 +83,8 @@ github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnTh github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= 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.4.5 h1:p4ih4Jb6VgGPLPxh3fSFVKAjFHtZd+7HVLCSFzcFx9Y= -github.com/compose-spec/compose-go/v2 v2.4.5/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc= +github.com/compose-spec/compose-go/v2 v2.4.6 h1:QiqXQ2L/f0OCbAl41bPpeiGAWVRIQ+GEDrYxO+dRPhQ= +github.com/compose-spec/compose-go/v2 v2.4.6/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0= github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0= diff --git a/vendor/github.com/compose-spec/compose-go/v2/loader/example1.label b/vendor/github.com/compose-spec/compose-go/v2/loader/example1.label new file mode 100644 index 00000000000..27d43cffac1 --- /dev/null +++ b/vendor/github.com/compose-spec/compose-go/v2/loader/example1.label @@ -0,0 +1,10 @@ +# passed through +FOO=foo_from_label_file +LABEL.WITH.DOT=ok +LABEL_WITH_UNDERSCORE=ok + +# overridden in example2.label +BAR=bar_from_label_file + +# overridden in full-example.yml +BAZ=baz_from_label_file diff --git a/vendor/github.com/compose-spec/compose-go/v2/loader/example2.label b/vendor/github.com/compose-spec/compose-go/v2/loader/example2.label new file mode 100644 index 00000000000..aa667c3047f --- /dev/null +++ b/vendor/github.com/compose-spec/compose-go/v2/loader/example2.label @@ -0,0 +1,4 @@ +BAR=bar_from_label_file_2 + +# overridden in configDetails.Labels +QUX=quz_from_label_file_2 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 c48b1e4b979..2a127d2c4d1 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 @@ -27,6 +27,10 @@ import ( "github.com/compose-spec/compose-go/v2/types" ) +// as we use another service definition by `extends`, we must exclude attributes which creates dependency to another service +// see https://github.com/compose-spec/compose-spec/blob/main/05-services.md#restrictions +var exclusions = []string{"extends", "depends_on", "volumes_from"} + func ApplyExtends(ctx context.Context, dict map[string]any, opts *Options, tracker *cycleTracker, post ...PostProcessor) error { a, ok := dict["services"] if !ok { @@ -123,7 +127,9 @@ func applyServiceExtends(ctx context.Context, name string, services map[string]a if err != nil { return nil, err } - delete(merged, "extends") + for _, exclusion := range exclusions { + delete(merged, exclusion) + } services[name] = merged return merged, nil } diff --git a/vendor/github.com/compose-spec/compose-go/v2/loader/full-example.yml b/vendor/github.com/compose-spec/compose-go/v2/loader/full-example.yml index 2f7b8c43c8e..944b2d47ff6 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/loader/full-example.yml +++ b/vendor/github.com/compose-spec/compose-go/v2/loader/full-example.yml @@ -210,6 +210,10 @@ services: # - "com.example.number=42" # - "com.example.empty-label" + label_file: + - ./example1.label + - ./example2.label + links: - db - db:database 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 8fb95088478..612b91caff4 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 @@ -637,6 +637,12 @@ func modelToProject(dict map[string]interface{}, opts *Options, configDetails ty return nil, err } } + + project, err = project.WithServicesLabelsResolved(opts.discardEnvFiles) + if err != nil { + return nil, err + } + return project, nil } diff --git a/vendor/github.com/compose-spec/compose-go/v2/loader/validate.go b/vendor/github.com/compose-spec/compose-go/v2/loader/validate.go index cfaf139faf4..46cdda7913e 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/loader/validate.go +++ b/vendor/github.com/compose-spec/compose-go/v2/loader/validate.go @@ -155,7 +155,7 @@ func checkConsistency(project *types.Project) error { if s.Develop != nil && s.Develop.Watch != nil { for _, watch := range s.Develop.Watch { - if watch.Action != types.WatchActionRebuild && watch.Target == "" { + if watch.Target == "" && watch.Action != types.WatchActionRebuild && watch.Action != types.WatchActionRestart { return fmt.Errorf("services.%s.develop.watch: target is required for non-rebuild actions: %w", s.Name, errdefs.ErrInvalid) } } diff --git a/vendor/github.com/compose-spec/compose-go/v2/override/merge.go b/vendor/github.com/compose-spec/compose-go/v2/override/merge.go index 697dbc74d41..8cb0ed52b6a 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/override/merge.go +++ b/vendor/github.com/compose-spec/compose-go/v2/override/merge.go @@ -57,6 +57,7 @@ func init() { mergeSpecials["services.*.dns_search"] = mergeToSequence mergeSpecials["services.*.entrypoint"] = override mergeSpecials["services.*.env_file"] = mergeToSequence + mergeSpecials["services.*.label_file"] = mergeToSequence mergeSpecials["services.*.environment"] = mergeToSequence mergeSpecials["services.*.extra_hosts"] = mergeExtraHosts mergeSpecials["services.*.healthcheck.test"] = override 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 303f39e208d..8bab0b43237 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 @@ -37,6 +37,7 @@ func ResolveRelativePaths(project map[string]any, base string, remotes []RemoteR "services.*.build.context": r.absContextPath, "services.*.build.additional_contexts.*": r.absContextPath, "services.*.env_file.*.path": r.absPath, + "services.*.label_file.*": r.absPath, "services.*.extends.file": r.absExtendsPath, "services.*.develop.watch.*.path": r.absSymbolicLink, "services.*.volumes.*": r.absVolumeMount, 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 b95a1498501..f95a7b9db59 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 @@ -240,6 +240,7 @@ "domainname": {"type": "string"}, "entrypoint": {"$ref": "#/definitions/command"}, "env_file": {"$ref": "#/definitions/env_file"}, + "label_file": {"$ref": "#/definitions/label_file"}, "environment": {"$ref": "#/definitions/list_or_dict"}, "expose": { @@ -499,8 +500,9 @@ "properties": { "ignore": {"type": "array", "items": {"type": "string"}}, "path": {"type": "string"}, - "action": {"type": "string", "enum": ["rebuild", "sync", "sync+restart"]}, - "target": {"type": "string"} + "action": {"type": "string", "enum": ["rebuild", "sync", "restart", "sync+restart", "sync+exec"]}, + "target": {"type": "string"}, + "exec": {"$ref": "#/definitions/service_hook"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} @@ -884,6 +886,16 @@ ] }, + "label_file": { + "oneOf": [ + {"type": "string"}, + { + "type": "array", + "items": {"type": "string"} + } + ] + }, + "string_or_list": { "oneOf": [ {"type": "string"}, @@ -918,9 +930,19 @@ "type": "object", "patternProperties": { ".+": { - "type": ["string", "array"] - }, - "uniqueItems": false + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": false + } + ] + } }, "additionalProperties": false }, 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 d33ae280555..f4f2db56641 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 @@ -485,6 +485,24 @@ func deriveDeepCopyService(dst, src *ServiceConfig) { } else { dst.Labels = nil } + if src.LabelFiles == nil { + dst.LabelFiles = nil + } else { + if dst.LabelFiles != nil { + if len(src.LabelFiles) > len(dst.LabelFiles) { + if cap(dst.LabelFiles) >= len(src.LabelFiles) { + dst.LabelFiles = (dst.LabelFiles)[:len(src.LabelFiles)] + } else { + dst.LabelFiles = make([]string, len(src.LabelFiles)) + } + } else if len(src.LabelFiles) < len(dst.LabelFiles) { + dst.LabelFiles = (dst.LabelFiles)[:len(src.LabelFiles)] + } + } else { + dst.LabelFiles = make([]string, len(src.LabelFiles)) + } + copy(dst.LabelFiles, src.LabelFiles) + } if src.CustomLabels != nil { dst.CustomLabels = make(map[string]string, len(src.CustomLabels)) deriveDeepCopy_4(dst.CustomLabels, src.CustomLabels) @@ -1428,6 +1446,12 @@ func deriveDeepCopy_24(dst, src *NetworkConfig) { } else { dst.Labels = nil } + if src.CustomLabels != nil { + dst.CustomLabels = make(map[string]string, len(src.CustomLabels)) + deriveDeepCopy_4(dst.CustomLabels, src.CustomLabels) + } else { + dst.CustomLabels = nil + } if src.EnableIPv6 == nil { dst.EnableIPv6 = nil } else { @@ -1459,6 +1483,12 @@ func deriveDeepCopy_25(dst, src *VolumeConfig) { } else { dst.Labels = nil } + if src.CustomLabels != nil { + dst.CustomLabels = make(map[string]string, len(src.CustomLabels)) + deriveDeepCopy_4(dst.CustomLabels, src.CustomLabels) + } else { + dst.CustomLabels = nil + } if src.Extensions != nil { dst.Extensions = make(map[string]any, len(src.Extensions)) src.Extensions.DeepCopy(dst.Extensions) @@ -1473,6 +1503,7 @@ func deriveDeepCopy_26(dst, src *SecretConfig) { dst.File = src.File dst.Environment = src.Environment dst.Content = src.Content + dst.marshallContent = src.marshallContent dst.External = src.External if src.Labels != nil { dst.Labels = make(map[string]string, len(src.Labels)) @@ -1502,6 +1533,7 @@ func deriveDeepCopy_27(dst, src *ConfigObjConfig) { dst.File = src.File dst.Environment = src.Environment dst.Content = src.Content + dst.marshallContent = src.marshallContent dst.External = src.External if src.Labels != nil { dst.Labels = make(map[string]string, len(src.Labels)) @@ -1981,6 +2013,11 @@ func deriveDeepCopy_46(dst, src *Trigger) { dst.Path = src.Path dst.Action = src.Action dst.Target = src.Target + func() { + field := new(ServiceHook) + deriveDeepCopy_44(field, &src.Exec) + dst.Exec = *field + }() if src.Ignore == nil { dst.Ignore = nil } else { diff --git a/vendor/github.com/compose-spec/compose-go/v2/types/develop.go b/vendor/github.com/compose-spec/compose-go/v2/types/develop.go index 24e142c3aeb..8f7c8fa553b 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/types/develop.go +++ b/vendor/github.com/compose-spec/compose-go/v2/types/develop.go @@ -27,13 +27,16 @@ type WatchAction string const ( WatchActionSync WatchAction = "sync" WatchActionRebuild WatchAction = "rebuild" + WatchActionRestart WatchAction = "restart" WatchActionSyncRestart WatchAction = "sync+restart" + WatchActionSyncExec WatchAction = "sync+exec" ) type Trigger struct { Path string `yaml:"path" json:"path"` Action WatchAction `yaml:"action" json:"action"` Target string `yaml:"target,omitempty" json:"target,omitempty"` + Exec ServiceHook `yaml:"exec,omitempty" json:"exec,omitempty"` Ignore []string `yaml:"ignore,omitempty" json:"ignore,omitempty"` Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"` } diff --git a/vendor/github.com/compose-spec/compose-go/v2/types/labels.go b/vendor/github.com/compose-spec/compose-go/v2/types/labels.go index 000476bf695..713c28f945b 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/types/labels.go +++ b/vendor/github.com/compose-spec/compose-go/v2/types/labels.go @@ -24,6 +24,16 @@ import ( // Labels is a mapping type for labels type Labels map[string]string +func NewLabelsFromMappingWithEquals(mapping MappingWithEquals) Labels { + labels := Labels{} + for k, v := range mapping { + if v != nil { + labels[k] = *v + } + } + return labels +} + func (l Labels) Add(key, value string) Labels { if l == nil { l = Labels{} @@ -42,6 +52,15 @@ func (l Labels) AsList() []string { return s } +func (l Labels) ToMappingWithEquals() MappingWithEquals { + mapping := MappingWithEquals{} + for k, v := range l { + v := v + mapping[k] = &v + } + return mapping +} + // label value can be a string | number | boolean | null (empty) func labelValue(e interface{}) string { if e == nil { diff --git a/vendor/github.com/compose-spec/compose-go/v2/types/mapping.go b/vendor/github.com/compose-spec/compose-go/v2/types/mapping.go index de6fb1233df..63f6e58b896 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/types/mapping.go +++ b/vendor/github.com/compose-spec/compose-go/v2/types/mapping.go @@ -72,6 +72,16 @@ func (m MappingWithEquals) RemoveEmpty() MappingWithEquals { return m } +func (m MappingWithEquals) ToMapping() Mapping { + o := Mapping{} + for k, v := range m { + if v != nil { + o[k] = *v + } + } + return o +} + func (m *MappingWithEquals) DecodeMapstructure(value interface{}) error { switch v := value.(type) { case map[string]interface{}: diff --git a/vendor/github.com/compose-spec/compose-go/v2/types/project.go b/vendor/github.com/compose-spec/compose-go/v2/types/project.go index fa4acc7c044..d8005c0ad78 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/types/project.go +++ b/vendor/github.com/compose-spec/compose-go/v2/types/project.go @@ -663,6 +663,44 @@ func (p Project) WithServicesEnvironmentResolved(discardEnvFiles bool) (*Project return newProject, nil } +// WithServicesLabelsResolved parses label_files set for services to resolve the actual label map for services +// It returns a new Project instance with the changes and keep the original Project unchanged +func (p Project) WithServicesLabelsResolved(discardLabelFiles bool) (*Project, error) { + newProject := p.deepCopy() + for i, service := range newProject.Services { + labels := MappingWithEquals{} + // resolve variables based on other files we already parsed + var resolve dotenv.LookupFn = func(s string) (string, bool) { + v, ok := labels[s] + if ok && v != nil { + return *v, ok + } + return "", false + } + + for _, labelFile := range service.LabelFiles { + vars, err := loadLabelFile(labelFile, resolve) + if err != nil { + return nil, err + } + labels.OverrideBy(vars.ToMappingWithEquals()) + } + + labels = labels.OverrideBy(service.Labels.ToMappingWithEquals()) + if len(labels) == 0 { + labels = nil + } else { + service.Labels = NewLabelsFromMappingWithEquals(labels) + } + + if discardLabelFiles { + service.LabelFiles = nil + } + newProject.Services[i] = service + } + return newProject, nil +} + func loadEnvFile(envFile EnvFile, resolve dotenv.LookupFn) (Mapping, error) { if _, err := os.Stat(envFile.Path); os.IsNotExist(err) { if envFile.Required { @@ -670,15 +708,28 @@ func loadEnvFile(envFile EnvFile, resolve dotenv.LookupFn) (Mapping, error) { } return nil, nil } - file, err := os.Open(envFile.Path) + + return loadMappingFile(envFile.Path, envFile.Format, resolve) +} + +func loadLabelFile(labelFile string, resolve dotenv.LookupFn) (Mapping, error) { + if _, err := os.Stat(labelFile); os.IsNotExist(err) { + return nil, fmt.Errorf("label file %s not found: %w", labelFile, err) + } + + return loadMappingFile(labelFile, "", resolve) +} + +func loadMappingFile(path string, format string, resolve dotenv.LookupFn) (Mapping, error) { + file, err := os.Open(path) if err != nil { return nil, err } defer file.Close() //nolint:errcheck var fileVars map[string]string - if envFile.Format != "" { - fileVars, err = dotenv.ParseWithFormat(file, envFile.Path, resolve, envFile.Format) + if format != "" { + fileVars, err = dotenv.ParseWithFormat(file, path, resolve, format) } else { fileVars, err = dotenv.ParseWithLookup(file, resolve) } 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 3774945083d..5da8b853e63 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 @@ -89,6 +89,7 @@ type ServiceConfig struct { Ipc string `yaml:"ipc,omitempty" json:"ipc,omitempty"` Isolation string `yaml:"isolation,omitempty" json:"isolation,omitempty"` Labels Labels `yaml:"labels,omitempty" json:"labels,omitempty"` + LabelFiles []string `yaml:"label_file,omitempty" json:"label_file,omitempty"` CustomLabels Labels `yaml:"-" json:"-"` Links []string `yaml:"links,omitempty" json:"links,omitempty"` Logging *LoggingConfig `yaml:"logging,omitempty" json:"logging,omitempty"` diff --git a/vendor/modules.txt b/vendor/modules.txt index cd9cd1846cf..0fa3b4784de 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -128,7 +128,7 @@ github.com/cenkalti/backoff/v4 # github.com/cespare/xxhash/v2 v2.3.0 ## explicit; go 1.11 github.com/cespare/xxhash/v2 -# github.com/compose-spec/compose-go/v2 v2.4.5 +# github.com/compose-spec/compose-go/v2 v2.4.6 ## explicit; go 1.21 github.com/compose-spec/compose-go/v2/cli github.com/compose-spec/compose-go/v2/consts