1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135 |
- package loader
- import (
- "fmt"
- "io/ioutil"
- "os"
- "sort"
- "testing"
- "time"
- "github.com/docker/docker/cli/compose/types"
- "github.com/stretchr/testify/assert"
- )
- func buildConfigDetails(source map[string]interface{}, env map[string]string) types.ConfigDetails {
- workingDir, err := os.Getwd()
- if err != nil {
- panic(err)
- }
- return types.ConfigDetails{
- WorkingDir: workingDir,
- ConfigFiles: []types.ConfigFile{
- {Filename: "filename.yml", Config: source},
- },
- Environment: env,
- }
- }
- func loadYAML(yaml string) (*types.Config, error) {
- return loadYAMLWithEnv(yaml, nil)
- }
- func loadYAMLWithEnv(yaml string, env map[string]string) (*types.Config, error) {
- dict, err := ParseYAML([]byte(yaml))
- if err != nil {
- return nil, err
- }
- return Load(buildConfigDetails(dict, env))
- }
- var sampleYAML = `
- version: "3"
- services:
- foo:
- image: busybox
- networks:
- with_me:
- bar:
- image: busybox
- environment:
- - FOO=1
- networks:
- - with_ipam
- volumes:
- hello:
- driver: default
- driver_opts:
- beep: boop
- networks:
- default:
- driver: bridge
- driver_opts:
- beep: boop
- with_ipam:
- ipam:
- driver: default
- config:
- - subnet: 172.28.0.0/16
- `
- var sampleDict = map[string]interface{}{
- "version": "3",
- "services": map[string]interface{}{
- "foo": map[string]interface{}{
- "image": "busybox",
- "networks": map[string]interface{}{"with_me": nil},
- },
- "bar": map[string]interface{}{
- "image": "busybox",
- "environment": []interface{}{"FOO=1"},
- "networks": []interface{}{"with_ipam"},
- },
- },
- "volumes": map[string]interface{}{
- "hello": map[string]interface{}{
- "driver": "default",
- "driver_opts": map[string]interface{}{
- "beep": "boop",
- },
- },
- },
- "networks": map[string]interface{}{
- "default": map[string]interface{}{
- "driver": "bridge",
- "driver_opts": map[string]interface{}{
- "beep": "boop",
- },
- },
- "with_ipam": map[string]interface{}{
- "ipam": map[string]interface{}{
- "driver": "default",
- "config": []interface{}{
- map[string]interface{}{
- "subnet": "172.28.0.0/16",
- },
- },
- },
- },
- },
- }
- func strPtr(val string) *string {
- return &val
- }
- var sampleConfig = types.Config{
- Services: []types.ServiceConfig{
- {
- Name: "foo",
- Image: "busybox",
- Environment: map[string]*string{},
- Networks: map[string]*types.ServiceNetworkConfig{
- "with_me": nil,
- },
- },
- {
- Name: "bar",
- Image: "busybox",
- Environment: map[string]*string{"FOO": strPtr("1")},
- Networks: map[string]*types.ServiceNetworkConfig{
- "with_ipam": nil,
- },
- },
- },
- Networks: map[string]types.NetworkConfig{
- "default": {
- Driver: "bridge",
- DriverOpts: map[string]string{
- "beep": "boop",
- },
- },
- "with_ipam": {
- Ipam: types.IPAMConfig{
- Driver: "default",
- Config: []*types.IPAMPool{
- {
- Subnet: "172.28.0.0/16",
- },
- },
- },
- },
- },
- Volumes: map[string]types.VolumeConfig{
- "hello": {
- Driver: "default",
- DriverOpts: map[string]string{
- "beep": "boop",
- },
- },
- },
- }
- func TestParseYAML(t *testing.T) {
- dict, err := ParseYAML([]byte(sampleYAML))
- if !assert.NoError(t, err) {
- return
- }
- assert.Equal(t, sampleDict, dict)
- }
- func TestLoad(t *testing.T) {
- actual, err := Load(buildConfigDetails(sampleDict, nil))
- if !assert.NoError(t, err) {
- return
- }
- assert.Equal(t, serviceSort(sampleConfig.Services), serviceSort(actual.Services))
- assert.Equal(t, sampleConfig.Networks, actual.Networks)
- assert.Equal(t, sampleConfig.Volumes, actual.Volumes)
- }
- func TestLoadV31(t *testing.T) {
- actual, err := loadYAML(`
- version: "3.1"
- services:
- foo:
- image: busybox
- secrets: [super]
- secrets:
- super:
- external: true
- `)
- if !assert.NoError(t, err) {
- return
- }
- assert.Equal(t, len(actual.Services), 1)
- assert.Equal(t, len(actual.Secrets), 1)
- }
- func TestParseAndLoad(t *testing.T) {
- actual, err := loadYAML(sampleYAML)
- if !assert.NoError(t, err) {
- return
- }
- assert.Equal(t, serviceSort(sampleConfig.Services), serviceSort(actual.Services))
- assert.Equal(t, sampleConfig.Networks, actual.Networks)
- assert.Equal(t, sampleConfig.Volumes, actual.Volumes)
- }
- func TestInvalidTopLevelObjectType(t *testing.T) {
- _, err := loadYAML("1")
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "Top-level object must be a mapping")
- _, err = loadYAML("\"hello\"")
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "Top-level object must be a mapping")
- _, err = loadYAML("[\"hello\"]")
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "Top-level object must be a mapping")
- }
- func TestNonStringKeys(t *testing.T) {
- _, err := loadYAML(`
- version: "3"
- 123:
- foo:
- image: busybox
- `)
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "Non-string key at top level: 123")
- _, err = loadYAML(`
- version: "3"
- services:
- foo:
- image: busybox
- 123:
- image: busybox
- `)
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "Non-string key in services: 123")
- _, err = loadYAML(`
- version: "3"
- services:
- foo:
- image: busybox
- networks:
- default:
- ipam:
- config:
- - 123: oh dear
- `)
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "Non-string key in networks.default.ipam.config[0]: 123")
- _, err = loadYAML(`
- version: "3"
- services:
- dict-env:
- image: busybox
- environment:
- 1: FOO
- `)
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "Non-string key in services.dict-env.environment: 1")
- }
- func TestSupportedVersion(t *testing.T) {
- _, err := loadYAML(`
- version: "3"
- services:
- foo:
- image: busybox
- `)
- assert.NoError(t, err)
- _, err = loadYAML(`
- version: "3.0"
- services:
- foo:
- image: busybox
- `)
- assert.NoError(t, err)
- }
- func TestUnsupportedVersion(t *testing.T) {
- _, err := loadYAML(`
- version: "2"
- services:
- foo:
- image: busybox
- `)
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "version")
- _, err = loadYAML(`
- version: "2.0"
- services:
- foo:
- image: busybox
- `)
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "version")
- }
- func TestInvalidVersion(t *testing.T) {
- _, err := loadYAML(`
- version: 3
- services:
- foo:
- image: busybox
- `)
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "version must be a string")
- }
- func TestV1Unsupported(t *testing.T) {
- _, err := loadYAML(`
- foo:
- image: busybox
- `)
- assert.Error(t, err)
- }
- func TestNonMappingObject(t *testing.T) {
- _, err := loadYAML(`
- version: "3"
- services:
- - foo:
- image: busybox
- `)
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "services must be a mapping")
- _, err = loadYAML(`
- version: "3"
- services:
- foo: busybox
- `)
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "services.foo must be a mapping")
- _, err = loadYAML(`
- version: "3"
- networks:
- - default:
- driver: bridge
- `)
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "networks must be a mapping")
- _, err = loadYAML(`
- version: "3"
- networks:
- default: bridge
- `)
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "networks.default must be a mapping")
- _, err = loadYAML(`
- version: "3"
- volumes:
- - data:
- driver: local
- `)
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "volumes must be a mapping")
- _, err = loadYAML(`
- version: "3"
- volumes:
- data: local
- `)
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "volumes.data must be a mapping")
- }
- func TestNonStringImage(t *testing.T) {
- _, err := loadYAML(`
- version: "3"
- services:
- foo:
- image: ["busybox", "latest"]
- `)
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "services.foo.image must be a string")
- }
- func TestLoadWithEnvironment(t *testing.T) {
- config, err := loadYAMLWithEnv(`
- version: "3"
- services:
- dict-env:
- image: busybox
- environment:
- FOO: "1"
- BAR: 2
- BAZ: 2.5
- QUX:
- QUUX:
- list-env:
- image: busybox
- environment:
- - FOO=1
- - BAR=2
- - BAZ=2.5
- - QUX=
- - QUUX
- `, map[string]string{"QUX": "qux"})
- assert.NoError(t, err)
- expected := types.MappingWithEquals{
- "FOO": strPtr("1"),
- "BAR": strPtr("2"),
- "BAZ": strPtr("2.5"),
- "QUX": strPtr("qux"),
- "QUUX": nil,
- }
- assert.Equal(t, 2, len(config.Services))
- for _, service := range config.Services {
- assert.Equal(t, expected, service.Environment)
- }
- }
- func TestInvalidEnvironmentValue(t *testing.T) {
- _, err := loadYAML(`
- version: "3"
- services:
- dict-env:
- image: busybox
- environment:
- FOO: ["1"]
- `)
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "services.dict-env.environment.FOO must be a string, number or null")
- }
- func TestInvalidEnvironmentObject(t *testing.T) {
- _, err := loadYAML(`
- version: "3"
- services:
- dict-env:
- image: busybox
- environment: "FOO=1"
- `)
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "services.dict-env.environment must be a mapping")
- }
- func TestEnvironmentInterpolation(t *testing.T) {
- home := "/home/foo"
- config, err := loadYAMLWithEnv(`
- version: "3"
- services:
- test:
- image: busybox
- labels:
- - home1=$HOME
- - home2=${HOME}
- - nonexistent=$NONEXISTENT
- - default=${NONEXISTENT-default}
- networks:
- test:
- driver: $HOME
- volumes:
- test:
- driver: $HOME
- `, map[string]string{
- "HOME": home,
- "FOO": "foo",
- })
- assert.NoError(t, err)
- expectedLabels := types.Labels{
- "home1": home,
- "home2": home,
- "nonexistent": "",
- "default": "default",
- }
- assert.Equal(t, expectedLabels, config.Services[0].Labels)
- assert.Equal(t, home, config.Networks["test"].Driver)
- assert.Equal(t, home, config.Volumes["test"].Driver)
- }
- func TestUnsupportedProperties(t *testing.T) {
- dict, err := ParseYAML([]byte(`
- version: "3"
- services:
- web:
- image: web
- build: ./web
- links:
- - bar
- db:
- image: db
- build: ./db
- `))
- assert.NoError(t, err)
- configDetails := buildConfigDetails(dict, nil)
- _, err = Load(configDetails)
- assert.NoError(t, err)
- unsupported := GetUnsupportedProperties(configDetails)
- assert.Equal(t, []string{"build", "links"}, unsupported)
- }
- func TestDeprecatedProperties(t *testing.T) {
- dict, err := ParseYAML([]byte(`
- version: "3"
- services:
- web:
- image: web
- container_name: web
- db:
- image: db
- container_name: db
- expose: ["5434"]
- `))
- assert.NoError(t, err)
- configDetails := buildConfigDetails(dict, nil)
- _, err = Load(configDetails)
- assert.NoError(t, err)
- deprecated := GetDeprecatedProperties(configDetails)
- assert.Equal(t, 2, len(deprecated))
- assert.Contains(t, deprecated, "container_name")
- assert.Contains(t, deprecated, "expose")
- }
- func TestForbiddenProperties(t *testing.T) {
- _, err := loadYAML(`
- version: "3"
- services:
- foo:
- image: busybox
- volumes:
- - /data
- volume_driver: some-driver
- bar:
- extends:
- service: foo
- `)
- assert.Error(t, err)
- assert.IsType(t, &ForbiddenPropertiesError{}, err)
- fmt.Println(err)
- forbidden := err.(*ForbiddenPropertiesError).Properties
- assert.Equal(t, 2, len(forbidden))
- assert.Contains(t, forbidden, "volume_driver")
- assert.Contains(t, forbidden, "extends")
- }
- func TestInvalidExternalAndDriverCombination(t *testing.T) {
- _, err := loadYAML(`
- version: "3"
- volumes:
- external_volume:
- external: true
- driver: foobar
- `)
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "conflicting parameters \"external\" and \"driver\" specified for volume")
- assert.Contains(t, err.Error(), "external_volume")
- }
- func TestInvalidExternalAndDirverOptsCombination(t *testing.T) {
- _, err := loadYAML(`
- version: "3"
- volumes:
- external_volume:
- external: true
- driver_opts:
- beep: boop
- `)
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "conflicting parameters \"external\" and \"driver_opts\" specified for volume")
- assert.Contains(t, err.Error(), "external_volume")
- }
- func TestInvalidExternalAndLabelsCombination(t *testing.T) {
- _, err := loadYAML(`
- version: "3"
- volumes:
- external_volume:
- external: true
- labels:
- - beep=boop
- `)
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "conflicting parameters \"external\" and \"labels\" specified for volume")
- assert.Contains(t, err.Error(), "external_volume")
- }
- func durationPtr(value time.Duration) *time.Duration {
- return &value
- }
- func int64Ptr(value int64) *int64 {
- return &value
- }
- func uint64Ptr(value uint64) *uint64 {
- return &value
- }
- func TestFullExample(t *testing.T) {
- bytes, err := ioutil.ReadFile("full-example.yml")
- assert.NoError(t, err)
- homeDir := "/home/foo"
- env := map[string]string{"HOME": homeDir, "QUX": "qux_from_environment"}
- config, err := loadYAMLWithEnv(string(bytes), env)
- if !assert.NoError(t, err) {
- return
- }
- workingDir, err := os.Getwd()
- assert.NoError(t, err)
- stopGracePeriod := time.Duration(20 * time.Second)
- expectedServiceConfig := types.ServiceConfig{
- Name: "foo",
- CapAdd: []string{"ALL"},
- CapDrop: []string{"NET_ADMIN", "SYS_ADMIN"},
- CgroupParent: "m-executor-abcd",
- Command: []string{"bundle", "exec", "thin", "-p", "3000"},
- ContainerName: "my-web-container",
- DependsOn: []string{"db", "redis"},
- Deploy: types.DeployConfig{
- Mode: "replicated",
- Replicas: uint64Ptr(6),
- Labels: map[string]string{"FOO": "BAR"},
- UpdateConfig: &types.UpdateConfig{
- Parallelism: uint64Ptr(3),
- Delay: time.Duration(10 * time.Second),
- FailureAction: "continue",
- Monitor: time.Duration(60 * time.Second),
- MaxFailureRatio: 0.3,
- },
- Resources: types.Resources{
- Limits: &types.Resource{
- NanoCPUs: "0.001",
- MemoryBytes: 50 * 1024 * 1024,
- },
- Reservations: &types.Resource{
- NanoCPUs: "0.0001",
- MemoryBytes: 20 * 1024 * 1024,
- },
- },
- RestartPolicy: &types.RestartPolicy{
- Condition: "on_failure",
- Delay: durationPtr(5 * time.Second),
- MaxAttempts: uint64Ptr(3),
- Window: durationPtr(2 * time.Minute),
- },
- Placement: types.Placement{
- Constraints: []string{"node=foo"},
- },
- EndpointMode: "dnsrr",
- },
- Devices: []string{"/dev/ttyUSB0:/dev/ttyUSB0"},
- DNS: []string{"8.8.8.8", "9.9.9.9"},
- DNSSearch: []string{"dc1.example.com", "dc2.example.com"},
- DomainName: "foo.com",
- Entrypoint: []string{"/code/entrypoint.sh", "-p", "3000"},
- Environment: map[string]*string{
- "FOO": strPtr("foo_from_env_file"),
- "BAR": strPtr("bar_from_env_file_2"),
- "BAZ": strPtr("baz_from_service_def"),
- "QUX": strPtr("qux_from_environment"),
- },
- EnvFile: []string{
- "./example1.env",
- "./example2.env",
- },
- Expose: []string{"3000", "8000"},
- ExternalLinks: []string{
- "redis_1",
- "project_db_1:mysql",
- "project_db_1:postgresql",
- },
- ExtraHosts: map[string]string{
- "otherhost": "50.31.209.229",
- "somehost": "162.242.195.82",
- },
- HealthCheck: &types.HealthCheckConfig{
- Test: types.HealthCheckTest([]string{"CMD-SHELL", "echo \"hello world\""}),
- Interval: "10s",
- Timeout: "1s",
- Retries: uint64Ptr(5),
- },
- Hostname: "foo",
- Image: "redis",
- Ipc: "host",
- Labels: map[string]string{
- "com.example.description": "Accounting webapp",
- "com.example.number": "42",
- "com.example.empty-label": "",
- },
- Links: []string{
- "db",
- "db:database",
- "redis",
- },
- Logging: &types.LoggingConfig{
- Driver: "syslog",
- Options: map[string]string{
- "syslog-address": "tcp://192.168.0.42:123",
- },
- },
- MacAddress: "02:42:ac:11:65:43",
- NetworkMode: "container:0cfeab0f748b9a743dc3da582046357c6ef497631c1a016d28d2bf9b4f899f7b",
- Networks: map[string]*types.ServiceNetworkConfig{
- "some-network": {
- Aliases: []string{"alias1", "alias3"},
- Ipv4Address: "",
- Ipv6Address: "",
- },
- "other-network": {
- Ipv4Address: "172.16.238.10",
- Ipv6Address: "2001:3984:3989::10",
- },
- "other-other-network": nil,
- },
- Pid: "host",
- Ports: []types.ServicePortConfig{
- //"3000",
- {
- Mode: "ingress",
- Target: 3000,
- Protocol: "tcp",
- },
- //"3000-3005",
- {
- Mode: "ingress",
- Target: 3000,
- Protocol: "tcp",
- },
- {
- Mode: "ingress",
- Target: 3001,
- Protocol: "tcp",
- },
- {
- Mode: "ingress",
- Target: 3002,
- Protocol: "tcp",
- },
- {
- Mode: "ingress",
- Target: 3003,
- Protocol: "tcp",
- },
- {
- Mode: "ingress",
- Target: 3004,
- Protocol: "tcp",
- },
- {
- Mode: "ingress",
- Target: 3005,
- Protocol: "tcp",
- },
- //"8000:8000",
- {
- Mode: "ingress",
- Target: 8000,
- Published: 8000,
- Protocol: "tcp",
- },
- //"9090-9091:8080-8081",
- {
- Mode: "ingress",
- Target: 8080,
- Published: 9090,
- Protocol: "tcp",
- },
- {
- Mode: "ingress",
- Target: 8081,
- Published: 9091,
- Protocol: "tcp",
- },
- //"49100:22",
- {
- Mode: "ingress",
- Target: 22,
- Published: 49100,
- Protocol: "tcp",
- },
- //"127.0.0.1:8001:8001",
- {
- Mode: "ingress",
- Target: 8001,
- Published: 8001,
- Protocol: "tcp",
- },
- //"127.0.0.1:5000-5010:5000-5010",
- {
- Mode: "ingress",
- Target: 5000,
- Published: 5000,
- Protocol: "tcp",
- },
- {
- Mode: "ingress",
- Target: 5001,
- Published: 5001,
- Protocol: "tcp",
- },
- {
- Mode: "ingress",
- Target: 5002,
- Published: 5002,
- Protocol: "tcp",
- },
- {
- Mode: "ingress",
- Target: 5003,
- Published: 5003,
- Protocol: "tcp",
- },
- {
- Mode: "ingress",
- Target: 5004,
- Published: 5004,
- Protocol: "tcp",
- },
- {
- Mode: "ingress",
- Target: 5005,
- Published: 5005,
- Protocol: "tcp",
- },
- {
- Mode: "ingress",
- Target: 5006,
- Published: 5006,
- Protocol: "tcp",
- },
- {
- Mode: "ingress",
- Target: 5007,
- Published: 5007,
- Protocol: "tcp",
- },
- {
- Mode: "ingress",
- Target: 5008,
- Published: 5008,
- Protocol: "tcp",
- },
- {
- Mode: "ingress",
- Target: 5009,
- Published: 5009,
- Protocol: "tcp",
- },
- {
- Mode: "ingress",
- Target: 5010,
- Published: 5010,
- Protocol: "tcp",
- },
- },
- Privileged: true,
- ReadOnly: true,
- Restart: "always",
- SecurityOpt: []string{
- "label=level:s0:c100,c200",
- "label=type:svirt_apache_t",
- },
- StdinOpen: true,
- StopSignal: "SIGUSR1",
- StopGracePeriod: &stopGracePeriod,
- Tmpfs: []string{"/run", "/tmp"},
- Tty: true,
- Ulimits: map[string]*types.UlimitsConfig{
- "nproc": {
- Single: 65535,
- },
- "nofile": {
- Soft: 20000,
- Hard: 40000,
- },
- },
- User: "someone",
- Volumes: []types.ServiceVolumeConfig{
- {Target: "/var/lib/mysql", Type: "volume"},
- {Source: "/opt/data", Target: "/var/lib/mysql", Type: "bind"},
- {Source: workingDir, Target: "/code", Type: "bind"},
- {Source: workingDir + "/static", Target: "/var/www/html", Type: "bind"},
- {Source: homeDir + "/configs", Target: "/etc/configs/", Type: "bind", ReadOnly: true},
- {Source: "datavolume", Target: "/var/lib/mysql", Type: "volume"},
- {Source: workingDir + "/opt", Target: "/opt", Consistency: "cached", Type: "bind"},
- },
- WorkingDir: "/code",
- }
- assert.Equal(t, []types.ServiceConfig{expectedServiceConfig}, config.Services)
- expectedNetworkConfig := map[string]types.NetworkConfig{
- "some-network": {},
- "other-network": {
- Driver: "overlay",
- DriverOpts: map[string]string{
- "foo": "bar",
- "baz": "1",
- },
- Ipam: types.IPAMConfig{
- Driver: "overlay",
- Config: []*types.IPAMPool{
- {Subnet: "172.16.238.0/24"},
- {Subnet: "2001:3984:3989::/64"},
- },
- },
- },
- "external-network": {
- External: types.External{
- Name: "external-network",
- External: true,
- },
- },
- "other-external-network": {
- External: types.External{
- Name: "my-cool-network",
- External: true,
- },
- },
- }
- assert.Equal(t, expectedNetworkConfig, config.Networks)
- expectedVolumeConfig := map[string]types.VolumeConfig{
- "some-volume": {},
- "other-volume": {
- Driver: "flocker",
- DriverOpts: map[string]string{
- "foo": "bar",
- "baz": "1",
- },
- },
- "external-volume": {
- External: types.External{
- Name: "external-volume",
- External: true,
- },
- },
- "other-external-volume": {
- External: types.External{
- Name: "my-cool-volume",
- External: true,
- },
- },
- }
- assert.Equal(t, expectedVolumeConfig, config.Volumes)
- }
- func serviceSort(services []types.ServiceConfig) []types.ServiceConfig {
- sort.Sort(servicesByName(services))
- return services
- }
- type servicesByName []types.ServiceConfig
- func (sbn servicesByName) Len() int { return len(sbn) }
- func (sbn servicesByName) Swap(i, j int) { sbn[i], sbn[j] = sbn[j], sbn[i] }
- func (sbn servicesByName) Less(i, j int) bool { return sbn[i].Name < sbn[j].Name }
- func TestLoadAttachableNetwork(t *testing.T) {
- config, err := loadYAML(`
- version: "3.2"
- networks:
- mynet1:
- driver: overlay
- attachable: true
- mynet2:
- driver: bridge
- `)
- if !assert.NoError(t, err) {
- return
- }
- expected := map[string]types.NetworkConfig{
- "mynet1": {
- Driver: "overlay",
- Attachable: true,
- },
- "mynet2": {
- Driver: "bridge",
- Attachable: false,
- },
- }
- assert.Equal(t, expected, config.Networks)
- }
- func TestLoadExpandedPortFormat(t *testing.T) {
- config, err := loadYAML(`
- version: "3.2"
- services:
- web:
- image: busybox
- ports:
- - "80-82:8080-8082"
- - "90-92:8090-8092/udp"
- - "85:8500"
- - 8600
- - protocol: udp
- target: 53
- published: 10053
- - mode: host
- target: 22
- published: 10022
- `)
- if !assert.NoError(t, err) {
- return
- }
- expected := []types.ServicePortConfig{
- {
- Mode: "ingress",
- Target: 8080,
- Published: 80,
- Protocol: "tcp",
- },
- {
- Mode: "ingress",
- Target: 8081,
- Published: 81,
- Protocol: "tcp",
- },
- {
- Mode: "ingress",
- Target: 8082,
- Published: 82,
- Protocol: "tcp",
- },
- {
- Mode: "ingress",
- Target: 8090,
- Published: 90,
- Protocol: "udp",
- },
- {
- Mode: "ingress",
- Target: 8091,
- Published: 91,
- Protocol: "udp",
- },
- {
- Mode: "ingress",
- Target: 8092,
- Published: 92,
- Protocol: "udp",
- },
- {
- Mode: "ingress",
- Target: 8500,
- Published: 85,
- Protocol: "tcp",
- },
- {
- Mode: "ingress",
- Target: 8600,
- Published: 0,
- Protocol: "tcp",
- },
- {
- Target: 53,
- Published: 10053,
- Protocol: "udp",
- },
- {
- Mode: "host",
- Target: 22,
- Published: 10022,
- },
- }
- assert.Equal(t, 1, len(config.Services))
- assert.Equal(t, expected, config.Services[0].Ports)
- }
- func TestLoadExpandedMountFormat(t *testing.T) {
- config, err := loadYAML(`
- version: "3.2"
- services:
- web:
- image: busybox
- volumes:
- - type: volume
- source: foo
- target: /target
- read_only: true
- volumes:
- foo: {}
- `)
- if !assert.NoError(t, err) {
- return
- }
- expected := types.ServiceVolumeConfig{
- Type: "volume",
- Source: "foo",
- Target: "/target",
- ReadOnly: true,
- }
- assert.Equal(t, 1, len(config.Services))
- assert.Equal(t, 1, len(config.Services[0].Volumes))
- assert.Equal(t, expected, config.Services[0].Volumes[0])
- }
|