From 5121d729c0f813f471d44ea3f9a5a93272c4393f Mon Sep 17 00:00:00 2001 From: Nick Santos Date: Wed, 29 Nov 2023 11:58:49 -0500 Subject: [PATCH] vendor: upgrade compose-spec (#6266) fixes https://github.com/tilt-dev/tilt/issues/6249 Signed-off-by: Nick Santos --- go.mod | 2 +- go.sum | 4 +- .../compose-spec/compose-go/cli/options.go | 8 + .../compose-spec/compose-go/dotenv/parser.go | 6 +- .../compose-go/loader/full-example.yml | 1 + .../compose-spec/compose-go/loader/include.go | 31 ++- .../compose-go/loader/interpolate.go | 15 -- .../compose-spec/compose-go/loader/loader.go | 158 +++-------- .../compose-go/loader/mapstructure.go | 53 ++++ .../compose-spec/compose-go/loader/merge.go | 4 +- .../compose-spec/compose-go/loader/paths.go | 7 + .../compose-go/schema/compose-spec.json | 83 ++++-- .../compose-go/template/template.go | 26 +- .../compose-spec/compose-go/types/bytes.go | 42 +++ .../compose-spec/compose-go/types/command.go | 86 ++++++ .../compose-spec/compose-go/types/develop.go | 36 +++ .../compose-spec/compose-go/types/device.go | 53 ++++ .../compose-spec/compose-go/types/duration.go | 60 +++++ .../compose-go/types/healthcheck.go | 53 ++++ .../compose-spec/compose-go/types/labels.go | 77 ++++++ .../compose-spec/compose-go/types/options.go | 46 ++++ .../compose-go/types/stringOrList.go | 61 +++++ .../compose-spec/compose-go/types/types.go | 254 ++++-------------- vendor/modules.txt | 2 +- 24 files changed, 782 insertions(+), 386 deletions(-) create mode 100644 vendor/github.com/compose-spec/compose-go/loader/mapstructure.go create mode 100644 vendor/github.com/compose-spec/compose-go/types/bytes.go create mode 100644 vendor/github.com/compose-spec/compose-go/types/command.go create mode 100644 vendor/github.com/compose-spec/compose-go/types/develop.go create mode 100644 vendor/github.com/compose-spec/compose-go/types/device.go create mode 100644 vendor/github.com/compose-spec/compose-go/types/duration.go create mode 100644 vendor/github.com/compose-spec/compose-go/types/healthcheck.go create mode 100644 vendor/github.com/compose-spec/compose-go/types/labels.go create mode 100644 vendor/github.com/compose-spec/compose-go/types/options.go create mode 100644 vendor/github.com/compose-spec/compose-go/types/stringOrList.go diff --git a/go.mod b/go.mod index c45f145468..e704baf26d 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/akutz/memconn v0.1.0 github.com/alessio/shellescape v1.4.1 github.com/blang/semver v3.5.1+incompatible - github.com/compose-spec/compose-go v1.18.4 + github.com/compose-spec/compose-go v1.20.2 github.com/davecgh/go-spew v1.1.1 github.com/distribution/reference v0.5.0 github.com/docker/cli v24.0.5+incompatible diff --git a/go.sum b/go.sum index ebb262fefa..cf4e7dab0a 100644 --- a/go.sum +++ b/go.sum @@ -140,8 +140,8 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= -github.com/compose-spec/compose-go v1.18.4 h1:yLYfsc3ATAYZVAJcXyx/V847/JVBmf3pfKfR13mXU4s= -github.com/compose-spec/compose-go v1.18.4/go.mod h1:+MdqXV4RA7wdFsahh/Kb8U0pAJqkg7mr4PM9tFKU8RM= +github.com/compose-spec/compose-go v1.20.2 h1:u/yfZHn4EaHGdidrZycWpxXgFffjYULlTbRfJ51ykjQ= +github.com/compose-spec/compose-go v1.20.2/go.mod h1:+MdqXV4RA7wdFsahh/Kb8U0pAJqkg7mr4PM9tFKU8RM= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= diff --git a/vendor/github.com/compose-spec/compose-go/cli/options.go b/vendor/github.com/compose-spec/compose-go/cli/options.go index 332ea3b5b9..6981ebc10b 100644 --- a/vendor/github.com/compose-spec/compose-go/cli/options.go +++ b/vendor/github.com/compose-spec/compose-go/cli/options.go @@ -328,6 +328,14 @@ func WithResourceLoader(r loader.ResourceLoader) ProjectOptionsFn { } } +// WithoutEnvironmentResolution disable environment resolution +func WithoutEnvironmentResolution(o *ProjectOptions) error { + o.loadOptions = append(o.loadOptions, func(options *loader.Options) { + options.SkipResolveEnvironment = true + }) + return nil +} + // DefaultFileNames defines the Compose file names for auto-discovery (in order of preference) var DefaultFileNames = []string{"compose.yaml", "compose.yml", "docker-compose.yml", "docker-compose.yaml"} diff --git a/vendor/github.com/compose-spec/compose-go/dotenv/parser.go b/vendor/github.com/compose-spec/compose-go/dotenv/parser.go index aec72a8801..11b6d027c9 100644 --- a/vendor/github.com/compose-spec/compose-go/dotenv/parser.go +++ b/vendor/github.com/compose-spec/compose-go/dotenv/parser.go @@ -98,7 +98,11 @@ func (p *parser) locateKeyName(src string) (string, string, bool, error) { var key string var inherited bool // trim "export" and space at beginning - src = strings.TrimLeftFunc(exportRegex.ReplaceAllString(src, ""), isSpace) + if exportRegex.MatchString(src) { + // we use a `strings.trim` to preserve the pointer to the same underlying memory. + // a regexp replace would copy the string. + src = strings.TrimLeftFunc(strings.TrimPrefix(src, "export"), isSpace) + } // locate key name end and validate it in single loop offset := 0 diff --git a/vendor/github.com/compose-spec/compose-go/loader/full-example.yml b/vendor/github.com/compose-spec/compose-go/loader/full-example.yml index 24d954578d..6ef27a9a42 100644 --- a/vendor/github.com/compose-spec/compose-go/loader/full-example.yml +++ b/vendor/github.com/compose-spec/compose-go/loader/full-example.yml @@ -235,6 +235,7 @@ services: other-network: ipv4_address: 172.16.238.10 ipv6_address: 2001:3984:3989::10 + mac_address: 02:42:72:98:65:08 other-other-network: pid: "host" diff --git a/vendor/github.com/compose-spec/compose-go/loader/include.go b/vendor/github.com/compose-spec/compose-go/loader/include.go index ea7841446a..aaebfd3019 100644 --- a/vendor/github.com/compose-spec/compose-go/loader/include.go +++ b/vendor/github.com/compose-spec/compose-go/loader/include.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "path/filepath" + "reflect" "github.com/compose-spec/compose-go/dotenv" interp "github.com/compose-spec/compose-go/interpolation" @@ -109,37 +110,55 @@ func loadInclude(ctx context.Context, filename string, configDetails types.Confi func importResources(model *types.Config, imported *types.Project, path []string) error { services := mapByName(model.Services) for _, service := range imported.Services { - if _, ok := services[service.Name]; ok { + if present, ok := services[service.Name]; ok { + if reflect.DeepEqual(present, service) { + continue + } return fmt.Errorf("imported compose file %s defines conflicting service %s", path, service.Name) } model.Services = append(model.Services, service) } for _, service := range imported.DisabledServices { - if _, ok := services[service.Name]; ok { + if disabled, ok := services[service.Name]; ok { + if reflect.DeepEqual(disabled, service) { + continue + } return fmt.Errorf("imported compose file %s defines conflicting service %s", path, service.Name) } model.Services = append(model.Services, service) } for n, network := range imported.Networks { - if _, ok := model.Networks[n]; ok { + if present, ok := model.Networks[n]; ok { + if reflect.DeepEqual(present, network) { + continue + } return fmt.Errorf("imported compose file %s defines conflicting network %s", path, n) } model.Networks[n] = network } for n, volume := range imported.Volumes { - if _, ok := model.Volumes[n]; ok { + if present, ok := model.Volumes[n]; ok { + if reflect.DeepEqual(present, volume) { + continue + } return fmt.Errorf("imported compose file %s defines conflicting volume %s", path, n) } model.Volumes[n] = volume } for n, secret := range imported.Secrets { - if _, ok := model.Secrets[n]; ok { + if present, ok := model.Secrets[n]; ok { + if reflect.DeepEqual(present, secret) { + continue + } return fmt.Errorf("imported compose file %s defines conflicting secret %s", path, n) } model.Secrets[n] = secret } for n, config := range imported.Configs { - if _, ok := model.Configs[n]; ok { + if present, ok := model.Configs[n]; ok { + if reflect.DeepEqual(present, config) { + continue + } return fmt.Errorf("imported compose file %s defines conflicting config %s", path, n) } model.Configs[n] = config diff --git a/vendor/github.com/compose-spec/compose-go/loader/interpolate.go b/vendor/github.com/compose-spec/compose-go/loader/interpolate.go index aae6dc3a4b..655e58e11f 100644 --- a/vendor/github.com/compose-spec/compose-go/loader/interpolate.go +++ b/vendor/github.com/compose-spec/compose-go/loader/interpolate.go @@ -46,10 +46,6 @@ var interpolateTypeCastMapping = map[tree.Path]interp.Cast{ servicePath("deploy", "placement", "max_replicas_per_node"): toInt, servicePath("healthcheck", "retries"): toInt, servicePath("healthcheck", "disable"): toBoolean, - servicePath("mem_limit"): toUnitBytes, - servicePath("mem_reservation"): toUnitBytes, - servicePath("memswap_limit"): toUnitBytes, - servicePath("mem_swappiness"): toUnitBytes, servicePath("oom_kill_disable"): toBoolean, servicePath("oom_score_adj"): toInt64, servicePath("pids_limit"): toInt64, @@ -58,16 +54,13 @@ var interpolateTypeCastMapping = map[tree.Path]interp.Cast{ servicePath("read_only"): toBoolean, servicePath("scale"): toInt, servicePath("secrets", tree.PathMatchList, "mode"): toInt, - servicePath("shm_size"): toUnitBytes, servicePath("stdin_open"): toBoolean, - servicePath("stop_grace_period"): toDuration, servicePath("tty"): toBoolean, servicePath("ulimits", tree.PathMatchAll): toInt, servicePath("ulimits", tree.PathMatchAll, "hard"): toInt, servicePath("ulimits", tree.PathMatchAll, "soft"): toInt, servicePath("volumes", tree.PathMatchList, "read_only"): toBoolean, servicePath("volumes", tree.PathMatchList, "volume", "nocopy"): toBoolean, - servicePath("volumes", tree.PathMatchList, "tmpfs", "size"): toUnitBytes, iPath("networks", tree.PathMatchAll, "external"): toBoolean, iPath("networks", tree.PathMatchAll, "internal"): toBoolean, iPath("networks", tree.PathMatchAll, "attachable"): toBoolean, @@ -93,14 +86,6 @@ func toInt64(value string) (interface{}, error) { return strconv.ParseInt(value, 10, 64) } -func toUnitBytes(value string) (interface{}, error) { - return transformSize(value) -} - -func toDuration(value string) (interface{}, error) { - return transformStringToDuration(value) -} - func toFloat(value string) (interface{}, error) { return strconv.ParseFloat(value, 64) } diff --git a/vendor/github.com/compose-spec/compose-go/loader/loader.go b/vendor/github.com/compose-spec/compose-go/loader/loader.go index 205dcd6732..a70004671d 100644 --- a/vendor/github.com/compose-spec/compose-go/loader/loader.go +++ b/vendor/github.com/compose-spec/compose-go/loader/loader.go @@ -28,15 +28,12 @@ import ( "regexp" "strconv" "strings" - "time" "github.com/compose-spec/compose-go/consts" interp "github.com/compose-spec/compose-go/interpolation" "github.com/compose-spec/compose-go/schema" "github.com/compose-spec/compose-go/template" "github.com/compose-spec/compose-go/types" - "github.com/docker/go-units" - "github.com/mattn/go-shellwords" "github.com/mitchellh/mapstructure" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -61,6 +58,8 @@ type Options struct { SkipExtends bool // SkipInclude will ignore `include` and only load model from file(s) set by ConfigDetails SkipInclude bool + // SkipResolveEnvironment will ignore computing `environment` for services + SkipResolveEnvironment bool // Interpolation options Interpolate *interp.Options // Discard 'env_file' entries after resolving to 'environment' section @@ -239,6 +238,15 @@ func LoadWithContext(ctx context.Context, configDetails types.ConfigDetails, opt return nil, err } opts.projectName = projectName + + // TODO(milas): this should probably ALWAYS set (overriding any existing) + if _, ok := configDetails.Environment[consts.ComposeProjectName]; !ok && projectName != "" { + if configDetails.Environment == nil { + configDetails.Environment = map[string]string{} + } + configDetails.Environment[consts.ComposeProjectName] = projectName + } + return load(ctx, configDetails, opts, nil) } @@ -255,7 +263,6 @@ func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options, loaded = append(loaded, mainFile) includeRefs := make(map[string][]types.IncludeConfig) - first := true for _, file := range configDetails.ConfigFiles { var postProcessor PostProcessor configDict := file.Config @@ -285,22 +292,21 @@ func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options, } } - if first { - first = false + if model == nil { model = cfg - return nil - } - merged, err := merge([]*types.Config{model, cfg}) - if err != nil { - return err + } else { + merged, err := merge([]*types.Config{model, cfg}) + if err != nil { + return err + } + model = merged } if postProcessor != nil { - err = postProcessor.Apply(merged) + err = postProcessor.Apply(model) if err != nil { return err } } - model = merged return nil } @@ -337,6 +343,10 @@ func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options, } } + if model == nil { + return nil, errors.New("empty compose file") + } + project := &types.Project{ Name: opts.projectName, WorkingDir: configDetails.WorkingDir, @@ -385,9 +395,14 @@ func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options, project.ApplyProfiles(opts.Profiles) - err := project.ResolveServicesEnvironment(opts.discardEnvFiles) + if !opts.SkipResolveEnvironment { + err := project.ResolveServicesEnvironment(opts.discardEnvFiles) + if err != nil { + return nil, err + } + } - return project, err + return project, nil } func InvalidProjectNameErr(v string) error { @@ -455,10 +470,6 @@ func projectName(details types.ConfigDetails, opts *Options) (string, error) { return "", InvalidProjectNameErr(projectName) } - // TODO(milas): this should probably ALWAYS set (overriding any existing) - if _, ok := details.Environment[consts.ComposeProjectName]; !ok && projectName != "" { - details.Environment[consts.ComposeProjectName] = projectName - } return projectName, nil } @@ -577,7 +588,7 @@ func Transform(source interface{}, target interface{}, additionalTransformers .. config := &mapstructure.DecoderConfig{ DecodeHook: mapstructure.ComposeDecodeHookFunc( createTransformHook(additionalTransformers...), - mapstructure.StringToTimeDurationHookFunc()), + decoderHook), Result: target, TagName: "yaml", Metadata: &data, @@ -601,28 +612,20 @@ type Transformer struct { func createTransformHook(additionalTransformers ...Transformer) mapstructure.DecodeHookFuncType { transforms := map[reflect.Type]func(interface{}) (interface{}, error){ reflect.TypeOf(types.External{}): transformExternal, - reflect.TypeOf(types.HealthCheckTest{}): transformHealthCheckTest, - reflect.TypeOf(types.ShellCommand{}): transformShellCommand, - reflect.TypeOf(types.StringList{}): transformStringList, - reflect.TypeOf(map[string]string{}): transformMapStringString, + reflect.TypeOf(types.Options{}): transformOptions, reflect.TypeOf(types.UlimitsConfig{}): transformUlimits, - reflect.TypeOf(types.UnitBytes(0)): transformSize, reflect.TypeOf([]types.ServicePortConfig{}): transformServicePort, reflect.TypeOf(types.ServiceSecretConfig{}): transformFileReferenceConfig, reflect.TypeOf(types.ServiceConfigObjConfig{}): transformFileReferenceConfig, - reflect.TypeOf(types.StringOrNumberList{}): transformStringOrNumberList, reflect.TypeOf(map[string]*types.ServiceNetworkConfig{}): transformServiceNetworkMap, reflect.TypeOf(types.Mapping{}): transformMappingOrListFunc("=", false), reflect.TypeOf(types.MappingWithEquals{}): transformMappingOrListFunc("=", true), - reflect.TypeOf(types.Labels{}): transformMappingOrListFunc("=", false), reflect.TypeOf(types.MappingWithColon{}): transformMappingOrListFunc(":", false), reflect.TypeOf(types.HostsList{}): transformMappingOrListFunc(":", false), reflect.TypeOf(types.ServiceVolumeConfig{}): transformServiceVolumeConfig, reflect.TypeOf(types.BuildConfig{}): transformBuildConfig, - reflect.TypeOf(types.Duration(0)): transformStringToDuration, reflect.TypeOf(types.DependsOnConfig{}): transformDependsOnConfig, reflect.TypeOf(types.ExtendsConfig{}): transformExtendsConfig, - reflect.TypeOf(types.DeviceRequest{}): transformServiceDeviceRequest, reflect.TypeOf(types.SSHConfig{}): transformSSHConfig, reflect.TypeOf(types.IncludeConfig{}): transformIncludeConfig, } @@ -1022,7 +1025,7 @@ func loadFileObjectConfig(name string, objType string, obj types.FileObjectConfi return obj, nil } -var transformMapStringString TransformerFunc = func(data interface{}) (interface{}, error) { +var transformOptions TransformerFunc = func(data interface{}) (interface{}, error) { switch value := data.(type) { case map[string]interface{}: return toMapStringString(value, false), nil @@ -1085,35 +1088,6 @@ var transformServicePort TransformerFunc = func(data interface{}) (interface{}, } } -var transformServiceDeviceRequest TransformerFunc = func(data interface{}) (interface{}, error) { - switch value := data.(type) { - case map[string]interface{}: - count, ok := value["count"] - if ok { - switch val := count.(type) { - case int: - return value, nil - case string: - if strings.ToLower(val) == "all" { - value["count"] = -1 - return value, nil - } - i, err := strconv.ParseInt(val, 10, 64) - if err == nil { - value["count"] = i - return value, nil - } - return data, errors.Errorf("invalid string value for 'count' (the only value allowed is 'all' or a number)") - default: - return data, errors.Errorf("invalid type %T for device count", val) - } - } - return data, nil - default: - return data, errors.Errorf("invalid type %T for resource reservation", value) - } -} - var transformFileReferenceConfig TransformerFunc = func(data interface{}) (interface{}, error) { switch value := data.(type) { case string: @@ -1249,26 +1223,6 @@ func ParseShortSSHSyntax(value string) ([]types.SSHKey, error) { return result, nil } -var transformStringOrNumberList TransformerFunc = func(value interface{}) (interface{}, error) { - list := value.([]interface{}) - result := make([]string, len(list)) - for i, item := range list { - result[i] = fmt.Sprint(item) - } - return result, nil -} - -var transformStringList TransformerFunc = func(data interface{}) (interface{}, error) { - switch value := data.(type) { - case string: - return []string{value}, nil - case []interface{}: - return value, nil - default: - return data, errors.Errorf("invalid type %T for string list", value) - } -} - func transformMappingOrListFunc(sep string, allowNil bool) TransformerFunc { return func(data interface{}) (interface{}, error) { return transformMappingOrList(data, sep, allowNil) @@ -1303,52 +1257,6 @@ func transformValueToMapEntry(value string, separator string, allowNil bool) (st } } -var transformShellCommand TransformerFunc = func(value interface{}) (interface{}, error) { - if str, ok := value.(string); ok { - return shellwords.Parse(str) - } - return value, nil -} - -var transformHealthCheckTest TransformerFunc = func(data interface{}) (interface{}, error) { - switch value := data.(type) { - case string: - return append([]string{"CMD-SHELL"}, value), nil - case []interface{}: - return value, nil - default: - return value, errors.Errorf("invalid type %T for healthcheck.test", value) - } -} - -var transformSize TransformerFunc = func(value interface{}) (interface{}, error) { - switch value := value.(type) { - case int: - return int64(value), nil - case int64, types.UnitBytes: - return value, nil - case string: - return units.RAMInBytes(value) - default: - return value, errors.Errorf("invalid type for size %T", value) - } -} - -var transformStringToDuration TransformerFunc = func(value interface{}) (interface{}, error) { - switch value := value.(type) { - case string: - d, err := time.ParseDuration(value) - if err != nil { - return value, err - } - return types.Duration(d), nil - case types.Duration: - return value, nil - default: - return value, errors.Errorf("invalid type %T for duration", value) - } -} - func toMapStringString(value map[string]interface{}, allowNil bool) map[string]interface{} { output := make(map[string]interface{}) for key, value := range value { diff --git a/vendor/github.com/compose-spec/compose-go/loader/mapstructure.go b/vendor/github.com/compose-spec/compose-go/loader/mapstructure.go new file mode 100644 index 0000000000..97a2e39c12 --- /dev/null +++ b/vendor/github.com/compose-spec/compose-go/loader/mapstructure.go @@ -0,0 +1,53 @@ +/* + 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 loader + +import "reflect" + +// comparable to yaml.Unmarshaler, decoder allow a type to define it's own custom logic to convert value +// see https://github.com/mitchellh/mapstructure/pull/294 +type decoder interface { + DecodeMapstructure(interface{}) error +} + +// see https://github.com/mitchellh/mapstructure/issues/115#issuecomment-735287466 +// adapted to support types derived from built-in types, as DecodeMapstructure would not be able to mutate internal +// value, so need to invoke DecodeMapstructure defined by pointer to type +func decoderHook(from reflect.Value, to reflect.Value) (interface{}, error) { + // If the destination implements the decoder interface + u, ok := to.Interface().(decoder) + if !ok { + // for non-struct types we need to invoke func (*type) DecodeMapstructure() + if to.CanAddr() { + pto := to.Addr() + u, ok = pto.Interface().(decoder) + } + if !ok { + return from.Interface(), nil + } + } + // If it is nil and a pointer, create and assign the target value first + if to.Type().Kind() == reflect.Ptr && to.IsNil() { + to.Set(reflect.New(to.Type().Elem())) + u = to.Interface().(decoder) + } + // Call the custom DecodeMapstructure method + if err := u.DecodeMapstructure(from.Interface()); err != nil { + return to.Interface(), err + } + return to.Interface(), nil +} diff --git a/vendor/github.com/compose-spec/compose-go/loader/merge.go b/vendor/github.com/compose-spec/compose-go/loader/merge.go index 3c4848e07a..654d711dee 100644 --- a/vendor/github.com/compose-spec/compose-go/loader/merge.go +++ b/vendor/github.com/compose-spec/compose-go/loader/merge.go @@ -320,8 +320,8 @@ func mergeLoggingConfig(dst, src reflect.Value) error { if getLoggingDriver(dst.Elem()) == "" { dst.Elem().FieldByName("Driver").SetString(getLoggingDriver(src.Elem())) } - dstOptions := dst.Elem().FieldByName("Options").Interface().(map[string]string) - srcOptions := src.Elem().FieldByName("Options").Interface().(map[string]string) + dstOptions := dst.Elem().FieldByName("Options").Interface().(types.Options) + srcOptions := src.Elem().FieldByName("Options").Interface().(types.Options) return mergo.Merge(&dstOptions, srcOptions, mergo.WithOverride) } // Different driver, override with src diff --git a/vendor/github.com/compose-spec/compose-go/loader/paths.go b/vendor/github.com/compose-spec/compose-go/loader/paths.go index 61e79f0111..519a6a6900 100644 --- a/vendor/github.com/compose-spec/compose-go/loader/paths.go +++ b/vendor/github.com/compose-spec/compose-go/loader/paths.go @@ -115,6 +115,13 @@ func ResolveServiceRelativePaths(workingDir string, s *types.ServiceConfig) { } s.Volumes[i].Source = resolveMaybeUnixPath(workingDir, vol.Source) } + + if s.Develop != nil { + for i, w := range s.Develop.Watch { + w.Path = absPath(workingDir, w.Path) + s.Develop.Watch[i] = w + } + } } func absPath(workingDir string, filePath string) string { diff --git a/vendor/github.com/compose-spec/compose-go/schema/compose-spec.json b/vendor/github.com/compose-spec/compose-go/schema/compose-spec.json index d39aa35ea1..0469c9e0dd 100644 --- a/vendor/github.com/compose-spec/compose-go/schema/compose-spec.json +++ b/vendor/github.com/compose-spec/compose-go/schema/compose-spec.json @@ -91,6 +91,7 @@ "type": "object", "properties": { + "develop": {"$ref": "#/definitions/development"}, "deploy": {"$ref": "#/definitions/deployment"}, "annotations": {"$ref": "#/definitions/list_or_dict"}, "attach": {"type": "boolean"}, @@ -119,6 +120,7 @@ "privileged": {"type": "boolean"}, "secrets": {"$ref": "#/definitions/service_config_or_secret"}, "tags": {"type": "array", "items": {"type": "string"}}, + "ulimits": {"$ref": "#/definitions/ulimits"}, "platforms": {"type": "array", "items": {"type": "string"}} }, "additionalProperties": false, @@ -292,6 +294,7 @@ "ipv4_address": {"type": "string"}, "ipv6_address": {"type": "string"}, "link_local_ips": {"$ref": "#/definitions/list_of_strings"}, + "mac_address": {"type": "string"}, "priority": {"type": "number"} }, "additionalProperties": false, @@ -355,26 +358,7 @@ "storage_opt": {"type": "object"}, "tmpfs": {"$ref": "#/definitions/string_or_list"}, "tty": {"type": "boolean"}, - "ulimits": { - "type": "object", - "patternProperties": { - "^[a-z]+$": { - "oneOf": [ - {"type": "integer"}, - { - "type": "object", - "properties": { - "hard": {"type": "integer"}, - "soft": {"type": "integer"} - }, - "required": ["soft", "hard"], - "additionalProperties": false, - "patternProperties": {"^x-": {}} - } - ] - } - } - }, + "ulimits": {"$ref": "#/definitions/ulimits"}, "user": {"type": "string"}, "uts": {"type": "string"}, "userns_mode": {"type": "string"}, @@ -463,6 +447,27 @@ "additionalProperties": false, "patternProperties": {"^x-": {}} }, + "development": { + "id": "#/definitions/development", + "type": ["object", "null"], + "properties": { + "watch": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ignore": {"type": "array", "items": {"type": "string"}}, + "path": {"type": "string"}, + "action": {"type": "string", "enum": ["rebuild", "sync", "sync+restart"]}, + "target": {"type": "string"} + } + }, + "required": ["path", "action"], + "additionalProperties": false, + "patternProperties": {"^x-": {}} + } + } + }, "deployment": { "id": "#/definitions/deployment", "type": ["object", "null"], @@ -592,12 +597,12 @@ "items": { "type": "object", "properties": { - "capabilities": {"$ref": "#/definitions/list_of_strings"}, - "count": {"type": ["string", "integer"]}, - "device_ids": {"$ref": "#/definitions/list_of_strings"}, - "driver":{"type": "string"}, - "options":{"$ref": "#/definitions/list_or_dict"} - }, + "capabilities": {"$ref": "#/definitions/list_of_strings"}, + "count": {"type": ["string", "integer"]}, + "device_ids": {"$ref": "#/definitions/list_of_strings"}, + "driver":{"type": "string"}, + "options":{"$ref": "#/definitions/list_or_dict"} + }, "additionalProperties": false, "patternProperties": {"^x-": {}} } @@ -743,6 +748,8 @@ "type": "object", "properties": { "name": {"type": "string"}, + "content": {"type": "string"}, + "environment": {"type": "string"}, "file": {"type": "string"}, "external": { "type": ["boolean", "object"], @@ -812,7 +819,6 @@ }, "additionalProperties": false }, - "service_config_or_secret": { "type": "array", "items": { @@ -833,7 +839,26 @@ ] } }, - + "ulimits": { + "type": "object", + "patternProperties": { + "^[a-z]+$": { + "oneOf": [ + {"type": "integer"}, + { + "type": "object", + "properties": { + "hard": {"type": "integer"}, + "soft": {"type": "integer"} + }, + "required": ["soft", "hard"], + "additionalProperties": false, + "patternProperties": {"^x-": {}} + } + ] + } + } + }, "constraints": { "service": { "id": "#/definitions/constraints/service", @@ -849,4 +874,4 @@ } } } -} +} \ No newline at end of file diff --git a/vendor/github.com/compose-spec/compose-go/template/template.go b/vendor/github.com/compose-spec/compose-go/template/template.go index cce4c62515..9367f39546 100644 --- a/vendor/github.com/compose-spec/compose-go/template/template.go +++ b/vendor/github.com/compose-spec/compose-go/template/template.go @@ -28,12 +28,20 @@ import ( var delimiter = "\\$" var substitutionNamed = "[_a-z][_a-z0-9]*" - var substitutionBraced = "[_a-z][_a-z0-9]*(?::?[-+?](.*))?" +var groupEscaped = "escaped" +var groupNamed = "named" +var groupBraced = "braced" +var groupInvalid = "invalid" + var patternString = fmt.Sprintf( - "%s(?i:(?P%s)|(?P%s)|{(?:(?P%s)}|(?P)))", - delimiter, delimiter, substitutionNamed, substitutionBraced, + "%s(?i:(?P<%s>%s)|(?P<%s>%s)|{(?:(?P<%s>%s)}|(?P<%s>)))", + delimiter, + groupEscaped, delimiter, + groupNamed, substitutionNamed, + groupBraced, substitutionBraced, + groupInvalid, ) var defaultPattern = regexp.MustCompile(patternString) @@ -164,14 +172,14 @@ func DefaultReplacementAppliedFunc(substring string, mapping Mapping, cfg *Confi matches := pattern.FindStringSubmatch(substring) groups := matchGroups(matches, pattern) - if escaped := groups["escaped"]; escaped != "" { + if escaped := groups[groupEscaped]; escaped != "" { return escaped, true, nil } braced := false - substitution := groups["named"] + substitution := groups[groupNamed] if substitution == "" { - substitution = groups["braced"] + substitution = groups[groupBraced] braced = true } @@ -322,12 +330,12 @@ func extractVariable(value interface{}, pattern *regexp.Regexp) ([]Variable, boo values := []Variable{} for _, match := range matches { groups := matchGroups(match, pattern) - if escaped := groups["escaped"]; escaped != "" { + if escaped := groups[groupEscaped]; escaped != "" { continue } - val := groups["named"] + val := groups[groupNamed] if val == "" { - val = groups["braced"] + val = groups[groupBraced] } name := val var defaultValue string diff --git a/vendor/github.com/compose-spec/compose-go/types/bytes.go b/vendor/github.com/compose-spec/compose-go/types/bytes.go new file mode 100644 index 0000000000..4c873cded0 --- /dev/null +++ b/vendor/github.com/compose-spec/compose-go/types/bytes.go @@ -0,0 +1,42 @@ +/* + 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 types + +import ( + "fmt" + + "github.com/docker/go-units" +) + +// UnitBytes is the bytes type +type UnitBytes int64 + +// MarshalYAML makes UnitBytes implement yaml.Marshaller +func (u UnitBytes) MarshalYAML() (interface{}, error) { + return fmt.Sprintf("%d", u), nil +} + +// MarshalJSON makes UnitBytes implement json.Marshaler +func (u UnitBytes) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%d"`, u)), nil +} + +func (u *UnitBytes) DecodeMapstructure(value interface{}) error { + v, err := units.RAMInBytes(fmt.Sprint(value)) + *u = UnitBytes(v) + return err +} diff --git a/vendor/github.com/compose-spec/compose-go/types/command.go b/vendor/github.com/compose-spec/compose-go/types/command.go new file mode 100644 index 0000000000..90d575d8cd --- /dev/null +++ b/vendor/github.com/compose-spec/compose-go/types/command.go @@ -0,0 +1,86 @@ +/* + 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 types + +import "github.com/mattn/go-shellwords" + +// ShellCommand is a string or list of string args. +// +// When marshaled to YAML, nil command fields will be omitted if `omitempty` +// is specified as a struct tag. Explicitly empty commands (i.e. `[]` or +// empty string will serialize to an empty array (`[]`). +// +// When marshaled to JSON, the `omitempty` struct must NOT be specified. +// If the command field is nil, it will be serialized as `null`. +// Explicitly empty commands (i.e. `[]` or empty string) will serialize to +// an empty array (`[]`). +// +// The distinction between nil and explicitly empty is important to distinguish +// between an unset value and a provided, but empty, value, which should be +// preserved so that it can override any base value (e.g. container entrypoint). +// +// The different semantics between YAML and JSON are due to limitations with +// JSON marshaling + `omitempty` in the Go stdlib, while gopkg.in/yaml.v3 gives +// us more flexibility via the yaml.IsZeroer interface. +// +// In the future, it might make sense to make fields of this type be +// `*ShellCommand` to avoid this situation, but that would constitute a +// breaking change. +type ShellCommand []string + +// IsZero returns true if the slice is nil. +// +// Empty (but non-nil) slices are NOT considered zero values. +func (s ShellCommand) IsZero() bool { + // we do NOT want len(s) == 0, ONLY explicitly nil + return s == nil +} + +// MarshalYAML returns nil (which will be serialized as `null`) for nil slices +// and delegates to the standard marshaller behavior otherwise. +// +// NOTE: Typically the nil case here is not hit because IsZero has already +// short-circuited marshalling, but this ensures that the type serializes +// accurately if the `omitempty` struct tag is omitted/forgotten. +// +// A similar MarshalJSON() implementation is not needed because the Go stdlib +// already serializes nil slices to `null`, whereas gopkg.in/yaml.v3 by default +// serializes nil slices to `[]`. +func (s ShellCommand) MarshalYAML() (interface{}, error) { + if s == nil { + return nil, nil + } + return []string(s), nil +} + +func (s *ShellCommand) DecodeMapstructure(value interface{}) error { + switch v := value.(type) { + case string: + cmd, err := shellwords.Parse(v) + if err != nil { + return err + } + *s = cmd + case []interface{}: + cmd := make([]string, len(v)) + for i, s := range v { + cmd[i] = s.(string) + } + *s = cmd + } + return nil +} diff --git a/vendor/github.com/compose-spec/compose-go/types/develop.go b/vendor/github.com/compose-spec/compose-go/types/develop.go new file mode 100644 index 0000000000..5fc10716f6 --- /dev/null +++ b/vendor/github.com/compose-spec/compose-go/types/develop.go @@ -0,0 +1,36 @@ +/* + 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 types + +type DevelopConfig struct { + Watch []Trigger `json:"watch,omitempty"` +} + +type WatchAction string + +const ( + WatchActionSync WatchAction = "sync" + WatchActionRebuild WatchAction = "rebuild" + WatchActionSyncRestart WatchAction = "sync+restart" +) + +type Trigger struct { + Path string `json:"path,omitempty"` + Action WatchAction `json:"action,omitempty"` + Target string `json:"target,omitempty"` + Ignore []string `json:"ignore,omitempty"` +} diff --git a/vendor/github.com/compose-spec/compose-go/types/device.go b/vendor/github.com/compose-spec/compose-go/types/device.go new file mode 100644 index 0000000000..81b4bea4aa --- /dev/null +++ b/vendor/github.com/compose-spec/compose-go/types/device.go @@ -0,0 +1,53 @@ +/* + 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 types + +import ( + "strconv" + "strings" + + "github.com/pkg/errors" +) + +type DeviceRequest struct { + Capabilities []string `yaml:"capabilities,omitempty" json:"capabilities,omitempty"` + Driver string `yaml:"driver,omitempty" json:"driver,omitempty"` + Count DeviceCount `yaml:"count,omitempty" json:"count,omitempty"` + IDs []string `yaml:"device_ids,omitempty" json:"device_ids,omitempty"` +} + +type DeviceCount int64 + +func (c *DeviceCount) DecodeMapstructure(value interface{}) error { + switch v := value.(type) { + case int: + *c = DeviceCount(v) + case string: + if strings.ToLower(v) == "all" { + *c = -1 + return nil + } + i, err := strconv.ParseInt(v, 10, 64) + if err != nil { + return errors.Errorf("invalid value %q, the only value allowed is 'all' or a number", v) + } + *c = DeviceCount(i) + default: + return errors.Errorf("invalid type %T for device count", v) + } + return nil +} diff --git a/vendor/github.com/compose-spec/compose-go/types/duration.go b/vendor/github.com/compose-spec/compose-go/types/duration.go new file mode 100644 index 0000000000..95f562a7cf --- /dev/null +++ b/vendor/github.com/compose-spec/compose-go/types/duration.go @@ -0,0 +1,60 @@ +/* + 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 types + +import ( + "encoding/json" + "fmt" + "strings" + "time" +) + +// Duration is a thin wrapper around time.Duration with improved JSON marshalling +type Duration time.Duration + +func (d Duration) String() string { + return time.Duration(d).String() +} + +func (d *Duration) DecodeMapstructure(value interface{}) error { + v, err := time.ParseDuration(fmt.Sprint(value)) + if err != nil { + return err + } + *d = Duration(v) + return nil +} + +// MarshalJSON makes Duration implement json.Marshaler +func (d Duration) MarshalJSON() ([]byte, error) { + return json.Marshal(d.String()) +} + +// MarshalYAML makes Duration implement yaml.Marshaler +func (d Duration) MarshalYAML() (interface{}, error) { + return d.String(), nil +} + +func (d *Duration) UnmarshalJSON(b []byte) error { + s := strings.Trim(string(b), "\"") + timeDuration, err := time.ParseDuration(s) + if err != nil { + return err + } + *d = Duration(timeDuration) + return nil +} diff --git a/vendor/github.com/compose-spec/compose-go/types/healthcheck.go b/vendor/github.com/compose-spec/compose-go/types/healthcheck.go new file mode 100644 index 0000000000..1bbf5e9e21 --- /dev/null +++ b/vendor/github.com/compose-spec/compose-go/types/healthcheck.go @@ -0,0 +1,53 @@ +/* + 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 types + +import ( + "fmt" +) + +// HealthCheckConfig the healthcheck configuration for a service +type HealthCheckConfig struct { + Test HealthCheckTest `yaml:"test,omitempty" json:"test,omitempty"` + Timeout *Duration `yaml:"timeout,omitempty" json:"timeout,omitempty"` + Interval *Duration `yaml:"interval,omitempty" json:"interval,omitempty"` + Retries *uint64 `yaml:"retries,omitempty" json:"retries,omitempty"` + StartPeriod *Duration `yaml:"start_period,omitempty" json:"start_period,omitempty"` + StartInterval *Duration `yaml:"start_interval,omitempty" json:"start_interval,omitempty"` + Disable bool `yaml:"disable,omitempty" json:"disable,omitempty"` + + Extensions Extensions `yaml:"#extensions,inline" json:"-"` +} + +// HealthCheckTest is the command run to test the health of a service +type HealthCheckTest []string + +func (l *HealthCheckTest) DecodeMapstructure(value interface{}) error { + switch v := value.(type) { + case string: + *l = []string{"CMD-SHELL", v} + case []interface{}: + seq := make([]string, len(v)) + for i, e := range v { + seq[i] = e.(string) + } + *l = seq + default: + return fmt.Errorf("unexpected value type %T for healthcheck.test", value) + } + return nil +} diff --git a/vendor/github.com/compose-spec/compose-go/types/labels.go b/vendor/github.com/compose-spec/compose-go/types/labels.go new file mode 100644 index 0000000000..000476bf69 --- /dev/null +++ b/vendor/github.com/compose-spec/compose-go/types/labels.go @@ -0,0 +1,77 @@ +/* + 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 types + +import ( + "fmt" + "strings" +) + +// Labels is a mapping type for labels +type Labels map[string]string + +func (l Labels) Add(key, value string) Labels { + if l == nil { + l = Labels{} + } + l[key] = value + return l +} + +func (l Labels) AsList() []string { + s := make([]string, len(l)) + i := 0 + for k, v := range l { + s[i] = fmt.Sprintf("%s=%s", k, v) + i++ + } + return s +} + +// label value can be a string | number | boolean | null (empty) +func labelValue(e interface{}) string { + if e == nil { + return "" + } + switch v := e.(type) { + case string: + return v + default: + return fmt.Sprint(v) + } +} + +func (l *Labels) DecodeMapstructure(value interface{}) error { + switch v := value.(type) { + case map[string]interface{}: + labels := make(map[string]string, len(v)) + for k, e := range v { + labels[k] = labelValue(e) + } + *l = labels + case []interface{}: + labels := make(map[string]string, len(v)) + for _, s := range v { + k, e, _ := strings.Cut(fmt.Sprint(s), "=") + labels[k] = labelValue(e) + } + *l = labels + default: + return fmt.Errorf("unexpected value type %T for labels", value) + } + return nil +} diff --git a/vendor/github.com/compose-spec/compose-go/types/options.go b/vendor/github.com/compose-spec/compose-go/types/options.go new file mode 100644 index 0000000000..7ae85793d1 --- /dev/null +++ b/vendor/github.com/compose-spec/compose-go/types/options.go @@ -0,0 +1,46 @@ +/* + 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 types + +import ( + "fmt" + + "github.com/pkg/errors" +) + +// Options is a mapping type for options we pass as-is to container runtime +type Options map[string]string + +func (d *Options) DecodeMapstructure(value interface{}) error { + switch v := value.(type) { + case map[string]interface{}: + m := make(map[string]string) + for key, e := range v { + if e == nil { + m[key] = "" + } else { + m[key] = fmt.Sprint(e) + } + } + *d = m + case map[string]string: + *d = v + default: + return errors.Errorf("invalid type %T for options", value) + } + return nil +} diff --git a/vendor/github.com/compose-spec/compose-go/types/stringOrList.go b/vendor/github.com/compose-spec/compose-go/types/stringOrList.go new file mode 100644 index 0000000000..3d91ad2a5c --- /dev/null +++ b/vendor/github.com/compose-spec/compose-go/types/stringOrList.go @@ -0,0 +1,61 @@ +/* + 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 types + +import ( + "fmt" + + "github.com/pkg/errors" +) + +// StringList is a type for fields that can be a string or list of strings +type StringList []string + +func (l *StringList) DecodeMapstructure(value interface{}) error { + switch v := value.(type) { + case string: + *l = []string{v} + case []interface{}: + list := make([]string, len(v)) + for i, e := range v { + list[i] = e.(string) + } + *l = list + default: + return errors.Errorf("invalid type %T for string list", value) + } + return nil +} + +// StringOrNumberList is a type for fields that can be a list of strings or numbers +type StringOrNumberList []string + +func (l *StringOrNumberList) DecodeMapstructure(value interface{}) error { + switch v := value.(type) { + case string: + *l = []string{v} + case []interface{}: + list := make([]string, len(v)) + for i, e := range v { + list[i] = fmt.Sprint(e) + } + *l = list + default: + return errors.Errorf("invalid type %T for string list", value) + } + return nil +} diff --git a/vendor/github.com/compose-spec/compose-go/types/types.go b/vendor/github.com/compose-spec/compose-go/types/types.go index 5864dc0042..d84be3ffc5 100644 --- a/vendor/github.com/compose-spec/compose-go/types/types.go +++ b/vendor/github.com/compose-spec/compose-go/types/types.go @@ -21,47 +21,10 @@ import ( "fmt" "sort" "strings" - "time" "github.com/docker/go-connections/nat" ) -// Duration is a thin wrapper around time.Duration with improved JSON marshalling -type Duration time.Duration - -func (d Duration) String() string { - return time.Duration(d).String() -} - -// ConvertDurationPtr converts a type defined Duration pointer to a time.Duration pointer with the same value. -func ConvertDurationPtr(d *Duration) *time.Duration { - if d == nil { - return nil - } - res := time.Duration(*d) - return &res -} - -// MarshalJSON makes Duration implement json.Marshaler -func (d Duration) MarshalJSON() ([]byte, error) { - return json.Marshal(d.String()) -} - -// MarshalYAML makes Duration implement yaml.Marshaler -func (d Duration) MarshalYAML() (interface{}, error) { - return d.String(), nil -} - -func (d *Duration) UnmarshalJSON(b []byte) error { - s := strings.Trim(string(b), "\"") - timeDuration, err := time.ParseDuration(s) - if err != nil { - return err - } - *d = Duration(timeDuration) - return nil -} - // Services is a list of ServiceConfig type Services []ServiceConfig @@ -88,23 +51,24 @@ type ServiceConfig struct { Name string `yaml:"-" json:"-"` Profiles []string `yaml:"profiles,omitempty" json:"profiles,omitempty"` - Annotations Mapping `yaml:"annotations,omitempty" json:"annotations,omitempty"` - Attach *bool `yaml:"attach,omitempty" json:"attach,omitempty"` - Build *BuildConfig `yaml:"build,omitempty" json:"build,omitempty"` - BlkioConfig *BlkioConfig `yaml:"blkio_config,omitempty" json:"blkio_config,omitempty"` - CapAdd []string `yaml:"cap_add,omitempty" json:"cap_add,omitempty"` - CapDrop []string `yaml:"cap_drop,omitempty" json:"cap_drop,omitempty"` - CgroupParent string `yaml:"cgroup_parent,omitempty" json:"cgroup_parent,omitempty"` - Cgroup string `yaml:"cgroup,omitempty" json:"cgroup,omitempty"` - CPUCount int64 `yaml:"cpu_count,omitempty" json:"cpu_count,omitempty"` - CPUPercent float32 `yaml:"cpu_percent,omitempty" json:"cpu_percent,omitempty"` - CPUPeriod int64 `yaml:"cpu_period,omitempty" json:"cpu_period,omitempty"` - CPUQuota int64 `yaml:"cpu_quota,omitempty" json:"cpu_quota,omitempty"` - CPURTPeriod int64 `yaml:"cpu_rt_period,omitempty" json:"cpu_rt_period,omitempty"` - CPURTRuntime int64 `yaml:"cpu_rt_runtime,omitempty" json:"cpu_rt_runtime,omitempty"` - CPUS float32 `yaml:"cpus,omitempty" json:"cpus,omitempty"` - CPUSet string `yaml:"cpuset,omitempty" json:"cpuset,omitempty"` - CPUShares int64 `yaml:"cpu_shares,omitempty" json:"cpu_shares,omitempty"` + Annotations Mapping `yaml:"annotations,omitempty" json:"annotations,omitempty"` + Attach *bool `yaml:"attach,omitempty" json:"attach,omitempty"` + Build *BuildConfig `yaml:"build,omitempty" json:"build,omitempty"` + Develop *DevelopConfig `yaml:"develop,omitempty" json:"develop,omitempty"` + BlkioConfig *BlkioConfig `yaml:"blkio_config,omitempty" json:"blkio_config,omitempty"` + CapAdd []string `yaml:"cap_add,omitempty" json:"cap_add,omitempty"` + CapDrop []string `yaml:"cap_drop,omitempty" json:"cap_drop,omitempty"` + CgroupParent string `yaml:"cgroup_parent,omitempty" json:"cgroup_parent,omitempty"` + Cgroup string `yaml:"cgroup,omitempty" json:"cgroup,omitempty"` + CPUCount int64 `yaml:"cpu_count,omitempty" json:"cpu_count,omitempty"` + CPUPercent float32 `yaml:"cpu_percent,omitempty" json:"cpu_percent,omitempty"` + CPUPeriod int64 `yaml:"cpu_period,omitempty" json:"cpu_period,omitempty"` + CPUQuota int64 `yaml:"cpu_quota,omitempty" json:"cpu_quota,omitempty"` + CPURTPeriod int64 `yaml:"cpu_rt_period,omitempty" json:"cpu_rt_period,omitempty"` + CPURTRuntime int64 `yaml:"cpu_rt_runtime,omitempty" json:"cpu_rt_runtime,omitempty"` + CPUS float32 `yaml:"cpus,omitempty" json:"cpus,omitempty"` + CPUSet string `yaml:"cpuset,omitempty" json:"cpuset,omitempty"` + CPUShares int64 `yaml:"cpu_shares,omitempty" json:"cpu_shares,omitempty"` // Command for the service containers. // If set, overrides COMMAND from the image. @@ -312,25 +276,26 @@ func (s set) toSlice() []string { // BuildConfig is a type for build type BuildConfig struct { - Context string `yaml:"context,omitempty" json:"context,omitempty"` - Dockerfile string `yaml:"dockerfile,omitempty" json:"dockerfile,omitempty"` - DockerfileInline string `yaml:"dockerfile_inline,omitempty" json:"dockerfile_inline,omitempty"` - Args MappingWithEquals `yaml:"args,omitempty" json:"args,omitempty"` - SSH SSHConfig `yaml:"ssh,omitempty" json:"ssh,omitempty"` - Labels Labels `yaml:"labels,omitempty" json:"labels,omitempty"` - CacheFrom StringList `yaml:"cache_from,omitempty" json:"cache_from,omitempty"` - CacheTo StringList `yaml:"cache_to,omitempty" json:"cache_to,omitempty"` - NoCache bool `yaml:"no_cache,omitempty" json:"no_cache,omitempty"` - AdditionalContexts Mapping `yaml:"additional_contexts,omitempty" json:"additional_contexts,omitempty"` - Pull bool `yaml:"pull,omitempty" json:"pull,omitempty"` - ExtraHosts HostsList `yaml:"extra_hosts,omitempty" json:"extra_hosts,omitempty"` - Isolation string `yaml:"isolation,omitempty" json:"isolation,omitempty"` - Network string `yaml:"network,omitempty" json:"network,omitempty"` - Target string `yaml:"target,omitempty" json:"target,omitempty"` - Secrets []ServiceSecretConfig `yaml:"secrets,omitempty" json:"secrets,omitempty"` - Tags StringList `yaml:"tags,omitempty" json:"tags,omitempty"` - Platforms StringList `yaml:"platforms,omitempty" json:"platforms,omitempty"` - Privileged bool `yaml:"privileged,omitempty" json:"privileged,omitempty"` + Context string `yaml:"context,omitempty" json:"context,omitempty"` + Dockerfile string `yaml:"dockerfile,omitempty" json:"dockerfile,omitempty"` + DockerfileInline string `yaml:"dockerfile_inline,omitempty" json:"dockerfile_inline,omitempty"` + Args MappingWithEquals `yaml:"args,omitempty" json:"args,omitempty"` + SSH SSHConfig `yaml:"ssh,omitempty" json:"ssh,omitempty"` + Labels Labels `yaml:"labels,omitempty" json:"labels,omitempty"` + CacheFrom StringList `yaml:"cache_from,omitempty" json:"cache_from,omitempty"` + CacheTo StringList `yaml:"cache_to,omitempty" json:"cache_to,omitempty"` + NoCache bool `yaml:"no_cache,omitempty" json:"no_cache,omitempty"` + AdditionalContexts Mapping `yaml:"additional_contexts,omitempty" json:"additional_contexts,omitempty"` + Pull bool `yaml:"pull,omitempty" json:"pull,omitempty"` + ExtraHosts HostsList `yaml:"extra_hosts,omitempty" json:"extra_hosts,omitempty"` + Isolation string `yaml:"isolation,omitempty" json:"isolation,omitempty"` + Network string `yaml:"network,omitempty" json:"network,omitempty"` + Target string `yaml:"target,omitempty" json:"target,omitempty"` + Secrets []ServiceSecretConfig `yaml:"secrets,omitempty" json:"secrets,omitempty"` + Tags StringList `yaml:"tags,omitempty" json:"tags,omitempty"` + Ulimits map[string]*UlimitsConfig `yaml:"ulimits,omitempty" json:"ulimits,omitempty"` + Platforms StringList `yaml:"platforms,omitempty" json:"platforms,omitempty"` + Privileged bool `yaml:"privileged,omitempty" json:"privileged,omitempty"` Extensions Extensions `yaml:"#extensions,inline" json:"-"` } @@ -363,62 +328,6 @@ type ThrottleDevice struct { Extensions Extensions `yaml:"#extensions,inline" json:"-"` } -// ShellCommand is a string or list of string args. -// -// When marshaled to YAML, nil command fields will be omitted if `omitempty` -// is specified as a struct tag. Explicitly empty commands (i.e. `[]` or -// empty string will serialize to an empty array (`[]`). -// -// When marshaled to JSON, the `omitempty` struct must NOT be specified. -// If the command field is nil, it will be serialized as `null`. -// Explicitly empty commands (i.e. `[]` or empty string) will serialize to -// an empty array (`[]`). -// -// The distinction between nil and explicitly empty is important to distinguish -// between an unset value and a provided, but empty, value, which should be -// preserved so that it can override any base value (e.g. container entrypoint). -// -// The different semantics between YAML and JSON are due to limitations with -// JSON marshaling + `omitempty` in the Go stdlib, while gopkg.in/yaml.v3 gives -// us more flexibility via the yaml.IsZeroer interface. -// -// In the future, it might make sense to make fields of this type be -// `*ShellCommand` to avoid this situation, but that would constitute a -// breaking change. -type ShellCommand []string - -// IsZero returns true if the slice is nil. -// -// Empty (but non-nil) slices are NOT considered zero values. -func (s ShellCommand) IsZero() bool { - // we do NOT want len(s) == 0, ONLY explicitly nil - return s == nil -} - -// MarshalYAML returns nil (which will be serialized as `null`) for nil slices -// and delegates to the standard marshaller behavior otherwise. -// -// NOTE: Typically the nil case here is not hit because IsZero has already -// short-circuited marshalling, but this ensures that the type serializes -// accurately if the `omitempty` struct tag is omitted/forgotten. -// -// A similar MarshalJSON() implementation is not needed because the Go stdlib -// already serializes nil slices to `null`, whereas gopkg.in/yaml.v3 by default -// serializes nil slices to `[]`. -func (s ShellCommand) MarshalYAML() (interface{}, error) { - if s == nil { - return nil, nil - } - return []string(s), nil -} - -// StringList is a type for fields that can be a string or list of strings -type StringList []string - -// StringOrNumberList is a type for fields that can be a list of strings or -// numbers -type StringOrNumberList []string - // MappingWithEquals is a mapping type that can be converted from a list of // key[=value] strings. // For the key with an empty value (`key=`), the mapped value is set to a pointer to `""`. @@ -534,17 +443,6 @@ func (m Mapping) Merge(o Mapping) Mapping { return m } -// Labels is a mapping type for labels -type Labels map[string]string - -func (l Labels) Add(key, value string) Labels { - if l == nil { - l = Labels{} - } - l[key] = value - return l -} - type SSHKey struct { ID string Path string @@ -608,8 +506,8 @@ func (h HostsList) MarshalJSON() ([]byte, error) { // LoggingConfig the logging configuration for a service type LoggingConfig struct { - Driver string `yaml:"driver,omitempty" json:"driver,omitempty"` - Options map[string]string `yaml:"options,omitempty" json:"options,omitempty"` + Driver string `yaml:"driver,omitempty" json:"driver,omitempty"` + Options Options `yaml:"options,omitempty" json:"options,omitempty"` Extensions Extensions `yaml:"#extensions,inline" json:"-"` } @@ -629,22 +527,6 @@ type DeployConfig struct { Extensions Extensions `yaml:"#extensions,inline" json:"-"` } -// HealthCheckConfig the healthcheck configuration for a service -type HealthCheckConfig struct { - Test HealthCheckTest `yaml:"test,omitempty" json:"test,omitempty"` - Timeout *Duration `yaml:"timeout,omitempty" json:"timeout,omitempty"` - Interval *Duration `yaml:"interval,omitempty" json:"interval,omitempty"` - Retries *uint64 `yaml:"retries,omitempty" json:"retries,omitempty"` - StartPeriod *Duration `yaml:"start_period,omitempty" json:"start_period,omitempty"` - StartInterval *Duration `yaml:"start_interval,omitempty" json:"start_interval,omitempty"` - Disable bool `yaml:"disable,omitempty" json:"disable,omitempty"` - - Extensions Extensions `yaml:"#extensions,inline" json:"-"` -} - -// HealthCheckTest is the command run to test the health of a service -type HealthCheckTest []string - // UpdateConfig the service update configuration type UpdateConfig struct { Parallelism *uint64 `yaml:"parallelism,omitempty" json:"parallelism,omitempty"` @@ -677,13 +559,6 @@ type Resource struct { Extensions Extensions `yaml:"#extensions,inline" json:"-"` } -type DeviceRequest struct { - Capabilities []string `yaml:"capabilities,omitempty" json:"capabilities,omitempty"` - Driver string `yaml:"driver,omitempty" json:"driver,omitempty"` - Count int64 `yaml:"count,omitempty" json:"count,omitempty"` - IDs []string `yaml:"device_ids,omitempty" json:"device_ids,omitempty"` -} - // GenericResource represents a "user defined" resource which can // only be an integer (e.g: SSD=3) for a service type GenericResource struct { @@ -703,19 +578,6 @@ type DiscreteGenericResource struct { Extensions Extensions `yaml:"#extensions,inline" json:"-"` } -// UnitBytes is the bytes type -type UnitBytes int64 - -// MarshalYAML makes UnitBytes implement yaml.Marshaller -func (u UnitBytes) MarshalYAML() (interface{}, error) { - return fmt.Sprintf("%d", u), nil -} - -// MarshalJSON makes UnitBytes implement json.Marshaler -func (u UnitBytes) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprintf(`"%d"`, u)), nil -} - // RestartPolicy the service restart policy type RestartPolicy struct { Condition string `yaml:"condition,omitempty" json:"condition,omitempty"` @@ -749,6 +611,7 @@ type ServiceNetworkConfig struct { Ipv4Address string `yaml:"ipv4_address,omitempty" json:"ipv4_address,omitempty"` Ipv6Address string `yaml:"ipv6_address,omitempty" json:"ipv6_address,omitempty"` LinkLocalIPs []string `yaml:"link_local_ips,omitempty" json:"link_local_ips,omitempty"` + MacAddress string `yaml:"mac_address,omitempty" json:"mac_address,omitempty"` Extensions Extensions `yaml:"#extensions,inline" json:"-"` } @@ -954,16 +817,16 @@ func (u *UlimitsConfig) MarshalJSON() ([]byte, error) { // NetworkConfig for a network type NetworkConfig struct { - Name string `yaml:"name,omitempty" json:"name,omitempty"` - Driver string `yaml:"driver,omitempty" json:"driver,omitempty"` - DriverOpts map[string]string `yaml:"driver_opts,omitempty" json:"driver_opts,omitempty"` - Ipam IPAMConfig `yaml:"ipam,omitempty" json:"ipam,omitempty"` - External External `yaml:"external,omitempty" json:"external,omitempty"` - Internal bool `yaml:"internal,omitempty" json:"internal,omitempty"` - Attachable bool `yaml:"attachable,omitempty" json:"attachable,omitempty"` - Labels Labels `yaml:"labels,omitempty" json:"labels,omitempty"` - EnableIPv6 bool `yaml:"enable_ipv6,omitempty" json:"enable_ipv6,omitempty"` - Extensions Extensions `yaml:"#extensions,inline" json:"-"` + Name string `yaml:"name,omitempty" json:"name,omitempty"` + Driver string `yaml:"driver,omitempty" json:"driver,omitempty"` + DriverOpts Options `yaml:"driver_opts,omitempty" json:"driver_opts,omitempty"` + Ipam IPAMConfig `yaml:"ipam,omitempty" json:"ipam,omitempty"` + External External `yaml:"external,omitempty" json:"external,omitempty"` + Internal bool `yaml:"internal,omitempty" json:"internal,omitempty"` + Attachable bool `yaml:"attachable,omitempty" json:"attachable,omitempty"` + Labels Labels `yaml:"labels,omitempty" json:"labels,omitempty"` + EnableIPv6 bool `yaml:"enable_ipv6,omitempty" json:"enable_ipv6,omitempty"` + Extensions Extensions `yaml:"#extensions,inline" json:"-"` } // IPAMConfig for a network @@ -978,18 +841,18 @@ type IPAMPool struct { Subnet string `yaml:"subnet,omitempty" json:"subnet,omitempty"` Gateway string `yaml:"gateway,omitempty" json:"gateway,omitempty"` IPRange string `yaml:"ip_range,omitempty" json:"ip_range,omitempty"` - AuxiliaryAddresses map[string]string `yaml:"aux_addresses,omitempty" json:"aux_addresses,omitempty"` + AuxiliaryAddresses Mapping `yaml:"aux_addresses,omitempty" json:"aux_addresses,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // VolumeConfig for a volume type VolumeConfig struct { - Name string `yaml:"name,omitempty" json:"name,omitempty"` - Driver string `yaml:"driver,omitempty" json:"driver,omitempty"` - DriverOpts map[string]string `yaml:"driver_opts,omitempty" json:"driver_opts,omitempty"` - External External `yaml:"external,omitempty" json:"external,omitempty"` - Labels Labels `yaml:"labels,omitempty" json:"labels,omitempty"` - Extensions Extensions `yaml:"#extensions,inline" json:"-"` + Name string `yaml:"name,omitempty" json:"name,omitempty"` + Driver string `yaml:"driver,omitempty" json:"driver,omitempty"` + DriverOpts Options `yaml:"driver_opts,omitempty" json:"driver_opts,omitempty"` + External External `yaml:"external,omitempty" json:"external,omitempty"` + Labels Labels `yaml:"labels,omitempty" json:"labels,omitempty"` + Extensions Extensions `yaml:"#extensions,inline" json:"-"` } // External identifies a Volume or Network as a reference to a resource that is @@ -1030,6 +893,7 @@ type FileObjectConfig struct { Name string `yaml:"name,omitempty" json:"name,omitempty"` File string `yaml:"file,omitempty" json:"file,omitempty"` Environment string `yaml:"environment,omitempty" json:"environment,omitempty"` + Content string `yaml:"content,omitempty" json:"content,omitempty"` External External `yaml:"external,omitempty" json:"external,omitempty"` Labels Labels `yaml:"labels,omitempty" json:"labels,omitempty"` Driver string `yaml:"driver,omitempty" json:"driver,omitempty"` diff --git a/vendor/modules.txt b/vendor/modules.txt index 5d4874dea0..166adc32e0 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -65,7 +65,7 @@ github.com/chai2010/gettext-go/plural github.com/chai2010/gettext-go/po # github.com/cloudflare/cfssl v1.4.1 ## explicit; go 1.13 -# github.com/compose-spec/compose-go v1.18.4 +# github.com/compose-spec/compose-go v1.20.2 ## explicit; go 1.19 github.com/compose-spec/compose-go/cli github.com/compose-spec/compose-go/consts