bump compose-go to v2.2.0

Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
This commit is contained in:
Guillaume Lours 2024-09-12 18:14:18 +02:00
parent 3f81293fd4
commit 4da753da79
No known key found for this signature in database
16 changed files with 197 additions and 31 deletions

2
go.mod
View File

@ -6,7 +6,7 @@ require (
github.com/Masterminds/semver/v3 v3.2.1 github.com/Masterminds/semver/v3 v3.2.1
github.com/Microsoft/go-winio v0.6.2 github.com/Microsoft/go-winio v0.6.2
github.com/aws/aws-sdk-go-v2/config v1.26.6 github.com/aws/aws-sdk-go-v2/config v1.26.6
github.com/compose-spec/compose-go/v2 v2.1.6 github.com/compose-spec/compose-go/v2 v2.2.0
github.com/containerd/console v1.0.4 github.com/containerd/console v1.0.4
github.com/containerd/containerd v1.7.21 github.com/containerd/containerd v1.7.21
github.com/containerd/continuity v0.4.3 github.com/containerd/continuity v0.4.3

4
go.sum
View File

@ -84,8 +84,8 @@ github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/P
github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
github.com/compose-spec/compose-go/v2 v2.1.6 h1:d0Cs0DffmOwmSzs0YPHwKCskknGq2jfGg4uGowlEpps= github.com/compose-spec/compose-go/v2 v2.2.0 h1:VsQosGhuO+H9wh5laiIiAe4TVd73kQ5NWwmNrdm0HRA=
github.com/compose-spec/compose-go/v2 v2.1.6/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc= github.com/compose-spec/compose-go/v2 v2.2.0/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=

View File

@ -403,7 +403,7 @@ func (o *ProjectOptions) GetWorkingDir() (string, error) {
return os.Getwd() return os.Getwd()
} }
func (o *ProjectOptions) GeConfigFiles() ([]types.ConfigFile, error) { func (o *ProjectOptions) GetConfigFiles() ([]types.ConfigFile, error) {
configPaths, err := o.getConfigPaths() configPaths, err := o.getConfigPaths()
if err != nil { if err != nil {
return nil, err return nil, err
@ -466,7 +466,7 @@ func (o *ProjectOptions) LoadModel(ctx context.Context) (map[string]any, error)
// prepare converts ProjectOptions into loader's types.ConfigDetails and configures default load options // prepare converts ProjectOptions into loader's types.ConfigDetails and configures default load options
func (o *ProjectOptions) prepare() (types.ConfigDetails, error) { func (o *ProjectOptions) prepare() (types.ConfigDetails, error) {
configs, err := o.GeConfigFiles() configs, err := o.GetConfigFiles()
if err != nil { if err != nil {
return types.ConfigDetails{}, err return types.ConfigDetails{}, err
} }

View File

@ -119,7 +119,7 @@ loop:
offset = i + 1 offset = i + 1
inherited = rune == '\n' inherited = rune == '\n'
break loop break loop
case '_', '.', '-', '[', ']': case '_', '.', '[', ']':
default: default:
// variable name should match [A-Za-z0-9_.-] // variable name should match [A-Za-z0-9_.-]
if unicode.IsLetter(rune) || unicode.IsNumber(rune) { if unicode.IsLetter(rune) || unicode.IsNumber(rune) {

View File

@ -79,7 +79,7 @@ func resolveSecretsEnvironment(dict map[string]any, environment types.Mapping) {
continue continue
} }
if found, ok := environment[env]; ok { if found, ok := environment[env]; ok {
secret["content"] = found secret[types.SecretConfigXValue] = found
} }
secrets[name] = secret secrets[name] = secret
} }

View File

@ -163,8 +163,15 @@ func getExtendsBaseFromFile(
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
services := source["services"].(map[string]any) m, ok := source["services"]
_, ok := services[ref] if !ok {
return nil, nil, fmt.Errorf("cannot extend service %q in %s: no services section", name, local)
}
services, ok := m.(map[string]any)
if !ok {
return nil, nil, fmt.Errorf("cannot extend service %q in %s: services must be a mapping", name, local)
}
_, ok = services[ref]
if !ok { if !ok {
return nil, nil, fmt.Errorf( return nil, nil, fmt.Errorf(
"cannot extend service %q in %s: service %q not found in %s", "cannot extend service %q in %s: service %q not found in %s",

View File

@ -118,7 +118,9 @@ services:
- "a 7:* rmw" - "a 7:* rmw"
devices: devices:
- "/dev/ttyUSB0:/dev/ttyUSB0" - source: /dev/ttyUSB0
target: /dev/ttyUSB0
permissions: rwm
# String or list # String or list
# dns: 8.8.8.8 # dns: 8.8.8.8

View File

@ -738,7 +738,9 @@ func Transform(source interface{}, target interface{}) error {
DecodeHook: mapstructure.ComposeDecodeHookFunc( DecodeHook: mapstructure.ComposeDecodeHookFunc(
nameServices, nameServices,
decoderHook, decoderHook,
cast), cast,
secretConfigDecoderHook,
),
Result: target, Result: target,
TagName: "yaml", TagName: "yaml",
Metadata: &data, Metadata: &data,
@ -764,6 +766,28 @@ func nameServices(from reflect.Value, to reflect.Value) (interface{}, error) {
return from.Interface(), nil return from.Interface(), nil
} }
func secretConfigDecoderHook(from, to reflect.Type, data interface{}) (interface{}, error) {
// Check if the input is a map and we're decoding into a SecretConfig
if from.Kind() == reflect.Map && to == reflect.TypeOf(types.SecretConfig{}) {
if v, ok := data.(map[string]interface{}); ok {
if ext, ok := v["#extensions"].(map[string]interface{}); ok {
if val, ok := ext[types.SecretConfigXValue].(string); ok {
// Return a map with the Content field populated
v["Content"] = val
delete(ext, types.SecretConfigXValue)
if len(ext) == 0 {
delete(v, "#extensions")
}
}
}
}
}
// Return the original data so the rest is handled by default mapstructure logic
return data, nil
}
// keys need to be converted to strings for jsonschema // keys need to be converted to strings for jsonschema
func convertToStringKeysRecursive(value interface{}, keyPrefix string) (interface{}, error) { func convertToStringKeysRecursive(value interface{}, keyPrefix string) (interface{}, error) {
if mapping, ok := value.(map[string]interface{}); ok { if mapping, ok := value.(map[string]interface{}); ok {

View File

@ -36,7 +36,6 @@ func init() {
unique["services.*.annotations"] = keyValueIndexer unique["services.*.annotations"] = keyValueIndexer
unique["services.*.build.args"] = keyValueIndexer unique["services.*.build.args"] = keyValueIndexer
unique["services.*.build.additional_contexts"] = keyValueIndexer unique["services.*.build.additional_contexts"] = keyValueIndexer
unique["services.*.build.extra_hosts"] = keyValueIndexer
unique["services.*.build.platform"] = keyValueIndexer unique["services.*.build.platform"] = keyValueIndexer
unique["services.*.build.tags"] = keyValueIndexer unique["services.*.build.tags"] = keyValueIndexer
unique["services.*.build.labels"] = keyValueIndexer unique["services.*.build.labels"] = keyValueIndexer
@ -51,7 +50,6 @@ func init() {
unique["services.*.environment"] = keyValueIndexer unique["services.*.environment"] = keyValueIndexer
unique["services.*.env_file"] = envFileIndexer unique["services.*.env_file"] = envFileIndexer
unique["services.*.expose"] = exposeIndexer unique["services.*.expose"] = exposeIndexer
unique["services.*.extra_hosts"] = keyValueIndexer
unique["services.*.labels"] = keyValueIndexer unique["services.*.labels"] = keyValueIndexer
unique["services.*.links"] = keyValueIndexer unique["services.*.links"] = keyValueIndexer
unique["services.*.networks.*.aliases"] = keyValueIndexer unique["services.*.networks.*.aliases"] = keyValueIndexer
@ -62,6 +60,7 @@ func init() {
unique["services.*.sysctls"] = keyValueIndexer unique["services.*.sysctls"] = keyValueIndexer
unique["services.*.tmpfs"] = keyValueIndexer unique["services.*.tmpfs"] = keyValueIndexer
unique["services.*.volumes"] = volumeIndexer unique["services.*.volumes"] = volumeIndexer
unique["services.*.devices"] = deviceMappingIndexer
} }
// EnforceUnicity removes redefinition of elements declared in a sequence // EnforceUnicity removes redefinition of elements declared in a sequence
@ -108,16 +107,16 @@ func enforceUnicity(value any, p tree.Path) (any, error) {
return value, nil return value, nil
} }
func keyValueIndexer(y any, p tree.Path) (string, error) { func keyValueIndexer(v any, p tree.Path) (string, error) {
switch value := y.(type) { switch value := v.(type) {
case string: case string:
key, _, found := strings.Cut(value, "=") key, _, found := strings.Cut(value, "=")
if !found { if found {
return value, nil return key, nil
} }
return key, nil return value, nil
default: default:
return "", fmt.Errorf("%s: unexpected type %T", p, y) return "", fmt.Errorf("%s: unexpected type %T", p, v)
} }
} }
@ -139,6 +138,24 @@ func volumeIndexer(y any, p tree.Path) (string, error) {
return "", nil return "", nil
} }
func deviceMappingIndexer(y any, p tree.Path) (string, error) {
switch value := y.(type) {
case map[string]any:
target, ok := value["target"].(string)
if !ok {
return "", fmt.Errorf("service device %s is missing a mount target", p)
}
return target, nil
case string:
arr := strings.Split(value, ":")
if len(arr) == 1 {
return arr[0], nil
}
return arr[1], nil
}
return "", nil
}
func exposeIndexer(a any, path tree.Path) (string, error) { func exposeIndexer(a any, path tree.Path) (string, error) {
switch v := a.(type) { switch v := a.(type) {
case string: case string:

View File

@ -19,7 +19,6 @@
"include": { "include": {
"type": "array", "type": "array",
"items": { "items": {
"type": "object",
"$ref": "#/definitions/include" "$ref": "#/definitions/include"
}, },
"description": "compose sub-projects to be included." "description": "compose sub-projects to be included."
@ -115,7 +114,7 @@
"pull": {"type": ["boolean", "string"]}, "pull": {"type": ["boolean", "string"]},
"target": {"type": "string"}, "target": {"type": "string"},
"shm_size": {"type": ["integer", "string"]}, "shm_size": {"type": ["integer", "string"]},
"extra_hosts": {"$ref": "#/definitions/list_or_dict"}, "extra_hosts": {"$ref": "#/definitions/extra_hosts"},
"isolation": {"type": "string"}, "isolation": {"type": "string"},
"privileged": {"type": ["boolean", "string"]}, "privileged": {"type": ["boolean", "string"]},
"secrets": {"$ref": "#/definitions/service_config_or_secret"}, "secrets": {"$ref": "#/definitions/service_config_or_secret"},
@ -216,7 +215,25 @@
] ]
}, },
"device_cgroup_rules": {"$ref": "#/definitions/list_of_strings"}, "device_cgroup_rules": {"$ref": "#/definitions/list_of_strings"},
"devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, "devices": {
"type": "array",
"items": {
"oneOf": [
{"type": "string"},
{
"type": "object",
"required": ["source"],
"properties": {
"source": {"type": "string"},
"target": {"type": "string"},
"permissions": {"type": "string"}
},
"additionalProperties": false,
"patternProperties": {"^x-": {}}
}
]
}
},
"dns": {"$ref": "#/definitions/string_or_list"}, "dns": {"$ref": "#/definitions/string_or_list"},
"dns_opt": {"type": "array","items": {"type": "string"}, "uniqueItems": true}, "dns_opt": {"type": "array","items": {"type": "string"}, "uniqueItems": true},
"dns_search": {"$ref": "#/definitions/string_or_list"}, "dns_search": {"$ref": "#/definitions/string_or_list"},
@ -249,7 +266,7 @@
] ]
}, },
"external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, "external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
"extra_hosts": {"$ref": "#/definitions/list_or_dict"}, "extra_hosts": {"$ref": "#/definitions/extra_hosts"},
"group_add": { "group_add": {
"type": "array", "type": "array",
"items": { "items": {
@ -854,6 +871,21 @@
] ]
}, },
"extra_hosts": {
"oneOf": [
{
"type": "object",
"patternProperties": {
".+": {
"type": ["string", "array"]
}
},
"additionalProperties": false
},
{"type": "array", "items": {"type": "string"}, "uniqueItems": true}
]
},
"blkio_limit": { "blkio_limit": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@ -33,6 +33,7 @@ func init() {
transformers["services.*.extends"] = transformExtends transformers["services.*.extends"] = transformExtends
transformers["services.*.networks"] = transformServiceNetworks transformers["services.*.networks"] = transformServiceNetworks
transformers["services.*.volumes.*"] = transformVolumeMount transformers["services.*.volumes.*"] = transformVolumeMount
transformers["services.*.devices.*"] = transformDeviceMapping
transformers["services.*.secrets.*"] = transformFileMount transformers["services.*.secrets.*"] = transformFileMount
transformers["services.*.configs.*"] = transformFileMount transformers["services.*.configs.*"] = transformFileMount
transformers["services.*.ports"] = transformPorts transformers["services.*.ports"] = transformPorts

View File

@ -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 transform
import (
"fmt"
"strings"
"github.com/compose-spec/compose-go/v2/tree"
)
func transformDeviceMapping(data any, p tree.Path, ignoreParseError bool) (any, error) {
switch v := data.(type) {
case map[string]any:
return v, nil
case string:
src := ""
dst := ""
permissions := "rwm"
arr := strings.Split(v, ":")
switch len(arr) {
case 3:
permissions = arr[2]
fallthrough
case 2:
dst = arr[1]
fallthrough
case 1:
src = arr[0]
default:
if !ignoreParseError {
return nil, fmt.Errorf("confusing device mapping, please use long syntax: %s", v)
}
}
if dst == "" {
dst = src
}
return map[string]any{
"source": src,
"target": dst,
"permissions": permissions,
}, nil
default:
return data, fmt.Errorf("%s: invalid type %T for service volume mount", p, v)
}
}

View File

@ -271,13 +271,13 @@ func deriveDeepCopyService(dst, src *ServiceConfig) {
if cap(dst.Devices) >= len(src.Devices) { if cap(dst.Devices) >= len(src.Devices) {
dst.Devices = (dst.Devices)[:len(src.Devices)] dst.Devices = (dst.Devices)[:len(src.Devices)]
} else { } else {
dst.Devices = make([]string, len(src.Devices)) dst.Devices = make([]DeviceMapping, len(src.Devices))
} }
} else if len(src.Devices) < len(dst.Devices) { } else if len(src.Devices) < len(dst.Devices) {
dst.Devices = (dst.Devices)[:len(src.Devices)] dst.Devices = (dst.Devices)[:len(src.Devices)]
} }
} else { } else {
dst.Devices = make([]string, len(src.Devices)) dst.Devices = make([]DeviceMapping, len(src.Devices))
} }
copy(dst.Devices, src.Devices) copy(dst.Devices, src.Devices)
} }

View File

@ -36,9 +36,9 @@ func NewHostsList(hosts []string) (HostsList, error) {
if ok { if ok {
// Mapping found with this separator, stop here. // Mapping found with this separator, stop here.
if ips, ok := list[host]; ok { if ips, ok := list[host]; ok {
list[host] = append(ips, ip) list[host] = append(ips, strings.Split(ip, ",")...)
} else { } else {
list[host] = []string{ip} list[host] = strings.Split(ip, ",")
} }
found = true found = true
break break
@ -89,7 +89,18 @@ func (h *HostsList) DecodeMapstructure(value interface{}) error {
if e == nil { if e == nil {
e = "" e = ""
} }
list[i] = []string{fmt.Sprint(e)} switch t := e.(type) {
case string:
list[i] = []string{t}
case []any:
hosts := make([]string, len(t))
for j, h := range t {
hosts[j] = fmt.Sprint(h)
}
list[i] = hosts
default:
return fmt.Errorf("unexpected value type %T for extra_hosts entry", value)
}
} }
err := list.cleanup() err := list.cleanup()
if err != nil { if err != nil {
@ -109,7 +120,7 @@ func (h *HostsList) DecodeMapstructure(value interface{}) error {
*h = list *h = list
return nil return nil
default: default:
return fmt.Errorf("unexpected value type %T for mapping", value) return fmt.Errorf("unexpected value type %T for extra_hosts", value)
} }
} }

View File

@ -62,7 +62,7 @@ type ServiceConfig struct {
DependsOn DependsOnConfig `yaml:"depends_on,omitempty" json:"depends_on,omitempty"` DependsOn DependsOnConfig `yaml:"depends_on,omitempty" json:"depends_on,omitempty"`
Deploy *DeployConfig `yaml:"deploy,omitempty" json:"deploy,omitempty"` Deploy *DeployConfig `yaml:"deploy,omitempty" json:"deploy,omitempty"`
DeviceCgroupRules []string `yaml:"device_cgroup_rules,omitempty" json:"device_cgroup_rules,omitempty"` DeviceCgroupRules []string `yaml:"device_cgroup_rules,omitempty" json:"device_cgroup_rules,omitempty"`
Devices []string `yaml:"devices,omitempty" json:"devices,omitempty"` Devices []DeviceMapping `yaml:"devices,omitempty" json:"devices,omitempty"`
DNS StringList `yaml:"dns,omitempty" json:"dns,omitempty"` DNS StringList `yaml:"dns,omitempty" json:"dns,omitempty"`
DNSOpts []string `yaml:"dns_opt,omitempty" json:"dns_opt,omitempty"` DNSOpts []string `yaml:"dns_opt,omitempty" json:"dns_opt,omitempty"`
DNSSearch StringList `yaml:"dns_search,omitempty" json:"dns_search,omitempty"` DNSSearch StringList `yaml:"dns_search,omitempty" json:"dns_search,omitempty"`
@ -239,6 +239,10 @@ const (
NetworkModeContainerPrefix = ContainerPrefix NetworkModeContainerPrefix = ContainerPrefix
) )
const (
SecretConfigXValue = "x-#value"
)
// GetDependencies retrieves all services this service depends on // GetDependencies retrieves all services this service depends on
func (s ServiceConfig) GetDependencies() []string { func (s ServiceConfig) GetDependencies() []string {
var dependencies []string var dependencies []string
@ -301,6 +305,14 @@ type BlkioConfig struct {
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"` Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
} }
type DeviceMapping struct {
Source string `yaml:"source,omitempty" json:"source,omitempty"`
Target string `yaml:"target,omitempty" json:"target,omitempty"`
Permissions string `yaml:"permissions,omitempty" json:"permissions,omitempty"`
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
}
// WeightDevice is a structure that holds device:weight pair // WeightDevice is a structure that holds device:weight pair
type WeightDevice struct { type WeightDevice struct {
Path string Path string

2
vendor/modules.txt vendored
View File

@ -128,7 +128,7 @@ github.com/cenkalti/backoff/v4
# github.com/cespare/xxhash/v2 v2.2.0 # github.com/cespare/xxhash/v2 v2.2.0
## explicit; go 1.11 ## explicit; go 1.11
github.com/cespare/xxhash/v2 github.com/cespare/xxhash/v2
# github.com/compose-spec/compose-go/v2 v2.1.6 # github.com/compose-spec/compose-go/v2 v2.2.0
## explicit; go 1.21 ## explicit; go 1.21
github.com/compose-spec/compose-go/v2/cli github.com/compose-spec/compose-go/v2/cli
github.com/compose-spec/compose-go/v2/consts github.com/compose-spec/compose-go/v2/consts