2016-12-20 16:26:49 -05:00
package loader
import (
2017-11-06 17:03:43 -05:00
"bytes"
2016-12-20 16:26:49 -05:00
"os"
2023-10-19 07:17:16 -04:00
"runtime"
2016-12-20 16:26:49 -05:00
"sort"
"testing"
"time"
2017-04-17 18:07:56 -04:00
"github.com/docker/cli/cli/compose/types"
2018-03-12 13:09:59 -04:00
"github.com/google/go-cmp/cmp/cmpopts"
2017-11-06 17:03:43 -05:00
"github.com/sirupsen/logrus"
2020-02-22 12:12:14 -05:00
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
2023-10-19 07:17:16 -04:00
"gotest.tools/v3/skip"
2016-12-20 16:26:49 -05:00
)
2017-03-22 10:42:03 -04:00
func buildConfigDetails ( source map [ string ] interface { } , env map [ string ] string ) types . ConfigDetails {
2016-12-20 16:26:49 -05:00
workingDir , err := os . Getwd ( )
if err != nil {
panic ( err )
}
return types . ConfigDetails {
WorkingDir : workingDir ,
ConfigFiles : [ ] types . ConfigFile {
{ Filename : "filename.yml" , Config : source } ,
} ,
2017-02-07 04:44:47 -05:00
Environment : env ,
2016-12-20 16:26:49 -05:00
}
}
2017-03-14 12:39:26 -04:00
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 ) )
}
2016-12-20 16:26:49 -05:00
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
`
2017-03-22 10:42:03 -04:00
var sampleDict = map [ string ] interface { } {
2016-12-20 16:26:49 -05:00
"version" : "3" ,
2017-03-22 10:42:03 -04:00
"services" : map [ string ] interface { } {
"foo" : map [ string ] interface { } {
2016-12-20 16:26:49 -05:00
"image" : "busybox" ,
2017-03-22 10:42:03 -04:00
"networks" : map [ string ] interface { } { "with_me" : nil } ,
2016-12-20 16:26:49 -05:00
} ,
2017-03-22 10:42:03 -04:00
"bar" : map [ string ] interface { } {
2016-12-20 16:26:49 -05:00
"image" : "busybox" ,
"environment" : [ ] interface { } { "FOO=1" } ,
"networks" : [ ] interface { } { "with_ipam" } ,
} ,
} ,
2017-03-22 10:42:03 -04:00
"volumes" : map [ string ] interface { } {
"hello" : map [ string ] interface { } {
2016-12-20 16:26:49 -05:00
"driver" : "default" ,
2017-03-22 10:42:03 -04:00
"driver_opts" : map [ string ] interface { } {
2016-12-20 16:26:49 -05:00
"beep" : "boop" ,
} ,
} ,
} ,
2017-03-22 10:42:03 -04:00
"networks" : map [ string ] interface { } {
"default" : map [ string ] interface { } {
2016-12-20 16:26:49 -05:00
"driver" : "bridge" ,
2017-03-22 10:42:03 -04:00
"driver_opts" : map [ string ] interface { } {
2016-12-20 16:26:49 -05:00
"beep" : "boop" ,
} ,
} ,
2017-03-22 10:42:03 -04:00
"with_ipam" : map [ string ] interface { } {
"ipam" : map [ string ] interface { } {
2016-12-20 16:26:49 -05:00
"driver" : "default" ,
"config" : [ ] interface { } {
2017-03-22 10:42:03 -04:00
map [ string ] interface { } {
2016-12-20 16:26:49 -05:00
"subnet" : "172.28.0.0/16" ,
} ,
} ,
} ,
} ,
} ,
}
2018-10-02 07:00:11 -04:00
var samplePortsConfig = [ ] 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 ,
} ,
}
2017-03-14 12:39:26 -04:00
func strPtr ( val string ) * string {
return & val
}
2016-12-20 16:26:49 -05:00
var sampleConfig = types . Config {
2023-10-19 06:52:15 -04:00
Version : "3.12" ,
2016-12-20 16:26:49 -05:00
Services : [ ] types . ServiceConfig {
{
Name : "foo" ,
Image : "busybox" ,
2017-03-14 12:39:26 -04:00
Environment : map [ string ] * string { } ,
2016-12-20 16:26:49 -05:00
Networks : map [ string ] * types . ServiceNetworkConfig {
"with_me" : nil ,
} ,
} ,
{
Name : "bar" ,
Image : "busybox" ,
2017-03-14 12:39:26 -04:00
Environment : map [ string ] * string { "FOO" : strPtr ( "1" ) } ,
2016-12-20 16:26:49 -05:00
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 ) )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
assert . Check ( t , is . DeepEqual ( sampleDict , dict ) )
2016-12-20 16:26:49 -05:00
}
func TestLoad ( t * testing . T ) {
2017-02-07 04:44:47 -05:00
actual , err := Load ( buildConfigDetails ( sampleDict , nil ) )
2018-03-05 18:53:52 -05:00
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 . Networks , actual . Networks ) )
assert . Check ( t , is . DeepEqual ( sampleConfig . Volumes , actual . Volumes ) )
2016-12-20 16:26:49 -05:00
}
2018-06-25 04:51:56 -04:00
func TestLoadExtras ( t * testing . T ) {
actual , err := loadYAML ( `
version : "3.7"
services :
foo :
image : busybox
x - foo : bar ` )
assert . NilError ( t , err )
assert . Check ( t , is . Len ( actual . Services , 1 ) )
service := actual . Services [ 0 ]
assert . Check ( t , is . Equal ( "busybox" , service . Image ) )
extras := map [ string ] interface { } {
"x-foo" : "bar" ,
}
assert . Check ( t , is . DeepEqual ( extras , service . Extras ) )
}
2017-01-10 17:40:53 -05:00
func TestLoadV31 ( t * testing . T ) {
actual , err := loadYAML ( `
version : "3.1"
services :
foo :
image : busybox
secrets : [ super ]
secrets :
super :
external : true
2017-03-14 12:39:26 -04:00
` )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
assert . Check ( t , is . Len ( actual . Services , 1 ) )
assert . Check ( t , is . Len ( actual . Secrets , 1 ) )
2017-01-10 17:40:53 -05:00
}
2017-05-11 08:30:04 -04:00
func TestLoadV33 ( t * testing . T ) {
actual , err := loadYAML ( `
version : "3.3"
services :
foo :
image : busybox
credential_spec :
2019-02-02 11:55:39 -05:00
file : "/foo"
2017-05-15 11:19:32 -04:00
configs : [ super ]
configs :
super :
external : true
2017-05-11 08:30:04 -04:00
` )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
assert . Assert ( t , is . Len ( actual . Services , 1 ) )
assert . Check ( t , is . Equal ( actual . Services [ 0 ] . CredentialSpec . File , "/foo" ) )
assert . Assert ( t , is . Len ( actual . Configs , 1 ) )
2017-05-11 08:30:04 -04:00
}
2019-02-02 10:35:26 -05:00
func TestLoadV38 ( t * testing . T ) {
actual , err := loadYAML ( `
version : "3.8"
services :
foo :
image : busybox
credential_spec :
config : "0bt9dmxjvjiqermk6xrop3ekq"
` )
assert . NilError ( t , err )
assert . Assert ( t , is . Len ( actual . Services , 1 ) )
assert . Check ( t , is . Equal ( actual . Services [ 0 ] . CredentialSpec . Config , "0bt9dmxjvjiqermk6xrop3ekq" ) )
}
2016-12-20 16:26:49 -05:00
func TestParseAndLoad ( t * testing . T ) {
2017-03-14 12:39:26 -04:00
actual , err := loadYAML ( sampleYAML )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
assert . Check ( t , is . DeepEqual ( serviceSort ( sampleConfig . Services ) , serviceSort ( actual . Services ) ) )
assert . Check ( t , is . DeepEqual ( sampleConfig . Networks , actual . Networks ) )
assert . Check ( t , is . DeepEqual ( sampleConfig . Volumes , actual . Volumes ) )
2016-12-20 16:26:49 -05:00
}
func TestInvalidTopLevelObjectType ( t * testing . T ) {
2017-03-14 12:39:26 -04:00
_ , err := loadYAML ( "1" )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "top-level object must be a mapping" ) )
2016-12-20 16:26:49 -05:00
2022-11-16 16:21:16 -05:00
_ , err = loadYAML ( ` "hello" ` )
assert . Check ( t , is . ErrorContains ( err , "top-level object must be a mapping" ) )
2016-12-20 16:26:49 -05:00
2022-11-16 16:21:16 -05:00
_ , err = loadYAML ( ` ["hello"] ` )
assert . Check ( t , is . ErrorContains ( err , "top-level object must be a mapping" ) )
2016-12-20 16:26:49 -05:00
}
func TestNonStringKeys ( t * testing . T ) {
_ , err := loadYAML ( `
version : "3"
123 :
foo :
image : busybox
2017-03-14 12:39:26 -04:00
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "non-string key at top level: 123" ) )
2016-12-20 16:26:49 -05:00
_ , err = loadYAML ( `
version : "3"
services :
foo :
image : busybox
123 :
image : busybox
2017-03-14 12:39:26 -04:00
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "non-string key in services: 123" ) )
2016-12-20 16:26:49 -05:00
_ , err = loadYAML ( `
version : "3"
services :
foo :
image : busybox
networks :
default :
ipam :
config :
- 123 : oh dear
2017-03-14 12:39:26 -04:00
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "non-string key in networks.default.ipam.config[0]: 123" ) )
2016-12-20 16:26:49 -05:00
_ , err = loadYAML ( `
version : "3"
services :
dict - env :
image : busybox
environment :
1 : FOO
2017-03-14 12:39:26 -04:00
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "non-string key in services.dict-env.environment: 1" ) )
2016-12-20 16:26:49 -05:00
}
func TestSupportedVersion ( t * testing . T ) {
_ , err := loadYAML ( `
version : "3"
services :
foo :
image : busybox
2017-03-14 12:39:26 -04:00
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , err )
2016-12-20 16:26:49 -05:00
_ , err = loadYAML ( `
version : "3.0"
services :
foo :
image : busybox
2017-03-14 12:39:26 -04:00
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , err )
2016-12-20 16:26:49 -05:00
}
func TestUnsupportedVersion ( t * testing . T ) {
_ , err := loadYAML ( `
version : "2"
services :
foo :
image : busybox
2017-03-14 12:39:26 -04:00
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "version" ) )
2016-12-20 16:26:49 -05:00
_ , err = loadYAML ( `
version : "2.0"
services :
foo :
image : busybox
2017-03-14 12:39:26 -04:00
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "version" ) )
2016-12-20 16:26:49 -05:00
}
func TestInvalidVersion ( t * testing . T ) {
_ , err := loadYAML ( `
version : 3
services :
foo :
image : busybox
2017-03-14 12:39:26 -04:00
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "version must be a string" ) )
2016-12-20 16:26:49 -05:00
}
func TestV1Unsupported ( t * testing . T ) {
_ , err := loadYAML ( `
foo :
image : busybox
2017-03-14 12:39:26 -04:00
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "(root) Additional property foo is not allowed" ) )
2021-08-16 08:00:37 -04:00
_ , err = loadYAML ( `
version : "1.0"
foo :
image : busybox
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "unsupported Compose file version: 1.0" ) )
2016-12-20 16:26:49 -05:00
}
func TestNonMappingObject ( t * testing . T ) {
_ , err := loadYAML ( `
version : "3"
services :
- foo :
image : busybox
2017-03-14 12:39:26 -04:00
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "services must be a mapping" ) )
2016-12-20 16:26:49 -05:00
_ , err = loadYAML ( `
version : "3"
services :
foo : busybox
2017-03-14 12:39:26 -04:00
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "services.foo must be a mapping" ) )
2016-12-20 16:26:49 -05:00
_ , err = loadYAML ( `
version : "3"
networks :
- default :
driver : bridge
2017-03-14 12:39:26 -04:00
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "networks must be a mapping" ) )
2016-12-20 16:26:49 -05:00
_ , err = loadYAML ( `
version : "3"
networks :
default : bridge
2017-03-14 12:39:26 -04:00
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "networks.default must be a mapping" ) )
2016-12-20 16:26:49 -05:00
_ , err = loadYAML ( `
version : "3"
volumes :
- data :
driver : local
2017-03-14 12:39:26 -04:00
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "volumes must be a mapping" ) )
2016-12-20 16:26:49 -05:00
_ , err = loadYAML ( `
version : "3"
volumes :
data : local
2017-03-14 12:39:26 -04:00
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "volumes.data must be a mapping" ) )
2016-12-20 16:26:49 -05:00
}
func TestNonStringImage ( t * testing . T ) {
_ , err := loadYAML ( `
version : "3"
services :
foo :
image : [ "busybox" , "latest" ]
2017-03-14 12:39:26 -04:00
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "services.foo.image must be a string" ) )
2016-12-20 16:26:49 -05:00
}
2023-04-18 09:51:49 -04:00
func TestIgnoreBuildProperties ( t * testing . T ) {
_ , err := loadYAML ( `
services :
foo :
image : busybox
build :
unsupported_prop : foo
` )
assert . NilError ( t , err )
}
2017-03-14 12:39:26 -04:00
func TestLoadWithEnvironment ( t * testing . T ) {
config , err := loadYAMLWithEnv ( `
2016-12-20 16:26:49 -05:00
version : "3"
services :
dict - env :
image : busybox
environment :
FOO : "1"
BAR : 2
BAZ : 2.5
2017-02-07 04:44:47 -05:00
QUX :
2016-12-20 16:26:49 -05:00
QUUX :
list - env :
image : busybox
environment :
- FOO = 1
- BAR = 2
- BAZ = 2.5
2017-03-14 12:39:26 -04:00
- QUX =
- QUUX
2017-02-07 04:44:47 -05:00
` , map [ string ] string { "QUX" : "qux" } )
2018-03-06 14:44:13 -05:00
assert . NilError ( t , err )
2016-12-20 16:26:49 -05:00
2017-01-18 15:27:02 -05:00
expected := types . MappingWithEquals {
2017-03-14 12:39:26 -04:00
"FOO" : strPtr ( "1" ) ,
"BAR" : strPtr ( "2" ) ,
"BAZ" : strPtr ( "2.5" ) ,
"QUX" : strPtr ( "qux" ) ,
"QUUX" : nil ,
2016-12-20 16:26:49 -05:00
}
2018-03-05 18:53:52 -05:00
assert . Check ( t , is . Equal ( 2 , len ( config . Services ) ) )
2016-12-20 16:26:49 -05:00
for _ , service := range config . Services {
2018-03-05 18:53:52 -05:00
assert . Check ( t , is . DeepEqual ( expected , service . Environment ) )
2016-12-20 16:26:49 -05:00
}
}
func TestInvalidEnvironmentValue ( t * testing . T ) {
_ , err := loadYAML ( `
version : "3"
services :
dict - env :
image : busybox
environment :
FOO : [ "1" ]
2017-03-14 12:39:26 -04:00
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "services.dict-env.environment.FOO must be a string, number or null" ) )
2016-12-20 16:26:49 -05:00
}
func TestInvalidEnvironmentObject ( t * testing . T ) {
_ , err := loadYAML ( `
version : "3"
services :
dict - env :
image : busybox
environment : "FOO=1"
2017-03-14 12:39:26 -04:00
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "services.dict-env.environment must be a mapping" ) )
2016-12-20 16:26:49 -05:00
}
2017-10-03 18:03:20 -04:00
func TestLoadWithEnvironmentInterpolation ( t * testing . T ) {
2017-02-07 04:44:47 -05:00
home := "/home/foo"
2017-03-14 12:39:26 -04:00
config , err := loadYAMLWithEnv ( `
2016-12-20 16:26:49 -05:00
version : "3"
services :
test :
image : busybox
labels :
- home1 = $ HOME
- home2 = $ { HOME }
- nonexistent = $ NONEXISTENT
- default = $ { NONEXISTENT - default }
networks :
test :
driver : $ HOME
volumes :
test :
driver : $ HOME
2017-02-07 04:44:47 -05:00
` , map [ string ] string {
"HOME" : home ,
"FOO" : "foo" ,
} )
2016-12-20 16:26:49 -05:00
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2016-12-20 16:26:49 -05:00
2017-03-14 12:39:26 -04:00
expectedLabels := types . Labels {
2016-12-20 16:26:49 -05:00
"home1" : home ,
"home2" : home ,
"nonexistent" : "" ,
"default" : "default" ,
}
2018-03-05 18:53:52 -05:00
assert . Check ( t , is . DeepEqual ( expectedLabels , config . Services [ 0 ] . Labels ) )
assert . Check ( t , is . Equal ( home , config . Networks [ "test" ] . Driver ) )
assert . Check ( t , is . Equal ( home , config . Volumes [ "test" ] . Driver ) )
2016-12-20 16:26:49 -05:00
}
2017-10-03 18:03:20 -04:00
func TestLoadWithInterpolationCastFull ( t * testing . T ) {
dict , err := ParseYAML ( [ ] byte ( `
2019-11-04 08:21:47 -05:00
version : "3.8"
2017-10-03 18:03:20 -04:00
services :
web :
configs :
- source : appconfig
mode : $ theint
secrets :
- source : super
mode : $ theint
healthcheck :
retries : $ { theint }
disable : $ thebool
deploy :
replicas : $ theint
update_config :
parallelism : $ theint
max_failure_ratio : $ thefloat
2019-06-26 09:22:08 -04:00
rollback_config :
parallelism : $ theint
max_failure_ratio : $ thefloat
2017-10-03 18:03:20 -04:00
restart_policy :
max_attempts : $ theint
2019-11-04 08:21:47 -05:00
placement :
max_replicas_per_node : $ theint
2017-10-03 18:03:20 -04:00
ports :
- $ theint
- "34567"
- target : $ theint
published : $ theint
ulimits :
2017-10-04 16:51:48 -04:00
nproc : $ theint
nofile :
hard : $ theint
2017-10-03 18:03:20 -04:00
soft : $ theint
privileged : $ thebool
read_only : $ thebool
stdin_open : $ { thebool }
tty : $ thebool
volumes :
- source : data
2017-10-04 16:51:48 -04:00
type : volume
2017-10-03 18:03:20 -04:00
read_only : $ thebool
volume :
nocopy : $ thebool
configs :
appconfig :
external : $ thebool
secrets :
super :
external : $ thebool
volumes :
data :
external : $ thebool
networks :
front :
external : $ thebool
internal : $ thebool
attachable : $ thebool
` ) )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2017-10-03 18:03:20 -04:00
env := map [ string ] string {
"theint" : "555" ,
"thefloat" : "3.14" ,
"thebool" : "true" ,
}
config , err := Load ( buildConfigDetails ( dict , env ) )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2017-10-04 16:51:48 -04:00
expected := & types . Config {
2017-09-29 08:21:40 -04:00
Filename : "filename.yml" ,
2019-11-04 08:21:47 -05:00
Version : "3.8" ,
2017-10-04 16:51:48 -04:00
Services : [ ] types . ServiceConfig {
{
Name : "web" ,
Configs : [ ] types . ServiceConfigObjConfig {
{
Source : "appconfig" ,
Mode : uint32Ptr ( 555 ) ,
} ,
} ,
Secrets : [ ] types . ServiceSecretConfig {
{
Source : "super" ,
Mode : uint32Ptr ( 555 ) ,
} ,
} ,
HealthCheck : & types . HealthCheckConfig {
Retries : uint64Ptr ( 555 ) ,
Disable : true ,
} ,
Deploy : types . DeployConfig {
Replicas : uint64Ptr ( 555 ) ,
UpdateConfig : & types . UpdateConfig {
Parallelism : uint64Ptr ( 555 ) ,
MaxFailureRatio : 3.14 ,
} ,
2019-06-26 09:22:08 -04:00
RollbackConfig : & types . UpdateConfig {
Parallelism : uint64Ptr ( 555 ) ,
MaxFailureRatio : 3.14 ,
} ,
2017-10-04 16:51:48 -04:00
RestartPolicy : & types . RestartPolicy {
MaxAttempts : uint64Ptr ( 555 ) ,
} ,
2019-11-04 08:21:47 -05:00
Placement : types . Placement {
MaxReplicas : 555 ,
} ,
2017-10-04 16:51:48 -04:00
} ,
Ports : [ ] types . ServicePortConfig {
{ Target : 555 , Mode : "ingress" , Protocol : "tcp" } ,
{ Target : 34567 , Mode : "ingress" , Protocol : "tcp" } ,
{ Target : 555 , Published : 555 } ,
} ,
Ulimits : map [ string ] * types . UlimitsConfig {
"nproc" : { Single : 555 } ,
"nofile" : { Hard : 555 , Soft : 555 } ,
} ,
Privileged : true ,
ReadOnly : true ,
StdinOpen : true ,
Tty : true ,
Volumes : [ ] types . ServiceVolumeConfig {
{
Source : "data" ,
Type : "volume" ,
ReadOnly : true ,
Volume : & types . ServiceVolumeVolume { NoCopy : true } ,
} ,
} ,
Environment : types . MappingWithEquals { } ,
} ,
} ,
Configs : map [ string ] types . ConfigObjConfig {
2017-11-10 08:35:29 -05:00
"appconfig" : { External : types . External { External : true } , Name : "appconfig" } ,
2017-10-04 16:51:48 -04:00
} ,
Secrets : map [ string ] types . SecretConfig {
2017-11-10 08:35:29 -05:00
"super" : { External : types . External { External : true } , Name : "super" } ,
2017-10-04 16:51:48 -04:00
} ,
Volumes : map [ string ] types . VolumeConfig {
2017-11-06 17:03:43 -05:00
"data" : { External : types . External { External : true } , Name : "data" } ,
2017-10-04 16:51:48 -04:00
} ,
Networks : map [ string ] types . NetworkConfig {
"front" : {
2017-11-22 14:21:32 -05:00
External : types . External { External : true } ,
Name : "front" ,
2017-10-04 16:51:48 -04:00
Internal : true ,
Attachable : true ,
} ,
} ,
}
2018-03-05 18:53:52 -05:00
assert . Check ( t , is . DeepEqual ( expected , config ) )
2017-10-03 18:03:20 -04:00
}
2016-12-20 16:26:49 -05:00
func TestUnsupportedProperties ( t * testing . T ) {
dict , err := ParseYAML ( [ ] byte ( `
version : "3"
services :
web :
image : web
2017-10-03 18:03:20 -04:00
build :
2017-09-13 10:47:17 -04:00
context : . / web
2016-12-20 16:26:49 -05:00
links :
- bar
2017-12-27 06:45:19 -05:00
pid : host
2016-12-20 16:26:49 -05:00
db :
image : db
2017-10-03 18:03:20 -04:00
build :
2017-09-13 10:47:17 -04:00
context : . / db
2016-12-20 16:26:49 -05:00
` ) )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2016-12-20 16:26:49 -05:00
2017-02-07 04:44:47 -05:00
configDetails := buildConfigDetails ( dict , nil )
2016-12-20 16:26:49 -05:00
_ , err = Load ( configDetails )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2016-12-20 16:26:49 -05:00
2017-09-29 08:21:40 -04:00
unsupported := GetUnsupportedProperties ( dict )
2018-03-05 18:53:52 -05:00
assert . Check ( t , is . DeepEqual ( [ ] string { "build" , "links" , "pid" } , unsupported ) )
2016-12-20 16:26:49 -05:00
}
2019-08-22 11:36:12 -04:00
func TestDiscardEnvFileOption ( t * testing . T ) {
dict , err := ParseYAML ( [ ] byte ( ` version : "3"
services :
web :
image : nginx
env_file :
- example1 . env
- example2 . env
` ) )
expectedEnvironmentMap := types . MappingWithEquals {
"FOO" : strPtr ( "foo_from_env_file" ) ,
"BAZ" : strPtr ( "baz_from_env_file" ) ,
"BAR" : strPtr ( "bar_from_env_file_2" ) , // Original value is overwritten by example2.env
"QUX" : strPtr ( "quz_from_env_file_2" ) ,
}
assert . NilError ( t , err )
configDetails := buildConfigDetails ( dict , nil )
// Default behavior keeps the `env_file` entries
configWithEnvFiles , err := Load ( configDetails )
assert . NilError ( t , err )
2022-11-16 16:21:16 -05:00
expected := types . StringList { "example1.env" , "example2.env" }
assert . Check ( t , is . DeepEqual ( expected , configWithEnvFiles . Services [ 0 ] . EnvFile ) )
assert . Check ( t , is . DeepEqual ( expectedEnvironmentMap , configWithEnvFiles . Services [ 0 ] . Environment ) )
2019-08-22 11:36:12 -04:00
// Custom behavior removes the `env_file` entries
configWithoutEnvFiles , err := Load ( configDetails , WithDiscardEnvFiles )
assert . NilError ( t , err )
2022-11-16 16:21:16 -05:00
expected = types . StringList ( nil )
assert . Check ( t , is . DeepEqual ( expected , configWithoutEnvFiles . Services [ 0 ] . EnvFile ) )
assert . Check ( t , is . DeepEqual ( expectedEnvironmentMap , configWithoutEnvFiles . Services [ 0 ] . Environment ) )
2019-08-22 11:36:12 -04:00
}
2017-09-20 05:42:08 -04:00
func TestBuildProperties ( t * testing . T ) {
dict , err := ParseYAML ( [ ] byte ( `
version : "3"
services :
web :
image : web
build : .
links :
- bar
db :
image : db
build :
context : . / db
` ) )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2017-09-20 05:42:08 -04:00
configDetails := buildConfigDetails ( dict , nil )
_ , err = Load ( configDetails )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2017-09-20 05:42:08 -04:00
}
2016-12-20 16:26:49 -05:00
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" ]
` ) )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2016-12-20 16:26:49 -05:00
2017-02-07 04:44:47 -05:00
configDetails := buildConfigDetails ( dict , nil )
2016-12-20 16:26:49 -05:00
_ , err = Load ( configDetails )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2016-12-20 16:26:49 -05:00
2017-09-29 08:21:40 -04:00
deprecated := GetDeprecatedProperties ( dict )
2018-03-05 18:53:52 -05:00
assert . Check ( t , is . Len ( deprecated , 2 ) )
assert . Check ( t , is . Contains ( deprecated , "container_name" ) )
assert . Check ( t , is . Contains ( deprecated , "expose" ) )
2016-12-20 16:26:49 -05:00
}
func TestForbiddenProperties ( t * testing . T ) {
_ , err := loadYAML ( `
version : "3"
services :
foo :
image : busybox
volumes :
- / data
volume_driver : some - driver
bar :
extends :
service : foo
2017-03-14 12:39:26 -04:00
` )
2016-12-20 16:26:49 -05:00
2022-09-29 09:34:20 -04:00
assert . ErrorType ( t , err , & ForbiddenPropertiesError { } )
2016-12-20 16:26:49 -05:00
2017-12-21 16:27:57 -05:00
props := err . ( * ForbiddenPropertiesError ) . Properties
2018-03-05 18:53:52 -05:00
assert . Check ( t , is . Len ( props , 2 ) )
assert . Check ( t , is . Contains ( props , "volume_driver" ) )
assert . Check ( t , is . Contains ( props , "extends" ) )
2016-12-20 16:26:49 -05:00
}
2017-08-17 18:41:21 -04:00
func TestInvalidResource ( t * testing . T ) {
_ , err := loadYAML ( `
version : "3"
services :
foo :
image : busybox
deploy :
resources :
impossible :
x : 1
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "Additional property impossible is not allowed" ) )
2017-08-17 18:41:21 -04:00
}
2017-02-20 01:12:36 -05:00
func TestInvalidExternalAndDriverCombination ( t * testing . T ) {
_ , err := loadYAML ( `
version : "3"
volumes :
external_volume :
external : true
driver : foobar
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , ` conflicting parameters "external" and "driver" specified for volume ` ) )
assert . Check ( t , is . ErrorContains ( err , ` external_volume ` ) )
2017-02-20 01:12:36 -05:00
}
func TestInvalidExternalAndDirverOptsCombination ( t * testing . T ) {
_ , err := loadYAML ( `
version : "3"
volumes :
external_volume :
external : true
driver_opts :
beep : boop
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , ` conflicting parameters "external" and "driver_opts" specified for volume ` ) )
assert . Check ( t , is . ErrorContains ( err , ` external_volume ` ) )
2017-02-20 01:12:36 -05:00
}
func TestInvalidExternalAndLabelsCombination ( t * testing . T ) {
_ , err := loadYAML ( `
version : "3"
volumes :
external_volume :
external : true
labels :
- beep = boop
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , ` conflicting parameters "external" and "labels" specified for volume ` ) )
assert . Check ( t , is . ErrorContains ( err , ` external_volume ` ) )
2017-02-20 01:12:36 -05:00
}
2017-11-22 14:21:32 -05:00
func TestLoadVolumeInvalidExternalNameAndNameCombination ( t * testing . T ) {
2017-06-29 19:25:50 -04:00
_ , err := loadYAML ( `
version : "3.4"
volumes :
external_volume :
name : user_specified_name
external :
2018-08-29 17:29:39 -04:00
name : external_name
2017-06-29 19:25:50 -04:00
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "volume.external.name and volume.name conflict; only use volume.name" ) )
assert . Check ( t , is . ErrorContains ( err , ` external_volume ` ) )
2017-06-29 19:25:50 -04:00
}
2018-08-29 17:29:39 -04:00
func durationPtr ( value time . Duration ) * types . Duration {
result := types . Duration ( value )
return & result
2016-12-20 16:26:49 -05:00
}
func uint64Ptr ( value uint64 ) * uint64 {
return & value
}
2017-10-04 16:51:48 -04:00
func uint32Ptr ( value uint32 ) * uint32 {
return & value
}
2016-12-20 16:26:49 -05:00
func TestFullExample ( t * testing . T ) {
2023-10-19 07:17:16 -04:00
skip . If ( t , runtime . GOOS == "windows" , "FIXME: TestFullExample substitutes platform-specific HOME-directories and requires platform-specific golden files; see https://github.com/docker/cli/pull/4610" )
2022-11-16 16:21:16 -05:00
data , err := os . ReadFile ( "full-example.yml" )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2016-12-20 16:26:49 -05:00
2017-02-07 04:44:47 -05:00
homeDir := "/home/foo"
2017-03-14 12:39:26 -04:00
env := map [ string ] string { "HOME" : homeDir , "QUX" : "qux_from_environment" }
2022-11-16 16:21:16 -05:00
config , err := loadYAMLWithEnv ( string ( data ) , env )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2016-12-20 16:26:49 -05:00
workingDir , err := os . Getwd ( )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2016-12-20 16:26:49 -05:00
2022-11-16 16:21:16 -05:00
expected := fullExampleConfig ( workingDir , homeDir )
2016-12-20 16:26:49 -05:00
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . DeepEqual ( expected . Services , config . Services ) )
assert . Check ( t , is . DeepEqual ( expected . Networks , config . Networks ) )
assert . Check ( t , is . DeepEqual ( expected . Volumes , config . Volumes ) )
assert . Check ( t , is . DeepEqual ( expected . Secrets , config . Secrets ) )
assert . Check ( t , is . DeepEqual ( expected . Configs , config . Configs ) )
assert . Check ( t , is . DeepEqual ( expected . Extras , config . Extras ) )
2016-12-20 16:26:49 -05:00
}
2018-01-16 11:52:26 -05:00
func TestLoadTmpfsVolume ( t * testing . T ) {
config , err := loadYAML ( `
version : "3.6"
services :
tmpfs :
image : nginx : latest
volumes :
- type : tmpfs
target : / app
tmpfs :
size : 10000
` )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2018-01-16 11:52:26 -05:00
expected := types . ServiceVolumeConfig {
Target : "/app" ,
Type : "tmpfs" ,
Tmpfs : & types . ServiceVolumeTmpfs {
Size : int64 ( 10000 ) ,
} ,
}
2018-03-05 18:53:52 -05:00
assert . Assert ( t , is . Len ( config . Services , 1 ) )
assert . Check ( t , is . Len ( config . Services [ 0 ] . Volumes , 1 ) )
assert . Check ( t , is . DeepEqual ( expected , config . Services [ 0 ] . Volumes [ 0 ] ) )
2018-01-16 11:52:26 -05:00
}
func TestLoadTmpfsVolumeAdditionalPropertyNotAllowed ( t * testing . T ) {
_ , err := loadYAML ( `
version : "3.5"
services :
tmpfs :
image : nginx : latest
volumes :
- type : tmpfs
target : / app
tmpfs :
size : 10000
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "services.tmpfs.volumes.0 Additional property tmpfs is not allowed" ) )
2018-01-16 11:52:26 -05:00
}
2018-01-21 00:22:19 -05:00
func TestLoadBindMountSourceMustNotBeEmpty ( t * testing . T ) {
_ , err := loadYAML ( `
version : "3.5"
services :
tmpfs :
image : nginx : latest
volumes :
- type : bind
target : / app
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . Error ( err , ` invalid mount config for type "bind": field Source must not be empty ` ) )
2018-01-21 00:22:19 -05:00
}
2019-07-09 16:13:19 -04:00
func TestLoadBindMountSourceIsWindowsAbsolute ( t * testing . T ) {
tests := [ ] struct {
doc string
yaml string
expected types . ServiceVolumeConfig
} {
{
doc : "Z-drive lowercase" ,
yaml : `
version : ' 3.3 '
services :
windows :
image : mcr . microsoft . com / windows / servercore / iis : windowsservercore - ltsc2019
volumes :
- type : bind
source : z : \
target : c : \ data
` ,
expected : types . ServiceVolumeConfig { Type : "bind" , Source : ` z:\ ` , Target : ` c:\data ` } ,
} ,
{
doc : "Z-drive uppercase" ,
yaml : `
version : ' 3.3 '
services :
windows :
image : mcr . microsoft . com / windows / servercore / iis : windowsservercore - ltsc2019
volumes :
- type : bind
source : Z : \
target : C : \ data
` ,
expected : types . ServiceVolumeConfig { Type : "bind" , Source : ` Z:\ ` , Target : ` C:\data ` } ,
} ,
{
doc : "Z-drive subdirectory" ,
yaml : `
version : ' 3.3 '
services :
windows :
image : mcr . microsoft . com / windows / servercore / iis : windowsservercore - ltsc2019
volumes :
- type : bind
source : Z : \ some - dir
target : C : \ data
` ,
expected : types . ServiceVolumeConfig { Type : "bind" , Source : ` Z:\some-dir ` , Target : ` C:\data ` } ,
} ,
{
doc : "forward-slashes" ,
yaml : `
version : ' 3.3 '
services :
app :
image : app : latest
volumes :
- type : bind
source : / z / some - dir
target : / c / data
` ,
expected : types . ServiceVolumeConfig { Type : "bind" , Source : ` /z/some-dir ` , Target : ` /c/data ` } ,
} ,
}
for _ , tc := range tests {
t . Run ( tc . doc , func ( t * testing . T ) {
config , err := loadYAML ( tc . yaml )
assert . NilError ( t , err )
assert . Check ( t , is . Len ( config . Services [ 0 ] . Volumes , 1 ) )
assert . Check ( t , is . DeepEqual ( tc . expected , config . Services [ 0 ] . Volumes [ 0 ] ) )
} )
}
}
2018-01-21 00:22:19 -05:00
func TestLoadBindMountWithSource ( t * testing . T ) {
config , err := loadYAML ( `
version : "3.5"
services :
bind :
image : nginx : latest
volumes :
- type : bind
target : / app
source : "."
` )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2018-01-21 00:22:19 -05:00
workingDir , err := os . Getwd ( )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2018-01-21 00:22:19 -05:00
expected := types . ServiceVolumeConfig {
Type : "bind" ,
Source : workingDir ,
Target : "/app" ,
}
2018-03-05 18:53:52 -05:00
assert . Assert ( t , is . Len ( config . Services , 1 ) )
assert . Check ( t , is . Len ( config . Services [ 0 ] . Volumes , 1 ) )
assert . Check ( t , is . DeepEqual ( expected , config . Services [ 0 ] . Volumes [ 0 ] ) )
2018-01-21 00:22:19 -05:00
}
2018-01-16 11:52:26 -05:00
func TestLoadTmpfsVolumeSizeCanBeZero ( t * testing . T ) {
config , err := loadYAML ( `
version : "3.6"
services :
tmpfs :
image : nginx : latest
volumes :
- type : tmpfs
target : / app
tmpfs :
size : 0
` )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2018-01-16 11:52:26 -05:00
expected := types . ServiceVolumeConfig {
Target : "/app" ,
Type : "tmpfs" ,
Tmpfs : & types . ServiceVolumeTmpfs { } ,
}
2018-03-05 18:53:52 -05:00
assert . Assert ( t , is . Len ( config . Services , 1 ) )
assert . Check ( t , is . Len ( config . Services [ 0 ] . Volumes , 1 ) )
assert . Check ( t , is . DeepEqual ( expected , config . Services [ 0 ] . Volumes [ 0 ] ) )
2018-01-16 11:52:26 -05:00
}
func TestLoadTmpfsVolumeSizeMustBeGTEQZero ( t * testing . T ) {
_ , err := loadYAML ( `
version : "3.6"
services :
tmpfs :
image : nginx : latest
volumes :
- type : tmpfs
target : / app
tmpfs :
size : - 1
` )
2017-12-21 16:27:57 -05:00
assert . ErrorContains ( t , err , "services.tmpfs.volumes.0.tmpfs.size Must be greater than or equal to 0" )
2018-01-16 11:52:26 -05:00
}
func TestLoadTmpfsVolumeSizeMustBeInteger ( t * testing . T ) {
_ , err := loadYAML ( `
version : "3.6"
services :
tmpfs :
image : nginx : latest
volumes :
- type : tmpfs
target : / app
tmpfs :
size : 0.0001
` )
2017-12-21 16:27:57 -05:00
assert . ErrorContains ( t , err , "services.tmpfs.volumes.0.tmpfs.size must be a integer" )
2018-01-16 11:52:26 -05:00
}
2016-12-20 16:26:49 -05:00
func serviceSort ( services [ ] types . ServiceConfig ) [ ] types . ServiceConfig {
2018-07-08 15:08:17 -04:00
sort . Slice ( services , func ( i , j int ) bool {
return services [ i ] . Name < services [ j ] . Name
} )
2016-12-20 16:26:49 -05:00
return services
}
2017-02-04 16:55:28 -05:00
func TestLoadAttachableNetwork ( t * testing . T ) {
config , err := loadYAML ( `
2017-03-13 15:05:30 -04:00
version : "3.2"
2017-02-04 16:55:28 -05:00
networks :
mynet1 :
driver : overlay
attachable : true
mynet2 :
driver : bridge
` )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2017-02-04 16:55:28 -05:00
expected := map [ string ] types . NetworkConfig {
"mynet1" : {
Driver : "overlay" ,
Attachable : true ,
} ,
"mynet2" : {
Driver : "bridge" ,
Attachable : false ,
} ,
}
2018-03-05 18:53:52 -05:00
assert . Check ( t , is . DeepEqual ( expected , config . Networks ) )
2017-02-04 16:55:28 -05:00
}
2017-01-31 15:45:45 -05:00
func TestLoadExpandedPortFormat ( t * testing . T ) {
config , err := loadYAML ( `
2017-03-13 15:05:30 -04:00
version : "3.2"
2017-01-31 15:45:45 -05:00
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
` )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2017-01-31 15:45:45 -05:00
2022-11-16 16:21:16 -05:00
expected := samplePortsConfig
2018-03-05 18:53:52 -05:00
assert . Check ( t , is . Len ( config . Services , 1 ) )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . DeepEqual ( expected , config . Services [ 0 ] . Ports ) )
2017-01-31 15:45:45 -05:00
}
2017-01-24 12:09:53 -05:00
func TestLoadExpandedMountFormat ( t * testing . T ) {
config , err := loadYAML ( `
2017-03-13 15:05:30 -04:00
version : "3.2"
2017-01-24 12:09:53 -05:00
services :
web :
image : busybox
volumes :
- type : volume
source : foo
target : / target
read_only : true
volumes :
foo : { }
` )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2017-01-24 12:09:53 -05:00
expected := types . ServiceVolumeConfig {
Type : "volume" ,
Source : "foo" ,
Target : "/target" ,
ReadOnly : true ,
}
2018-03-05 18:53:52 -05:00
assert . Assert ( t , is . Len ( config . Services , 1 ) )
assert . Check ( t , is . Len ( config . Services [ 0 ] . Volumes , 1 ) )
assert . Check ( t , is . DeepEqual ( expected , config . Services [ 0 ] . Volumes [ 0 ] ) )
2017-01-24 12:09:53 -05:00
}
Preserve sort-order of extra hosts, and allow duplicate entries
Extra hosts (`extra_hosts` in compose-file, or `--hosts` in services) adds
custom host/ip mappings to the container's `/etc/hosts`.
The current implementation used a `map[string]string{}` as intermediate
storage, and sorted the results alphabetically when converting to a service-spec.
As a result, duplicate hosts were removed, and order of host/ip mappings was not
preserved (in case the compose-file used a list instead of a map).
According to the **host.conf(5)** man page (http://man7.org/linux/man-pages/man5/host.conf.5.html)
multi Valid values are on and off. If set to on, the resolver
library will return all valid addresses for a host that
appears in the /etc/hosts file, instead of only the first.
This is off by default, as it may cause a substantial
performance loss at sites with large hosts files.
Multiple entries for a host are allowed, and even required for some situations,
for example, to add mappings for IPv4 and IPv6 addreses for a host, as illustrated
by the example hosts file in the **hosts(5)** man page (http://man7.org/linux/man-pages/man5/hosts.5.html):
# The following lines are desirable for IPv4 capable hosts
127.0.0.1 localhost
# 127.0.1.1 is often used for the FQDN of the machine
127.0.1.1 thishost.mydomain.org thishost
192.168.1.10 foo.mydomain.org foo
192.168.1.13 bar.mydomain.org bar
146.82.138.7 master.debian.org master
209.237.226.90 www.opensource.org
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
This patch changes the intermediate storage format to use a `[]string`, and only
sorts entries if the input format in the compose file is a mapping. If the input
format is a list, the original sort-order is preserved.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2017-10-29 20:33:23 -04:00
func TestLoadExtraHostsMap ( t * testing . T ) {
config , err := loadYAML ( `
version : "3.2"
services :
web :
image : busybox
extra_hosts :
"zulu" : "162.242.195.82"
"alpha" : "50.31.209.229"
2020-04-15 03:13:11 -04:00
"host.docker.internal" : "host-gateway"
Preserve sort-order of extra hosts, and allow duplicate entries
Extra hosts (`extra_hosts` in compose-file, or `--hosts` in services) adds
custom host/ip mappings to the container's `/etc/hosts`.
The current implementation used a `map[string]string{}` as intermediate
storage, and sorted the results alphabetically when converting to a service-spec.
As a result, duplicate hosts were removed, and order of host/ip mappings was not
preserved (in case the compose-file used a list instead of a map).
According to the **host.conf(5)** man page (http://man7.org/linux/man-pages/man5/host.conf.5.html)
multi Valid values are on and off. If set to on, the resolver
library will return all valid addresses for a host that
appears in the /etc/hosts file, instead of only the first.
This is off by default, as it may cause a substantial
performance loss at sites with large hosts files.
Multiple entries for a host are allowed, and even required for some situations,
for example, to add mappings for IPv4 and IPv6 addreses for a host, as illustrated
by the example hosts file in the **hosts(5)** man page (http://man7.org/linux/man-pages/man5/hosts.5.html):
# The following lines are desirable for IPv4 capable hosts
127.0.0.1 localhost
# 127.0.1.1 is often used for the FQDN of the machine
127.0.1.1 thishost.mydomain.org thishost
192.168.1.10 foo.mydomain.org foo
192.168.1.13 bar.mydomain.org bar
146.82.138.7 master.debian.org master
209.237.226.90 www.opensource.org
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
This patch changes the intermediate storage format to use a `[]string`, and only
sorts entries if the input format in the compose file is a mapping. If the input
format is a list, the original sort-order is preserved.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2017-10-29 20:33:23 -04:00
` )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
Preserve sort-order of extra hosts, and allow duplicate entries
Extra hosts (`extra_hosts` in compose-file, or `--hosts` in services) adds
custom host/ip mappings to the container's `/etc/hosts`.
The current implementation used a `map[string]string{}` as intermediate
storage, and sorted the results alphabetically when converting to a service-spec.
As a result, duplicate hosts were removed, and order of host/ip mappings was not
preserved (in case the compose-file used a list instead of a map).
According to the **host.conf(5)** man page (http://man7.org/linux/man-pages/man5/host.conf.5.html)
multi Valid values are on and off. If set to on, the resolver
library will return all valid addresses for a host that
appears in the /etc/hosts file, instead of only the first.
This is off by default, as it may cause a substantial
performance loss at sites with large hosts files.
Multiple entries for a host are allowed, and even required for some situations,
for example, to add mappings for IPv4 and IPv6 addreses for a host, as illustrated
by the example hosts file in the **hosts(5)** man page (http://man7.org/linux/man-pages/man5/hosts.5.html):
# The following lines are desirable for IPv4 capable hosts
127.0.0.1 localhost
# 127.0.1.1 is often used for the FQDN of the machine
127.0.1.1 thishost.mydomain.org thishost
192.168.1.10 foo.mydomain.org foo
192.168.1.13 bar.mydomain.org bar
146.82.138.7 master.debian.org master
209.237.226.90 www.opensource.org
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
This patch changes the intermediate storage format to use a `[]string`, and only
sorts entries if the input format in the compose file is a mapping. If the input
format is a list, the original sort-order is preserved.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2017-10-29 20:33:23 -04:00
expected := types . HostsList {
"alpha:50.31.209.229" ,
2020-04-15 03:13:11 -04:00
"host.docker.internal:host-gateway" ,
Preserve sort-order of extra hosts, and allow duplicate entries
Extra hosts (`extra_hosts` in compose-file, or `--hosts` in services) adds
custom host/ip mappings to the container's `/etc/hosts`.
The current implementation used a `map[string]string{}` as intermediate
storage, and sorted the results alphabetically when converting to a service-spec.
As a result, duplicate hosts were removed, and order of host/ip mappings was not
preserved (in case the compose-file used a list instead of a map).
According to the **host.conf(5)** man page (http://man7.org/linux/man-pages/man5/host.conf.5.html)
multi Valid values are on and off. If set to on, the resolver
library will return all valid addresses for a host that
appears in the /etc/hosts file, instead of only the first.
This is off by default, as it may cause a substantial
performance loss at sites with large hosts files.
Multiple entries for a host are allowed, and even required for some situations,
for example, to add mappings for IPv4 and IPv6 addreses for a host, as illustrated
by the example hosts file in the **hosts(5)** man page (http://man7.org/linux/man-pages/man5/hosts.5.html):
# The following lines are desirable for IPv4 capable hosts
127.0.0.1 localhost
# 127.0.1.1 is often used for the FQDN of the machine
127.0.1.1 thishost.mydomain.org thishost
192.168.1.10 foo.mydomain.org foo
192.168.1.13 bar.mydomain.org bar
146.82.138.7 master.debian.org master
209.237.226.90 www.opensource.org
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
This patch changes the intermediate storage format to use a `[]string`, and only
sorts entries if the input format in the compose file is a mapping. If the input
format is a list, the original sort-order is preserved.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2017-10-29 20:33:23 -04:00
"zulu:162.242.195.82" ,
}
2018-03-05 18:53:52 -05:00
assert . Assert ( t , is . Len ( config . Services , 1 ) )
assert . Check ( t , is . DeepEqual ( expected , config . Services [ 0 ] . ExtraHosts ) )
Preserve sort-order of extra hosts, and allow duplicate entries
Extra hosts (`extra_hosts` in compose-file, or `--hosts` in services) adds
custom host/ip mappings to the container's `/etc/hosts`.
The current implementation used a `map[string]string{}` as intermediate
storage, and sorted the results alphabetically when converting to a service-spec.
As a result, duplicate hosts were removed, and order of host/ip mappings was not
preserved (in case the compose-file used a list instead of a map).
According to the **host.conf(5)** man page (http://man7.org/linux/man-pages/man5/host.conf.5.html)
multi Valid values are on and off. If set to on, the resolver
library will return all valid addresses for a host that
appears in the /etc/hosts file, instead of only the first.
This is off by default, as it may cause a substantial
performance loss at sites with large hosts files.
Multiple entries for a host are allowed, and even required for some situations,
for example, to add mappings for IPv4 and IPv6 addreses for a host, as illustrated
by the example hosts file in the **hosts(5)** man page (http://man7.org/linux/man-pages/man5/hosts.5.html):
# The following lines are desirable for IPv4 capable hosts
127.0.0.1 localhost
# 127.0.1.1 is often used for the FQDN of the machine
127.0.1.1 thishost.mydomain.org thishost
192.168.1.10 foo.mydomain.org foo
192.168.1.13 bar.mydomain.org bar
146.82.138.7 master.debian.org master
209.237.226.90 www.opensource.org
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
This patch changes the intermediate storage format to use a `[]string`, and only
sorts entries if the input format in the compose file is a mapping. If the input
format is a list, the original sort-order is preserved.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2017-10-29 20:33:23 -04:00
}
func TestLoadExtraHostsList ( t * testing . T ) {
config , err := loadYAML ( `
version : "3.2"
services :
web :
image : busybox
extra_hosts :
- "zulu:162.242.195.82"
- "alpha:50.31.209.229"
- "zulu:ff02::1"
2020-04-15 03:13:11 -04:00
- "host.docker.internal:host-gateway"
Preserve sort-order of extra hosts, and allow duplicate entries
Extra hosts (`extra_hosts` in compose-file, or `--hosts` in services) adds
custom host/ip mappings to the container's `/etc/hosts`.
The current implementation used a `map[string]string{}` as intermediate
storage, and sorted the results alphabetically when converting to a service-spec.
As a result, duplicate hosts were removed, and order of host/ip mappings was not
preserved (in case the compose-file used a list instead of a map).
According to the **host.conf(5)** man page (http://man7.org/linux/man-pages/man5/host.conf.5.html)
multi Valid values are on and off. If set to on, the resolver
library will return all valid addresses for a host that
appears in the /etc/hosts file, instead of only the first.
This is off by default, as it may cause a substantial
performance loss at sites with large hosts files.
Multiple entries for a host are allowed, and even required for some situations,
for example, to add mappings for IPv4 and IPv6 addreses for a host, as illustrated
by the example hosts file in the **hosts(5)** man page (http://man7.org/linux/man-pages/man5/hosts.5.html):
# The following lines are desirable for IPv4 capable hosts
127.0.0.1 localhost
# 127.0.1.1 is often used for the FQDN of the machine
127.0.1.1 thishost.mydomain.org thishost
192.168.1.10 foo.mydomain.org foo
192.168.1.13 bar.mydomain.org bar
146.82.138.7 master.debian.org master
209.237.226.90 www.opensource.org
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
This patch changes the intermediate storage format to use a `[]string`, and only
sorts entries if the input format in the compose file is a mapping. If the input
format is a list, the original sort-order is preserved.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2017-10-29 20:33:23 -04:00
` )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
Preserve sort-order of extra hosts, and allow duplicate entries
Extra hosts (`extra_hosts` in compose-file, or `--hosts` in services) adds
custom host/ip mappings to the container's `/etc/hosts`.
The current implementation used a `map[string]string{}` as intermediate
storage, and sorted the results alphabetically when converting to a service-spec.
As a result, duplicate hosts were removed, and order of host/ip mappings was not
preserved (in case the compose-file used a list instead of a map).
According to the **host.conf(5)** man page (http://man7.org/linux/man-pages/man5/host.conf.5.html)
multi Valid values are on and off. If set to on, the resolver
library will return all valid addresses for a host that
appears in the /etc/hosts file, instead of only the first.
This is off by default, as it may cause a substantial
performance loss at sites with large hosts files.
Multiple entries for a host are allowed, and even required for some situations,
for example, to add mappings for IPv4 and IPv6 addreses for a host, as illustrated
by the example hosts file in the **hosts(5)** man page (http://man7.org/linux/man-pages/man5/hosts.5.html):
# The following lines are desirable for IPv4 capable hosts
127.0.0.1 localhost
# 127.0.1.1 is often used for the FQDN of the machine
127.0.1.1 thishost.mydomain.org thishost
192.168.1.10 foo.mydomain.org foo
192.168.1.13 bar.mydomain.org bar
146.82.138.7 master.debian.org master
209.237.226.90 www.opensource.org
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
This patch changes the intermediate storage format to use a `[]string`, and only
sorts entries if the input format in the compose file is a mapping. If the input
format is a list, the original sort-order is preserved.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2017-10-29 20:33:23 -04:00
expected := types . HostsList {
"zulu:162.242.195.82" ,
"alpha:50.31.209.229" ,
"zulu:ff02::1" ,
2020-04-15 03:13:11 -04:00
"host.docker.internal:host-gateway" ,
Preserve sort-order of extra hosts, and allow duplicate entries
Extra hosts (`extra_hosts` in compose-file, or `--hosts` in services) adds
custom host/ip mappings to the container's `/etc/hosts`.
The current implementation used a `map[string]string{}` as intermediate
storage, and sorted the results alphabetically when converting to a service-spec.
As a result, duplicate hosts were removed, and order of host/ip mappings was not
preserved (in case the compose-file used a list instead of a map).
According to the **host.conf(5)** man page (http://man7.org/linux/man-pages/man5/host.conf.5.html)
multi Valid values are on and off. If set to on, the resolver
library will return all valid addresses for a host that
appears in the /etc/hosts file, instead of only the first.
This is off by default, as it may cause a substantial
performance loss at sites with large hosts files.
Multiple entries for a host are allowed, and even required for some situations,
for example, to add mappings for IPv4 and IPv6 addreses for a host, as illustrated
by the example hosts file in the **hosts(5)** man page (http://man7.org/linux/man-pages/man5/hosts.5.html):
# The following lines are desirable for IPv4 capable hosts
127.0.0.1 localhost
# 127.0.1.1 is often used for the FQDN of the machine
127.0.1.1 thishost.mydomain.org thishost
192.168.1.10 foo.mydomain.org foo
192.168.1.13 bar.mydomain.org bar
146.82.138.7 master.debian.org master
209.237.226.90 www.opensource.org
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
This patch changes the intermediate storage format to use a `[]string`, and only
sorts entries if the input format in the compose file is a mapping. If the input
format is a list, the original sort-order is preserved.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2017-10-29 20:33:23 -04:00
}
2018-03-05 18:53:52 -05:00
assert . Assert ( t , is . Len ( config . Services , 1 ) )
assert . Check ( t , is . DeepEqual ( expected , config . Services [ 0 ] . ExtraHosts ) )
Preserve sort-order of extra hosts, and allow duplicate entries
Extra hosts (`extra_hosts` in compose-file, or `--hosts` in services) adds
custom host/ip mappings to the container's `/etc/hosts`.
The current implementation used a `map[string]string{}` as intermediate
storage, and sorted the results alphabetically when converting to a service-spec.
As a result, duplicate hosts were removed, and order of host/ip mappings was not
preserved (in case the compose-file used a list instead of a map).
According to the **host.conf(5)** man page (http://man7.org/linux/man-pages/man5/host.conf.5.html)
multi Valid values are on and off. If set to on, the resolver
library will return all valid addresses for a host that
appears in the /etc/hosts file, instead of only the first.
This is off by default, as it may cause a substantial
performance loss at sites with large hosts files.
Multiple entries for a host are allowed, and even required for some situations,
for example, to add mappings for IPv4 and IPv6 addreses for a host, as illustrated
by the example hosts file in the **hosts(5)** man page (http://man7.org/linux/man-pages/man5/hosts.5.html):
# The following lines are desirable for IPv4 capable hosts
127.0.0.1 localhost
# 127.0.1.1 is often used for the FQDN of the machine
127.0.1.1 thishost.mydomain.org thishost
192.168.1.10 foo.mydomain.org foo
192.168.1.13 bar.mydomain.org bar
146.82.138.7 master.debian.org master
209.237.226.90 www.opensource.org
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
This patch changes the intermediate storage format to use a `[]string`, and only
sorts entries if the input format in the compose file is a mapping. If the input
format is a list, the original sort-order is preserved.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2017-10-29 20:33:23 -04:00
}
2017-11-06 17:03:43 -05:00
func TestLoadVolumesWarnOnDeprecatedExternalNameVersion34 ( t * testing . T ) {
buf , cleanup := patchLogrus ( )
defer cleanup ( )
source := map [ string ] interface { } {
"foo" : map [ string ] interface { } {
"external" : map [ string ] interface { } {
"name" : "oops" ,
} ,
} ,
}
2022-11-16 16:21:16 -05:00
vols , err := LoadVolumes ( source , "3.4" )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2017-11-06 17:03:43 -05:00
expected := map [ string ] types . VolumeConfig {
"foo" : {
Name : "oops" ,
External : types . External { External : true } ,
} ,
}
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . DeepEqual ( expected , vols ) )
2018-03-05 18:53:52 -05:00
assert . Check ( t , is . Contains ( buf . String ( ) , "volume.external.name is deprecated" ) )
2017-11-06 17:03:43 -05:00
}
func patchLogrus ( ) ( * bytes . Buffer , func ( ) ) {
buf := new ( bytes . Buffer )
out := logrus . StandardLogger ( ) . Out
logrus . SetOutput ( buf )
return buf , func ( ) { logrus . SetOutput ( out ) }
}
func TestLoadVolumesWarnOnDeprecatedExternalNameVersion33 ( t * testing . T ) {
buf , cleanup := patchLogrus ( )
defer cleanup ( )
source := map [ string ] interface { } {
"foo" : map [ string ] interface { } {
"external" : map [ string ] interface { } {
"name" : "oops" ,
} ,
} ,
}
2022-11-16 16:21:16 -05:00
vols , err := LoadVolumes ( source , "3.3" )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2017-11-06 17:03:43 -05:00
expected := map [ string ] types . VolumeConfig {
"foo" : {
Name : "oops" ,
External : types . External { External : true } ,
} ,
}
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . DeepEqual ( expected , vols ) )
2018-03-05 18:53:52 -05:00
assert . Check ( t , is . Equal ( "" , buf . String ( ) ) )
2017-11-17 09:31:13 -05:00
}
2017-11-06 17:03:43 -05:00
2017-11-17 09:31:13 -05:00
func TestLoadV35 ( t * testing . T ) {
actual , err := loadYAML ( `
version : "3.5"
services :
foo :
image : busybox
isolation : process
configs :
2017-11-11 00:11:29 -05:00
foo :
name : fooqux
2017-11-17 09:31:13 -05:00
external : true
2017-11-11 00:11:29 -05:00
bar :
name : barqux
file : . / example1 . env
secrets :
foo :
name : fooqux
external : true
bar :
name : barqux
file : . / full - example . yml
2017-11-17 09:31:13 -05:00
` )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
assert . Check ( t , is . Len ( actual . Services , 1 ) )
assert . Check ( t , is . Len ( actual . Secrets , 2 ) )
assert . Check ( t , is . Len ( actual . Configs , 2 ) )
assert . Check ( t , is . Equal ( "process" , actual . Services [ 0 ] . Isolation ) )
2017-11-17 09:31:13 -05:00
}
func TestLoadV35InvalidIsolation ( t * testing . T ) {
// validation should be done only on the daemon side
actual , err := loadYAML ( `
version : "3.5"
services :
foo :
image : busybox
isolation : invalid
configs :
super :
external : true
` )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
assert . Assert ( t , is . Len ( actual . Services , 1 ) )
assert . Check ( t , is . Equal ( "invalid" , actual . Services [ 0 ] . Isolation ) )
2017-11-06 17:03:43 -05:00
}
2017-11-10 08:35:29 -05:00
2017-11-22 14:21:32 -05:00
func TestLoadSecretInvalidExternalNameAndNameCombination ( t * testing . T ) {
2017-11-10 08:35:29 -05:00
_ , err := loadYAML ( `
version : "3.5"
secrets :
external_secret :
name : user_specified_name
external :
2018-08-29 17:29:39 -04:00
name : external_name
2017-11-10 08:35:29 -05:00
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "secret.external.name and secret.name conflict; only use secret.name" ) )
assert . Check ( t , is . ErrorContains ( err , "external_secret" ) )
2017-11-10 08:35:29 -05:00
}
func TestLoadSecretsWarnOnDeprecatedExternalNameVersion35 ( t * testing . T ) {
buf , cleanup := patchLogrus ( )
defer cleanup ( )
source := map [ string ] interface { } {
"foo" : map [ string ] interface { } {
"external" : map [ string ] interface { } {
"name" : "oops" ,
} ,
} ,
}
2017-11-11 00:11:29 -05:00
details := types . ConfigDetails {
Version : "3.5" ,
2017-11-10 08:35:29 -05:00
}
2022-11-16 16:21:16 -05:00
s , err := LoadSecrets ( source , details )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2017-11-10 08:35:29 -05:00
expected := map [ string ] types . SecretConfig {
"foo" : {
Name : "oops" ,
External : types . External { External : true } ,
} ,
}
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . DeepEqual ( expected , s ) )
2018-03-05 18:53:52 -05:00
assert . Check ( t , is . Contains ( buf . String ( ) , "secret.external.name is deprecated" ) )
2017-11-10 08:35:29 -05:00
}
2017-11-22 14:21:32 -05:00
func TestLoadNetworksWarnOnDeprecatedExternalNameVersion35 ( t * testing . T ) {
buf , cleanup := patchLogrus ( )
defer cleanup ( )
source := map [ string ] interface { } {
"foo" : map [ string ] interface { } {
"external" : map [ string ] interface { } {
"name" : "oops" ,
} ,
} ,
}
2022-11-16 16:21:16 -05:00
nws , err := LoadNetworks ( source , "3.5" )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2017-11-22 14:21:32 -05:00
expected := map [ string ] types . NetworkConfig {
"foo" : {
Name : "oops" ,
External : types . External { External : true } ,
} ,
}
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . DeepEqual ( expected , nws ) )
2018-03-05 18:53:52 -05:00
assert . Check ( t , is . Contains ( buf . String ( ) , "network.external.name is deprecated" ) )
2017-11-22 14:21:32 -05:00
}
func TestLoadNetworksWarnOnDeprecatedExternalNameVersion34 ( t * testing . T ) {
buf , cleanup := patchLogrus ( )
defer cleanup ( )
source := map [ string ] interface { } {
"foo" : map [ string ] interface { } {
"external" : map [ string ] interface { } {
"name" : "oops" ,
} ,
} ,
}
networks , err := LoadNetworks ( source , "3.4" )
2018-03-05 18:53:52 -05:00
assert . NilError ( t , err )
2017-11-22 14:21:32 -05:00
expected := map [ string ] types . NetworkConfig {
"foo" : {
Name : "oops" ,
External : types . External { External : true } ,
} ,
}
2018-03-05 18:53:52 -05:00
assert . Check ( t , is . DeepEqual ( expected , networks ) )
assert . Check ( t , is . Equal ( "" , buf . String ( ) ) )
2017-11-22 14:21:32 -05:00
}
func TestLoadNetworkInvalidExternalNameAndNameCombination ( t * testing . T ) {
_ , err := loadYAML ( `
version : "3.5"
networks :
foo :
name : user_specified_name
external :
2018-08-29 17:29:39 -04:00
name : external_name
2017-11-22 14:21:32 -05:00
` )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . ErrorContains ( err , "network.external.name and network.name conflict; only use network.name" ) )
assert . Check ( t , is . ErrorContains ( err , "foo" ) )
2017-11-22 14:21:32 -05:00
}
2018-03-12 13:09:59 -04:00
func TestLoadNetworkWithName ( t * testing . T ) {
config , err := loadYAML ( `
version : ' 3.5 '
services :
hello - world :
image : redis : alpine
networks :
- network1
- network3
networks :
network1 :
name : network2
network3 :
` )
assert . NilError ( t , err )
expected := & types . Config {
Filename : "filename.yml" ,
Version : "3.5" ,
Services : types . Services {
{
Name : "hello-world" ,
Image : "redis:alpine" ,
Networks : map [ string ] * types . ServiceNetworkConfig {
"network1" : nil ,
"network3" : nil ,
} ,
} ,
} ,
Networks : map [ string ] types . NetworkConfig {
"network1" : { Name : "network2" } ,
"network3" : { } ,
} ,
}
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . DeepEqual ( expected , config , cmpopts . EquateEmpty ( ) ) )
2018-03-12 13:09:59 -04:00
}
2018-06-18 04:48:03 -04:00
func TestLoadInit ( t * testing . T ) {
booleanTrue := true
2018-06-25 05:09:38 -04:00
booleanFalse := false
2022-09-29 09:34:20 -04:00
testcases := [ ] struct {
2018-06-25 05:09:38 -04:00
doc string
yaml string
init * bool
} {
{
doc : "no init defined" ,
yaml : `
2018-06-18 04:48:03 -04:00
version : ' 3.7 '
services :
foo :
2018-06-25 05:09:38 -04:00
image : alpine ` ,
2018-06-18 04:48:03 -04:00
} ,
2018-06-25 05:09:38 -04:00
{
doc : "has true init" ,
yaml : `
2018-06-18 04:48:03 -04:00
version : ' 3.7 '
services :
foo :
image : alpine
2018-06-25 05:09:38 -04:00
init : true ` ,
init : & booleanTrue ,
2018-06-18 04:48:03 -04:00
} ,
2018-06-25 05:09:38 -04:00
{
doc : "has false init" ,
yaml : `
2018-06-18 04:48:03 -04:00
version : ' 3.7 '
services :
foo :
image : alpine
2018-06-25 05:09:38 -04:00
init : false ` ,
init : & booleanFalse ,
2018-06-18 04:48:03 -04:00
} ,
2018-06-25 05:09:38 -04:00
}
for _ , testcase := range testcases {
2019-10-29 08:37:58 -04:00
testcase := testcase
2018-06-25 05:09:38 -04:00
t . Run ( testcase . doc , func ( t * testing . T ) {
config , err := loadYAML ( testcase . yaml )
assert . NilError ( t , err )
assert . Check ( t , is . Len ( config . Services , 1 ) )
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . DeepEqual ( testcase . init , config . Services [ 0 ] . Init ) )
2018-06-25 05:09:38 -04:00
} )
}
2018-06-18 04:48:03 -04:00
}
2018-10-02 07:00:11 -04:00
2019-02-12 10:07:07 -05:00
func TestLoadSysctls ( t * testing . T ) {
config , err := loadYAML ( `
version : "3.8"
services :
web :
image : busybox
sysctls :
- net . core . somaxconn = 1024
- net . ipv4 . tcp_syncookies = 0
- testing . one . one =
- testing . one . two
` )
assert . NilError ( t , err )
expected := types . Mapping {
"net.core.somaxconn" : "1024" ,
"net.ipv4.tcp_syncookies" : "0" ,
"testing.one.one" : "" ,
"testing.one.two" : "" ,
}
assert . Assert ( t , is . Len ( config . Services , 1 ) )
assert . Check ( t , is . DeepEqual ( expected , config . Services [ 0 ] . Sysctls ) )
config , err = loadYAML ( `
version : "3.8"
services :
web :
image : busybox
sysctls :
net . core . somaxconn : 1024
net . ipv4 . tcp_syncookies : 0
testing . one . one : ""
testing . one . two :
` )
assert . NilError ( t , err )
assert . Assert ( t , is . Len ( config . Services , 1 ) )
assert . Check ( t , is . DeepEqual ( expected , config . Services [ 0 ] . Sysctls ) )
}
2018-10-02 07:00:11 -04:00
func TestTransform ( t * testing . T ) {
2022-09-29 09:34:20 -04:00
source := [ ] interface { } {
2018-10-02 07:00:11 -04:00
"80-82:8080-8082" ,
"90-92:8090-8092/udp" ,
"85:8500" ,
8600 ,
map [ string ] interface { } {
"protocol" : "udp" ,
"target" : 53 ,
"published" : 10053 ,
} ,
map [ string ] interface { } {
"mode" : "host" ,
"target" : 22 ,
"published" : 10022 ,
} ,
}
var ports [ ] types . ServicePortConfig
err := Transform ( source , & ports )
assert . NilError ( t , err )
assert . Check ( t , is . DeepEqual ( samplePortsConfig , ports ) )
}
2018-06-27 08:05:51 -04:00
func TestLoadTemplateDriver ( t * testing . T ) {
config , err := loadYAML ( `
version : ' 3.8 '
services :
hello - world :
image : redis : alpine
secrets :
- secret
configs :
- config
configs :
config :
name : config
external : true
template_driver : config - driver
secrets :
secret :
name : secret
external : true
template_driver : secret - driver
` )
assert . NilError ( t , err )
expected := & types . Config {
Filename : "filename.yml" ,
Version : "3.8" ,
Services : types . Services {
{
Name : "hello-world" ,
Image : "redis:alpine" ,
Configs : [ ] types . ServiceConfigObjConfig {
{
Source : "config" ,
} ,
} ,
Secrets : [ ] types . ServiceSecretConfig {
{
Source : "secret" ,
} ,
} ,
} ,
} ,
Configs : map [ string ] types . ConfigObjConfig {
"config" : {
Name : "config" ,
External : types . External { External : true } ,
TemplateDriver : "config-driver" ,
} ,
} ,
Secrets : map [ string ] types . SecretConfig {
"secret" : {
Name : "secret" ,
External : types . External { External : true } ,
TemplateDriver : "secret-driver" ,
} ,
} ,
}
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . DeepEqual ( expected , config , cmpopts . EquateEmpty ( ) ) )
2018-06-27 08:05:51 -04:00
}
2019-03-28 11:17:15 -04:00
func TestLoadSecretDriver ( t * testing . T ) {
config , err := loadYAML ( `
version : ' 3.8 '
services :
hello - world :
image : redis : alpine
secrets :
- secret
configs :
- config
configs :
config :
name : config
external : true
secrets :
secret :
name : secret
driver : secret - bucket
driver_opts :
OptionA : value for driver option A
OptionB : value for driver option B
` )
assert . NilError ( t , err )
expected := & types . Config {
Filename : "filename.yml" ,
Version : "3.8" ,
Services : types . Services {
{
Name : "hello-world" ,
Image : "redis:alpine" ,
Configs : [ ] types . ServiceConfigObjConfig {
{
Source : "config" ,
} ,
} ,
Secrets : [ ] types . ServiceSecretConfig {
{
Source : "secret" ,
} ,
} ,
} ,
} ,
Configs : map [ string ] types . ConfigObjConfig {
"config" : {
Name : "config" ,
External : types . External { External : true } ,
} ,
} ,
Secrets : map [ string ] types . SecretConfig {
"secret" : {
Name : "secret" ,
Driver : "secret-bucket" ,
DriverOpts : map [ string ] string {
"OptionA" : "value for driver option A" ,
"OptionB" : "value for driver option B" ,
} ,
} ,
} ,
}
2022-11-16 16:21:16 -05:00
assert . Check ( t , is . DeepEqual ( expected , config , cmpopts . EquateEmpty ( ) ) )
2019-03-28 11:17:15 -04:00
}