github.com/itscaro/cli@v0.0.0-20190705081621-c9db0fe93829/cli/compose/loader/merge_test.go (about) 1 package loader 2 3 import ( 4 "testing" 5 6 "github.com/docker/cli/cli/compose/types" 7 "gotest.tools/assert" 8 ) 9 10 func TestLoadTwoDifferentVersion(t *testing.T) { 11 configDetails := types.ConfigDetails{ 12 ConfigFiles: []types.ConfigFile{ 13 {Filename: "base.yml", Config: map[string]interface{}{ 14 "version": "3.1", 15 }}, 16 {Filename: "override.yml", Config: map[string]interface{}{ 17 "version": "3.4", 18 }}, 19 }, 20 } 21 _, err := Load(configDetails) 22 assert.Error(t, err, "version mismatched between two composefiles : 3.1 and 3.4") 23 } 24 25 func TestLoadLogging(t *testing.T) { 26 loggingCases := []struct { 27 name string 28 loggingBase map[string]interface{} 29 loggingOverride map[string]interface{} 30 expected *types.LoggingConfig 31 }{ 32 { 33 name: "no_override_driver", 34 loggingBase: map[string]interface{}{ 35 "logging": map[string]interface{}{ 36 "driver": "json-file", 37 "options": map[string]interface{}{ 38 "frequency": "2000", 39 "timeout": "23", 40 }, 41 }, 42 }, 43 loggingOverride: map[string]interface{}{ 44 "logging": map[string]interface{}{ 45 "options": map[string]interface{}{ 46 "timeout": "360", 47 "pretty-print": "on", 48 }, 49 }, 50 }, 51 expected: &types.LoggingConfig{ 52 Driver: "json-file", 53 Options: map[string]string{ 54 "frequency": "2000", 55 "timeout": "360", 56 "pretty-print": "on", 57 }, 58 }, 59 }, 60 { 61 name: "override_driver", 62 loggingBase: map[string]interface{}{ 63 "logging": map[string]interface{}{ 64 "driver": "json-file", 65 "options": map[string]interface{}{ 66 "frequency": "2000", 67 "timeout": "23", 68 }, 69 }, 70 }, 71 loggingOverride: map[string]interface{}{ 72 "logging": map[string]interface{}{ 73 "driver": "syslog", 74 "options": map[string]interface{}{ 75 "timeout": "360", 76 "pretty-print": "on", 77 }, 78 }, 79 }, 80 expected: &types.LoggingConfig{ 81 Driver: "syslog", 82 Options: map[string]string{ 83 "timeout": "360", 84 "pretty-print": "on", 85 }, 86 }, 87 }, 88 { 89 name: "no_base_driver", 90 loggingBase: map[string]interface{}{ 91 "logging": map[string]interface{}{ 92 "options": map[string]interface{}{ 93 "frequency": "2000", 94 "timeout": "23", 95 }, 96 }, 97 }, 98 loggingOverride: map[string]interface{}{ 99 "logging": map[string]interface{}{ 100 "driver": "json-file", 101 "options": map[string]interface{}{ 102 "timeout": "360", 103 "pretty-print": "on", 104 }, 105 }, 106 }, 107 expected: &types.LoggingConfig{ 108 Driver: "json-file", 109 Options: map[string]string{ 110 "frequency": "2000", 111 "timeout": "360", 112 "pretty-print": "on", 113 }, 114 }, 115 }, 116 { 117 name: "no_driver", 118 loggingBase: map[string]interface{}{ 119 "logging": map[string]interface{}{ 120 "options": map[string]interface{}{ 121 "frequency": "2000", 122 "timeout": "23", 123 }, 124 }, 125 }, 126 loggingOverride: map[string]interface{}{ 127 "logging": map[string]interface{}{ 128 "options": map[string]interface{}{ 129 "timeout": "360", 130 "pretty-print": "on", 131 }, 132 }, 133 }, 134 expected: &types.LoggingConfig{ 135 Options: map[string]string{ 136 "frequency": "2000", 137 "timeout": "360", 138 "pretty-print": "on", 139 }, 140 }, 141 }, 142 { 143 name: "no_override_options", 144 loggingBase: map[string]interface{}{ 145 "logging": map[string]interface{}{ 146 "driver": "json-file", 147 "options": map[string]interface{}{ 148 "frequency": "2000", 149 "timeout": "23", 150 }, 151 }, 152 }, 153 loggingOverride: map[string]interface{}{ 154 "logging": map[string]interface{}{ 155 "driver": "syslog", 156 }, 157 }, 158 expected: &types.LoggingConfig{ 159 Driver: "syslog", 160 }, 161 }, 162 { 163 name: "no_base", 164 loggingBase: map[string]interface{}{}, 165 loggingOverride: map[string]interface{}{ 166 "logging": map[string]interface{}{ 167 "driver": "json-file", 168 "options": map[string]interface{}{ 169 "frequency": "2000", 170 }, 171 }, 172 }, 173 expected: &types.LoggingConfig{ 174 Driver: "json-file", 175 Options: map[string]string{ 176 "frequency": "2000", 177 }, 178 }, 179 }, 180 } 181 182 for _, tc := range loggingCases { 183 t.Run(tc.name, func(t *testing.T) { 184 configDetails := types.ConfigDetails{ 185 ConfigFiles: []types.ConfigFile{ 186 { 187 Filename: "base.yml", 188 Config: map[string]interface{}{ 189 "version": "3.4", 190 "services": map[string]interface{}{ 191 "foo": tc.loggingBase, 192 }, 193 }, 194 }, 195 { 196 Filename: "override.yml", 197 Config: map[string]interface{}{ 198 "version": "3.4", 199 "services": map[string]interface{}{ 200 "foo": tc.loggingOverride, 201 }, 202 }, 203 }, 204 }, 205 } 206 config, err := Load(configDetails) 207 assert.NilError(t, err) 208 assert.DeepEqual(t, &types.Config{ 209 Filename: "base.yml", 210 Version: "3.4", 211 Services: []types.ServiceConfig{ 212 { 213 Name: "foo", 214 Logging: tc.expected, 215 Environment: types.MappingWithEquals{}, 216 }, 217 }, 218 Networks: map[string]types.NetworkConfig{}, 219 Volumes: map[string]types.VolumeConfig{}, 220 Secrets: map[string]types.SecretConfig{}, 221 Configs: map[string]types.ConfigObjConfig{}, 222 }, config) 223 }) 224 } 225 } 226 227 func TestLoadMultipleServicePorts(t *testing.T) { 228 portsCases := []struct { 229 name string 230 portBase map[string]interface{} 231 portOverride map[string]interface{} 232 expected []types.ServicePortConfig 233 }{ 234 { 235 name: "no_override", 236 portBase: map[string]interface{}{ 237 "ports": []interface{}{ 238 "8080:80", 239 }, 240 }, 241 portOverride: map[string]interface{}{}, 242 expected: []types.ServicePortConfig{ 243 { 244 Mode: "ingress", 245 Published: 8080, 246 Target: 80, 247 Protocol: "tcp", 248 }, 249 }, 250 }, 251 { 252 name: "override_different_published", 253 portBase: map[string]interface{}{ 254 "ports": []interface{}{ 255 "8080:80", 256 }, 257 }, 258 portOverride: map[string]interface{}{ 259 "ports": []interface{}{ 260 "8081:80", 261 }, 262 }, 263 expected: []types.ServicePortConfig{ 264 { 265 Mode: "ingress", 266 Published: 8080, 267 Target: 80, 268 Protocol: "tcp", 269 }, 270 { 271 Mode: "ingress", 272 Published: 8081, 273 Target: 80, 274 Protocol: "tcp", 275 }, 276 }, 277 }, 278 { 279 name: "override_same_published", 280 portBase: map[string]interface{}{ 281 "ports": []interface{}{ 282 "8080:80", 283 }, 284 }, 285 portOverride: map[string]interface{}{ 286 "ports": []interface{}{ 287 "8080:81", 288 }, 289 }, 290 expected: []types.ServicePortConfig{ 291 { 292 Mode: "ingress", 293 Published: 8080, 294 Target: 81, 295 Protocol: "tcp", 296 }, 297 }, 298 }, 299 } 300 301 for _, tc := range portsCases { 302 t.Run(tc.name, func(t *testing.T) { 303 configDetails := types.ConfigDetails{ 304 ConfigFiles: []types.ConfigFile{ 305 { 306 Filename: "base.yml", 307 Config: map[string]interface{}{ 308 "version": "3.4", 309 "services": map[string]interface{}{ 310 "foo": tc.portBase, 311 }, 312 }, 313 }, 314 { 315 Filename: "override.yml", 316 Config: map[string]interface{}{ 317 "version": "3.4", 318 "services": map[string]interface{}{ 319 "foo": tc.portOverride, 320 }, 321 }, 322 }, 323 }, 324 } 325 config, err := Load(configDetails) 326 assert.NilError(t, err) 327 assert.DeepEqual(t, &types.Config{ 328 Filename: "base.yml", 329 Version: "3.4", 330 Services: []types.ServiceConfig{ 331 { 332 Name: "foo", 333 Ports: tc.expected, 334 Environment: types.MappingWithEquals{}, 335 }, 336 }, 337 Networks: map[string]types.NetworkConfig{}, 338 Volumes: map[string]types.VolumeConfig{}, 339 Secrets: map[string]types.SecretConfig{}, 340 Configs: map[string]types.ConfigObjConfig{}, 341 }, config) 342 }) 343 } 344 } 345 346 func TestLoadMultipleSecretsConfig(t *testing.T) { 347 portsCases := []struct { 348 name string 349 secretBase map[string]interface{} 350 secretOverride map[string]interface{} 351 expected []types.ServiceSecretConfig 352 }{ 353 { 354 name: "no_override", 355 secretBase: map[string]interface{}{ 356 "secrets": []interface{}{ 357 "my_secret", 358 }, 359 }, 360 secretOverride: map[string]interface{}{}, 361 expected: []types.ServiceSecretConfig{ 362 { 363 Source: "my_secret", 364 }, 365 }, 366 }, 367 { 368 name: "override_simple", 369 secretBase: map[string]interface{}{ 370 "secrets": []interface{}{ 371 "foo_secret", 372 }, 373 }, 374 secretOverride: map[string]interface{}{ 375 "secrets": []interface{}{ 376 "bar_secret", 377 }, 378 }, 379 expected: []types.ServiceSecretConfig{ 380 { 381 Source: "bar_secret", 382 }, 383 { 384 Source: "foo_secret", 385 }, 386 }, 387 }, 388 { 389 name: "override_same_source", 390 secretBase: map[string]interface{}{ 391 "secrets": []interface{}{ 392 "foo_secret", 393 map[string]interface{}{ 394 "source": "bar_secret", 395 "target": "waw_secret", 396 }, 397 }, 398 }, 399 secretOverride: map[string]interface{}{ 400 "secrets": []interface{}{ 401 map[string]interface{}{ 402 "source": "bar_secret", 403 "target": "bof_secret", 404 }, 405 map[string]interface{}{ 406 "source": "baz_secret", 407 "target": "waw_secret", 408 }, 409 }, 410 }, 411 expected: []types.ServiceSecretConfig{ 412 { 413 Source: "bar_secret", 414 Target: "bof_secret", 415 }, 416 { 417 Source: "baz_secret", 418 Target: "waw_secret", 419 }, 420 { 421 Source: "foo_secret", 422 }, 423 }, 424 }, 425 } 426 427 for _, tc := range portsCases { 428 t.Run(tc.name, func(t *testing.T) { 429 configDetails := types.ConfigDetails{ 430 ConfigFiles: []types.ConfigFile{ 431 { 432 Filename: "base.yml", 433 Config: map[string]interface{}{ 434 "version": "3.4", 435 "services": map[string]interface{}{ 436 "foo": tc.secretBase, 437 }, 438 }, 439 }, 440 { 441 Filename: "override.yml", 442 Config: map[string]interface{}{ 443 "version": "3.4", 444 "services": map[string]interface{}{ 445 "foo": tc.secretOverride, 446 }, 447 }, 448 }, 449 }, 450 } 451 config, err := Load(configDetails) 452 assert.NilError(t, err) 453 assert.DeepEqual(t, &types.Config{ 454 Filename: "base.yml", 455 Version: "3.4", 456 Services: []types.ServiceConfig{ 457 { 458 Name: "foo", 459 Secrets: tc.expected, 460 Environment: types.MappingWithEquals{}, 461 }, 462 }, 463 Networks: map[string]types.NetworkConfig{}, 464 Volumes: map[string]types.VolumeConfig{}, 465 Secrets: map[string]types.SecretConfig{}, 466 Configs: map[string]types.ConfigObjConfig{}, 467 }, config) 468 }) 469 } 470 } 471 472 func TestLoadMultipleConfigobjsConfig(t *testing.T) { 473 portsCases := []struct { 474 name string 475 configBase map[string]interface{} 476 configOverride map[string]interface{} 477 expected []types.ServiceConfigObjConfig 478 }{ 479 { 480 name: "no_override", 481 configBase: map[string]interface{}{ 482 "configs": []interface{}{ 483 "my_config", 484 }, 485 }, 486 configOverride: map[string]interface{}{}, 487 expected: []types.ServiceConfigObjConfig{ 488 { 489 Source: "my_config", 490 }, 491 }, 492 }, 493 { 494 name: "override_simple", 495 configBase: map[string]interface{}{ 496 "configs": []interface{}{ 497 "foo_config", 498 }, 499 }, 500 configOverride: map[string]interface{}{ 501 "configs": []interface{}{ 502 "bar_config", 503 }, 504 }, 505 expected: []types.ServiceConfigObjConfig{ 506 { 507 Source: "bar_config", 508 }, 509 { 510 Source: "foo_config", 511 }, 512 }, 513 }, 514 { 515 name: "override_same_source", 516 configBase: map[string]interface{}{ 517 "configs": []interface{}{ 518 "foo_config", 519 map[string]interface{}{ 520 "source": "bar_config", 521 "target": "waw_config", 522 }, 523 }, 524 }, 525 configOverride: map[string]interface{}{ 526 "configs": []interface{}{ 527 map[string]interface{}{ 528 "source": "bar_config", 529 "target": "bof_config", 530 }, 531 map[string]interface{}{ 532 "source": "baz_config", 533 "target": "waw_config", 534 }, 535 }, 536 }, 537 expected: []types.ServiceConfigObjConfig{ 538 { 539 Source: "bar_config", 540 Target: "bof_config", 541 }, 542 { 543 Source: "baz_config", 544 Target: "waw_config", 545 }, 546 { 547 Source: "foo_config", 548 }, 549 }, 550 }, 551 } 552 553 for _, tc := range portsCases { 554 t.Run(tc.name, func(t *testing.T) { 555 configDetails := types.ConfigDetails{ 556 ConfigFiles: []types.ConfigFile{ 557 { 558 Filename: "base.yml", 559 Config: map[string]interface{}{ 560 "version": "3.4", 561 "services": map[string]interface{}{ 562 "foo": tc.configBase, 563 }, 564 }, 565 }, 566 { 567 Filename: "override.yml", 568 Config: map[string]interface{}{ 569 "version": "3.4", 570 "services": map[string]interface{}{ 571 "foo": tc.configOverride, 572 }, 573 }, 574 }, 575 }, 576 } 577 config, err := Load(configDetails) 578 assert.NilError(t, err) 579 assert.DeepEqual(t, &types.Config{ 580 Filename: "base.yml", 581 Version: "3.4", 582 Services: []types.ServiceConfig{ 583 { 584 Name: "foo", 585 Configs: tc.expected, 586 Environment: types.MappingWithEquals{}, 587 }, 588 }, 589 Networks: map[string]types.NetworkConfig{}, 590 Volumes: map[string]types.VolumeConfig{}, 591 Secrets: map[string]types.SecretConfig{}, 592 Configs: map[string]types.ConfigObjConfig{}, 593 }, config) 594 }) 595 } 596 } 597 598 func TestLoadMultipleUlimits(t *testing.T) { 599 ulimitCases := []struct { 600 name string 601 ulimitBase map[string]interface{} 602 ulimitOverride map[string]interface{} 603 expected map[string]*types.UlimitsConfig 604 }{ 605 { 606 name: "no_override", 607 ulimitBase: map[string]interface{}{ 608 "ulimits": map[string]interface{}{ 609 "noproc": 65535, 610 }, 611 }, 612 ulimitOverride: map[string]interface{}{}, 613 expected: map[string]*types.UlimitsConfig{ 614 "noproc": { 615 Single: 65535, 616 }, 617 }, 618 }, 619 { 620 name: "override_simple", 621 ulimitBase: map[string]interface{}{ 622 "ulimits": map[string]interface{}{ 623 "noproc": 65535, 624 }, 625 }, 626 ulimitOverride: map[string]interface{}{ 627 "ulimits": map[string]interface{}{ 628 "noproc": 44444, 629 }, 630 }, 631 expected: map[string]*types.UlimitsConfig{ 632 "noproc": { 633 Single: 44444, 634 }, 635 }, 636 }, 637 { 638 name: "override_different_notation", 639 ulimitBase: map[string]interface{}{ 640 "ulimits": map[string]interface{}{ 641 "nofile": map[string]interface{}{ 642 "soft": 11111, 643 "hard": 99999, 644 }, 645 "noproc": 44444, 646 }, 647 }, 648 ulimitOverride: map[string]interface{}{ 649 "ulimits": map[string]interface{}{ 650 "nofile": 55555, 651 "noproc": map[string]interface{}{ 652 "soft": 22222, 653 "hard": 33333, 654 }, 655 }, 656 }, 657 expected: map[string]*types.UlimitsConfig{ 658 "noproc": { 659 Soft: 22222, 660 Hard: 33333, 661 }, 662 "nofile": { 663 Single: 55555, 664 }, 665 }, 666 }, 667 } 668 669 for _, tc := range ulimitCases { 670 t.Run(tc.name, func(t *testing.T) { 671 configDetails := types.ConfigDetails{ 672 ConfigFiles: []types.ConfigFile{ 673 { 674 Filename: "base.yml", 675 Config: map[string]interface{}{ 676 "version": "3.4", 677 "services": map[string]interface{}{ 678 "foo": tc.ulimitBase, 679 }, 680 }, 681 }, 682 { 683 Filename: "override.yml", 684 Config: map[string]interface{}{ 685 "version": "3.4", 686 "services": map[string]interface{}{ 687 "foo": tc.ulimitOverride, 688 }, 689 }, 690 }, 691 }, 692 } 693 config, err := Load(configDetails) 694 assert.NilError(t, err) 695 assert.DeepEqual(t, &types.Config{ 696 Filename: "base.yml", 697 Version: "3.4", 698 Services: []types.ServiceConfig{ 699 { 700 Name: "foo", 701 Ulimits: tc.expected, 702 Environment: types.MappingWithEquals{}, 703 }, 704 }, 705 Networks: map[string]types.NetworkConfig{}, 706 Volumes: map[string]types.VolumeConfig{}, 707 Secrets: map[string]types.SecretConfig{}, 708 Configs: map[string]types.ConfigObjConfig{}, 709 }, config) 710 }) 711 } 712 } 713 714 func TestLoadMultipleServiceNetworks(t *testing.T) { 715 networkCases := []struct { 716 name string 717 networkBase map[string]interface{} 718 networkOverride map[string]interface{} 719 expected map[string]*types.ServiceNetworkConfig 720 }{ 721 { 722 name: "no_override", 723 networkBase: map[string]interface{}{ 724 "networks": []interface{}{ 725 "net1", 726 "net2", 727 }, 728 }, 729 networkOverride: map[string]interface{}{}, 730 expected: map[string]*types.ServiceNetworkConfig{ 731 "net1": nil, 732 "net2": nil, 733 }, 734 }, 735 { 736 name: "override_simple", 737 networkBase: map[string]interface{}{ 738 "networks": []interface{}{ 739 "net1", 740 "net2", 741 }, 742 }, 743 networkOverride: map[string]interface{}{ 744 "networks": []interface{}{ 745 "net1", 746 "net3", 747 }, 748 }, 749 expected: map[string]*types.ServiceNetworkConfig{ 750 "net1": nil, 751 "net2": nil, 752 "net3": nil, 753 }, 754 }, 755 { 756 name: "override_with_aliases", 757 networkBase: map[string]interface{}{ 758 "networks": map[string]interface{}{ 759 "net1": map[string]interface{}{ 760 "aliases": []interface{}{ 761 "alias1", 762 }, 763 }, 764 "net2": nil, 765 }, 766 }, 767 networkOverride: map[string]interface{}{ 768 "networks": map[string]interface{}{ 769 "net1": map[string]interface{}{ 770 "aliases": []interface{}{ 771 "alias2", 772 "alias3", 773 }, 774 }, 775 "net3": map[string]interface{}{}, 776 }, 777 }, 778 expected: map[string]*types.ServiceNetworkConfig{ 779 "net1": { 780 Aliases: []string{"alias2", "alias3"}, 781 }, 782 "net2": nil, 783 "net3": {}, 784 }, 785 }, 786 } 787 788 for _, tc := range networkCases { 789 t.Run(tc.name, func(t *testing.T) { 790 configDetails := types.ConfigDetails{ 791 ConfigFiles: []types.ConfigFile{ 792 { 793 Filename: "base.yml", 794 Config: map[string]interface{}{ 795 "version": "3.4", 796 "services": map[string]interface{}{ 797 "foo": tc.networkBase, 798 }, 799 }, 800 }, 801 { 802 Filename: "override.yml", 803 Config: map[string]interface{}{ 804 "version": "3.4", 805 "services": map[string]interface{}{ 806 "foo": tc.networkOverride, 807 }, 808 }, 809 }, 810 }, 811 } 812 config, err := Load(configDetails) 813 assert.NilError(t, err) 814 assert.DeepEqual(t, &types.Config{ 815 Filename: "base.yml", 816 Version: "3.4", 817 Services: []types.ServiceConfig{ 818 { 819 Name: "foo", 820 Networks: tc.expected, 821 Environment: types.MappingWithEquals{}, 822 }, 823 }, 824 Networks: map[string]types.NetworkConfig{}, 825 Volumes: map[string]types.VolumeConfig{}, 826 Secrets: map[string]types.SecretConfig{}, 827 Configs: map[string]types.ConfigObjConfig{}, 828 }, config) 829 }) 830 } 831 } 832 833 func TestLoadMultipleConfigs(t *testing.T) { 834 base := map[string]interface{}{ 835 "version": "3.4", 836 "services": map[string]interface{}{ 837 "foo": map[string]interface{}{ 838 "image": "foo", 839 "build": map[string]interface{}{ 840 "context": ".", 841 "dockerfile": "bar.Dockerfile", 842 }, 843 "ports": []interface{}{ 844 "8080:80", 845 "9090:90", 846 }, 847 "labels": []interface{}{ 848 "foo=bar", 849 }, 850 "cap_add": []interface{}{ 851 "NET_ADMIN", 852 }, 853 }, 854 }, 855 "volumes": map[string]interface{}{}, 856 "networks": map[string]interface{}{}, 857 "secrets": map[string]interface{}{}, 858 "configs": map[string]interface{}{}, 859 } 860 override := map[string]interface{}{ 861 "version": "3.4", 862 "services": map[string]interface{}{ 863 "foo": map[string]interface{}{ 864 "image": "baz", 865 "build": map[string]interface{}{ 866 "dockerfile": "foo.Dockerfile", 867 "args": []interface{}{ 868 "buildno=1", 869 "password=secret", 870 }, 871 }, 872 "ports": []interface{}{ 873 map[string]interface{}{ 874 "target": 81, 875 "published": 8080, 876 }, 877 }, 878 "labels": map[string]interface{}{ 879 "foo": "baz", 880 }, 881 "cap_add": []interface{}{ 882 "SYS_ADMIN", 883 }, 884 }, 885 "bar": map[string]interface{}{ 886 "image": "bar", 887 }, 888 }, 889 "volumes": map[string]interface{}{}, 890 "networks": map[string]interface{}{}, 891 "secrets": map[string]interface{}{}, 892 "configs": map[string]interface{}{}, 893 } 894 configDetails := types.ConfigDetails{ 895 ConfigFiles: []types.ConfigFile{ 896 {Filename: "base.yml", Config: base}, 897 {Filename: "override.yml", Config: override}, 898 }, 899 } 900 config, err := Load(configDetails) 901 assert.NilError(t, err) 902 assert.DeepEqual(t, &types.Config{ 903 Filename: "base.yml", 904 Version: "3.4", 905 Services: []types.ServiceConfig{ 906 { 907 Name: "bar", 908 Image: "bar", 909 Environment: types.MappingWithEquals{}, 910 }, 911 { 912 Name: "foo", 913 Image: "baz", 914 Build: types.BuildConfig{ 915 Context: ".", 916 Dockerfile: "foo.Dockerfile", 917 Args: types.MappingWithEquals{ 918 "buildno": strPtr("1"), 919 "password": strPtr("secret"), 920 }, 921 }, 922 Ports: []types.ServicePortConfig{ 923 { 924 Target: 81, 925 Published: 8080, 926 }, 927 { 928 Mode: "ingress", 929 Target: 90, 930 Published: 9090, 931 Protocol: "tcp", 932 }, 933 }, 934 Labels: types.Labels{ 935 "foo": "baz", 936 }, 937 CapAdd: []string{"NET_ADMIN", "SYS_ADMIN"}, 938 Environment: types.MappingWithEquals{}, 939 }}, 940 Networks: map[string]types.NetworkConfig{}, 941 Volumes: map[string]types.VolumeConfig{}, 942 Secrets: map[string]types.SecretConfig{}, 943 Configs: map[string]types.ConfigObjConfig{}, 944 }, config) 945 } 946 947 // Issue#972 948 func TestLoadMultipleNetworks(t *testing.T) { 949 base := map[string]interface{}{ 950 "version": "3.4", 951 "services": map[string]interface{}{ 952 "foo": map[string]interface{}{ 953 "image": "baz", 954 }, 955 }, 956 "volumes": map[string]interface{}{}, 957 "networks": map[string]interface{}{ 958 "hostnet": map[string]interface{}{ 959 "driver": "overlay", 960 "ipam": map[string]interface{}{ 961 "driver": "default", 962 "config": []interface{}{ 963 map[string]interface{}{ 964 "subnet": "10.0.0.0/20", 965 }, 966 }, 967 }, 968 }, 969 }, 970 "secrets": map[string]interface{}{}, 971 "configs": map[string]interface{}{}, 972 } 973 override := map[string]interface{}{ 974 "version": "3.4", 975 "services": map[string]interface{}{}, 976 "volumes": map[string]interface{}{}, 977 "networks": map[string]interface{}{ 978 "hostnet": map[string]interface{}{ 979 "external": map[string]interface{}{ 980 "name": "host", 981 }, 982 }, 983 }, 984 "secrets": map[string]interface{}{}, 985 "configs": map[string]interface{}{}, 986 } 987 configDetails := types.ConfigDetails{ 988 ConfigFiles: []types.ConfigFile{ 989 {Filename: "base.yml", Config: base}, 990 {Filename: "override.yml", Config: override}, 991 }, 992 } 993 config, err := Load(configDetails) 994 assert.NilError(t, err) 995 assert.DeepEqual(t, &types.Config{ 996 Filename: "base.yml", 997 Version: "3.4", 998 Services: []types.ServiceConfig{ 999 { 1000 Name: "foo", 1001 Image: "baz", 1002 Environment: types.MappingWithEquals{}, 1003 }}, 1004 Networks: map[string]types.NetworkConfig{ 1005 "hostnet": { 1006 Name: "host", 1007 External: types.External{ 1008 External: true, 1009 }, 1010 }, 1011 }, 1012 Volumes: map[string]types.VolumeConfig{}, 1013 Secrets: map[string]types.SecretConfig{}, 1014 Configs: map[string]types.ConfigObjConfig{}, 1015 }, config) 1016 }