github.com/yamamoto-febc/docker@v1.9.0/runconfig/parse_test.go (about)

     1  package runconfig
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"strings"
     7  	"testing"
     8  
     9  	flag "github.com/docker/docker/pkg/mflag"
    10  	"github.com/docker/docker/pkg/nat"
    11  	"github.com/docker/docker/pkg/parsers"
    12  )
    13  
    14  func parseRun(args []string) (*Config, *HostConfig, *flag.FlagSet, error) {
    15  	cmd := flag.NewFlagSet("run", flag.ContinueOnError)
    16  	cmd.SetOutput(ioutil.Discard)
    17  	cmd.Usage = nil
    18  	return Parse(cmd, args)
    19  }
    20  
    21  func parse(t *testing.T, args string) (*Config, *HostConfig, error) {
    22  	config, hostConfig, _, err := parseRun(strings.Split(args+" ubuntu bash", " "))
    23  	return config, hostConfig, err
    24  }
    25  
    26  func mustParse(t *testing.T, args string) (*Config, *HostConfig) {
    27  	config, hostConfig, err := parse(t, args)
    28  	if err != nil {
    29  		t.Fatal(err)
    30  	}
    31  	return config, hostConfig
    32  }
    33  
    34  // check if (a == c && b == d) || (a == d && b == c)
    35  // because maps are randomized
    36  func compareRandomizedStrings(a, b, c, d string) error {
    37  	if a == c && b == d {
    38  		return nil
    39  	}
    40  	if a == d && b == c {
    41  		return nil
    42  	}
    43  	return fmt.Errorf("strings don't match")
    44  }
    45  func TestParseRunLinks(t *testing.T) {
    46  	if _, hostConfig := mustParse(t, "--link a:b"); len(hostConfig.Links) == 0 || hostConfig.Links[0] != "a:b" {
    47  		t.Fatalf("Error parsing links. Expected []string{\"a:b\"}, received: %v", hostConfig.Links)
    48  	}
    49  	if _, hostConfig := mustParse(t, "--link a:b --link c:d"); len(hostConfig.Links) < 2 || hostConfig.Links[0] != "a:b" || hostConfig.Links[1] != "c:d" {
    50  		t.Fatalf("Error parsing links. Expected []string{\"a:b\", \"c:d\"}, received: %v", hostConfig.Links)
    51  	}
    52  	if _, hostConfig := mustParse(t, ""); len(hostConfig.Links) != 0 {
    53  		t.Fatalf("Error parsing links. No link expected, received: %v", hostConfig.Links)
    54  	}
    55  }
    56  
    57  func TestParseRunAttach(t *testing.T) {
    58  	if config, _ := mustParse(t, "-a stdin"); !config.AttachStdin || config.AttachStdout || config.AttachStderr {
    59  		t.Fatalf("Error parsing attach flags. Expect only Stdin enabled. Received: in: %v, out: %v, err: %v", config.AttachStdin, config.AttachStdout, config.AttachStderr)
    60  	}
    61  	if config, _ := mustParse(t, "-a stdin -a stdout"); !config.AttachStdin || !config.AttachStdout || config.AttachStderr {
    62  		t.Fatalf("Error parsing attach flags. Expect only Stdin and Stdout enabled. Received: in: %v, out: %v, err: %v", config.AttachStdin, config.AttachStdout, config.AttachStderr)
    63  	}
    64  	if config, _ := mustParse(t, "-a stdin -a stdout -a stderr"); !config.AttachStdin || !config.AttachStdout || !config.AttachStderr {
    65  		t.Fatalf("Error parsing attach flags. Expect all attach enabled. Received: in: %v, out: %v, err: %v", config.AttachStdin, config.AttachStdout, config.AttachStderr)
    66  	}
    67  	if config, _ := mustParse(t, ""); config.AttachStdin || !config.AttachStdout || !config.AttachStderr {
    68  		t.Fatalf("Error parsing attach flags. Expect Stdin disabled. Received: in: %v, out: %v, err: %v", config.AttachStdin, config.AttachStdout, config.AttachStderr)
    69  	}
    70  	if config, _ := mustParse(t, "-i"); !config.AttachStdin || !config.AttachStdout || !config.AttachStderr {
    71  		t.Fatalf("Error parsing attach flags. Expect Stdin enabled. Received: in: %v, out: %v, err: %v", config.AttachStdin, config.AttachStdout, config.AttachStderr)
    72  	}
    73  
    74  	if _, _, err := parse(t, "-a"); err == nil {
    75  		t.Fatalf("Error parsing attach flags, `-a` should be an error but is not")
    76  	}
    77  	if _, _, err := parse(t, "-a invalid"); err == nil {
    78  		t.Fatalf("Error parsing attach flags, `-a invalid` should be an error but is not")
    79  	}
    80  	if _, _, err := parse(t, "-a invalid -a stdout"); err == nil {
    81  		t.Fatalf("Error parsing attach flags, `-a stdout -a invalid` should be an error but is not")
    82  	}
    83  	if _, _, err := parse(t, "-a stdout -a stderr -d"); err == nil {
    84  		t.Fatalf("Error parsing attach flags, `-a stdout -a stderr -d` should be an error but is not")
    85  	}
    86  	if _, _, err := parse(t, "-a stdin -d"); err == nil {
    87  		t.Fatalf("Error parsing attach flags, `-a stdin -d` should be an error but is not")
    88  	}
    89  	if _, _, err := parse(t, "-a stdout -d"); err == nil {
    90  		t.Fatalf("Error parsing attach flags, `-a stdout -d` should be an error but is not")
    91  	}
    92  	if _, _, err := parse(t, "-a stderr -d"); err == nil {
    93  		t.Fatalf("Error parsing attach flags, `-a stderr -d` should be an error but is not")
    94  	}
    95  	if _, _, err := parse(t, "-d --rm"); err == nil {
    96  		t.Fatalf("Error parsing attach flags, `-d --rm` should be an error but is not")
    97  	}
    98  }
    99  
   100  func TestParseRunVolumes(t *testing.T) {
   101  	if config, hostConfig := mustParse(t, "-v /tmp"); hostConfig.Binds != nil {
   102  		t.Fatalf("Error parsing volume flags, `-v /tmp` should not mount-bind anything. Received %v", hostConfig.Binds)
   103  	} else if _, exists := config.Volumes["/tmp"]; !exists {
   104  		t.Fatalf("Error parsing volume flags, `-v /tmp` is missing from volumes. Received %v", config.Volumes)
   105  	}
   106  
   107  	if config, hostConfig := mustParse(t, "-v /tmp -v /var"); hostConfig.Binds != nil {
   108  		t.Fatalf("Error parsing volume flags, `-v /tmp -v /var` should not mount-bind anything. Received %v", hostConfig.Binds)
   109  	} else if _, exists := config.Volumes["/tmp"]; !exists {
   110  		t.Fatalf("Error parsing volume flags, `-v /tmp` is missing from volumes. Received %v", config.Volumes)
   111  	} else if _, exists := config.Volumes["/var"]; !exists {
   112  		t.Fatalf("Error parsing volume flags, `-v /var` is missing from volumes. Received %v", config.Volumes)
   113  	}
   114  
   115  	if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp"); hostConfig.Binds == nil || hostConfig.Binds[0] != "/hostTmp:/containerTmp" {
   116  		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp` should mount-bind /hostTmp into /containerTmp. Received %v", hostConfig.Binds)
   117  	}
   118  
   119  	if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp -v /hostVar:/containerVar"); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], "/hostTmp:/containerTmp", "/hostVar:/containerVar") != nil {
   120  		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp -v /hostVar:/containerVar` should mount-bind /hostTmp into /containerTmp and /hostVar into /hostContainer. Received %v", hostConfig.Binds)
   121  	}
   122  
   123  	if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp:ro -v /hostVar:/containerVar:rw"); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], "/hostTmp:/containerTmp:ro", "/hostVar:/containerVar:rw") != nil {
   124  		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp:ro -v /hostVar:/containerVar:rw` should mount-bind /hostTmp into /containerTmp and /hostVar into /hostContainer. Received %v", hostConfig.Binds)
   125  	}
   126  
   127  	if _, hostConfig := mustParse(t, "-v /containerTmp:ro -v /containerVar:rw"); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], "/containerTmp:ro", "/containerVar:rw") != nil {
   128  		t.Fatalf("Error parsing volume flags, `-v /containerTmp:ro -v /containerVar:rw` should mount-bind /containerTmp into /ro and /containerVar into /rw. Received %v", hostConfig.Binds)
   129  	}
   130  
   131  	if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp:ro,Z -v /hostVar:/containerVar:rw,Z"); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], "/hostTmp:/containerTmp:ro,Z", "/hostVar:/containerVar:rw,Z") != nil {
   132  		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp:ro,Z -v /hostVar:/containerVar:rw,Z` should mount-bind /hostTmp into /containerTmp and /hostVar into /hostContainer. Received %v", hostConfig.Binds)
   133  	}
   134  
   135  	if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp:Z -v /hostVar:/containerVar:z"); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], "/hostTmp:/containerTmp:Z", "/hostVar:/containerVar:z") != nil {
   136  		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp:Z -v /hostVar:/containerVar:z` should mount-bind /hostTmp into /containerTmp and /hostVar into /hostContainer. Received %v", hostConfig.Binds)
   137  	}
   138  
   139  	if config, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp -v /containerVar"); hostConfig.Binds == nil || len(hostConfig.Binds) > 1 || hostConfig.Binds[0] != "/hostTmp:/containerTmp" {
   140  		t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp -v /containerVar` should mount-bind only /hostTmp into /containerTmp. Received %v", hostConfig.Binds)
   141  	} else if _, exists := config.Volumes["/containerVar"]; !exists {
   142  		t.Fatalf("Error parsing volume flags, `-v /containerVar` is missing from volumes. Received %v", config.Volumes)
   143  	}
   144  
   145  	if config, hostConfig := mustParse(t, ""); hostConfig.Binds != nil {
   146  		t.Fatalf("Error parsing volume flags, without volume, nothing should be mount-binded. Received %v", hostConfig.Binds)
   147  	} else if len(config.Volumes) != 0 {
   148  		t.Fatalf("Error parsing volume flags, without volume, no volume should be present. Received %v", config.Volumes)
   149  	}
   150  
   151  	if _, _, err := parse(t, "-v /"); err == nil {
   152  		t.Fatalf("Expected error, but got none")
   153  	}
   154  
   155  	if _, _, err := parse(t, "-v /:/"); err == nil {
   156  		t.Fatalf("Error parsing volume flags, `-v /:/` should fail but didn't")
   157  	}
   158  	if _, _, err := parse(t, "-v"); err == nil {
   159  		t.Fatalf("Error parsing volume flags, `-v` should fail but didn't")
   160  	}
   161  	if _, _, err := parse(t, "-v /tmp:"); err == nil {
   162  		t.Fatalf("Error parsing volume flags, `-v /tmp:` should fail but didn't")
   163  	}
   164  	if _, _, err := parse(t, "-v /tmp::"); err == nil {
   165  		t.Fatalf("Error parsing volume flags, `-v /tmp::` should fail but didn't")
   166  	}
   167  	if _, _, err := parse(t, "-v :"); err == nil {
   168  		t.Fatalf("Error parsing volume flags, `-v :` should fail but didn't")
   169  	}
   170  	if _, _, err := parse(t, "-v ::"); err == nil {
   171  		t.Fatalf("Error parsing volume flags, `-v ::` should fail but didn't")
   172  	}
   173  	if _, _, err := parse(t, "-v /tmp:/tmp:/tmp:/tmp"); err == nil {
   174  		t.Fatalf("Error parsing volume flags, `-v /tmp:/tmp:/tmp:/tmp` should fail but didn't")
   175  	}
   176  }
   177  
   178  func TestParseLxcConfOpt(t *testing.T) {
   179  	opts := []string{"lxc.utsname=docker", "lxc.utsname = docker "}
   180  
   181  	for _, o := range opts {
   182  		k, v, err := parsers.ParseKeyValueOpt(o)
   183  		if err != nil {
   184  			t.FailNow()
   185  		}
   186  		if k != "lxc.utsname" {
   187  			t.Fail()
   188  		}
   189  		if v != "docker" {
   190  			t.Fail()
   191  		}
   192  	}
   193  
   194  	// With parseRun too
   195  	_, hostconfig, _, err := parseRun([]string{"lxc.utsname=docker", "lxc.utsname = docker ", "img", "cmd"})
   196  	if err != nil {
   197  		t.Fatal(err)
   198  	}
   199  	for _, lxcConf := range hostconfig.LxcConf.Slice() {
   200  		if lxcConf.Key != "lxc.utsname" || lxcConf.Value != "docker" {
   201  			t.Fail()
   202  		}
   203  	}
   204  
   205  }
   206  
   207  // Simple parse with MacAddress validatation
   208  func TestParseWithMacAddress(t *testing.T) {
   209  	invalidMacAddress := "--mac-address=invalidMacAddress"
   210  	validMacAddress := "--mac-address=92:d0:c6:0a:29:33"
   211  	if _, _, _, err := parseRun([]string{invalidMacAddress, "img", "cmd"}); err != nil && err.Error() != "invalidMacAddress is not a valid mac address" {
   212  		t.Fatalf("Expected an error with %v mac-address, got %v", invalidMacAddress, err)
   213  	}
   214  	if config, _ := mustParse(t, validMacAddress); config.MacAddress != "92:d0:c6:0a:29:33" {
   215  		t.Fatalf("Expected the config to have '92:d0:c6:0a:29:33' as MacAddress, got '%v'", config.MacAddress)
   216  	}
   217  }
   218  
   219  func TestParseWithMemory(t *testing.T) {
   220  	invalidMemory := "--memory=invalid"
   221  	validMemory := "--memory=1G"
   222  	if _, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"}); err != nil && err.Error() != "invalid size: 'invalid'" {
   223  		t.Fatalf("Expected an error with '%v' Memory, got '%v'", invalidMemory, err)
   224  	}
   225  	if _, hostconfig := mustParse(t, validMemory); hostconfig.Memory != 1073741824 {
   226  		t.Fatalf("Expected the config to have '1G' as Memory, got '%v'", hostconfig.Memory)
   227  	}
   228  }
   229  
   230  func TestParseWithMemorySwap(t *testing.T) {
   231  	invalidMemory := "--memory-swap=invalid"
   232  	validMemory := "--memory-swap=1G"
   233  	anotherValidMemory := "--memory-swap=-1"
   234  	if _, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"}); err == nil || err.Error() != "invalid size: 'invalid'" {
   235  		t.Fatalf("Expected an error with '%v' MemorySwap, got '%v'", invalidMemory, err)
   236  	}
   237  	if _, hostconfig := mustParse(t, validMemory); hostconfig.MemorySwap != 1073741824 {
   238  		t.Fatalf("Expected the config to have '1073741824' as MemorySwap, got '%v'", hostconfig.MemorySwap)
   239  	}
   240  	if _, hostconfig := mustParse(t, anotherValidMemory); hostconfig.MemorySwap != -1 {
   241  		t.Fatalf("Expected the config to have '-1' as MemorySwap, got '%v'", hostconfig.MemorySwap)
   242  	}
   243  }
   244  
   245  func TestParseHostname(t *testing.T) {
   246  	hostname := "--hostname=hostname"
   247  	hostnameWithDomain := "--hostname=hostname.domainname"
   248  	hostnameWithDomainTld := "--hostname=hostname.domainname.tld"
   249  	if config, _ := mustParse(t, hostname); config.Hostname != "hostname" && config.Domainname != "" {
   250  		t.Fatalf("Expected the config to have 'hostname' as hostname, got '%v'", config.Hostname)
   251  	}
   252  	if config, _ := mustParse(t, hostnameWithDomain); config.Hostname != "hostname" && config.Domainname != "domainname" {
   253  		t.Fatalf("Expected the config to have 'hostname' as hostname, got '%v'", config.Hostname)
   254  	}
   255  	if config, _ := mustParse(t, hostnameWithDomainTld); config.Hostname != "hostname" && config.Domainname != "domainname.tld" {
   256  		t.Fatalf("Expected the config to have 'hostname' as hostname, got '%v'", config.Hostname)
   257  	}
   258  }
   259  
   260  func TestParseWithExpose(t *testing.T) {
   261  	invalids := map[string]string{
   262  		":":                   "Invalid port format for --expose: :",
   263  		"8080:9090":           "Invalid port format for --expose: 8080:9090",
   264  		"/tcp":                "Invalid range format for --expose: /tcp, error: Empty string specified for ports.",
   265  		"/udp":                "Invalid range format for --expose: /udp, error: Empty string specified for ports.",
   266  		"NaN/tcp":             `Invalid range format for --expose: NaN/tcp, error: strconv.ParseUint: parsing "NaN": invalid syntax`,
   267  		"NaN-NaN/tcp":         `Invalid range format for --expose: NaN-NaN/tcp, error: strconv.ParseUint: parsing "NaN": invalid syntax`,
   268  		"8080-NaN/tcp":        `Invalid range format for --expose: 8080-NaN/tcp, error: strconv.ParseUint: parsing "NaN": invalid syntax`,
   269  		"1234567890-8080/tcp": `Invalid range format for --expose: 1234567890-8080/tcp, error: strconv.ParseUint: parsing "1234567890": value out of range`,
   270  	}
   271  	valids := map[string][]nat.Port{
   272  		"8080/tcp":      {"8080/tcp"},
   273  		"8080/udp":      {"8080/udp"},
   274  		"8080/ncp":      {"8080/ncp"},
   275  		"8080-8080/udp": {"8080/udp"},
   276  		"8080-8082/tcp": {"8080/tcp", "8081/tcp", "8082/tcp"},
   277  	}
   278  	for expose, expectedError := range invalids {
   279  		if _, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"}); err == nil || err.Error() != expectedError {
   280  			t.Fatalf("Expected error '%v' with '--expose=%v', got '%v'", expectedError, expose, err)
   281  		}
   282  	}
   283  	for expose, exposedPorts := range valids {
   284  		config, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"})
   285  		if err != nil {
   286  			t.Fatal(err)
   287  		}
   288  		if len(config.ExposedPorts) != len(exposedPorts) {
   289  			t.Fatalf("Expected %v exposed port, got %v", len(exposedPorts), len(config.ExposedPorts))
   290  		}
   291  		for _, port := range exposedPorts {
   292  			if _, ok := config.ExposedPorts[port]; !ok {
   293  				t.Fatalf("Expected %v, got %v", exposedPorts, config.ExposedPorts)
   294  			}
   295  		}
   296  	}
   297  	// Merge with actual published port
   298  	config, _, _, err := parseRun([]string{"--publish=80", "--expose=80-81/tcp", "img", "cmd"})
   299  	if err != nil {
   300  		t.Fatal(err)
   301  	}
   302  	if len(config.ExposedPorts) != 2 {
   303  		t.Fatalf("Expected 2 exposed ports, got %v", config.ExposedPorts)
   304  	}
   305  	ports := []nat.Port{"80/tcp", "81/tcp"}
   306  	for _, port := range ports {
   307  		if _, ok := config.ExposedPorts[port]; !ok {
   308  			t.Fatalf("Expected %v, got %v", ports, config.ExposedPorts)
   309  		}
   310  	}
   311  }
   312  
   313  func TestParseDevice(t *testing.T) {
   314  	valids := map[string]DeviceMapping{
   315  		"/dev/snd": {
   316  			PathOnHost:        "/dev/snd",
   317  			PathInContainer:   "/dev/snd",
   318  			CgroupPermissions: "rwm",
   319  		},
   320  		"/dev/snd:rw": {
   321  			PathOnHost:        "/dev/snd",
   322  			PathInContainer:   "/dev/snd",
   323  			CgroupPermissions: "rw",
   324  		},
   325  		"/dev/snd:/something": {
   326  			PathOnHost:        "/dev/snd",
   327  			PathInContainer:   "/something",
   328  			CgroupPermissions: "rwm",
   329  		},
   330  		"/dev/snd:/something:rw": {
   331  			PathOnHost:        "/dev/snd",
   332  			PathInContainer:   "/something",
   333  			CgroupPermissions: "rw",
   334  		},
   335  	}
   336  	for device, deviceMapping := range valids {
   337  		_, hostconfig, _, err := parseRun([]string{fmt.Sprintf("--device=%v", device), "img", "cmd"})
   338  		if err != nil {
   339  			t.Fatal(err)
   340  		}
   341  		if len(hostconfig.Devices) != 1 {
   342  			t.Fatalf("Expected 1 devices, got %v", hostconfig.Devices)
   343  		}
   344  		if hostconfig.Devices[0] != deviceMapping {
   345  			t.Fatalf("Expected %v, got %v", deviceMapping, hostconfig.Devices)
   346  		}
   347  	}
   348  
   349  }
   350  
   351  func TestParseModes(t *testing.T) {
   352  	// ipc ko
   353  	if _, _, _, err := parseRun([]string{"--ipc=container:", "img", "cmd"}); err == nil || err.Error() != "--ipc: invalid IPC mode" {
   354  		t.Fatalf("Expected an error with message '--ipc: invalid IPC mode', got %v", err)
   355  	}
   356  	// ipc ok
   357  	_, hostconfig, _, err := parseRun([]string{"--ipc=host", "img", "cmd"})
   358  	if err != nil {
   359  		t.Fatal(err)
   360  	}
   361  	if !hostconfig.IpcMode.Valid() {
   362  		t.Fatalf("Expected a valid IpcMode, got %v", hostconfig.IpcMode)
   363  	}
   364  	// pid ko
   365  	if _, _, _, err := parseRun([]string{"--pid=container:", "img", "cmd"}); err == nil || err.Error() != "--pid: invalid PID mode" {
   366  		t.Fatalf("Expected an error with message '--pid: invalid PID mode', got %v", err)
   367  	}
   368  	// pid ok
   369  	_, hostconfig, _, err = parseRun([]string{"--pid=host", "img", "cmd"})
   370  	if err != nil {
   371  		t.Fatal(err)
   372  	}
   373  	if !hostconfig.PidMode.Valid() {
   374  		t.Fatalf("Expected a valid PidMode, got %v", hostconfig.PidMode)
   375  	}
   376  	// uts ko
   377  	if _, _, _, err := parseRun([]string{"--uts=container:", "img", "cmd"}); err == nil || err.Error() != "--uts: invalid UTS mode" {
   378  		t.Fatalf("Expected an error with message '--uts: invalid UTS mode', got %v", err)
   379  	}
   380  	// uts ok
   381  	_, hostconfig, _, err = parseRun([]string{"--uts=host", "img", "cmd"})
   382  	if err != nil {
   383  		t.Fatal(err)
   384  	}
   385  	if !hostconfig.UTSMode.Valid() {
   386  		t.Fatalf("Expected a valid UTSMode, got %v", hostconfig.UTSMode)
   387  	}
   388  }
   389  
   390  func TestParseRestartPolicy(t *testing.T) {
   391  	invalids := map[string]string{
   392  		"something":          "invalid restart policy something",
   393  		"always:2":           "maximum restart count not valid with restart policy of \"always\"",
   394  		"always:2:3":         "maximum restart count not valid with restart policy of \"always\"",
   395  		"on-failure:invalid": `strconv.ParseInt: parsing "invalid": invalid syntax`,
   396  		"on-failure:2:5":     "restart count format is not valid, usage: 'on-failure:N' or 'on-failure'",
   397  	}
   398  	valids := map[string]RestartPolicy{
   399  		"": {},
   400  		"always": {
   401  			Name:              "always",
   402  			MaximumRetryCount: 0,
   403  		},
   404  		"on-failure:1": {
   405  			Name:              "on-failure",
   406  			MaximumRetryCount: 1,
   407  		},
   408  	}
   409  	for restart, expectedError := range invalids {
   410  		if _, _, _, err := parseRun([]string{fmt.Sprintf("--restart=%s", restart), "img", "cmd"}); err == nil || err.Error() != expectedError {
   411  			t.Fatalf("Expected an error with message '%v' for %v, got %v", expectedError, restart, err)
   412  		}
   413  	}
   414  	for restart, expected := range valids {
   415  		_, hostconfig, _, err := parseRun([]string{fmt.Sprintf("--restart=%v", restart), "img", "cmd"})
   416  		if err != nil {
   417  			t.Fatal(err)
   418  		}
   419  		if hostconfig.RestartPolicy != expected {
   420  			t.Fatalf("Expected %v, got %v", expected, hostconfig.RestartPolicy)
   421  		}
   422  	}
   423  }
   424  
   425  func TestParseLoggingOpts(t *testing.T) {
   426  	// logging opts ko
   427  	if _, _, _, err := parseRun([]string{"--log-driver=none", "--log-opt=anything", "img", "cmd"}); err == nil || err.Error() != "Invalid logging opts for driver none" {
   428  		t.Fatalf("Expected an error with message 'Invalid logging opts for driver none', got %v", err)
   429  	}
   430  	// logging opts ok
   431  	_, hostconfig, _, err := parseRun([]string{"--log-driver=syslog", "--log-opt=something", "img", "cmd"})
   432  	if err != nil {
   433  		t.Fatal(err)
   434  	}
   435  	if hostconfig.LogConfig.Type != "syslog" || len(hostconfig.LogConfig.Config) != 1 {
   436  		t.Fatalf("Expected a 'syslog' LogConfig with one config, got %v", hostconfig.RestartPolicy)
   437  	}
   438  }
   439  
   440  func TestParseEnvfileVariables(t *testing.T) {
   441  	// env ko
   442  	if _, _, _, err := parseRun([]string{"--env-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != "open nonexistent: no such file or directory" {
   443  		t.Fatalf("Expected an error with message 'open nonexistent: no such file or directory', got %v", err)
   444  	}
   445  	// env ok
   446  	config, _, _, err := parseRun([]string{"--env-file=fixtures/valid.env", "img", "cmd"})
   447  	if err != nil {
   448  		t.Fatal(err)
   449  	}
   450  	if len(config.Env) != 1 || config.Env[0] != "ENV1=value1" {
   451  		t.Fatalf("Expected a a config with [ENV1=value1], got %v", config.Env)
   452  	}
   453  	config, _, _, err = parseRun([]string{"--env-file=fixtures/valid.env", "--env=ENV2=value2", "img", "cmd"})
   454  	if err != nil {
   455  		t.Fatal(err)
   456  	}
   457  	if len(config.Env) != 2 || config.Env[0] != "ENV1=value1" || config.Env[1] != "ENV2=value2" {
   458  		t.Fatalf("Expected a a config with [ENV1=value1 ENV2=value2], got %v", config.Env)
   459  	}
   460  }
   461  
   462  func TestParseLabelfileVariables(t *testing.T) {
   463  	// label ko
   464  	if _, _, _, err := parseRun([]string{"--label-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != "open nonexistent: no such file or directory" {
   465  		t.Fatalf("Expected an error with message 'open nonexistent: no such file or directory', got %v", err)
   466  	}
   467  	// label ok
   468  	config, _, _, err := parseRun([]string{"--label-file=fixtures/valid.label", "img", "cmd"})
   469  	if err != nil {
   470  		t.Fatal(err)
   471  	}
   472  	if len(config.Labels) != 1 || config.Labels["LABEL1"] != "value1" {
   473  		t.Fatalf("Expected a a config with [LABEL1:value1], got %v", config.Labels)
   474  	}
   475  	config, _, _, err = parseRun([]string{"--label-file=fixtures/valid.label", "--label=LABEL2=value2", "img", "cmd"})
   476  	if err != nil {
   477  		t.Fatal(err)
   478  	}
   479  	if len(config.Labels) != 2 || config.Labels["LABEL1"] != "value1" || config.Labels["LABEL2"] != "value2" {
   480  		t.Fatalf("Expected a a config with [LABEL1:value1 LABEL2:value2], got %v", config.Labels)
   481  	}
   482  }
   483  
   484  func TestParseEntryPoint(t *testing.T) {
   485  	config, _, _, err := parseRun([]string{"--entrypoint=anything", "cmd", "img"})
   486  	if err != nil {
   487  		t.Fatal(err)
   488  	}
   489  	if config.Entrypoint.Len() != 1 && config.Entrypoint.Slice()[0] != "anything" {
   490  		t.Fatalf("Expected entrypoint 'anything', got %v", config.Entrypoint)
   491  	}
   492  }