github.com/juju/charm/v11@v11.2.0/overlay_test.go (about) 1 // Copyright 2019 Canonical Ltd. 2 // Licensed under the LGPLv3, see LICENCE file for details. 3 4 package charm_test 5 6 import ( 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 "sort" 11 "strings" 12 13 "github.com/juju/errors" 14 "github.com/juju/testing" 15 jc "github.com/juju/testing/checkers" 16 gc "gopkg.in/check.v1" 17 yaml "gopkg.in/yaml.v2" 18 19 "github.com/juju/charm/v11" 20 ) 21 22 type bundleDataOverlaySuite struct { 23 testing.IsolationSuite 24 } 25 26 var _ = gc.Suite(&bundleDataOverlaySuite{}) 27 28 func (*bundleDataOverlaySuite) TestEmptyBaseApplication(c *gc.C) { 29 data := ` 30 applications: 31 apache2: 32 --- 33 series: trusty 34 applications: 35 apache2: 36 charm: cs:apache2-42 37 series: bionic 38 `[1:] 39 40 _, err := charm.ReadAndMergeBundleData(mustCreateStringDataSource(c, data)) 41 c.Assert(err, gc.ErrorMatches, `base application "apache2" has no body`) 42 } 43 44 func (*bundleDataOverlaySuite) TestExtractBaseAndOverlayParts(c *gc.C) { 45 data := ` 46 applications: 47 apache2: 48 charm: apache2 49 exposed-endpoints: 50 www: 51 expose-to-spaces: 52 - dmz 53 expose-to-cidrs: 54 - 13.37.0.0/16 55 offers: 56 my-offer: 57 endpoints: 58 - apache-website 59 - website-cache 60 acl: 61 admin: admin 62 foo: consume 63 my-other-offer: 64 endpoints: 65 - apache-website 66 saas: 67 apache2: 68 url: production:admin/info.apache 69 series: bionic 70 `[1:] 71 72 expBase := ` 73 applications: 74 apache2: 75 charm: apache2 76 saas: 77 apache2: 78 url: production:admin/info.apache 79 series: bionic 80 `[1:] 81 82 expOverlay := ` 83 applications: 84 apache2: 85 exposed-endpoints: 86 www: 87 expose-to-spaces: 88 - dmz 89 expose-to-cidrs: 90 - 13.37.0.0/16 91 offers: 92 my-offer: 93 endpoints: 94 - apache-website 95 - website-cache 96 acl: 97 admin: admin 98 foo: consume 99 my-other-offer: 100 endpoints: 101 - apache-website 102 `[1:] 103 104 bd, err := charm.ReadBundleData(strings.NewReader(data)) 105 c.Assert(err, gc.IsNil) 106 107 base, overlay, err := charm.ExtractBaseAndOverlayParts(bd) 108 c.Assert(err, jc.ErrorIsNil) 109 110 baseYaml, err := yaml.Marshal(base) 111 c.Assert(err, jc.ErrorIsNil) 112 c.Assert(string(baseYaml), gc.Equals, expBase) 113 114 overlayYaml, err := yaml.Marshal(overlay) 115 c.Assert(err, jc.ErrorIsNil) 116 c.Assert(string(overlayYaml), gc.Equals, expOverlay) 117 } 118 119 func (*bundleDataOverlaySuite) TestExtractBaseAndOverlayPartsWithNoOverlayFields(c *gc.C) { 120 data := ` 121 bundle: kubernetes 122 applications: 123 mysql: 124 charm: cs:mysql 125 scale: 1 126 wordpress: 127 charm: cs:wordpress 128 scale: 2 129 relations: 130 - - wordpress:db 131 - mysql:mysql 132 `[1:] 133 134 expBase := ` 135 bundle: kubernetes 136 applications: 137 mysql: 138 charm: cs:mysql 139 num_units: 1 140 wordpress: 141 charm: cs:wordpress 142 num_units: 2 143 relations: 144 - - wordpress:db 145 - mysql:mysql 146 `[1:] 147 148 expOverlay := ` 149 {} 150 `[1:] 151 152 bd, err := charm.ReadBundleData(strings.NewReader(data)) 153 c.Assert(err, gc.IsNil) 154 155 base, overlay, err := charm.ExtractBaseAndOverlayParts(bd) 156 c.Assert(err, jc.ErrorIsNil) 157 158 baseYaml, err := yaml.Marshal(base) 159 c.Assert(err, jc.ErrorIsNil) 160 c.Assert(string(baseYaml), gc.Equals, expBase) 161 162 overlayYaml, err := yaml.Marshal(overlay) 163 c.Assert(err, jc.ErrorIsNil) 164 c.Assert(string(overlayYaml), gc.Equals, expOverlay) 165 } 166 167 func (*bundleDataOverlaySuite) TestExtractAndMergeWithMixedOverlayBits(c *gc.C) { 168 // In this example, mysql defines an offer whereas wordpress does not. 169 // 170 // When the visitor code examines the application map, it should report 171 // back that the filtered "mysql" application key should be retained 172 // but the "wordpress" application key should NOT be retained. The 173 // applications map should be retained because at least one of its keys 174 // has to be retained. However, the "wordpress" entry must be removed. 175 // If not, it would be encoded as an empty object which the overlay 176 // merge code would mis-interpret as a request to delete the "wordpress" 177 // application from the base bundle! 178 data := ` 179 bundle: kubernetes 180 applications: 181 mysql: 182 charm: cs:mysql 183 scale: 1 184 offers: 185 my-offer: 186 endpoints: 187 - apache-website 188 - website-cache 189 acl: 190 admin: admin 191 foo: consume 192 wordpress: 193 charm: cs:wordpress 194 channel: edge 195 scale: 2 196 options: 197 foo: bar 198 relations: 199 - - wordpress:db 200 - mysql:mysql 201 `[1:] 202 203 expBase := ` 204 bundle: kubernetes 205 applications: 206 mysql: 207 charm: cs:mysql 208 num_units: 1 209 wordpress: 210 charm: cs:wordpress 211 channel: edge 212 num_units: 2 213 options: 214 foo: bar 215 relations: 216 - - wordpress:db 217 - mysql:mysql 218 `[1:] 219 220 expOverlay := ` 221 applications: 222 mysql: 223 offers: 224 my-offer: 225 endpoints: 226 - apache-website 227 - website-cache 228 acl: 229 admin: admin 230 foo: consume 231 `[1:] 232 233 bd, err := charm.ReadBundleData(strings.NewReader(data)) 234 c.Assert(err, gc.IsNil) 235 236 base, overlay, err := charm.ExtractBaseAndOverlayParts(bd) 237 c.Assert(err, jc.ErrorIsNil) 238 239 baseYaml, err := yaml.Marshal(base) 240 c.Assert(err, jc.ErrorIsNil) 241 c.Assert(string(baseYaml), gc.Equals, expBase) 242 243 overlayYaml, err := yaml.Marshal(overlay) 244 c.Assert(err, jc.ErrorIsNil) 245 c.Assert(string(overlayYaml), gc.Equals, expOverlay) 246 247 // Check that merging the output back into a bundle yields the original 248 r := strings.NewReader(string(baseYaml) + "\n---\n" + string(overlayYaml)) 249 ds, err := charm.StreamBundleDataSource(r, "") 250 c.Assert(err, jc.ErrorIsNil) 251 252 newBd, err := charm.ReadAndMergeBundleData(ds) 253 c.Assert(err, jc.ErrorIsNil) 254 c.Assert(newBd, gc.DeepEquals, bd) 255 } 256 257 func (*bundleDataOverlaySuite) TestVerifyNoOverlayFieldsPresent(c *gc.C) { 258 data := ` 259 applications: 260 apache2: 261 charm: apache2 262 offers: 263 my-offer: 264 endpoints: 265 - apache-website 266 - website-cache 267 acl: 268 admin: admin 269 foo: consume 270 my-other-offer: 271 endpoints: 272 - apache-website 273 saas: 274 apache2: 275 url: production:admin/info.apache 276 series: bionic 277 ` 278 279 bd, err := charm.ReadBundleData(strings.NewReader(data)) 280 c.Assert(err, gc.IsNil) 281 282 static, overlay, err := charm.ExtractBaseAndOverlayParts(bd) 283 c.Assert(err, jc.ErrorIsNil) 284 285 c.Assert(charm.VerifyNoOverlayFieldsPresent(static), gc.Equals, nil) 286 287 expErrors := []string{ 288 "applications.apache2.offers can only appear in an overlay section", 289 "applications.apache2.offers.my-offer.endpoints can only appear in an overlay section", 290 "applications.apache2.offers.my-offer.acl can only appear in an overlay section", 291 "applications.apache2.offers.my-other-offer.endpoints can only appear in an overlay section", 292 } 293 err = charm.VerifyNoOverlayFieldsPresent(overlay) 294 c.Assert(err, gc.FitsTypeOf, (*charm.VerificationError)(nil)) 295 errors := err.(*charm.VerificationError).Errors 296 errStrings := make([]string, len(errors)) 297 for i, err := range errors { 298 errStrings[i] = err.Error() 299 } 300 sort.Strings(errStrings) 301 sort.Strings(expErrors) 302 c.Assert(errStrings, jc.DeepEquals, expErrors) 303 } 304 305 func (*bundleDataOverlaySuite) TestVerifyNoOverlayFieldsPresentOnNilOptionValue(c *gc.C) { 306 data := ` 307 # ssl_ca is left uninitialized so it resolves to nil 308 ssl_ca: &ssl_ca 309 310 applications: 311 apache2: 312 options: 313 foo: bar 314 ssl_ca: *ssl_ca 315 series: bionic 316 ` 317 318 bd, err := charm.ReadBundleData(strings.NewReader(data)) 319 c.Assert(err, gc.IsNil) 320 321 static, _, err := charm.ExtractBaseAndOverlayParts(bd) 322 c.Assert(err, jc.ErrorIsNil) 323 324 c.Assert(charm.VerifyNoOverlayFieldsPresent(static), gc.Equals, nil) 325 } 326 327 func (*bundleDataOverlaySuite) TestOverrideCharmAndSeries(c *gc.C) { 328 testBundleMergeResult(c, ` 329 applications: 330 apache2: 331 charm: apache2 332 num_units: 1 333 --- 334 series: trusty 335 applications: 336 apache2: 337 charm: cs:apache2-42 338 series: bionic 339 `, ` 340 applications: 341 apache2: 342 charm: cs:apache2-42 343 series: bionic 344 num_units: 1 345 series: trusty 346 `, 347 ) 348 } 349 350 func (*bundleDataOverlaySuite) TestOverrideScale(c *gc.C) { 351 testBundleMergeResult(c, ` 352 applications: 353 apache2: 354 charm: apache2 355 scale: 1 356 --- 357 applications: 358 apache2: 359 scale: 2 360 `, ` 361 applications: 362 apache2: 363 charm: apache2 364 num_units: 2 365 `, 366 ) 367 } 368 369 func (*bundleDataOverlaySuite) TestOverrideScaleWithNumUnits(c *gc.C) { 370 // This shouldn't be allowed, but the code does, so we should test it! 371 // Notice that scale doesn't exist. 372 testBundleMergeResult(c, ` 373 applications: 374 apache2: 375 charm: apache2 376 scale: 1 377 --- 378 applications: 379 apache2: 380 num_units: 2 381 `, ` 382 applications: 383 apache2: 384 charm: apache2 385 num_units: 2 386 `, 387 ) 388 } 389 390 func (*bundleDataOverlaySuite) TestMultipleOverrideScale(c *gc.C) { 391 testBundleMergeResult(c, ` 392 applications: 393 apache2: 394 charm: apache2 395 scale: 1 396 --- 397 applications: 398 apache2: 399 scale: 50 400 --- 401 applications: 402 apache2: 403 scale: 3 404 `, ` 405 applications: 406 apache2: 407 charm: apache2 408 num_units: 3 409 `, 410 ) 411 } 412 413 func (*bundleDataOverlaySuite) TestOverrideScaleWithZero(c *gc.C) { 414 testBundleMergeResult(c, ` 415 applications: 416 apache2: 417 charm: apache2 418 scale: 1 419 --- 420 applications: 421 apache2: 422 scale: 0 423 `, ` 424 applications: 425 apache2: 426 charm: apache2 427 num_units: 1 428 `, 429 ) 430 } 431 432 func (*bundleDataOverlaySuite) TestAddAndOverrideResourcesStorageDevicesAndBindings(c *gc.C) { 433 testBundleMergeResult(c, ` 434 applications: 435 apache2: 436 charm: apache2 437 resources: 438 res1: foo 439 storage: 440 dsk0: dsk0 441 devices: 442 dev0: dev0 443 --- 444 applications: 445 apache2: 446 resources: 447 res1: bar 448 res2: new 449 storage: 450 dsk0: vol0 451 dsk1: new 452 devices: 453 dev0: net 454 dev1: new 455 bindings: 456 bnd0: new 457 `, ` 458 applications: 459 apache2: 460 charm: apache2 461 resources: 462 res1: bar 463 res2: new 464 storage: 465 dsk0: vol0 466 dsk1: new 467 devices: 468 dev0: net 469 dev1: new 470 bindings: 471 bnd0: new 472 `, 473 ) 474 } 475 476 func (*bundleDataOverlaySuite) TestAddAndOverrideOptionsAndAnnotations(c *gc.C) { 477 testBundleMergeResult(c, ` 478 applications: 479 apache2: 480 charm: apache2 481 options: 482 opt1: foo 483 opt1: bar 484 mapOpt: 485 foo: bar 486 --- 487 applications: 488 apache2: 489 options: 490 opt1: foo 491 opt2: "" 492 mapOpt: 493 annotations: 494 ann1: new 495 `, ` 496 applications: 497 apache2: 498 charm: apache2 499 options: 500 opt1: foo 501 opt2: "" 502 annotations: 503 ann1: new 504 `, 505 ) 506 } 507 508 func (*bundleDataOverlaySuite) TestOverrideUnitsTrustConstraintsAndExposeFlags(c *gc.C) { 509 testBundleMergeResult(c, ` 510 applications: 511 apache2: 512 charm: apache2 513 --- 514 applications: 515 apache2: 516 num_units: 4 517 to: 518 - lxd/0 519 - lxd/1 520 - lxd/2 521 - lxd/3 522 expose: true 523 `, ` 524 applications: 525 apache2: 526 charm: apache2 527 num_units: 4 528 to: 529 - lxd/0 530 - lxd/1 531 - lxd/2 532 - lxd/3 533 expose: true 534 `, 535 ) 536 } 537 538 func (*bundleDataOverlaySuite) TestAddModifyAndRemoveApplicationsAndRelations(c *gc.C) { 539 testBundleMergeResult(c, ` 540 applications: 541 apache2: 542 charm: apache2 543 wordpress: 544 charm: wordpress 545 dummy: 546 charm: dummy 547 relations: 548 - - wordpress:www 549 - apache2:www 550 --- 551 applications: 552 apache2: 553 charm: apache2 554 wordpress: 555 relations: 556 - - dummy:www 557 - apache2:www 558 `, ` 559 applications: 560 apache2: 561 charm: apache2 562 dummy: 563 charm: dummy 564 relations: 565 - - dummy:www 566 - apache2:www 567 `, 568 ) 569 } 570 571 func (*bundleDataOverlaySuite) TestAddModifyAndRemoveSaasBlocksAndRelations(c *gc.C) { 572 testBundleMergeResult(c, ` 573 saas: 574 postgres: 575 url: jaas:admin/default.postgres 576 applications: 577 wordpress: 578 charm: wordpress 579 relations: 580 - - wordpress:db 581 - postgres:db 582 --- 583 saas: 584 postgres: 585 cockroachdb: 586 url: jaas:admin/default.cockroachdb 587 `, ` 588 applications: 589 wordpress: 590 charm: wordpress 591 saas: 592 cockroachdb: 593 url: jaas:admin/default.cockroachdb 594 `, 595 ) 596 } 597 598 func (*bundleDataOverlaySuite) TestAddAndRemoveOffers(c *gc.C) { 599 testBundleMergeResult(c, ` 600 applications: 601 apache2: 602 charm: apache2 603 channel: stable 604 revision: 26 605 --- # offer blocks are overlay-specific 606 applications: 607 apache2: 608 offers: 609 my-offer: 610 endpoints: 611 - apache-website 612 - website-cache 613 acl: 614 admin: admin 615 foo: consume 616 my-other-offer: 617 endpoints: 618 - apache-website 619 --- 620 applications: 621 apache2: 622 offers: 623 my-other-offer: 624 `, ` 625 applications: 626 apache2: 627 charm: apache2 628 channel: stable 629 revision: 26 630 offers: 631 my-offer: 632 endpoints: 633 - apache-website 634 - website-cache 635 acl: 636 admin: admin 637 foo: consume 638 `, 639 ) 640 } 641 642 func (*bundleDataOverlaySuite) TestAddAndRemoveMachines(c *gc.C) { 643 testBundleMergeResult(c, ` 644 applications: 645 apache2: 646 charm: apache2 647 channel: stable 648 revision: 26 649 machines: 650 "0": {} 651 "1": {} 652 --- 653 machines: 654 "2": {} 655 `, ` 656 applications: 657 apache2: 658 charm: apache2 659 channel: stable 660 revision: 26 661 machines: 662 "2": {} 663 `, 664 ) 665 } 666 667 func (*bundleDataOverlaySuite) TestYAMLInterpolation(c *gc.C) { 668 base := ` 669 applications: 670 django: 671 expose: true 672 charm: django 673 num_units: 1 674 options: 675 general: good 676 annotations: 677 key1: value1 678 key2: value2 679 to: [1] 680 memcached: 681 charm: mem 682 revision: 47 683 series: trusty 684 num_units: 1 685 options: 686 key: value 687 relations: 688 - - "django" 689 - "memcached" 690 machines: 691 1: 692 annotations: {foo: bar}` 693 694 removeDjango := ` 695 applications: 696 django: 697 ` 698 699 addWiki := ` 700 defaultwiki: &DEFAULTWIKI 701 charm: "mediawiki" 702 revision: 5 703 series: trusty 704 num_units: 1 705 options: &WIKIOPTS 706 debug: false 707 name: Please set name of wiki 708 skin: vector 709 710 applications: 711 wiki: 712 <<: *DEFAULTWIKI 713 options: 714 <<: *WIKIOPTS 715 name: The name override 716 relations: 717 - - "wiki" 718 - "memcached" 719 ` 720 721 bd, err := charm.ReadAndMergeBundleData( 722 mustCreateStringDataSource(c, base), 723 mustCreateStringDataSource(c, removeDjango), 724 mustCreateStringDataSource(c, addWiki), 725 ) 726 c.Assert(err, gc.IsNil) 727 728 merged, err := yaml.Marshal(bd) 729 c.Assert(err, gc.IsNil) 730 731 exp := ` 732 applications: 733 memcached: 734 charm: mem 735 revision: 47 736 series: trusty 737 num_units: 1 738 options: 739 key: value 740 wiki: 741 charm: mediawiki 742 revision: 5 743 series: trusty 744 num_units: 1 745 options: 746 debug: false 747 name: The name override 748 skin: vector 749 machines: 750 "1": 751 annotations: 752 foo: bar 753 relations: 754 - - wiki 755 - memcached 756 ` 757 758 c.Assert("\n"+string(merged), gc.Equals, exp) 759 } 760 761 func (*bundleDataOverlaySuite) TestReadAndMergeBundleDataWithIncludes(c *gc.C) { 762 data := ` 763 applications: 764 apache2: 765 options: 766 opt-raw: include-file://foo 767 opt-b64: include-base64://foo 768 opt-other: 769 some: value 770 annotations: 771 anno-raw: include-file://foo 772 anno-b64: include-base64://foo 773 anno-other: value 774 machines: 775 "0": {} 776 "1": 777 annotations: 778 anno-raw: include-file://foo 779 anno-b64: include-base64://foo 780 anno-other: value 781 ` 782 783 ds := srcWithFakeIncludeResolver{ 784 src: mustCreateStringDataSource(c, data), 785 resolveMap: map[string][]byte{ 786 "foo": []byte("lorem$ipsum$"), 787 }, 788 } 789 790 bd, err := charm.ReadAndMergeBundleData(ds) 791 c.Assert(err, gc.IsNil) 792 793 merged, err := yaml.Marshal(bd) 794 c.Assert(err, gc.IsNil) 795 796 exp := ` 797 applications: 798 apache2: 799 options: 800 opt-b64: bG9yZW0kaXBzdW0k 801 opt-other: 802 some: value 803 opt-raw: lorem$ipsum$ 804 annotations: 805 anno-b64: bG9yZW0kaXBzdW0k 806 anno-other: value 807 anno-raw: lorem$ipsum$ 808 machines: 809 "0": {} 810 "1": 811 annotations: 812 anno-b64: bG9yZW0kaXBzdW0k 813 anno-other: value 814 anno-raw: lorem$ipsum$ 815 ` 816 817 c.Assert("\n"+string(merged), gc.Equals, exp) 818 } 819 820 func (*bundleDataOverlaySuite) TestBundleDataSourceRelativeIncludes(c *gc.C) { 821 base := ` 822 applications: 823 django: 824 charm: cs:django 825 options: 826 opt1: include-file://relative-to-base.txt 827 ` 828 829 overlays := ` 830 applications: 831 django: 832 charm: cs:django 833 options: 834 opt2: include-file://relative-to-overlay.txt 835 --- 836 applications: 837 django: 838 charm: cs:django 839 options: 840 opt3: include-file://relative-to-overlay.txt 841 ` 842 843 baseDir := c.MkDir() 844 mustWriteFile(c, filepath.Join(baseDir, "bundle.yaml"), base) 845 mustWriteFile(c, filepath.Join(baseDir, "relative-to-base.txt"), "lorem ipsum") 846 847 ovlDir := c.MkDir() 848 mustWriteFile(c, filepath.Join(ovlDir, "overlays.yaml"), overlays) 849 mustWriteFile(c, filepath.Join(ovlDir, "relative-to-overlay.txt"), "dolor") 850 851 bd, err := charm.ReadAndMergeBundleData( 852 mustCreateLocalDataSource(c, filepath.Join(baseDir, "bundle.yaml")), 853 mustCreateLocalDataSource(c, filepath.Join(ovlDir, "overlays.yaml")), 854 ) 855 c.Assert(err, gc.IsNil) 856 857 merged, err := yaml.Marshal(bd) 858 c.Assert(err, gc.IsNil) 859 860 exp := ` 861 applications: 862 django: 863 charm: cs:django 864 options: 865 opt1: lorem ipsum 866 opt2: dolor 867 opt3: dolor 868 ` 869 870 c.Assert("\n"+string(merged), gc.Equals, exp) 871 } 872 873 func (*bundleDataOverlaySuite) TestBundleDataSourceWithEmptyOverlay(c *gc.C) { 874 base := ` 875 applications: 876 django: 877 charm: cs:django 878 ` 879 880 overlays := ` 881 --- 882 ` 883 884 baseDir := c.MkDir() 885 mustWriteFile(c, filepath.Join(baseDir, "bundle.yaml"), base) 886 887 ovlDir := c.MkDir() 888 mustWriteFile(c, filepath.Join(ovlDir, "overlays.yaml"), overlays) 889 890 bd, err := charm.ReadAndMergeBundleData( 891 mustCreateLocalDataSource(c, filepath.Join(baseDir, "bundle.yaml")), 892 mustCreateLocalDataSource(c, filepath.Join(ovlDir, "overlays.yaml")), 893 ) 894 c.Assert(err, gc.IsNil) 895 896 merged, err := yaml.Marshal(bd) 897 c.Assert(err, gc.IsNil) 898 899 exp := ` 900 applications: 901 django: 902 charm: cs:django 903 ` 904 905 c.Assert("\n"+string(merged), gc.Equals, exp) 906 } 907 908 func (*bundleDataOverlaySuite) TestReadAndMergeBundleDataWithRelativeCharmPaths(c *gc.C) { 909 base := ` 910 applications: 911 apache2: 912 charm: ./apache 913 mysql: 914 charm: cs:mysql 915 varnish: 916 charm: /some/absolute/path 917 ` 918 919 overlay := ` 920 applications: 921 wordpress: 922 charm: ./wordpress 923 ` 924 bd, err := charm.ReadAndMergeBundleData( 925 mustCreateStringDataSourceWithBasePath(c, base, "/tmp/base"), 926 mustCreateStringDataSourceWithBasePath(c, overlay, "/overlay"), 927 ) 928 c.Assert(err, gc.IsNil) 929 930 merged, err := yaml.Marshal(bd) 931 c.Assert(err, gc.IsNil) 932 933 exp := ` 934 applications: 935 apache2: 936 charm: /tmp/base/apache 937 mysql: 938 charm: cs:mysql 939 varnish: 940 charm: /some/absolute/path 941 wordpress: 942 charm: /overlay/wordpress 943 `[1:] 944 945 c.Assert(string(merged), gc.Equals, exp) 946 } 947 948 type srcWithFakeIncludeResolver struct { 949 src charm.BundleDataSource 950 resolveMap map[string][]byte 951 } 952 953 func (s srcWithFakeIncludeResolver) Parts() []*charm.BundleDataPart { 954 return s.src.Parts() 955 } 956 957 func (s srcWithFakeIncludeResolver) BasePath() string { 958 return s.src.BasePath() 959 } 960 961 func (s srcWithFakeIncludeResolver) ResolveInclude(path string) ([]byte, error) { 962 var ( 963 data []byte 964 found bool 965 ) 966 967 if s.resolveMap != nil { 968 data, found = s.resolveMap[path] 969 } 970 971 if !found { 972 return nil, errors.NotFoundf(path) 973 } 974 975 return data, nil 976 } 977 978 // testBundleMergeResult reads and merges the bundle and any overlays in src, 979 // serializes the merged bundle back to yaml and compares it with exp. 980 func testBundleMergeResult(c *gc.C, src, exp string) { 981 bd, err := charm.ReadAndMergeBundleData(mustCreateStringDataSource(c, src)) 982 c.Assert(err, gc.IsNil) 983 984 merged, err := yaml.Marshal(bd) 985 c.Assert(err, gc.IsNil) 986 c.Assert("\n"+string(merged), gc.Equals, exp) 987 } 988 989 func mustWriteFile(c *gc.C, path, content string) { 990 err := ioutil.WriteFile(path, []byte(content), os.ModePerm) 991 c.Assert(err, gc.IsNil) 992 } 993 994 func mustCreateLocalDataSource(c *gc.C, path string) charm.BundleDataSource { 995 ds, err := charm.LocalBundleDataSource(path) 996 c.Assert(err, gc.IsNil, gc.Commentf(path)) 997 return ds 998 } 999 1000 func mustCreateStringDataSource(c *gc.C, data string) charm.BundleDataSource { 1001 ds, err := charm.StreamBundleDataSource(strings.NewReader(data), "") 1002 c.Assert(err, gc.IsNil) 1003 return ds 1004 } 1005 1006 func mustCreateStringDataSourceWithBasePath(c *gc.C, data, basePath string) charm.BundleDataSource { 1007 ds, err := charm.StreamBundleDataSource(strings.NewReader(data), basePath) 1008 c.Assert(err, gc.IsNil) 1009 return ds 1010 }