github.com/thajeztah/cli@v0.0.0-20240223162942-dc6bfac81a8b/cli/compose/convert/service_test.go (about) 1 package convert 2 3 import ( 4 "context" 5 "os" 6 "strings" 7 "testing" 8 "time" 9 10 composetypes "github.com/docker/cli/cli/compose/types" 11 "github.com/docker/docker/api/types" 12 "github.com/docker/docker/api/types/container" 13 "github.com/docker/docker/api/types/swarm" 14 "github.com/docker/docker/client" 15 "github.com/pkg/errors" 16 "gotest.tools/v3/assert" 17 is "gotest.tools/v3/assert/cmp" 18 ) 19 20 func TestConvertRestartPolicyFromNone(t *testing.T) { 21 policy, err := convertRestartPolicy("no", nil) 22 assert.NilError(t, err) 23 assert.Check(t, is.DeepEqual((*swarm.RestartPolicy)(nil), policy)) 24 } 25 26 func TestConvertRestartPolicyFromUnknown(t *testing.T) { 27 _, err := convertRestartPolicy("unknown", nil) 28 assert.Error(t, err, "unknown restart policy: unknown") 29 } 30 31 func TestConvertRestartPolicyFromAlways(t *testing.T) { 32 policy, err := convertRestartPolicy("always", nil) 33 expected := &swarm.RestartPolicy{ 34 Condition: swarm.RestartPolicyConditionAny, 35 } 36 assert.NilError(t, err) 37 assert.Check(t, is.DeepEqual(expected, policy)) 38 } 39 40 func TestConvertRestartPolicyFromFailure(t *testing.T) { 41 policy, err := convertRestartPolicy("on-failure:4", nil) 42 attempts := uint64(4) 43 expected := &swarm.RestartPolicy{ 44 Condition: swarm.RestartPolicyConditionOnFailure, 45 MaxAttempts: &attempts, 46 } 47 assert.NilError(t, err) 48 assert.Check(t, is.DeepEqual(expected, policy)) 49 } 50 51 func strPtr(val string) *string { 52 return &val 53 } 54 55 func TestConvertEnvironment(t *testing.T) { 56 source := map[string]*string{ 57 "foo": strPtr("bar"), 58 "key": strPtr("value"), 59 } 60 env := convertEnvironment(source) 61 assert.Check(t, is.DeepEqual([]string{"foo=bar", "key=value"}, env)) 62 } 63 64 func TestConvertExtraHosts(t *testing.T) { 65 source := composetypes.HostsList{ 66 "zulu:127.0.0.2", 67 "alpha:127.0.0.1", 68 "zulu:ff02::1", 69 } 70 assert.Check(t, is.DeepEqual([]string{"127.0.0.2 zulu", "127.0.0.1 alpha", "ff02::1 zulu"}, convertExtraHosts(source))) 71 } 72 73 func TestConvertResourcesFull(t *testing.T) { 74 source := composetypes.Resources{ 75 Limits: &composetypes.ResourceLimit{ 76 NanoCPUs: "0.003", 77 MemoryBytes: composetypes.UnitBytes(300000000), 78 }, 79 Reservations: &composetypes.Resource{ 80 NanoCPUs: "0.002", 81 MemoryBytes: composetypes.UnitBytes(200000000), 82 }, 83 } 84 resources, err := convertResources(source) 85 assert.NilError(t, err) 86 87 expected := &swarm.ResourceRequirements{ 88 Limits: &swarm.Limit{ 89 NanoCPUs: 3000000, 90 MemoryBytes: 300000000, 91 }, 92 Reservations: &swarm.Resources{ 93 NanoCPUs: 2000000, 94 MemoryBytes: 200000000, 95 }, 96 } 97 assert.Check(t, is.DeepEqual(expected, resources)) 98 } 99 100 func TestConvertResourcesOnlyMemory(t *testing.T) { 101 source := composetypes.Resources{ 102 Limits: &composetypes.ResourceLimit{ 103 MemoryBytes: composetypes.UnitBytes(300000000), 104 }, 105 Reservations: &composetypes.Resource{ 106 MemoryBytes: composetypes.UnitBytes(200000000), 107 }, 108 } 109 resources, err := convertResources(source) 110 assert.NilError(t, err) 111 112 expected := &swarm.ResourceRequirements{ 113 Limits: &swarm.Limit{ 114 MemoryBytes: 300000000, 115 }, 116 Reservations: &swarm.Resources{ 117 MemoryBytes: 200000000, 118 }, 119 } 120 assert.Check(t, is.DeepEqual(expected, resources)) 121 } 122 123 func TestConvertHealthcheck(t *testing.T) { 124 retries := uint64(10) 125 timeout := composetypes.Duration(30 * time.Second) 126 interval := composetypes.Duration(2 * time.Millisecond) 127 startPeriod := composetypes.Duration(time.Minute) 128 startInterval := composetypes.Duration(1 * time.Second) 129 130 source := &composetypes.HealthCheckConfig{ 131 Test: []string{"EXEC", "touch", "/foo"}, 132 Timeout: &timeout, 133 Interval: &interval, 134 Retries: &retries, 135 StartPeriod: &startPeriod, 136 StartInterval: &startInterval, 137 } 138 expected := &container.HealthConfig{ 139 Test: source.Test, 140 Timeout: time.Duration(timeout), 141 Interval: time.Duration(interval), 142 StartPeriod: time.Duration(startPeriod), 143 StartInterval: time.Duration(startInterval), 144 Retries: 10, 145 } 146 147 healthcheck, err := convertHealthcheck(source) 148 assert.NilError(t, err) 149 assert.Check(t, is.DeepEqual(expected, healthcheck)) 150 } 151 152 func TestConvertHealthcheckDisable(t *testing.T) { 153 source := &composetypes.HealthCheckConfig{Disable: true} 154 expected := &container.HealthConfig{ 155 Test: []string{"NONE"}, 156 } 157 158 healthcheck, err := convertHealthcheck(source) 159 assert.NilError(t, err) 160 assert.Check(t, is.DeepEqual(expected, healthcheck)) 161 } 162 163 func TestConvertHealthcheckDisableWithTest(t *testing.T) { 164 source := &composetypes.HealthCheckConfig{ 165 Disable: true, 166 Test: []string{"EXEC", "touch"}, 167 } 168 _, err := convertHealthcheck(source) 169 assert.Error(t, err, "test and disable can't be set at the same time") 170 } 171 172 func TestConvertEndpointSpec(t *testing.T) { 173 source := []composetypes.ServicePortConfig{ 174 { 175 Protocol: "udp", 176 Target: 53, 177 Published: 1053, 178 Mode: "host", 179 }, 180 { 181 Target: 8080, 182 Published: 80, 183 }, 184 } 185 endpoint := convertEndpointSpec("vip", source) 186 187 expected := swarm.EndpointSpec{ 188 Mode: swarm.ResolutionMode(strings.ToLower("vip")), 189 Ports: []swarm.PortConfig{ 190 { 191 TargetPort: 8080, 192 PublishedPort: 80, 193 }, 194 { 195 Protocol: "udp", 196 TargetPort: 53, 197 PublishedPort: 1053, 198 PublishMode: "host", 199 }, 200 }, 201 } 202 203 assert.Check(t, is.DeepEqual(expected, *endpoint)) 204 } 205 206 func TestConvertServiceNetworksOnlyDefault(t *testing.T) { 207 networkConfigs := networkMap{} 208 209 configs, err := convertServiceNetworks( 210 nil, networkConfigs, NewNamespace("foo"), "service") 211 212 expected := []swarm.NetworkAttachmentConfig{ 213 { 214 Target: "foo_default", 215 Aliases: []string{"service"}, 216 }, 217 } 218 219 assert.NilError(t, err) 220 assert.Check(t, is.DeepEqual(expected, configs)) 221 } 222 223 func TestConvertServiceNetworks(t *testing.T) { 224 networkConfigs := networkMap{ 225 "front": composetypes.NetworkConfig{ 226 External: composetypes.External{External: true}, 227 Name: "fronttier", 228 }, 229 "back": composetypes.NetworkConfig{}, 230 } 231 networks := map[string]*composetypes.ServiceNetworkConfig{ 232 "front": { 233 Aliases: []string{"something"}, 234 }, 235 "back": { 236 Aliases: []string{"other"}, 237 }, 238 } 239 240 configs, err := convertServiceNetworks( 241 networks, networkConfigs, NewNamespace("foo"), "service") 242 243 expected := []swarm.NetworkAttachmentConfig{ 244 { 245 Target: "foo_back", 246 Aliases: []string{"other", "service"}, 247 }, 248 { 249 Target: "fronttier", 250 Aliases: []string{"something", "service"}, 251 }, 252 } 253 254 assert.NilError(t, err) 255 assert.Check(t, is.DeepEqual(expected, configs)) 256 } 257 258 func TestConvertServiceNetworksCustomDefault(t *testing.T) { 259 networkConfigs := networkMap{ 260 "default": composetypes.NetworkConfig{ 261 External: composetypes.External{External: true}, 262 Name: "custom", 263 }, 264 } 265 networks := map[string]*composetypes.ServiceNetworkConfig{} 266 267 configs, err := convertServiceNetworks( 268 networks, networkConfigs, NewNamespace("foo"), "service") 269 270 expected := []swarm.NetworkAttachmentConfig{ 271 { 272 Target: "custom", 273 Aliases: []string{"service"}, 274 }, 275 } 276 277 assert.NilError(t, err) 278 assert.Check(t, is.DeepEqual(expected, configs)) 279 } 280 281 func TestConvertDNSConfigEmpty(t *testing.T) { 282 dnsConfig := convertDNSConfig(nil, nil) 283 assert.Check(t, is.DeepEqual((*swarm.DNSConfig)(nil), dnsConfig)) 284 } 285 286 var ( 287 nameservers = []string{"8.8.8.8", "9.9.9.9"} 288 search = []string{"dc1.example.com", "dc2.example.com"} 289 ) 290 291 func TestConvertDNSConfigAll(t *testing.T) { 292 dnsConfig := convertDNSConfig(nameservers, search) 293 assert.Check(t, is.DeepEqual(&swarm.DNSConfig{ 294 Nameservers: nameservers, 295 Search: search, 296 }, dnsConfig)) 297 } 298 299 func TestConvertDNSConfigNameservers(t *testing.T) { 300 dnsConfig := convertDNSConfig(nameservers, nil) 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 := convertDNSConfig(nil, search) 309 assert.Check(t, is.DeepEqual(&swarm.DNSConfig{ 310 Nameservers: nil, 311 Search: search, 312 }, dnsConfig)) 313 } 314 315 func TestConvertCredentialSpec(t *testing.T) { 316 tests := []struct { 317 name string 318 in composetypes.CredentialSpecConfig 319 out *swarm.CredentialSpec 320 configs []*swarm.ConfigReference 321 expectedErr string 322 }{ 323 { 324 name: "empty", 325 }, 326 { 327 name: "config-and-file", 328 in: composetypes.CredentialSpecConfig{Config: "0bt9dmxjvjiqermk6xrop3ekq", File: "somefile.json"}, 329 expectedErr: `invalid credential spec: cannot specify both "Config" and "File"`, 330 }, 331 { 332 name: "config-and-registry", 333 in: composetypes.CredentialSpecConfig{Config: "0bt9dmxjvjiqermk6xrop3ekq", Registry: "testing"}, 334 expectedErr: `invalid credential spec: cannot specify both "Config" and "Registry"`, 335 }, 336 { 337 name: "file-and-registry", 338 in: composetypes.CredentialSpecConfig{File: "somefile.json", Registry: "testing"}, 339 expectedErr: `invalid credential spec: cannot specify both "File" and "Registry"`, 340 }, 341 { 342 name: "config-and-file-and-registry", 343 in: composetypes.CredentialSpecConfig{Config: "0bt9dmxjvjiqermk6xrop3ekq", File: "somefile.json", Registry: "testing"}, 344 expectedErr: `invalid credential spec: cannot specify both "Config", "File", and "Registry"`, 345 }, 346 { 347 name: "missing-config-reference", 348 in: composetypes.CredentialSpecConfig{Config: "missing"}, 349 expectedErr: "invalid credential spec: spec specifies config missing, but no such config can be found", 350 configs: []*swarm.ConfigReference{ 351 { 352 ConfigName: "someName", 353 ConfigID: "missing", 354 }, 355 }, 356 }, 357 { 358 name: "namespaced-config", 359 in: composetypes.CredentialSpecConfig{Config: "name"}, 360 configs: []*swarm.ConfigReference{ 361 { 362 ConfigName: "namespaced-config_name", 363 ConfigID: "someID", 364 }, 365 }, 366 out: &swarm.CredentialSpec{Config: "someID"}, 367 }, 368 { 369 name: "config", 370 in: composetypes.CredentialSpecConfig{Config: "someName"}, 371 configs: []*swarm.ConfigReference{ 372 { 373 ConfigName: "someOtherName", 374 ConfigID: "someOtherID", 375 }, { 376 ConfigName: "someName", 377 ConfigID: "someID", 378 }, 379 }, 380 out: &swarm.CredentialSpec{Config: "someID"}, 381 }, 382 { 383 name: "file", 384 in: composetypes.CredentialSpecConfig{File: "somefile.json"}, 385 out: &swarm.CredentialSpec{File: "somefile.json"}, 386 }, 387 { 388 name: "registry", 389 in: composetypes.CredentialSpecConfig{Registry: "testing"}, 390 out: &swarm.CredentialSpec{Registry: "testing"}, 391 }, 392 } 393 394 for _, tc := range tests { 395 tc := tc 396 t.Run(tc.name, func(t *testing.T) { 397 namespace := NewNamespace(tc.name) 398 swarmSpec, err := convertCredentialSpec(namespace, tc.in, tc.configs) 399 400 if tc.expectedErr != "" { 401 assert.Error(t, err, tc.expectedErr) 402 } else { 403 assert.NilError(t, err) 404 } 405 assert.DeepEqual(t, swarmSpec, tc.out) 406 }) 407 } 408 } 409 410 func TestConvertUpdateConfigOrder(t *testing.T) { 411 // test default behavior 412 updateConfig := convertUpdateConfig(&composetypes.UpdateConfig{}) 413 assert.Check(t, is.Equal("", updateConfig.Order)) 414 415 // test start-first 416 updateConfig = convertUpdateConfig(&composetypes.UpdateConfig{ 417 Order: "start-first", 418 }) 419 assert.Check(t, is.Equal(updateConfig.Order, "start-first")) 420 421 // test stop-first 422 updateConfig = convertUpdateConfig(&composetypes.UpdateConfig{ 423 Order: "stop-first", 424 }) 425 assert.Check(t, is.Equal(updateConfig.Order, "stop-first")) 426 } 427 428 func TestConvertFileObject(t *testing.T) { 429 namespace := NewNamespace("testing") 430 config := composetypes.FileReferenceConfig{ 431 Source: "source", 432 Target: "target", 433 UID: "user", 434 GID: "group", 435 Mode: uint32Ptr(0o644), 436 } 437 swarmRef, err := convertFileObject(namespace, config, lookupConfig) 438 assert.NilError(t, err) 439 440 expected := swarmReferenceObject{ 441 Name: "testing_source", 442 File: swarmReferenceTarget{ 443 Name: config.Target, 444 UID: config.UID, 445 GID: config.GID, 446 Mode: os.FileMode(0o644), 447 }, 448 } 449 assert.Check(t, is.DeepEqual(expected, swarmRef)) 450 } 451 452 func lookupConfig(key string) (composetypes.FileObjectConfig, error) { 453 if key != "source" { 454 return composetypes.FileObjectConfig{}, errors.New("bad key") 455 } 456 return composetypes.FileObjectConfig{}, nil 457 } 458 459 func TestConvertFileObjectDefaults(t *testing.T) { 460 namespace := NewNamespace("testing") 461 config := composetypes.FileReferenceConfig{Source: "source"} 462 swarmRef, err := convertFileObject(namespace, config, lookupConfig) 463 assert.NilError(t, err) 464 465 expected := swarmReferenceObject{ 466 Name: "testing_source", 467 File: swarmReferenceTarget{ 468 Name: config.Source, 469 UID: "0", 470 GID: "0", 471 Mode: os.FileMode(0o444), 472 }, 473 } 474 assert.Check(t, is.DeepEqual(expected, swarmRef)) 475 } 476 477 func TestServiceConvertsIsolation(t *testing.T) { 478 src := composetypes.ServiceConfig{ 479 Isolation: "hyperv", 480 } 481 result, err := Service("1.35", Namespace{name: "foo"}, src, nil, nil, nil, nil) 482 assert.NilError(t, err) 483 assert.Check(t, is.Equal(container.IsolationHyperV, result.TaskTemplate.ContainerSpec.Isolation)) 484 } 485 486 func TestConvertServiceSecrets(t *testing.T) { 487 namespace := Namespace{name: "foo"} 488 secrets := []composetypes.ServiceSecretConfig{ 489 {Source: "foo_secret"}, 490 {Source: "bar_secret"}, 491 } 492 secretSpecs := map[string]composetypes.SecretConfig{ 493 "foo_secret": { 494 Name: "foo_secret", 495 }, 496 "bar_secret": { 497 Name: "bar_secret", 498 }, 499 } 500 apiClient := &fakeClient{ 501 secretListFunc: func(opts types.SecretListOptions) ([]swarm.Secret, error) { 502 assert.Check(t, is.Contains(opts.Filters.Get("name"), "foo_secret")) 503 assert.Check(t, is.Contains(opts.Filters.Get("name"), "bar_secret")) 504 return []swarm.Secret{ 505 {Spec: swarm.SecretSpec{Annotations: swarm.Annotations{Name: "foo_secret"}}}, 506 {Spec: swarm.SecretSpec{Annotations: swarm.Annotations{Name: "bar_secret"}}}, 507 }, nil 508 }, 509 } 510 ctx := context.Background() 511 refs, err := convertServiceSecrets(ctx, apiClient, namespace, secrets, secretSpecs) 512 assert.NilError(t, err) 513 expected := []*swarm.SecretReference{ 514 { 515 SecretName: "bar_secret", 516 File: &swarm.SecretReferenceFileTarget{ 517 Name: "bar_secret", 518 UID: "0", 519 GID: "0", 520 Mode: 0o444, 521 }, 522 }, 523 { 524 SecretName: "foo_secret", 525 File: &swarm.SecretReferenceFileTarget{ 526 Name: "foo_secret", 527 UID: "0", 528 GID: "0", 529 Mode: 0o444, 530 }, 531 }, 532 } 533 assert.DeepEqual(t, expected, refs) 534 } 535 536 func TestConvertServiceConfigs(t *testing.T) { 537 namespace := Namespace{name: "foo"} 538 service := composetypes.ServiceConfig{ 539 Configs: []composetypes.ServiceConfigObjConfig{ 540 {Source: "foo_config"}, 541 {Source: "bar_config"}, 542 }, 543 CredentialSpec: composetypes.CredentialSpecConfig{ 544 Config: "baz_config", 545 }, 546 } 547 configSpecs := map[string]composetypes.ConfigObjConfig{ 548 "foo_config": { 549 Name: "foo_config", 550 }, 551 "bar_config": { 552 Name: "bar_config", 553 }, 554 "baz_config": { 555 Name: "baz_config", 556 }, 557 } 558 apiClient := &fakeClient{ 559 configListFunc: func(opts types.ConfigListOptions) ([]swarm.Config, error) { 560 assert.Check(t, is.Contains(opts.Filters.Get("name"), "foo_config")) 561 assert.Check(t, is.Contains(opts.Filters.Get("name"), "bar_config")) 562 assert.Check(t, is.Contains(opts.Filters.Get("name"), "baz_config")) 563 return []swarm.Config{ 564 {Spec: swarm.ConfigSpec{Annotations: swarm.Annotations{Name: "foo_config"}}}, 565 {Spec: swarm.ConfigSpec{Annotations: swarm.Annotations{Name: "bar_config"}}}, 566 {Spec: swarm.ConfigSpec{Annotations: swarm.Annotations{Name: "baz_config"}}}, 567 }, nil 568 }, 569 } 570 ctx := context.Background() 571 refs, err := convertServiceConfigObjs(ctx, apiClient, namespace, service, configSpecs) 572 assert.NilError(t, err) 573 expected := []*swarm.ConfigReference{ 574 { 575 ConfigName: "bar_config", 576 File: &swarm.ConfigReferenceFileTarget{ 577 Name: "bar_config", 578 UID: "0", 579 GID: "0", 580 Mode: 0o444, 581 }, 582 }, 583 { 584 ConfigName: "baz_config", 585 Runtime: &swarm.ConfigReferenceRuntimeTarget{}, 586 }, 587 { 588 ConfigName: "foo_config", 589 File: &swarm.ConfigReferenceFileTarget{ 590 Name: "foo_config", 591 UID: "0", 592 GID: "0", 593 Mode: 0o444, 594 }, 595 }, 596 } 597 assert.DeepEqual(t, expected, refs) 598 } 599 600 type fakeClient struct { 601 client.Client 602 secretListFunc func(types.SecretListOptions) ([]swarm.Secret, error) 603 configListFunc func(types.ConfigListOptions) ([]swarm.Config, error) 604 } 605 606 func (c *fakeClient) SecretList(_ context.Context, options types.SecretListOptions) ([]swarm.Secret, error) { 607 if c.secretListFunc != nil { 608 return c.secretListFunc(options) 609 } 610 return []swarm.Secret{}, nil 611 } 612 613 func (c *fakeClient) ConfigList(_ context.Context, options types.ConfigListOptions) ([]swarm.Config, error) { 614 if c.configListFunc != nil { 615 return c.configListFunc(options) 616 } 617 return []swarm.Config{}, nil 618 } 619 620 func TestConvertUpdateConfigParallelism(t *testing.T) { 621 parallel := uint64(4) 622 623 // test default behavior 624 updateConfig := convertUpdateConfig(&composetypes.UpdateConfig{}) 625 assert.Check(t, is.Equal(uint64(1), updateConfig.Parallelism)) 626 627 // Non default value 628 updateConfig = convertUpdateConfig(&composetypes.UpdateConfig{ 629 Parallelism: ¶llel, 630 }) 631 assert.Check(t, is.Equal(parallel, updateConfig.Parallelism)) 632 } 633 634 func TestConvertServiceCapAddAndCapDrop(t *testing.T) { 635 tests := []struct { 636 title string 637 in, out composetypes.ServiceConfig 638 }{ 639 { 640 title: "default behavior", 641 }, 642 { 643 title: "some values", 644 in: composetypes.ServiceConfig{ 645 CapAdd: []string{"SYS_NICE", "CAP_NET_ADMIN"}, 646 CapDrop: []string{"CHOWN", "CAP_NET_ADMIN", "DAC_OVERRIDE", "CAP_FSETID", "CAP_FOWNER"}, 647 }, 648 out: composetypes.ServiceConfig{ 649 CapAdd: []string{"CAP_NET_ADMIN", "CAP_SYS_NICE"}, 650 CapDrop: []string{"CAP_CHOWN", "CAP_DAC_OVERRIDE", "CAP_FOWNER", "CAP_FSETID"}, 651 }, 652 }, 653 { 654 title: "adding ALL capabilities", 655 in: composetypes.ServiceConfig{ 656 CapAdd: []string{"ALL", "CAP_NET_ADMIN"}, 657 CapDrop: []string{"CHOWN", "CAP_NET_ADMIN", "DAC_OVERRIDE", "CAP_FSETID", "CAP_FOWNER"}, 658 }, 659 out: composetypes.ServiceConfig{ 660 CapAdd: []string{"ALL"}, 661 CapDrop: []string{"CAP_CHOWN", "CAP_DAC_OVERRIDE", "CAP_FOWNER", "CAP_FSETID", "CAP_NET_ADMIN"}, 662 }, 663 }, 664 { 665 title: "dropping ALL capabilities", 666 in: composetypes.ServiceConfig{ 667 CapAdd: []string{"CHOWN", "CAP_NET_ADMIN", "DAC_OVERRIDE", "CAP_FSETID", "CAP_FOWNER"}, 668 CapDrop: []string{"ALL", "CAP_NET_ADMIN", "CAP_FOO"}, 669 }, 670 out: composetypes.ServiceConfig{ 671 CapAdd: []string{"CAP_CHOWN", "CAP_DAC_OVERRIDE", "CAP_FOWNER", "CAP_FSETID", "CAP_NET_ADMIN"}, 672 CapDrop: []string{"ALL"}, 673 }, 674 }, 675 } 676 for _, tc := range tests { 677 tc := tc 678 t.Run(tc.title, func(t *testing.T) { 679 result, err := Service("1.41", Namespace{name: "foo"}, tc.in, nil, nil, nil, nil) 680 assert.NilError(t, err) 681 assert.Check(t, is.DeepEqual(result.TaskTemplate.ContainerSpec.CapabilityAdd, tc.out.CapAdd)) 682 assert.Check(t, is.DeepEqual(result.TaskTemplate.ContainerSpec.CapabilityDrop, tc.out.CapDrop)) 683 }) 684 } 685 }