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