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/Microsoft/go-winio v0.6.2
github.com/aws/aws-sdk-go-v2/config v1.26.6
github.com/compose-spec/compose-go/v2 v2.1.6
github.com/compose-spec/compose-go/v2 v2.2.0
github.com/containerd/console v1.0.4
github.com/containerd/containerd v1.7.21
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/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
github.com/compose-spec/compose-go/v2 v2.1.6 h1:d0Cs0DffmOwmSzs0YPHwKCskknGq2jfGg4uGowlEpps=
github.com/compose-spec/compose-go/v2 v2.1.6/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc=
github.com/compose-spec/compose-go/v2 v2.2.0 h1:VsQosGhuO+H9wh5laiIiAe4TVd73kQ5NWwmNrdm0HRA=
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/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
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()
}
func (o *ProjectOptions) GeConfigFiles() ([]types.ConfigFile, error) {
func (o *ProjectOptions) GetConfigFiles() ([]types.ConfigFile, error) {
configPaths, err := o.getConfigPaths()
if err != nil {
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
func (o *ProjectOptions) prepare() (types.ConfigDetails, error) {
configs, err := o.GeConfigFiles()
configs, err := o.GetConfigFiles()
if err != nil {
return types.ConfigDetails{}, err
}

View File

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

View File

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

View File

@ -163,8 +163,15 @@ func getExtendsBaseFromFile(
if err != nil {
return nil, nil, err
}
services := source["services"].(map[string]any)
_, ok := services[ref]
m, ok := source["services"]
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 {
return nil, nil, fmt.Errorf(
"cannot extend service %q in %s: service %q not found in %s",

View File

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

View File

@ -738,7 +738,9 @@ func Transform(source interface{}, target interface{}) error {
DecodeHook: mapstructure.ComposeDecodeHookFunc(
nameServices,
decoderHook,
cast),
cast,
secretConfigDecoderHook,
),
Result: target,
TagName: "yaml",
Metadata: &data,
@ -764,6 +766,28 @@ func nameServices(from reflect.Value, to reflect.Value) (interface{}, error) {
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
func convertToStringKeysRecursive(value interface{}, keyPrefix string) (interface{}, error) {
if mapping, ok := value.(map[string]interface{}); ok {

View File

@ -36,7 +36,6 @@ func init() {
unique["services.*.annotations"] = keyValueIndexer
unique["services.*.build.args"] = keyValueIndexer
unique["services.*.build.additional_contexts"] = keyValueIndexer
unique["services.*.build.extra_hosts"] = keyValueIndexer
unique["services.*.build.platform"] = keyValueIndexer
unique["services.*.build.tags"] = keyValueIndexer
unique["services.*.build.labels"] = keyValueIndexer
@ -51,7 +50,6 @@ func init() {
unique["services.*.environment"] = keyValueIndexer
unique["services.*.env_file"] = envFileIndexer
unique["services.*.expose"] = exposeIndexer
unique["services.*.extra_hosts"] = keyValueIndexer
unique["services.*.labels"] = keyValueIndexer
unique["services.*.links"] = keyValueIndexer
unique["services.*.networks.*.aliases"] = keyValueIndexer
@ -62,6 +60,7 @@ func init() {
unique["services.*.sysctls"] = keyValueIndexer
unique["services.*.tmpfs"] = keyValueIndexer
unique["services.*.volumes"] = volumeIndexer
unique["services.*.devices"] = deviceMappingIndexer
}
// 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
}
func keyValueIndexer(y any, p tree.Path) (string, error) {
switch value := y.(type) {
func keyValueIndexer(v any, p tree.Path) (string, error) {
switch value := v.(type) {
case string:
key, _, found := strings.Cut(value, "=")
if !found {
return value, nil
}
if found {
return key, nil
}
return value, nil
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
}
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) {
switch v := a.(type) {
case string:

View File

@ -19,7 +19,6 @@
"include": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/include"
},
"description": "compose sub-projects to be included."
@ -115,7 +114,7 @@
"pull": {"type": ["boolean", "string"]},
"target": {"type": "string"},
"shm_size": {"type": ["integer", "string"]},
"extra_hosts": {"$ref": "#/definitions/list_or_dict"},
"extra_hosts": {"$ref": "#/definitions/extra_hosts"},
"isolation": {"type": "string"},
"privileged": {"type": ["boolean", "string"]},
"secrets": {"$ref": "#/definitions/service_config_or_secret"},
@ -216,7 +215,25 @@
]
},
"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_opt": {"type": "array","items": {"type": "string"}, "uniqueItems": true},
"dns_search": {"$ref": "#/definitions/string_or_list"},
@ -249,7 +266,7 @@
]
},
"external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
"extra_hosts": {"$ref": "#/definitions/list_or_dict"},
"extra_hosts": {"$ref": "#/definitions/extra_hosts"},
"group_add": {
"type": "array",
"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": {
"type": "object",
"properties": {

View File

@ -33,6 +33,7 @@ func init() {
transformers["services.*.extends"] = transformExtends
transformers["services.*.networks"] = transformServiceNetworks
transformers["services.*.volumes.*"] = transformVolumeMount
transformers["services.*.devices.*"] = transformDeviceMapping
transformers["services.*.secrets.*"] = transformFileMount
transformers["services.*.configs.*"] = transformFileMount
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) {
dst.Devices = (dst.Devices)[:len(src.Devices)]
} else {
dst.Devices = make([]string, len(src.Devices))
dst.Devices = make([]DeviceMapping, len(src.Devices))
}
} else if len(src.Devices) < len(dst.Devices) {
dst.Devices = (dst.Devices)[:len(src.Devices)]
}
} else {
dst.Devices = make([]string, len(src.Devices))
dst.Devices = make([]DeviceMapping, len(src.Devices))
}
copy(dst.Devices, src.Devices)
}

View File

@ -36,9 +36,9 @@ func NewHostsList(hosts []string) (HostsList, error) {
if ok {
// Mapping found with this separator, stop here.
if ips, ok := list[host]; ok {
list[host] = append(ips, ip)
list[host] = append(ips, strings.Split(ip, ",")...)
} else {
list[host] = []string{ip}
list[host] = strings.Split(ip, ",")
}
found = true
break
@ -89,7 +89,18 @@ func (h *HostsList) DecodeMapstructure(value interface{}) error {
if e == nil {
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()
if err != nil {
@ -109,7 +120,7 @@ func (h *HostsList) DecodeMapstructure(value interface{}) error {
*h = list
return nil
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"`
Deploy *DeployConfig `yaml:"deploy,omitempty" json:"deploy,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"`
DNSOpts []string `yaml:"dns_opt,omitempty" json:"dns_opt,omitempty"`
DNSSearch StringList `yaml:"dns_search,omitempty" json:"dns_search,omitempty"`
@ -239,6 +239,10 @@ const (
NetworkModeContainerPrefix = ContainerPrefix
)
const (
SecretConfigXValue = "x-#value"
)
// GetDependencies retrieves all services this service depends on
func (s ServiceConfig) GetDependencies() []string {
var dependencies []string
@ -301,6 +305,14 @@ type BlkioConfig struct {
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
type WeightDevice struct {
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
## explicit; go 1.11
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
github.com/compose-spec/compose-go/v2/cli
github.com/compose-spec/compose-go/v2/consts