Merge pull request #3867 from thaJeztah/yaml_fixes

Some fixes in handling YAML files, and updates to tests
This commit is contained in:
Sebastiaan van Stijn 2022-11-17 16:20:37 +01:00 committed by GitHub
commit 099c5e7415
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 126 additions and 131 deletions

View File

@ -1,24 +1,24 @@
package manager package manager
import ( import (
"encoding/json"
"fmt" "fmt"
"testing" "testing"
"github.com/pkg/errors"
"gopkg.in/yaml.v2"
"gotest.tools/v3/assert" "gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
) )
func TestPluginError(t *testing.T) { func TestPluginError(t *testing.T) {
err := NewPluginError("new error") err := NewPluginError("new error")
assert.Error(t, err, "new error") assert.Check(t, is.Error(err, "new error"))
inner := fmt.Errorf("testing") inner := fmt.Errorf("testing")
err = wrapAsPluginError(inner, "wrapping") err = wrapAsPluginError(inner, "wrapping")
assert.Error(t, err, "wrapping: testing") assert.Check(t, is.Error(err, "wrapping: testing"))
assert.Assert(t, errors.Is(err, inner)) assert.Check(t, is.ErrorIs(err, inner))
actual, err := yaml.Marshal(err) actual, err := json.Marshal(err)
assert.NilError(t, err) assert.Check(t, err)
assert.Equal(t, "'wrapping: testing'\n", string(actual)) assert.Check(t, is.Equal(`"wrapping: testing"`, string(actual)))
} }

View File

@ -17,29 +17,30 @@ func TestConfigWithEmptyComposeFile(t *testing.T) {
assert.ErrorContains(t, cmd.Execute(), `Please specify a Compose file`) assert.ErrorContains(t, cmd.Execute(), `Please specify a Compose file`)
} }
var configMergeTests = []struct { func TestConfigMergeInterpolation(t *testing.T) {
name string tests := []struct {
skipInterpolation bool name string
first string skipInterpolation bool
second string fileOne string
merged string fileTwo string
}{ expected string
{ }{
name: "With Interpolation", {
skipInterpolation: false, name: "With Interpolation",
first: `version: "3.7" skipInterpolation: false,
fileOne: `version: "3.7"
services: services:
foo: foo:
image: busybox:latest image: busybox:latest
command: cat file1.txt command: cat file1.txt
`, `,
second: `version: "3.7" fileTwo: `version: "3.7"
services: services:
foo: foo:
image: busybox:${VERSION} image: busybox:${VERSION}
command: cat file2.txt command: cat file2.txt
`, `,
merged: `version: "3.7" expected: `version: "3.7"
services: services:
foo: foo:
command: command:
@ -47,23 +48,23 @@ services:
- file2.txt - file2.txt
image: busybox:1.0 image: busybox:1.0
`, `,
}, },
{ {
name: "Without Interpolation", name: "Without Interpolation",
skipInterpolation: true, skipInterpolation: true,
first: `version: "3.7" fileOne: `version: "3.7"
services: services:
foo: foo:
image: busybox:latest image: busybox:latest
command: cat file1.txt command: cat file1.txt
`, `,
second: `version: "3.7" fileTwo: `version: "3.7"
services: services:
foo: foo:
image: busybox:${VERSION} image: busybox:${VERSION}
command: cat file2.txt command: cat file2.txt
`, `,
merged: `version: "3.7" expected: `version: "3.7"
services: services:
foo: foo:
command: command:
@ -71,34 +72,27 @@ services:
- file2.txt - file2.txt
image: busybox:${VERSION} image: busybox:${VERSION}
`, `,
}, },
} }
func TestConfigMergeInterpolation(t *testing.T) { for _, tc := range tests {
for _, tt := range configMergeTests { t.Run(tc.name, func(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { firstConfigData, err := loader.ParseYAML([]byte(tc.fileOne))
firstConfig := []byte(tt.first) assert.Check(t, err)
secondConfig := []byte(tt.second) secondConfigData, err := loader.ParseYAML([]byte(tc.fileTwo))
assert.Check(t, err)
firstConfigData, err := loader.ParseYAML(firstConfig) actual, err := outputConfig(composetypes.ConfigDetails{
assert.NilError(t, err)
secondConfigData, err := loader.ParseYAML(secondConfig)
assert.NilError(t, err)
env := map[string]string{
"VERSION": "1.0",
}
cfg, err := outputConfig(composetypes.ConfigDetails{
ConfigFiles: []composetypes.ConfigFile{ ConfigFiles: []composetypes.ConfigFile{
{Config: firstConfigData, Filename: "firstConfig"}, {Config: firstConfigData, Filename: "firstConfig"},
{Config: secondConfigData, Filename: "secondConfig"}, {Config: secondConfigData, Filename: "secondConfig"},
}, },
Environment: env, Environment: map[string]string{
}, tt.skipInterpolation) "VERSION": "1.0",
assert.NilError(t, err) },
}, tc.skipInterpolation)
assert.Equal(t, cfg, tt.merged) assert.Check(t, err)
assert.Equal(t, tc.expected, actual)
}) })
} }
} }

View File

@ -197,7 +197,7 @@ func services(workingDir, homeDir string) []types.ServiceConfig {
}, },
Pid: "host", Pid: "host",
Ports: []types.ServicePortConfig{ Ports: []types.ServicePortConfig{
//"3000", // "3000",
{ {
Mode: "ingress", Mode: "ingress",
Target: 3000, Target: 3000,
@ -228,14 +228,14 @@ func services(workingDir, homeDir string) []types.ServiceConfig {
Target: 3005, Target: 3005,
Protocol: "tcp", Protocol: "tcp",
}, },
//"8000:8000", // "8000:8000",
{ {
Mode: "ingress", Mode: "ingress",
Target: 8000, Target: 8000,
Published: 8000, Published: 8000,
Protocol: "tcp", Protocol: "tcp",
}, },
//"9090-9091:8080-8081", // "9090-9091:8080-8081",
{ {
Mode: "ingress", Mode: "ingress",
Target: 8080, Target: 8080,
@ -248,21 +248,21 @@ func services(workingDir, homeDir string) []types.ServiceConfig {
Published: 9091, Published: 9091,
Protocol: "tcp", Protocol: "tcp",
}, },
//"49100:22", // "49100:22",
{ {
Mode: "ingress", Mode: "ingress",
Target: 22, Target: 22,
Published: 49100, Published: 49100,
Protocol: "tcp", Protocol: "tcp",
}, },
//"127.0.0.1:8001:8001", // "127.0.0.1:8001:8001",
{ {
Mode: "ingress", Mode: "ingress",
Target: 8001, Target: 8001,
Published: 8001, Published: 8001,
Protocol: "tcp", Protocol: "tcp",
}, },
//"127.0.0.1:5000-5010:5000-5010", // "127.0.0.1:5000-5010:5000-5010",
{ {
Mode: "ingress", Mode: "ingress",
Target: 5000, Target: 5000,

View File

@ -51,7 +51,7 @@ func ParseYAML(source []byte) (map[string]interface{}, error) {
} }
cfgMap, ok := cfg.(map[interface{}]interface{}) cfgMap, ok := cfg.(map[interface{}]interface{})
if !ok { if !ok {
return nil, errors.Errorf("Top-level object must be a mapping") return nil, errors.Errorf("top-level object must be a mapping")
} }
converted, err := convertToStringKeysRecursive(cfgMap, "") converted, err := convertToStringKeysRecursive(cfgMap, "")
if err != nil { if err != nil {
@ -386,9 +386,9 @@ func formatInvalidKeyError(keyPrefix string, key interface{}) error {
if keyPrefix == "" { if keyPrefix == "" {
location = "at top level" location = "at top level"
} else { } else {
location = fmt.Sprintf("in %s", keyPrefix) location = "in " + keyPrefix
} }
return errors.Errorf("Non-string key %s: %#v", location, key) return errors.Errorf("non-string key %s: %#v", location, key)
} }
// LoadServices produces a ServiceConfig map from a compose file Dict // LoadServices produces a ServiceConfig map from a compose file Dict

View File

@ -317,13 +317,13 @@ func TestParseAndLoad(t *testing.T) {
func TestInvalidTopLevelObjectType(t *testing.T) { func TestInvalidTopLevelObjectType(t *testing.T) {
_, err := loadYAML("1") _, err := loadYAML("1")
assert.ErrorContains(t, err, "Top-level object must be a mapping") assert.Check(t, is.ErrorContains(err, "top-level object must be a mapping"))
_, err = loadYAML("\"hello\"") _, err = loadYAML(`"hello"`)
assert.ErrorContains(t, err, "Top-level object must be a mapping") assert.Check(t, is.ErrorContains(err, "top-level object must be a mapping"))
_, err = loadYAML("[\"hello\"]") _, err = loadYAML(`["hello"]`)
assert.ErrorContains(t, err, "Top-level object must be a mapping") assert.Check(t, is.ErrorContains(err, "top-level object must be a mapping"))
} }
func TestNonStringKeys(t *testing.T) { func TestNonStringKeys(t *testing.T) {
@ -333,7 +333,7 @@ version: "3"
foo: foo:
image: busybox image: busybox
`) `)
assert.ErrorContains(t, err, "Non-string key at top level: 123") assert.Check(t, is.ErrorContains(err, "non-string key at top level: 123"))
_, err = loadYAML(` _, err = loadYAML(`
version: "3" version: "3"
@ -343,7 +343,7 @@ services:
123: 123:
image: busybox image: busybox
`) `)
assert.ErrorContains(t, err, "Non-string key in services: 123") assert.Check(t, is.ErrorContains(err, "non-string key in services: 123"))
_, err = loadYAML(` _, err = loadYAML(`
version: "3" version: "3"
@ -356,7 +356,7 @@ networks:
config: config:
- 123: oh dear - 123: oh dear
`) `)
assert.ErrorContains(t, err, "Non-string key in networks.default.ipam.config[0]: 123") assert.Check(t, is.ErrorContains(err, "non-string key in networks.default.ipam.config[0]: 123"))
_, err = loadYAML(` _, err = loadYAML(`
version: "3" version: "3"
@ -366,7 +366,7 @@ services:
environment: environment:
1: FOO 1: FOO
`) `)
assert.ErrorContains(t, err, "Non-string key in services.dict-env.environment: 1") assert.Check(t, is.ErrorContains(err, "non-string key in services.dict-env.environment: 1"))
} }
func TestSupportedVersion(t *testing.T) { func TestSupportedVersion(t *testing.T) {
@ -376,7 +376,7 @@ services:
foo: foo:
image: busybox image: busybox
`) `)
assert.NilError(t, err) assert.Check(t, err)
_, err = loadYAML(` _, err = loadYAML(`
version: "3.0" version: "3.0"
@ -384,7 +384,7 @@ services:
foo: foo:
image: busybox image: busybox
`) `)
assert.NilError(t, err) assert.Check(t, err)
} }
func TestUnsupportedVersion(t *testing.T) { func TestUnsupportedVersion(t *testing.T) {
@ -394,7 +394,7 @@ services:
foo: foo:
image: busybox image: busybox
`) `)
assert.ErrorContains(t, err, "version") assert.Check(t, is.ErrorContains(err, "version"))
_, err = loadYAML(` _, err = loadYAML(`
version: "2.0" version: "2.0"
@ -402,7 +402,7 @@ services:
foo: foo:
image: busybox image: busybox
`) `)
assert.ErrorContains(t, err, "version") assert.Check(t, is.ErrorContains(err, "version"))
} }
func TestInvalidVersion(t *testing.T) { func TestInvalidVersion(t *testing.T) {
@ -412,7 +412,7 @@ services:
foo: foo:
image: busybox image: busybox
`) `)
assert.ErrorContains(t, err, "version must be a string") assert.Check(t, is.ErrorContains(err, "version must be a string"))
} }
func TestV1Unsupported(t *testing.T) { func TestV1Unsupported(t *testing.T) {
@ -420,7 +420,7 @@ func TestV1Unsupported(t *testing.T) {
foo: foo:
image: busybox image: busybox
`) `)
assert.ErrorContains(t, err, "(root) Additional property foo is not allowed") assert.Check(t, is.ErrorContains(err, "(root) Additional property foo is not allowed"))
_, err = loadYAML(` _, err = loadYAML(`
version: "1.0" version: "1.0"
@ -428,7 +428,7 @@ foo:
image: busybox image: busybox
`) `)
assert.ErrorContains(t, err, "unsupported Compose file version: 1.0") assert.Check(t, is.ErrorContains(err, "unsupported Compose file version: 1.0"))
} }
func TestNonMappingObject(t *testing.T) { func TestNonMappingObject(t *testing.T) {
@ -438,14 +438,14 @@ services:
- foo: - foo:
image: busybox image: busybox
`) `)
assert.ErrorContains(t, err, "services must be a mapping") assert.Check(t, is.ErrorContains(err, "services must be a mapping"))
_, err = loadYAML(` _, err = loadYAML(`
version: "3" version: "3"
services: services:
foo: busybox foo: busybox
`) `)
assert.ErrorContains(t, err, "services.foo must be a mapping") assert.Check(t, is.ErrorContains(err, "services.foo must be a mapping"))
_, err = loadYAML(` _, err = loadYAML(`
version: "3" version: "3"
@ -453,14 +453,14 @@ networks:
- default: - default:
driver: bridge driver: bridge
`) `)
assert.ErrorContains(t, err, "networks must be a mapping") assert.Check(t, is.ErrorContains(err, "networks must be a mapping"))
_, err = loadYAML(` _, err = loadYAML(`
version: "3" version: "3"
networks: networks:
default: bridge default: bridge
`) `)
assert.ErrorContains(t, err, "networks.default must be a mapping") assert.Check(t, is.ErrorContains(err, "networks.default must be a mapping"))
_, err = loadYAML(` _, err = loadYAML(`
version: "3" version: "3"
@ -468,14 +468,14 @@ volumes:
- data: - data:
driver: local driver: local
`) `)
assert.ErrorContains(t, err, "volumes must be a mapping") assert.Check(t, is.ErrorContains(err, "volumes must be a mapping"))
_, err = loadYAML(` _, err = loadYAML(`
version: "3" version: "3"
volumes: volumes:
data: local data: local
`) `)
assert.ErrorContains(t, err, "volumes.data must be a mapping") assert.Check(t, is.ErrorContains(err, "volumes.data must be a mapping"))
} }
func TestNonStringImage(t *testing.T) { func TestNonStringImage(t *testing.T) {
@ -485,7 +485,7 @@ services:
foo: foo:
image: ["busybox", "latest"] image: ["busybox", "latest"]
`) `)
assert.ErrorContains(t, err, "services.foo.image must be a string") assert.Check(t, is.ErrorContains(err, "services.foo.image must be a string"))
} }
func TestLoadWithEnvironment(t *testing.T) { func TestLoadWithEnvironment(t *testing.T) {
@ -535,7 +535,7 @@ services:
environment: environment:
FOO: ["1"] FOO: ["1"]
`) `)
assert.ErrorContains(t, err, "services.dict-env.environment.FOO must be a string, number or null") assert.Check(t, is.ErrorContains(err, "services.dict-env.environment.FOO must be a string, number or null"))
} }
func TestInvalidEnvironmentObject(t *testing.T) { func TestInvalidEnvironmentObject(t *testing.T) {
@ -546,7 +546,7 @@ services:
image: busybox image: busybox
environment: "FOO=1" environment: "FOO=1"
`) `)
assert.ErrorContains(t, err, "services.dict-env.environment must be a mapping") assert.Check(t, is.ErrorContains(err, "services.dict-env.environment must be a mapping"))
} }
func TestLoadWithEnvironmentInterpolation(t *testing.T) { func TestLoadWithEnvironmentInterpolation(t *testing.T) {
@ -791,17 +791,16 @@ services:
// Default behavior keeps the `env_file` entries // Default behavior keeps the `env_file` entries
configWithEnvFiles, err := Load(configDetails) configWithEnvFiles, err := Load(configDetails)
assert.NilError(t, err) assert.NilError(t, err)
assert.DeepEqual(t, configWithEnvFiles.Services[0].EnvFile, types.StringList{ expected := types.StringList{"example1.env", "example2.env"}
"example1.env", assert.Check(t, is.DeepEqual(expected, configWithEnvFiles.Services[0].EnvFile))
"example2.env", assert.Check(t, is.DeepEqual(expectedEnvironmentMap, configWithEnvFiles.Services[0].Environment))
})
assert.DeepEqual(t, configWithEnvFiles.Services[0].Environment, expectedEnvironmentMap)
// Custom behavior removes the `env_file` entries // Custom behavior removes the `env_file` entries
configWithoutEnvFiles, err := Load(configDetails, WithDiscardEnvFiles) configWithoutEnvFiles, err := Load(configDetails, WithDiscardEnvFiles)
assert.NilError(t, err) assert.NilError(t, err)
assert.DeepEqual(t, configWithoutEnvFiles.Services[0].EnvFile, types.StringList(nil)) expected = types.StringList(nil)
assert.DeepEqual(t, configWithoutEnvFiles.Services[0].Environment, expectedEnvironmentMap) assert.Check(t, is.DeepEqual(expected, configWithoutEnvFiles.Services[0].EnvFile))
assert.Check(t, is.DeepEqual(expectedEnvironmentMap, configWithoutEnvFiles.Services[0].Environment))
} }
func TestBuildProperties(t *testing.T) { func TestBuildProperties(t *testing.T) {
@ -882,7 +881,7 @@ func TestInvalidResource(t *testing.T) {
impossible: impossible:
x: 1 x: 1
`) `)
assert.ErrorContains(t, err, "Additional property impossible is not allowed") assert.Check(t, is.ErrorContains(err, "Additional property impossible is not allowed"))
} }
func TestInvalidExternalAndDriverCombination(t *testing.T) { func TestInvalidExternalAndDriverCombination(t *testing.T) {
@ -894,8 +893,8 @@ volumes:
driver: foobar driver: foobar
`) `)
assert.ErrorContains(t, err, "conflicting parameters \"external\" and \"driver\" specified for volume") assert.Check(t, is.ErrorContains(err, `conflicting parameters "external" and "driver" specified for volume`))
assert.ErrorContains(t, err, "external_volume") assert.Check(t, is.ErrorContains(err, `external_volume`))
} }
func TestInvalidExternalAndDirverOptsCombination(t *testing.T) { func TestInvalidExternalAndDirverOptsCombination(t *testing.T) {
@ -908,8 +907,8 @@ volumes:
beep: boop beep: boop
`) `)
assert.ErrorContains(t, err, "conflicting parameters \"external\" and \"driver_opts\" specified for volume") assert.Check(t, is.ErrorContains(err, `conflicting parameters "external" and "driver_opts" specified for volume`))
assert.ErrorContains(t, err, "external_volume") assert.Check(t, is.ErrorContains(err, `external_volume`))
} }
func TestInvalidExternalAndLabelsCombination(t *testing.T) { func TestInvalidExternalAndLabelsCombination(t *testing.T) {
@ -922,8 +921,8 @@ volumes:
- beep=boop - beep=boop
`) `)
assert.ErrorContains(t, err, "conflicting parameters \"external\" and \"labels\" specified for volume") assert.Check(t, is.ErrorContains(err, `conflicting parameters "external" and "labels" specified for volume`))
assert.ErrorContains(t, err, "external_volume") assert.Check(t, is.ErrorContains(err, `external_volume`))
} }
func TestLoadVolumeInvalidExternalNameAndNameCombination(t *testing.T) { func TestLoadVolumeInvalidExternalNameAndNameCombination(t *testing.T) {
@ -936,8 +935,8 @@ volumes:
name: external_name name: external_name
`) `)
assert.ErrorContains(t, err, "volume.external.name and volume.name conflict; only use volume.name") assert.Check(t, is.ErrorContains(err, "volume.external.name and volume.name conflict; only use volume.name"))
assert.ErrorContains(t, err, "external_volume") assert.Check(t, is.ErrorContains(err, `external_volume`))
} }
func durationPtr(value time.Duration) *types.Duration { func durationPtr(value time.Duration) *types.Duration {
@ -954,25 +953,25 @@ func uint32Ptr(value uint32) *uint32 {
} }
func TestFullExample(t *testing.T) { func TestFullExample(t *testing.T) {
bytes, err := os.ReadFile("full-example.yml") data, err := os.ReadFile("full-example.yml")
assert.NilError(t, err) assert.NilError(t, err)
homeDir := "/home/foo" homeDir := "/home/foo"
env := map[string]string{"HOME": homeDir, "QUX": "qux_from_environment"} env := map[string]string{"HOME": homeDir, "QUX": "qux_from_environment"}
config, err := loadYAMLWithEnv(string(bytes), env) config, err := loadYAMLWithEnv(string(data), env)
assert.NilError(t, err) assert.NilError(t, err)
workingDir, err := os.Getwd() workingDir, err := os.Getwd()
assert.NilError(t, err) assert.NilError(t, err)
expectedConfig := fullExampleConfig(workingDir, homeDir) expected := fullExampleConfig(workingDir, homeDir)
assert.Check(t, is.DeepEqual(expectedConfig.Services, config.Services)) assert.Check(t, is.DeepEqual(expected.Services, config.Services))
assert.Check(t, is.DeepEqual(expectedConfig.Networks, config.Networks)) assert.Check(t, is.DeepEqual(expected.Networks, config.Networks))
assert.Check(t, is.DeepEqual(expectedConfig.Volumes, config.Volumes)) assert.Check(t, is.DeepEqual(expected.Volumes, config.Volumes))
assert.Check(t, is.DeepEqual(expectedConfig.Secrets, config.Secrets)) assert.Check(t, is.DeepEqual(expected.Secrets, config.Secrets))
assert.Check(t, is.DeepEqual(expectedConfig.Configs, config.Configs)) assert.Check(t, is.DeepEqual(expected.Configs, config.Configs))
assert.Check(t, is.DeepEqual(expectedConfig.Extras, config.Extras)) assert.Check(t, is.DeepEqual(expected.Extras, config.Extras))
} }
func TestLoadTmpfsVolume(t *testing.T) { func TestLoadTmpfsVolume(t *testing.T) {
@ -1014,7 +1013,7 @@ services:
tmpfs: tmpfs:
size: 10000 size: 10000
`) `)
assert.ErrorContains(t, err, "services.tmpfs.volumes.0 Additional property tmpfs is not allowed") assert.Check(t, is.ErrorContains(err, "services.tmpfs.volumes.0 Additional property tmpfs is not allowed"))
} }
func TestLoadBindMountSourceMustNotBeEmpty(t *testing.T) { func TestLoadBindMountSourceMustNotBeEmpty(t *testing.T) {
@ -1027,7 +1026,7 @@ services:
- type: bind - type: bind
target: /app target: /app
`) `)
assert.Error(t, err, `invalid mount config for type "bind": field Source must not be empty`) assert.Check(t, is.Error(err, `invalid mount config for type "bind": field Source must not be empty`))
} }
func TestLoadBindMountSourceIsWindowsAbsolute(t *testing.T) { func TestLoadBindMountSourceIsWindowsAbsolute(t *testing.T) {
@ -1243,8 +1242,9 @@ services:
`) `)
assert.NilError(t, err) assert.NilError(t, err)
expected := samplePortsConfig
assert.Check(t, is.Len(config.Services, 1)) assert.Check(t, is.Len(config.Services, 1))
assert.Check(t, is.DeepEqual(samplePortsConfig, config.Services[0].Ports)) assert.Check(t, is.DeepEqual(expected, config.Services[0].Ports))
} }
func TestLoadExpandedMountFormat(t *testing.T) { func TestLoadExpandedMountFormat(t *testing.T) {
@ -1334,7 +1334,7 @@ func TestLoadVolumesWarnOnDeprecatedExternalNameVersion34(t *testing.T) {
}, },
}, },
} }
volumes, err := LoadVolumes(source, "3.4") vols, err := LoadVolumes(source, "3.4")
assert.NilError(t, err) assert.NilError(t, err)
expected := map[string]types.VolumeConfig{ expected := map[string]types.VolumeConfig{
"foo": { "foo": {
@ -1342,7 +1342,7 @@ func TestLoadVolumesWarnOnDeprecatedExternalNameVersion34(t *testing.T) {
External: types.External{External: true}, External: types.External{External: true},
}, },
} }
assert.Check(t, is.DeepEqual(expected, volumes)) assert.Check(t, is.DeepEqual(expected, vols))
assert.Check(t, is.Contains(buf.String(), "volume.external.name is deprecated")) assert.Check(t, is.Contains(buf.String(), "volume.external.name is deprecated"))
} }
@ -1364,7 +1364,7 @@ func TestLoadVolumesWarnOnDeprecatedExternalNameVersion33(t *testing.T) {
}, },
}, },
} }
volumes, err := LoadVolumes(source, "3.3") vols, err := LoadVolumes(source, "3.3")
assert.NilError(t, err) assert.NilError(t, err)
expected := map[string]types.VolumeConfig{ expected := map[string]types.VolumeConfig{
"foo": { "foo": {
@ -1372,7 +1372,7 @@ func TestLoadVolumesWarnOnDeprecatedExternalNameVersion33(t *testing.T) {
External: types.External{External: true}, External: types.External{External: true},
}, },
} }
assert.Check(t, is.DeepEqual(expected, volumes)) assert.Check(t, is.DeepEqual(expected, vols))
assert.Check(t, is.Equal("", buf.String())) assert.Check(t, is.Equal("", buf.String()))
} }
@ -1432,8 +1432,8 @@ secrets:
name: external_name name: external_name
`) `)
assert.ErrorContains(t, err, "secret.external.name and secret.name conflict; only use secret.name") assert.Check(t, is.ErrorContains(err, "secret.external.name and secret.name conflict; only use secret.name"))
assert.ErrorContains(t, err, "external_secret") assert.Check(t, is.ErrorContains(err, "external_secret"))
} }
func TestLoadSecretsWarnOnDeprecatedExternalNameVersion35(t *testing.T) { func TestLoadSecretsWarnOnDeprecatedExternalNameVersion35(t *testing.T) {
@ -1450,7 +1450,7 @@ func TestLoadSecretsWarnOnDeprecatedExternalNameVersion35(t *testing.T) {
details := types.ConfigDetails{ details := types.ConfigDetails{
Version: "3.5", Version: "3.5",
} }
secrets, err := LoadSecrets(source, details) s, err := LoadSecrets(source, details)
assert.NilError(t, err) assert.NilError(t, err)
expected := map[string]types.SecretConfig{ expected := map[string]types.SecretConfig{
"foo": { "foo": {
@ -1458,7 +1458,7 @@ func TestLoadSecretsWarnOnDeprecatedExternalNameVersion35(t *testing.T) {
External: types.External{External: true}, External: types.External{External: true},
}, },
} }
assert.Check(t, is.DeepEqual(expected, secrets)) assert.Check(t, is.DeepEqual(expected, s))
assert.Check(t, is.Contains(buf.String(), "secret.external.name is deprecated")) assert.Check(t, is.Contains(buf.String(), "secret.external.name is deprecated"))
} }
@ -1473,7 +1473,7 @@ func TestLoadNetworksWarnOnDeprecatedExternalNameVersion35(t *testing.T) {
}, },
}, },
} }
networks, err := LoadNetworks(source, "3.5") nws, err := LoadNetworks(source, "3.5")
assert.NilError(t, err) assert.NilError(t, err)
expected := map[string]types.NetworkConfig{ expected := map[string]types.NetworkConfig{
"foo": { "foo": {
@ -1481,7 +1481,7 @@ func TestLoadNetworksWarnOnDeprecatedExternalNameVersion35(t *testing.T) {
External: types.External{External: true}, External: types.External{External: true},
}, },
} }
assert.Check(t, is.DeepEqual(expected, networks)) assert.Check(t, is.DeepEqual(expected, nws))
assert.Check(t, is.Contains(buf.String(), "network.external.name is deprecated")) assert.Check(t, is.Contains(buf.String(), "network.external.name is deprecated"))
} }
@ -1518,8 +1518,8 @@ networks:
name: external_name name: external_name
`) `)
assert.ErrorContains(t, err, "network.external.name and network.name conflict; only use network.name") assert.Check(t, is.ErrorContains(err, "network.external.name and network.name conflict; only use network.name"))
assert.ErrorContains(t, err, "foo") assert.Check(t, is.ErrorContains(err, "foo"))
} }
func TestLoadNetworkWithName(t *testing.T) { func TestLoadNetworkWithName(t *testing.T) {
@ -1556,7 +1556,7 @@ networks:
"network3": {}, "network3": {},
}, },
} }
assert.DeepEqual(t, config, expected, cmpopts.EquateEmpty()) assert.Check(t, is.DeepEqual(expected, config, cmpopts.EquateEmpty()))
} }
func TestLoadInit(t *testing.T) { func TestLoadInit(t *testing.T) {
@ -1603,7 +1603,7 @@ services:
config, err := loadYAML(testcase.yaml) config, err := loadYAML(testcase.yaml)
assert.NilError(t, err) assert.NilError(t, err)
assert.Check(t, is.Len(config.Services, 1)) assert.Check(t, is.Len(config.Services, 1))
assert.Check(t, is.DeepEqual(config.Services[0].Init, testcase.init)) assert.Check(t, is.DeepEqual(testcase.init, config.Services[0].Init))
}) })
} }
} }
@ -1731,7 +1731,7 @@ secrets:
}, },
}, },
} }
assert.DeepEqual(t, config, expected, cmpopts.EquateEmpty()) assert.Check(t, is.DeepEqual(expected, config, cmpopts.EquateEmpty()))
} }
func TestLoadSecretDriver(t *testing.T) { func TestLoadSecretDriver(t *testing.T) {
@ -1795,5 +1795,5 @@ secrets:
}, },
}, },
} }
assert.DeepEqual(t, config, expected, cmpopts.EquateEmpty()) assert.Check(t, is.DeepEqual(expected, config, cmpopts.EquateEmpty()))
} }

View File

@ -19,8 +19,8 @@ func TestMarshallConfig(t *testing.T) {
assert.NilError(t, err) assert.NilError(t, err)
assert.Check(t, is.Equal(expected, string(actual))) assert.Check(t, is.Equal(expected, string(actual)))
// Make sure the expected still // Make sure the expected can be parsed.
dict, err := ParseYAML([]byte("version: '3.10'\n" + expected)) dict, err := ParseYAML([]byte(expected))
assert.NilError(t, err) assert.NilError(t, err)
_, err = Load(buildConfigDetails(dict, map[string]string{})) _, err = Load(buildConfigDetails(dict, map[string]string{}))
assert.NilError(t, err) assert.NilError(t, err)

View File

@ -441,7 +441,8 @@ func (u *UlimitsConfig) MarshalYAML() (interface{}, error) {
if u.Single != 0 { if u.Single != 0 {
return u.Single, nil return u.Single, nil
} }
return u, nil // Return as a value to avoid re-entering this method and use the default implementation
return *u, nil
} }
// MarshalJSON makes UlimitsConfig implement json.Marshaller // MarshalJSON makes UlimitsConfig implement json.Marshaller