github.com/AliyunContainerService/cli@v0.0.0-20181009023821-814ced4b30d0/cli/compose/convert/service_test.go (about) 1 package convert 2 3 import ( 4 "context" 5 "os" 6 "sort" 7 "strings" 8 "testing" 9 "time" 10 11 composetypes "github.com/docker/cli/cli/compose/types" 12 "github.com/docker/docker/api/types" 13 "github.com/docker/docker/api/types/container" 14 "github.com/docker/docker/api/types/swarm" 15 "github.com/docker/docker/client" 16 "github.com/pkg/errors" 17 "gotest.tools/assert" 18 is "gotest.tools/assert/cmp" 19 ) 20 21 func TestConvertRestartPolicyFromNone(t *testing.T) { 22 policy, err := convertRestartPolicy("no", nil) 23 assert.NilError(t, err) 24 assert.Check(t, is.DeepEqual((*swarm.RestartPolicy)(nil), policy)) 25 } 26 27 func TestConvertRestartPolicyFromUnknown(t *testing.T) { 28 _, err := convertRestartPolicy("unknown", nil) 29 assert.Error(t, err, "unknown restart policy: unknown") 30 } 31 32 func TestConvertRestartPolicyFromAlways(t *testing.T) { 33 policy, err := convertRestartPolicy("always", nil) 34 expected := &swarm.RestartPolicy{ 35 Condition: swarm.RestartPolicyConditionAny, 36 } 37 assert.NilError(t, err) 38 assert.Check(t, is.DeepEqual(expected, policy)) 39 } 40 41 func TestConvertRestartPolicyFromFailure(t *testing.T) { 42 policy, err := convertRestartPolicy("on-failure:4", nil) 43 attempts := uint64(4) 44 expected := &swarm.RestartPolicy{ 45 Condition: swarm.RestartPolicyConditionOnFailure, 46 MaxAttempts: &attempts, 47 } 48 assert.NilError(t, err) 49 assert.Check(t, is.DeepEqual(expected, policy)) 50 } 51 52 func strPtr(val string) *string { 53 return &val 54 } 55 56 func TestConvertEnvironment(t *testing.T) { 57 source := map[string]*string{ 58 "foo": strPtr("bar"), 59 "key": strPtr("value"), 60 } 61 env := convertEnvironment(source) 62 sort.Strings(env) 63 assert.Check(t, is.DeepEqual([]string{"foo=bar", "key=value"}, env)) 64 } 65 66 func TestConvertExtraHosts(t *testing.T) { 67 source := composetypes.HostsList{ 68 "zulu:127.0.0.2", 69 "alpha:127.0.0.1", 70 "zulu:ff02::1", 71 } 72 assert.Check(t, is.DeepEqual([]string{"127.0.0.2 zulu", "127.0.0.1 alpha", "ff02::1 zulu"}, convertExtraHosts(source))) 73 } 74 75 func TestConvertResourcesFull(t *testing.T) { 76 source := composetypes.Resources{ 77 Limits: &composetypes.Resource{ 78 NanoCPUs: "0.003", 79 MemoryBytes: composetypes.UnitBytes(300000000), 80 }, 81 Reservations: &composetypes.Resource{ 82 NanoCPUs: "0.002", 83 MemoryBytes: composetypes.UnitBytes(200000000), 84 }, 85 } 86 resources, err := convertResources(source) 87 assert.NilError(t, err) 88 89 expected := &swarm.ResourceRequirements{ 90 Limits: &swarm.Resources{ 91 NanoCPUs: 3000000, 92 MemoryBytes: 300000000, 93 }, 94 Reservations: &swarm.Resources{ 95 NanoCPUs: 2000000, 96 MemoryBytes: 200000000, 97 }, 98 } 99 assert.Check(t, is.DeepEqual(expected, resources)) 100 } 101 102 func TestConvertResourcesOnlyMemory(t *testing.T) { 103 source := composetypes.Resources{ 104 Limits: &composetypes.Resource{ 105 MemoryBytes: composetypes.UnitBytes(300000000), 106 }, 107 Reservations: &composetypes.Resource{ 108 MemoryBytes: composetypes.UnitBytes(200000000), 109 }, 110 } 111 resources, err := convertResources(source) 112 assert.NilError(t, err) 113 114 expected := &swarm.ResourceRequirements{ 115 Limits: &swarm.Resources{ 116 MemoryBytes: 300000000, 117 }, 118 Reservations: &swarm.Resources{ 119 MemoryBytes: 200000000, 120 }, 121 } 122 assert.Check(t, is.DeepEqual(expected, resources)) 123 } 124 125 func TestConvertHealthcheck(t *testing.T) { 126 retries := uint64(10) 127 timeout := composetypes.Duration(30 * time.Second) 128 interval := composetypes.Duration(2 * time.Millisecond) 129 source := &composetypes.HealthCheckConfig{ 130 Test: []string{"EXEC", "touch", "/foo"}, 131 Timeout: &timeout, 132 Interval: &interval, 133 Retries: &retries, 134 } 135 expected := &container.HealthConfig{ 136 Test: source.Test, 137 Timeout: time.Duration(timeout), 138 Interval: time.Duration(interval), 139 Retries: 10, 140 } 141 142 healthcheck, err := convertHealthcheck(source) 143 assert.NilError(t, err) 144 assert.Check(t, is.DeepEqual(expected, healthcheck)) 145 } 146 147 func TestConvertHealthcheckDisable(t *testing.T) { 148 source := &composetypes.HealthCheckConfig{Disable: true} 149 expected := &container.HealthConfig{ 150 Test: []string{"NONE"}, 151 } 152 153 healthcheck, err := convertHealthcheck(source) 154 assert.NilError(t, err) 155 assert.Check(t, is.DeepEqual(expected, healthcheck)) 156 } 157 158 func TestConvertHealthcheckDisableWithTest(t *testing.T) { 159 source := &composetypes.HealthCheckConfig{ 160 Disable: true, 161 Test: []string{"EXEC", "touch"}, 162 } 163 _, err := convertHealthcheck(source) 164 assert.Error(t, err, "test and disable can't be set at the same time") 165 } 166 167 func TestConvertEndpointSpec(t *testing.T) { 168 source := []composetypes.ServicePortConfig{ 169 { 170 Protocol: "udp", 171 Target: 53, 172 Published: 1053, 173 Mode: "host", 174 }, 175 { 176 Target: 8080, 177 Published: 80, 178 }, 179 } 180 endpoint, err := convertEndpointSpec("vip", source) 181 182 expected := swarm.EndpointSpec{ 183 Mode: swarm.ResolutionMode(strings.ToLower("vip")), 184 Ports: []swarm.PortConfig{ 185 { 186 TargetPort: 8080, 187 PublishedPort: 80, 188 }, 189 { 190 Protocol: "udp", 191 TargetPort: 53, 192 PublishedPort: 1053, 193 PublishMode: "host", 194 }, 195 }, 196 } 197 198 assert.NilError(t, err) 199 assert.Check(t, is.DeepEqual(expected, *endpoint)) 200 } 201 202 func TestConvertServiceNetworksOnlyDefault(t *testing.T) { 203 networkConfigs := networkMap{} 204 205 configs, err := convertServiceNetworks( 206 nil, networkConfigs, NewNamespace("foo"), "service") 207 208 expected := []swarm.NetworkAttachmentConfig{ 209 { 210 Target: "foo_default", 211 Aliases: []string{"service"}, 212 }, 213 } 214 215 assert.NilError(t, err) 216 assert.Check(t, is.DeepEqual(expected, configs)) 217 } 218 219 func TestConvertServiceNetworks(t *testing.T) { 220 networkConfigs := networkMap{ 221 "front": composetypes.NetworkConfig{ 222 External: composetypes.External{External: true}, 223 Name: "fronttier", 224 }, 225 "back": composetypes.NetworkConfig{}, 226 } 227 networks := map[string]*composetypes.ServiceNetworkConfig{ 228 "front": { 229 Aliases: []string{"something"}, 230 }, 231 "back": { 232 Aliases: []string{"other"}, 233 }, 234 } 235 236 configs, err := convertServiceNetworks( 237 networks, networkConfigs, NewNamespace("foo"), "service") 238 239 expected := []swarm.NetworkAttachmentConfig{ 240 { 241 Target: "foo_back", 242 Aliases: []string{"other", "service"}, 243 }, 244 { 245 Target: "fronttier", 246 Aliases: []string{"something", "service"}, 247 }, 248 } 249 250 assert.NilError(t, err) 251 assert.Check(t, is.DeepEqual(expected, configs)) 252 } 253 254 func TestConvertServiceNetworksCustomDefault(t *testing.T) { 255 networkConfigs := networkMap{ 256 "default": composetypes.NetworkConfig{ 257 External: composetypes.External{External: true}, 258 Name: "custom", 259 }, 260 } 261 networks := map[string]*composetypes.ServiceNetworkConfig{} 262 263 configs, err := convertServiceNetworks( 264 networks, networkConfigs, NewNamespace("foo"), "service") 265 266 expected := []swarm.NetworkAttachmentConfig{ 267 { 268 Target: "custom", 269 Aliases: []string{"service"}, 270 }, 271 } 272 273 assert.NilError(t, err) 274 assert.Check(t, is.DeepEqual(expected, []swarm.NetworkAttachmentConfig(configs))) 275 } 276 277 func TestConvertDNSConfigEmpty(t *testing.T) { 278 dnsConfig, err := convertDNSConfig(nil, nil) 279 280 assert.NilError(t, err) 281 assert.Check(t, is.DeepEqual((*swarm.DNSConfig)(nil), dnsConfig)) 282 } 283 284 var ( 285 nameservers = []string{"8.8.8.8", "9.9.9.9"} 286 search = []string{"dc1.example.com", "dc2.example.com"} 287 ) 288 289 func TestConvertDNSConfigAll(t *testing.T) { 290 dnsConfig, err := convertDNSConfig(nameservers, search) 291 assert.NilError(t, err) 292 assert.Check(t, is.DeepEqual(&swarm.DNSConfig{ 293 Nameservers: nameservers, 294 Search: search, 295 }, dnsConfig)) 296 } 297 298 func TestConvertDNSConfigNameservers(t *testing.T) { 299 dnsConfig, err := convertDNSConfig(nameservers, nil) 300 assert.NilError(t, err) 301 assert.Check(t, is.DeepEqual(&swarm.DNSConfig{ 302 Nameservers: nameservers, 303 Search: nil, 304 }, dnsConfig)) 305 } 306 307 func TestConvertDNSConfigSearch(t *testing.T) { 308 dnsConfig, err := convertDNSConfig(nil, search) 309 assert.NilError(t, err) 310 assert.Check(t, is.DeepEqual(&swarm.DNSConfig{ 311 Nameservers: nil, 312 Search: search, 313 }, dnsConfig)) 314 } 315 316 func TestConvertCredentialSpec(t *testing.T) { 317 swarmSpec, err := convertCredentialSpec(composetypes.CredentialSpecConfig{}) 318 assert.NilError(t, err) 319 assert.Check(t, is.Nil(swarmSpec)) 320 321 swarmSpec, err = convertCredentialSpec(composetypes.CredentialSpecConfig{ 322 File: "/foo", 323 }) 324 assert.NilError(t, err) 325 assert.Check(t, is.Equal(swarmSpec.File, "/foo")) 326 assert.Check(t, is.Equal(swarmSpec.Registry, "")) 327 328 swarmSpec, err = convertCredentialSpec(composetypes.CredentialSpecConfig{ 329 Registry: "foo", 330 }) 331 assert.NilError(t, err) 332 assert.Check(t, is.Equal(swarmSpec.File, "")) 333 assert.Check(t, is.Equal(swarmSpec.Registry, "foo")) 334 335 swarmSpec, err = convertCredentialSpec(composetypes.CredentialSpecConfig{ 336 File: "/asdf", 337 Registry: "foo", 338 }) 339 assert.Check(t, is.ErrorContains(err, "")) 340 assert.Check(t, is.Nil(swarmSpec)) 341 } 342 343 func TestConvertUpdateConfigOrder(t *testing.T) { 344 // test default behavior 345 updateConfig := convertUpdateConfig(&composetypes.UpdateConfig{}) 346 assert.Check(t, is.Equal("", updateConfig.Order)) 347 348 // test start-first 349 updateConfig = convertUpdateConfig(&composetypes.UpdateConfig{ 350 Order: "start-first", 351 }) 352 assert.Check(t, is.Equal(updateConfig.Order, "start-first")) 353 354 // test stop-first 355 updateConfig = convertUpdateConfig(&composetypes.UpdateConfig{ 356 Order: "stop-first", 357 }) 358 assert.Check(t, is.Equal(updateConfig.Order, "stop-first")) 359 } 360 361 func TestConvertFileObject(t *testing.T) { 362 namespace := NewNamespace("testing") 363 config := composetypes.FileReferenceConfig{ 364 Source: "source", 365 Target: "target", 366 UID: "user", 367 GID: "group", 368 Mode: uint32Ptr(0644), 369 } 370 swarmRef, err := convertFileObject(namespace, config, lookupConfig) 371 assert.NilError(t, err) 372 373 expected := swarmReferenceObject{ 374 Name: "testing_source", 375 File: swarmReferenceTarget{ 376 Name: config.Target, 377 UID: config.UID, 378 GID: config.GID, 379 Mode: os.FileMode(0644), 380 }, 381 } 382 assert.Check(t, is.DeepEqual(expected, swarmRef)) 383 } 384 385 func lookupConfig(key string) (composetypes.FileObjectConfig, error) { 386 if key != "source" { 387 return composetypes.FileObjectConfig{}, errors.New("bad key") 388 } 389 return composetypes.FileObjectConfig{}, nil 390 } 391 392 func TestConvertFileObjectDefaults(t *testing.T) { 393 namespace := NewNamespace("testing") 394 config := composetypes.FileReferenceConfig{Source: "source"} 395 swarmRef, err := convertFileObject(namespace, config, lookupConfig) 396 assert.NilError(t, err) 397 398 expected := swarmReferenceObject{ 399 Name: "testing_source", 400 File: swarmReferenceTarget{ 401 Name: config.Source, 402 UID: "0", 403 GID: "0", 404 Mode: os.FileMode(0444), 405 }, 406 } 407 assert.Check(t, is.DeepEqual(expected, swarmRef)) 408 } 409 410 func TestServiceConvertsIsolation(t *testing.T) { 411 src := composetypes.ServiceConfig{ 412 Isolation: "hyperv", 413 } 414 result, err := Service("1.35", Namespace{name: "foo"}, src, nil, nil, nil, nil) 415 assert.NilError(t, err) 416 assert.Check(t, is.Equal(container.IsolationHyperV, result.TaskTemplate.ContainerSpec.Isolation)) 417 } 418 419 func TestConvertServiceSecrets(t *testing.T) { 420 namespace := Namespace{name: "foo"} 421 secrets := []composetypes.ServiceSecretConfig{ 422 {Source: "foo_secret"}, 423 {Source: "bar_secret"}, 424 } 425 secretSpecs := map[string]composetypes.SecretConfig{ 426 "foo_secret": { 427 Name: "foo_secret", 428 }, 429 "bar_secret": { 430 Name: "bar_secret", 431 }, 432 } 433 client := &fakeClient{ 434 secretListFunc: func(opts types.SecretListOptions) ([]swarm.Secret, error) { 435 assert.Check(t, is.Contains(opts.Filters.Get("name"), "foo_secret")) 436 assert.Check(t, is.Contains(opts.Filters.Get("name"), "bar_secret")) 437 return []swarm.Secret{ 438 {Spec: swarm.SecretSpec{Annotations: swarm.Annotations{Name: "foo_secret"}}}, 439 {Spec: swarm.SecretSpec{Annotations: swarm.Annotations{Name: "bar_secret"}}}, 440 }, nil 441 }, 442 } 443 refs, err := convertServiceSecrets(client, namespace, secrets, secretSpecs) 444 assert.NilError(t, err) 445 expected := []*swarm.SecretReference{ 446 { 447 SecretName: "bar_secret", 448 File: &swarm.SecretReferenceFileTarget{ 449 Name: "bar_secret", 450 UID: "0", 451 GID: "0", 452 Mode: 0444, 453 }, 454 }, 455 { 456 SecretName: "foo_secret", 457 File: &swarm.SecretReferenceFileTarget{ 458 Name: "foo_secret", 459 UID: "0", 460 GID: "0", 461 Mode: 0444, 462 }, 463 }, 464 } 465 assert.DeepEqual(t, expected, refs) 466 } 467 468 func TestConvertServiceConfigs(t *testing.T) { 469 namespace := Namespace{name: "foo"} 470 configs := []composetypes.ServiceConfigObjConfig{ 471 {Source: "foo_config"}, 472 {Source: "bar_config"}, 473 } 474 configSpecs := map[string]composetypes.ConfigObjConfig{ 475 "foo_config": { 476 Name: "foo_config", 477 }, 478 "bar_config": { 479 Name: "bar_config", 480 }, 481 } 482 client := &fakeClient{ 483 configListFunc: func(opts types.ConfigListOptions) ([]swarm.Config, error) { 484 assert.Check(t, is.Contains(opts.Filters.Get("name"), "foo_config")) 485 assert.Check(t, is.Contains(opts.Filters.Get("name"), "bar_config")) 486 return []swarm.Config{ 487 {Spec: swarm.ConfigSpec{Annotations: swarm.Annotations{Name: "foo_config"}}}, 488 {Spec: swarm.ConfigSpec{Annotations: swarm.Annotations{Name: "bar_config"}}}, 489 }, nil 490 }, 491 } 492 refs, err := convertServiceConfigObjs(client, namespace, configs, configSpecs) 493 assert.NilError(t, err) 494 expected := []*swarm.ConfigReference{ 495 { 496 ConfigName: "bar_config", 497 File: &swarm.ConfigReferenceFileTarget{ 498 Name: "bar_config", 499 UID: "0", 500 GID: "0", 501 Mode: 0444, 502 }, 503 }, 504 { 505 ConfigName: "foo_config", 506 File: &swarm.ConfigReferenceFileTarget{ 507 Name: "foo_config", 508 UID: "0", 509 GID: "0", 510 Mode: 0444, 511 }, 512 }, 513 } 514 assert.DeepEqual(t, expected, refs) 515 } 516 517 type fakeClient struct { 518 client.Client 519 secretListFunc func(types.SecretListOptions) ([]swarm.Secret, error) 520 configListFunc func(types.ConfigListOptions) ([]swarm.Config, error) 521 } 522 523 func (c *fakeClient) SecretList(ctx context.Context, options types.SecretListOptions) ([]swarm.Secret, error) { 524 if c.secretListFunc != nil { 525 return c.secretListFunc(options) 526 } 527 return []swarm.Secret{}, nil 528 } 529 530 func (c *fakeClient) ConfigList(ctx context.Context, options types.ConfigListOptions) ([]swarm.Config, error) { 531 if c.configListFunc != nil { 532 return c.configListFunc(options) 533 } 534 return []swarm.Config{}, nil 535 } 536 537 func TestConvertUpdateConfigParallelism(t *testing.T) { 538 parallel := uint64(4) 539 540 // test default behavior 541 updateConfig := convertUpdateConfig(&composetypes.UpdateConfig{}) 542 assert.Check(t, is.Equal(uint64(1), updateConfig.Parallelism)) 543 544 // Non default value 545 updateConfig = convertUpdateConfig(&composetypes.UpdateConfig{ 546 Parallelism: ¶llel, 547 }) 548 assert.Check(t, is.Equal(parallel, updateConfig.Parallelism)) 549 }