diff --git a/go.mod b/go.mod index e4ac9d1e..3998d895 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/Masterminds/semver/v3 v3.2.1 github.com/aws/aws-sdk-go-v2/config v1.18.16 - github.com/compose-spec/compose-go v1.19.0 + github.com/compose-spec/compose-go v1.20.0 github.com/containerd/console v1.0.3 github.com/containerd/containerd v1.7.2 github.com/containerd/continuity v0.4.1 diff --git a/go.sum b/go.sum index a8638369..1a2b06bd 100644 --- a/go.sum +++ b/go.sum @@ -123,8 +123,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/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= -github.com/compose-spec/compose-go v1.19.0 h1:t68gAcwStDg0hy2kFvqHJIksf6xkqRnlSKfL45/ETqo= -github.com/compose-spec/compose-go v1.19.0/go.mod h1:+MdqXV4RA7wdFsahh/Kb8U0pAJqkg7mr4PM9tFKU8RM= +github.com/compose-spec/compose-go v1.20.0 h1:h4ZKOst1EF/DwZp7dWkb+wbTVE4nEyT9Lc89to84Ol4= +github.com/compose-spec/compose-go v1.20.0/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/dotenv/parser.go b/vendor/github.com/compose-spec/compose-go/dotenv/parser.go index aec72a88..11b6d027 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/include.go b/vendor/github.com/compose-spec/compose-go/loader/include.go index ea784144..aaebfd30 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 aae6dc3a..655e58e1 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 19ca5346..a7000467 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" @@ -241,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) } @@ -464,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 } @@ -586,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, @@ -610,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, } @@ -1031,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 @@ -1094,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: @@ -1258,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) @@ -1312,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 00000000..97a2e39c --- /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 3c4848e0..654d711d 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/schema/compose-spec.json b/vendor/github.com/compose-spec/compose-go/schema/compose-spec.json index ba80eecd..d4bff384 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 @@ -475,7 +475,7 @@ "properties": { "ignore": {"type": "array", "items": {"type": "string"}}, "path": {"type": "string"}, - "action": {"type": "string", "enum": ["rebuild", "sync"]}, + "action": {"type": "string", "enum": ["rebuild", "sync", "sync+restart"]}, "target": {"type": "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 00000000..4c873cde --- /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 00000000..90d575d8 --- /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 index fb580607..5fc10716 100644 --- a/vendor/github.com/compose-spec/compose-go/types/develop.go +++ b/vendor/github.com/compose-spec/compose-go/types/develop.go @@ -23,8 +23,9 @@ type DevelopConfig struct { type WatchAction string const ( - WatchActionSync WatchAction = "sync" - WatchActionRebuild WatchAction = "rebuild" + WatchActionSync WatchAction = "sync" + WatchActionRebuild WatchAction = "rebuild" + WatchActionSyncRestart WatchAction = "sync+restart" ) type Trigger struct { 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 00000000..81b4bea4 --- /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 00000000..95f562a7 --- /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 00000000..1bbf5e9e --- /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 00000000..f30699b5 --- /dev/null +++ b/vendor/github.com/compose-spec/compose-go/types/labels.go @@ -0,0 +1,80 @@ +/* + 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, ok := strings.Cut(fmt.Sprint(s), "=") + if !ok { + return fmt.Errorf("invalid label %q", v) + } + 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 00000000..7ae85793 --- /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 00000000..3d91ad2a --- /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 e8df460c..0407462a 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 @@ -364,62 +327,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 `""`. @@ -535,17 +442,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 @@ -609,8 +505,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:"-"` } @@ -630,22 +526,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"` @@ -678,13 +558,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 { @@ -704,19 +577,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"` @@ -955,16 +815,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 @@ -979,18 +839,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 diff --git a/vendor/modules.txt b/vendor/modules.txt index 39ade400..22110388 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -118,7 +118,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 v1.19.0 +# github.com/compose-spec/compose-go v1.20.0 ## explicit; go 1.19 github.com/compose-spec/compose-go/cli github.com/compose-spec/compose-go/consts