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 }