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