github.com/olljanat/moby@v1.13.1/cli/command/service/update_test.go (about)

     1  package service
     2  
     3  import (
     4  	"reflect"
     5  	"sort"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/docker/docker/api/types/container"
    10  	mounttypes "github.com/docker/docker/api/types/mount"
    11  	"github.com/docker/docker/api/types/swarm"
    12  	"github.com/docker/docker/pkg/testutil/assert"
    13  )
    14  
    15  func TestUpdateServiceArgs(t *testing.T) {
    16  	flags := newUpdateCommand(nil).Flags()
    17  	flags.Set("args", "the \"new args\"")
    18  
    19  	spec := &swarm.ServiceSpec{}
    20  	cspec := &spec.TaskTemplate.ContainerSpec
    21  	cspec.Args = []string{"old", "args"}
    22  
    23  	updateService(flags, spec)
    24  	assert.EqualStringSlice(t, cspec.Args, []string{"the", "new args"})
    25  }
    26  
    27  func TestUpdateLabels(t *testing.T) {
    28  	flags := newUpdateCommand(nil).Flags()
    29  	flags.Set("label-add", "toadd=newlabel")
    30  	flags.Set("label-rm", "toremove")
    31  
    32  	labels := map[string]string{
    33  		"toremove": "thelabeltoremove",
    34  		"tokeep":   "value",
    35  	}
    36  
    37  	updateLabels(flags, &labels)
    38  	assert.Equal(t, len(labels), 2)
    39  	assert.Equal(t, labels["tokeep"], "value")
    40  	assert.Equal(t, labels["toadd"], "newlabel")
    41  }
    42  
    43  func TestUpdateLabelsRemoveALabelThatDoesNotExist(t *testing.T) {
    44  	flags := newUpdateCommand(nil).Flags()
    45  	flags.Set("label-rm", "dne")
    46  
    47  	labels := map[string]string{"foo": "theoldlabel"}
    48  	updateLabels(flags, &labels)
    49  	assert.Equal(t, len(labels), 1)
    50  }
    51  
    52  func TestUpdatePlacement(t *testing.T) {
    53  	flags := newUpdateCommand(nil).Flags()
    54  	flags.Set("constraint-add", "node=toadd")
    55  	flags.Set("constraint-rm", "node!=toremove")
    56  
    57  	placement := &swarm.Placement{
    58  		Constraints: []string{"node!=toremove", "container=tokeep"},
    59  	}
    60  
    61  	updatePlacement(flags, placement)
    62  	assert.Equal(t, len(placement.Constraints), 2)
    63  	assert.Equal(t, placement.Constraints[0], "container=tokeep")
    64  	assert.Equal(t, placement.Constraints[1], "node=toadd")
    65  }
    66  
    67  func TestUpdateEnvironment(t *testing.T) {
    68  	flags := newUpdateCommand(nil).Flags()
    69  	flags.Set("env-add", "toadd=newenv")
    70  	flags.Set("env-rm", "toremove")
    71  
    72  	envs := []string{"toremove=theenvtoremove", "tokeep=value"}
    73  
    74  	updateEnvironment(flags, &envs)
    75  	assert.Equal(t, len(envs), 2)
    76  	// Order has been removed in updateEnvironment (map)
    77  	sort.Strings(envs)
    78  	assert.Equal(t, envs[0], "toadd=newenv")
    79  	assert.Equal(t, envs[1], "tokeep=value")
    80  }
    81  
    82  func TestUpdateEnvironmentWithDuplicateValues(t *testing.T) {
    83  	flags := newUpdateCommand(nil).Flags()
    84  	flags.Set("env-add", "foo=newenv")
    85  	flags.Set("env-add", "foo=dupe")
    86  	flags.Set("env-rm", "foo")
    87  
    88  	envs := []string{"foo=value"}
    89  
    90  	updateEnvironment(flags, &envs)
    91  	assert.Equal(t, len(envs), 0)
    92  }
    93  
    94  func TestUpdateEnvironmentWithDuplicateKeys(t *testing.T) {
    95  	// Test case for #25404
    96  	flags := newUpdateCommand(nil).Flags()
    97  	flags.Set("env-add", "A=b")
    98  
    99  	envs := []string{"A=c"}
   100  
   101  	updateEnvironment(flags, &envs)
   102  	assert.Equal(t, len(envs), 1)
   103  	assert.Equal(t, envs[0], "A=b")
   104  }
   105  
   106  func TestUpdateGroups(t *testing.T) {
   107  	flags := newUpdateCommand(nil).Flags()
   108  	flags.Set("group-add", "wheel")
   109  	flags.Set("group-add", "docker")
   110  	flags.Set("group-rm", "root")
   111  	flags.Set("group-add", "foo")
   112  	flags.Set("group-rm", "docker")
   113  
   114  	groups := []string{"bar", "root"}
   115  
   116  	updateGroups(flags, &groups)
   117  	assert.Equal(t, len(groups), 3)
   118  	assert.Equal(t, groups[0], "bar")
   119  	assert.Equal(t, groups[1], "foo")
   120  	assert.Equal(t, groups[2], "wheel")
   121  }
   122  
   123  func TestUpdateDNSConfig(t *testing.T) {
   124  	flags := newUpdateCommand(nil).Flags()
   125  
   126  	// IPv4, with duplicates
   127  	flags.Set("dns-add", "1.1.1.1")
   128  	flags.Set("dns-add", "1.1.1.1")
   129  	flags.Set("dns-add", "2.2.2.2")
   130  	flags.Set("dns-rm", "3.3.3.3")
   131  	flags.Set("dns-rm", "2.2.2.2")
   132  	// IPv6
   133  	flags.Set("dns-add", "2001:db8:abc8::1")
   134  	// Invalid dns record
   135  	assert.Error(t, flags.Set("dns-add", "x.y.z.w"), "x.y.z.w is not an ip address")
   136  
   137  	// domains with duplicates
   138  	flags.Set("dns-search-add", "example.com")
   139  	flags.Set("dns-search-add", "example.com")
   140  	flags.Set("dns-search-add", "example.org")
   141  	flags.Set("dns-search-rm", "example.org")
   142  	// Invalid dns search domain
   143  	assert.Error(t, flags.Set("dns-search-add", "example$com"), "example$com is not a valid domain")
   144  
   145  	flags.Set("dns-option-add", "ndots:9")
   146  	flags.Set("dns-option-rm", "timeout:3")
   147  
   148  	config := &swarm.DNSConfig{
   149  		Nameservers: []string{"3.3.3.3", "5.5.5.5"},
   150  		Search:      []string{"localdomain"},
   151  		Options:     []string{"timeout:3"},
   152  	}
   153  
   154  	updateDNSConfig(flags, &config)
   155  
   156  	assert.Equal(t, len(config.Nameservers), 3)
   157  	assert.Equal(t, config.Nameservers[0], "1.1.1.1")
   158  	assert.Equal(t, config.Nameservers[1], "2001:db8:abc8::1")
   159  	assert.Equal(t, config.Nameservers[2], "5.5.5.5")
   160  
   161  	assert.Equal(t, len(config.Search), 2)
   162  	assert.Equal(t, config.Search[0], "example.com")
   163  	assert.Equal(t, config.Search[1], "localdomain")
   164  
   165  	assert.Equal(t, len(config.Options), 1)
   166  	assert.Equal(t, config.Options[0], "ndots:9")
   167  }
   168  
   169  func TestUpdateMounts(t *testing.T) {
   170  	flags := newUpdateCommand(nil).Flags()
   171  	flags.Set("mount-add", "type=volume,source=vol2,target=/toadd")
   172  	flags.Set("mount-rm", "/toremove")
   173  
   174  	mounts := []mounttypes.Mount{
   175  		{Target: "/toremove", Source: "vol1", Type: mounttypes.TypeBind},
   176  		{Target: "/tokeep", Source: "vol3", Type: mounttypes.TypeBind},
   177  	}
   178  
   179  	updateMounts(flags, &mounts)
   180  	assert.Equal(t, len(mounts), 2)
   181  	assert.Equal(t, mounts[0].Target, "/toadd")
   182  	assert.Equal(t, mounts[1].Target, "/tokeep")
   183  
   184  }
   185  
   186  func TestUpdateMountsWithDuplicateMounts(t *testing.T) {
   187  	flags := newUpdateCommand(nil).Flags()
   188  	flags.Set("mount-add", "type=volume,source=vol4,target=/toadd")
   189  
   190  	mounts := []mounttypes.Mount{
   191  		{Target: "/tokeep1", Source: "vol1", Type: mounttypes.TypeBind},
   192  		{Target: "/toadd", Source: "vol2", Type: mounttypes.TypeBind},
   193  		{Target: "/tokeep2", Source: "vol3", Type: mounttypes.TypeBind},
   194  	}
   195  
   196  	updateMounts(flags, &mounts)
   197  	assert.Equal(t, len(mounts), 3)
   198  	assert.Equal(t, mounts[0].Target, "/tokeep1")
   199  	assert.Equal(t, mounts[1].Target, "/tokeep2")
   200  	assert.Equal(t, mounts[2].Target, "/toadd")
   201  }
   202  
   203  func TestUpdatePorts(t *testing.T) {
   204  	flags := newUpdateCommand(nil).Flags()
   205  	flags.Set("publish-add", "1000:1000")
   206  	flags.Set("publish-rm", "333/udp")
   207  
   208  	portConfigs := []swarm.PortConfig{
   209  		{TargetPort: 333, Protocol: swarm.PortConfigProtocolUDP},
   210  		{TargetPort: 555},
   211  	}
   212  
   213  	err := updatePorts(flags, &portConfigs)
   214  	assert.Equal(t, err, nil)
   215  	assert.Equal(t, len(portConfigs), 2)
   216  	// Do a sort to have the order (might have changed by map)
   217  	targetPorts := []int{int(portConfigs[0].TargetPort), int(portConfigs[1].TargetPort)}
   218  	sort.Ints(targetPorts)
   219  	assert.Equal(t, targetPorts[0], 555)
   220  	assert.Equal(t, targetPorts[1], 1000)
   221  }
   222  
   223  func TestUpdatePortsDuplicate(t *testing.T) {
   224  	// Test case for #25375
   225  	flags := newUpdateCommand(nil).Flags()
   226  	flags.Set("publish-add", "80:80")
   227  
   228  	portConfigs := []swarm.PortConfig{
   229  		{
   230  			TargetPort:    80,
   231  			PublishedPort: 80,
   232  			Protocol:      swarm.PortConfigProtocolTCP,
   233  			PublishMode:   swarm.PortConfigPublishModeIngress,
   234  		},
   235  	}
   236  
   237  	err := updatePorts(flags, &portConfigs)
   238  	assert.Equal(t, err, nil)
   239  	assert.Equal(t, len(portConfigs), 1)
   240  	assert.Equal(t, portConfigs[0].TargetPort, uint32(80))
   241  }
   242  
   243  func TestUpdateHealthcheckTable(t *testing.T) {
   244  	type test struct {
   245  		flags    [][2]string
   246  		initial  *container.HealthConfig
   247  		expected *container.HealthConfig
   248  		err      string
   249  	}
   250  	testCases := []test{
   251  		{
   252  			flags:    [][2]string{{"no-healthcheck", "true"}},
   253  			initial:  &container.HealthConfig{Test: []string{"CMD-SHELL", "cmd1"}, Retries: 10},
   254  			expected: &container.HealthConfig{Test: []string{"NONE"}},
   255  		},
   256  		{
   257  			flags:    [][2]string{{"health-cmd", "cmd1"}},
   258  			initial:  &container.HealthConfig{Test: []string{"NONE"}},
   259  			expected: &container.HealthConfig{Test: []string{"CMD-SHELL", "cmd1"}},
   260  		},
   261  		{
   262  			flags:    [][2]string{{"health-retries", "10"}},
   263  			initial:  &container.HealthConfig{Test: []string{"NONE"}},
   264  			expected: &container.HealthConfig{Retries: 10},
   265  		},
   266  		{
   267  			flags:    [][2]string{{"health-retries", "10"}},
   268  			initial:  &container.HealthConfig{Test: []string{"CMD", "cmd1"}},
   269  			expected: &container.HealthConfig{Test: []string{"CMD", "cmd1"}, Retries: 10},
   270  		},
   271  		{
   272  			flags:    [][2]string{{"health-interval", "1m"}},
   273  			initial:  &container.HealthConfig{Test: []string{"CMD", "cmd1"}},
   274  			expected: &container.HealthConfig{Test: []string{"CMD", "cmd1"}, Interval: time.Minute},
   275  		},
   276  		{
   277  			flags:    [][2]string{{"health-cmd", ""}},
   278  			initial:  &container.HealthConfig{Test: []string{"CMD", "cmd1"}, Retries: 10},
   279  			expected: &container.HealthConfig{Retries: 10},
   280  		},
   281  		{
   282  			flags:    [][2]string{{"health-retries", "0"}},
   283  			initial:  &container.HealthConfig{Test: []string{"CMD", "cmd1"}, Retries: 10},
   284  			expected: &container.HealthConfig{Test: []string{"CMD", "cmd1"}},
   285  		},
   286  		{
   287  			flags: [][2]string{{"health-cmd", "cmd1"}, {"no-healthcheck", "true"}},
   288  			err:   "--no-healthcheck conflicts with --health-* options",
   289  		},
   290  		{
   291  			flags: [][2]string{{"health-interval", "10m"}, {"no-healthcheck", "true"}},
   292  			err:   "--no-healthcheck conflicts with --health-* options",
   293  		},
   294  		{
   295  			flags: [][2]string{{"health-timeout", "1m"}, {"no-healthcheck", "true"}},
   296  			err:   "--no-healthcheck conflicts with --health-* options",
   297  		},
   298  	}
   299  	for i, c := range testCases {
   300  		flags := newUpdateCommand(nil).Flags()
   301  		for _, flag := range c.flags {
   302  			flags.Set(flag[0], flag[1])
   303  		}
   304  		cspec := &swarm.ContainerSpec{
   305  			Healthcheck: c.initial,
   306  		}
   307  		err := updateHealthcheck(flags, cspec)
   308  		if c.err != "" {
   309  			assert.Error(t, err, c.err)
   310  		} else {
   311  			assert.NilError(t, err)
   312  			if !reflect.DeepEqual(cspec.Healthcheck, c.expected) {
   313  				t.Errorf("incorrect result for test %d, expected health config:\n\t%#v\ngot:\n\t%#v", i, c.expected, cspec.Healthcheck)
   314  			}
   315  		}
   316  	}
   317  }
   318  
   319  func TestUpdateHosts(t *testing.T) {
   320  	flags := newUpdateCommand(nil).Flags()
   321  	flags.Set("host-add", "example.net:2.2.2.2")
   322  	flags.Set("host-add", "ipv6.net:2001:db8:abc8::1")
   323  	// remove with ipv6 should work
   324  	flags.Set("host-rm", "example.net:2001:db8:abc8::1")
   325  	// just hostname should work as well
   326  	flags.Set("host-rm", "example.net")
   327  	// bad format error
   328  	assert.Error(t, flags.Set("host-add", "$example.com$"), "bad format for add-host:")
   329  
   330  	hosts := []string{"1.2.3.4 example.com", "4.3.2.1 example.org", "2001:db8:abc8::1 example.net"}
   331  
   332  	updateHosts(flags, &hosts)
   333  	assert.Equal(t, len(hosts), 3)
   334  	assert.Equal(t, hosts[0], "1.2.3.4 example.com")
   335  	assert.Equal(t, hosts[1], "2001:db8:abc8::1 ipv6.net")
   336  	assert.Equal(t, hosts[2], "4.3.2.1 example.org")
   337  }
   338  
   339  func TestUpdatePortsRmWithProtocol(t *testing.T) {
   340  	flags := newUpdateCommand(nil).Flags()
   341  	flags.Set("publish-add", "8081:81")
   342  	flags.Set("publish-add", "8082:82")
   343  	flags.Set("publish-rm", "80")
   344  	flags.Set("publish-rm", "81/tcp")
   345  	flags.Set("publish-rm", "82/udp")
   346  
   347  	portConfigs := []swarm.PortConfig{
   348  		{
   349  			TargetPort:    80,
   350  			PublishedPort: 8080,
   351  			Protocol:      swarm.PortConfigProtocolTCP,
   352  			PublishMode:   swarm.PortConfigPublishModeIngress,
   353  		},
   354  	}
   355  
   356  	err := updatePorts(flags, &portConfigs)
   357  	assert.Equal(t, err, nil)
   358  	assert.Equal(t, len(portConfigs), 2)
   359  	assert.Equal(t, portConfigs[0].TargetPort, uint32(81))
   360  	assert.Equal(t, portConfigs[1].TargetPort, uint32(82))
   361  }
   362  
   363  // FIXME(vdemeester) port to opts.PortOpt
   364  func TestValidatePort(t *testing.T) {
   365  	validPorts := []string{"80/tcp", "80", "80/udp"}
   366  	invalidPorts := map[string]string{
   367  		"9999999":   "out of range",
   368  		"80:80/tcp": "invalid port format",
   369  		"53:53/udp": "invalid port format",
   370  		"80:80":     "invalid port format",
   371  		"80/xyz":    "invalid protocol",
   372  		"tcp":       "invalid syntax",
   373  		"udp":       "invalid syntax",
   374  		"":          "invalid protocol",
   375  	}
   376  	for _, port := range validPorts {
   377  		_, err := validatePublishRemove(port)
   378  		assert.Equal(t, err, nil)
   379  	}
   380  	for port, e := range invalidPorts {
   381  		_, err := validatePublishRemove(port)
   382  		assert.Error(t, err, e)
   383  	}
   384  }