mirror of
https://github.com/docker/cli.git
synced 2026-09-27 01:40:31 -04:00
cli/compose: modernize with slices and maps packages
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
@@ -11,7 +11,6 @@ import (
|
||||
"net/netip"
|
||||
"os"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -235,8 +234,8 @@ func convertServiceNetworks(
|
||||
nets = append(nets, netAttachConfig)
|
||||
}
|
||||
|
||||
sort.Slice(nets, func(i, j int) bool {
|
||||
return nets[i].Target < nets[j].Target
|
||||
slices.SortFunc(nets, func(a, b swarm.NetworkAttachmentConfig) int {
|
||||
return cmp.Compare(a.Target, b.Target)
|
||||
})
|
||||
return nets, nil
|
||||
}
|
||||
@@ -276,8 +275,10 @@ func convertServiceSecrets(
|
||||
return nil, err
|
||||
}
|
||||
// sort to ensure idempotence (don't restart services just because the entries are in different order)
|
||||
sort.SliceStable(secrs, func(i, j int) bool { return secrs[i].SecretName < secrs[j].SecretName })
|
||||
return secrs, err
|
||||
slices.SortStableFunc(secrs, func(a, b *swarm.SecretReference) int {
|
||||
return cmp.Compare(a.SecretName, b.SecretName)
|
||||
})
|
||||
return secrs, nil
|
||||
}
|
||||
|
||||
// convertServiceConfigObjs takes an API client, a namespace, a ServiceConfig,
|
||||
@@ -352,8 +353,10 @@ func convertServiceConfigObjs(
|
||||
return nil, err
|
||||
}
|
||||
// sort to ensure idempotence (don't restart services just because the entries are in different order)
|
||||
sort.SliceStable(confs, func(i, j int) bool { return confs[i].ConfigName < confs[j].ConfigName })
|
||||
return confs, err
|
||||
slices.SortStableFunc(confs, func(a, b *swarm.ConfigReference) int {
|
||||
return cmp.Compare(a.ConfigName, b.ConfigName)
|
||||
})
|
||||
return confs, nil
|
||||
}
|
||||
|
||||
type swarmReferenceTarget struct {
|
||||
@@ -609,8 +612,7 @@ func convertEndpointSpec(endpointMode string, source []composetypes.ServicePortC
|
||||
// convertEnvironment converts key/value mappings to a slice, and sorts
|
||||
// the results.
|
||||
func convertEnvironment(source map[string]*string) []string {
|
||||
var output []string
|
||||
|
||||
output := make([]string, 0, len(source))
|
||||
for name, value := range source {
|
||||
switch value {
|
||||
case nil:
|
||||
@@ -619,7 +621,7 @@ func convertEnvironment(source map[string]*string) []string {
|
||||
output = append(output, name+"="+*value)
|
||||
}
|
||||
}
|
||||
sort.Strings(output)
|
||||
slices.Sort(output)
|
||||
return output
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ package loader
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -35,20 +36,16 @@ var interpolateTypeCastMapping = map[interp.Path]interp.Cast{
|
||||
servicePath("tty"): toBoolean,
|
||||
servicePath("volumes", interp.PathMatchList, "read_only"): toBoolean,
|
||||
servicePath("volumes", interp.PathMatchList, "volume", "nocopy"): toBoolean,
|
||||
iPath("networks", interp.PathMatchAll, "external"): toBoolean,
|
||||
iPath("networks", interp.PathMatchAll, "internal"): toBoolean,
|
||||
iPath("networks", interp.PathMatchAll, "attachable"): toBoolean,
|
||||
iPath("volumes", interp.PathMatchAll, "external"): toBoolean,
|
||||
iPath("secrets", interp.PathMatchAll, "external"): toBoolean,
|
||||
iPath("configs", interp.PathMatchAll, "external"): toBoolean,
|
||||
}
|
||||
|
||||
func iPath(parts ...string) interp.Path {
|
||||
return interp.NewPath(parts...)
|
||||
interp.NewPath("networks", interp.PathMatchAll, "external"): toBoolean,
|
||||
interp.NewPath("networks", interp.PathMatchAll, "internal"): toBoolean,
|
||||
interp.NewPath("networks", interp.PathMatchAll, "attachable"): toBoolean,
|
||||
interp.NewPath("volumes", interp.PathMatchAll, "external"): toBoolean,
|
||||
interp.NewPath("secrets", interp.PathMatchAll, "external"): toBoolean,
|
||||
interp.NewPath("configs", interp.PathMatchAll, "external"): toBoolean,
|
||||
}
|
||||
|
||||
func servicePath(parts ...string) interp.Path {
|
||||
return iPath(append([]string{"services", interp.PathMatchAll}, parts...)...)
|
||||
return interp.NewPath(slices.Concat([]string{"services", interp.PathMatchAll}, parts)...)
|
||||
}
|
||||
|
||||
func toInt(value string) (any, error) {
|
||||
@@ -70,7 +67,3 @@ func toBoolean(value string) (any, error) {
|
||||
return nil, fmt.Errorf("invalid boolean: %s", value)
|
||||
}
|
||||
}
|
||||
|
||||
func interpolateConfig(configDict map[string]any, opts interp.Options) (map[string]any, error) {
|
||||
return interp.Interpolate(configDict, opts)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -111,7 +111,7 @@ func Load(configDetails types.ConfigDetails, opt ...func(*Options)) (*types.Conf
|
||||
}
|
||||
|
||||
if !options.SkipInterpolation {
|
||||
configDict, err = interpolateConfig(configDict, *options.Interpolate)
|
||||
configDict, err = interp.Interpolate(configDict, *options.Interpolate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -231,16 +231,7 @@ func GetUnsupportedProperties(configDicts ...map[string]any) []string {
|
||||
}
|
||||
}
|
||||
|
||||
return sortedKeys(unsupported)
|
||||
}
|
||||
|
||||
func sortedKeys(set map[string]bool) []string {
|
||||
keys := make([]string, 0, len(set))
|
||||
for key := range set {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
return slices.Sorted(maps.Keys(unsupported))
|
||||
}
|
||||
|
||||
// GetDeprecatedProperties returns the list of any deprecated properties that
|
||||
@@ -827,8 +818,7 @@ func transformListOrMapping(listOrMapping any, sep string, allowNil bool, allowS
|
||||
result := make([]string, 0, len(value))
|
||||
for _, entry := range value {
|
||||
for i, allowSep := range allowSeps {
|
||||
entry := fmt.Sprint(entry)
|
||||
k, v, ok := strings.Cut(entry, allowSep)
|
||||
k, v, ok := strings.Cut(fmt.Sprint(entry), allowSep)
|
||||
if ok {
|
||||
// Entry uses this allowed separator. Add it to the result, using
|
||||
// sep as a separator.
|
||||
@@ -885,7 +875,7 @@ var transformShellCommand TransformerFunc = func(value any) (any, error) {
|
||||
var transformHealthCheckTest TransformerFunc = func(data any) (any, error) {
|
||||
switch value := data.(type) {
|
||||
case string:
|
||||
return append([]string{"CMD-SHELL"}, value), nil
|
||||
return []string{"CMD-SHELL", value}, nil
|
||||
case []any:
|
||||
return value, nil
|
||||
default:
|
||||
@@ -925,16 +915,10 @@ func toServicePortConfigs(value string) ([]any, error) {
|
||||
return nil, err
|
||||
}
|
||||
// We need to sort the key of the ports to make sure it is consistent
|
||||
keys := make([]string, 0, len(ports))
|
||||
for port := range ports {
|
||||
keys = append(keys, string(port))
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
var portConfigs []any
|
||||
for _, key := range keys {
|
||||
for _, key := range slices.Sorted(maps.Keys(ports)) {
|
||||
// Reuse ConvertPortToPortConfig so that it is consistent
|
||||
port, err := network.ParsePort(key)
|
||||
port, err := network.ParsePort(string(key))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -975,13 +959,13 @@ func toString(value any, allowNil bool) any {
|
||||
}
|
||||
|
||||
func toStringList(value map[string]any, separator string, allowNil bool) []string {
|
||||
output := []string{}
|
||||
output := make([]string, 0, len(value))
|
||||
for key, value := range value {
|
||||
if value == nil && !allowNil {
|
||||
continue
|
||||
}
|
||||
output = append(output, fmt.Sprintf("%s%s%s", key, separator, value))
|
||||
output = append(output, fmt.Sprintf("%s%s%v", key, separator, value))
|
||||
}
|
||||
sort.Strings(output)
|
||||
slices.Sort(output)
|
||||
return output
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"bytes"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/compose/types"
|
||||
@@ -236,7 +235,9 @@ func TestLoad(t *testing.T) {
|
||||
actual, err := Load(buildConfigDetails(sampleDict, nil))
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(sampleConfig.Version, actual.Version))
|
||||
assert.Check(t, is.DeepEqual(serviceSort(sampleConfig.Services), serviceSort(actual.Services)))
|
||||
assert.Check(t, is.DeepEqual(sampleConfig.Services, actual.Services, cmpopts.SortSlices(func(a, b types.ServiceConfig) bool {
|
||||
return a.Name < b.Name
|
||||
})))
|
||||
assert.Check(t, is.DeepEqual(sampleConfig.Networks, actual.Networks))
|
||||
assert.Check(t, is.DeepEqual(sampleConfig.Volumes, actual.Volumes))
|
||||
}
|
||||
@@ -310,7 +311,9 @@ services:
|
||||
func TestParseAndLoad(t *testing.T) {
|
||||
actual, err := loadYAML(sampleYAML)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(serviceSort(sampleConfig.Services), serviceSort(actual.Services)))
|
||||
assert.Check(t, is.DeepEqual(sampleConfig.Services, actual.Services, cmpopts.SortSlices(func(a, b types.ServiceConfig) bool {
|
||||
return a.Name < b.Name
|
||||
})))
|
||||
assert.Check(t, is.DeepEqual(sampleConfig.Networks, actual.Networks))
|
||||
assert.Check(t, is.DeepEqual(sampleConfig.Volumes, actual.Volumes))
|
||||
}
|
||||
@@ -1191,13 +1194,6 @@ services:
|
||||
assert.ErrorContains(t, err, "services.tmpfs.volumes.0.tmpfs.size: must be an integer")
|
||||
}
|
||||
|
||||
func serviceSort(services []types.ServiceConfig) []types.ServiceConfig {
|
||||
sort.Slice(services, func(i, j int) bool {
|
||||
return services[i].Name < services[j].Name
|
||||
})
|
||||
return services
|
||||
}
|
||||
|
||||
func TestLoadAttachableNetwork(t *testing.T) {
|
||||
config, err := loadYAML(`
|
||||
version: "3.2"
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sort"
|
||||
|
||||
"dario.cat/mergo"
|
||||
"github.com/docker/cli/cli/compose/types"
|
||||
@@ -152,7 +151,9 @@ func toServiceSecretConfigsSlice(dst reflect.Value, m map[any]any) error {
|
||||
for _, v := range m {
|
||||
s = append(s, v.(types.ServiceSecretConfig))
|
||||
}
|
||||
sort.Slice(s, func(i, j int) bool { return s[i].Source < s[j].Source })
|
||||
slices.SortFunc(s, func(a, b types.ServiceSecretConfig) int {
|
||||
return cmp.Compare(a.Source, b.Source)
|
||||
})
|
||||
dst.Set(reflect.ValueOf(s))
|
||||
return nil
|
||||
}
|
||||
@@ -162,7 +163,9 @@ func toSServiceConfigObjConfigsSlice(dst reflect.Value, m map[any]any) error {
|
||||
for _, v := range m {
|
||||
s = append(s, v.(types.ServiceConfigObjConfig))
|
||||
}
|
||||
sort.Slice(s, func(i, j int) bool { return s[i].Source < s[j].Source })
|
||||
slices.SortFunc(s, func(a, b types.ServiceConfigObjConfig) int {
|
||||
return cmp.Compare(a.Source, b.Source)
|
||||
})
|
||||
dst.Set(reflect.ValueOf(s))
|
||||
return nil
|
||||
}
|
||||
@@ -172,7 +175,9 @@ func toServicePortConfigsSlice(dst reflect.Value, m map[any]any) error {
|
||||
for _, v := range m {
|
||||
s = append(s, v.(types.ServicePortConfig))
|
||||
}
|
||||
sort.Slice(s, func(i, j int) bool { return s[i].Published < s[j].Published })
|
||||
slices.SortFunc(s, func(a, b types.ServicePortConfig) int {
|
||||
return cmp.Compare(a.Published, b.Published)
|
||||
})
|
||||
dst.Set(reflect.ValueOf(s))
|
||||
return nil
|
||||
}
|
||||
@@ -182,7 +187,9 @@ func toServiceVolumeConfigsSlice(dst reflect.Value, m map[any]any) error {
|
||||
for _, v := range m {
|
||||
s = append(s, v.(types.ServiceVolumeConfig))
|
||||
}
|
||||
sort.Slice(s, func(i, j int) bool { return s[i].Target < s[j].Target })
|
||||
slices.SortFunc(s, func(a, b types.ServiceVolumeConfig) int {
|
||||
return cmp.Compare(a.Target, b.Target)
|
||||
})
|
||||
dst.Set(reflect.ValueOf(s))
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user