github.com/juju/charm/v11@v11.2.0/bundledatasrc_test.go (about)

     1  // Copyright 2019 Canonical Ltd.
     2  // Licensed under the LGPLv3, see LICENCE file for details.
     3  
     4  package charm
     5  
     6  import (
     7  	"archive/zip"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"os"
    11  	"path/filepath"
    12  	"strings"
    13  
    14  	"github.com/juju/testing"
    15  	jc "github.com/juju/testing/checkers"
    16  	gc "gopkg.in/check.v1"
    17  )
    18  
    19  type BundleDataSourceSuite struct {
    20  	testing.IsolationSuite
    21  }
    22  
    23  var _ = gc.Suite(&BundleDataSourceSuite{})
    24  
    25  func (s *BundleDataSourceSuite) TestReadBundleFromLocalFile(c *gc.C) {
    26  	path := bundleDirPath(c, "wordpress-multidoc")
    27  	src, err := LocalBundleDataSource(filepath.Join(path, "bundle.yaml"))
    28  	c.Assert(err, gc.IsNil)
    29  	assertBundleSourceProcessed(c, src)
    30  }
    31  
    32  func (s *BundleDataSourceSuite) TestReadBundleFromExplodedArchiveFolder(c *gc.C) {
    33  	path := bundleDirPath(c, "wordpress-multidoc")
    34  	src, err := LocalBundleDataSource(path)
    35  	c.Assert(err, gc.IsNil)
    36  	assertBundleSourceProcessed(c, src)
    37  }
    38  
    39  func (s *BundleDataSourceSuite) TestReadBundleFromArchive(c *gc.C) {
    40  	path := archiveBundleDirPath(c, "wordpress-multidoc")
    41  	src, err := LocalBundleDataSource(path)
    42  	c.Assert(err, gc.IsNil)
    43  	assertBundleSourceProcessed(c, src)
    44  }
    45  
    46  func (s *BundleDataSourceSuite) TestReadBundleFromStream(c *gc.C) {
    47  	r := strings.NewReader(`
    48  applications:
    49    wordpress:
    50      charm: wordpress
    51    mysql:
    52      charm: mysql
    53      num_units: 1
    54  relations:
    55    - ["wordpress:db", "mysql:server"]
    56  --- # overlay.yaml
    57  applications:
    58    wordpress:
    59      offers:
    60        offer1:
    61          endpoints:
    62            - "some-endpoint"
    63  --- # overlay2.yaml
    64  applications:
    65    wordpress:
    66      offers:
    67        offer1:
    68          acl:
    69            admin: "admin"
    70            foo: "consume"
    71  `)
    72  
    73  	src, err := StreamBundleDataSource(r, "https://example.com")
    74  	c.Assert(err, gc.IsNil)
    75  	assertBundleSourceProcessed(c, src)
    76  }
    77  
    78  func assertBundleSourceProcessed(c *gc.C, src BundleDataSource) {
    79  	parts := src.Parts()
    80  	c.Assert(parts, gc.HasLen, 3)
    81  	assertFieldPresent(c, parts[1], "applications.wordpress.offers.offer1.endpoints")
    82  	assertFieldPresent(c, parts[2], "applications.wordpress.offers.offer1.acl.admin")
    83  }
    84  
    85  func assertFieldPresent(c *gc.C, part *BundleDataPart, path string) {
    86  	var (
    87  		segments             = strings.Split(path, ".")
    88  		next     interface{} = part.PresenceMap
    89  	)
    90  
    91  	for segIndex, segment := range segments {
    92  		c.Assert(next, gc.NotNil, gc.Commentf("incomplete path: %s", strings.Join(segments[:segIndex], ".")))
    93  		switch typ := next.(type) {
    94  		case FieldPresenceMap:
    95  			next = typ[segment]
    96  			c.Assert(next, gc.NotNil, gc.Commentf("incomplete path: %s", strings.Join(segments[:segIndex+1], ".")))
    97  		default:
    98  			c.Fatalf("unexpected type %T at path: %s", typ, strings.Join(segments[:segIndex], "."))
    99  		}
   100  	}
   101  }
   102  
   103  func (s *BundleDataSourceSuite) TestParseBundlePartsStrict(c *gc.C) {
   104  	r := strings.NewReader(`
   105  applications:
   106    wordpress:
   107      charm: wordpress
   108      constrain: "mem=8G"
   109    mysql:
   110      charm: mysql
   111      num_uns: 1
   112  relations:
   113    - ["wordpress:db", "mysql:server"]
   114  --- # overlay.yaml
   115  applications:
   116    wordpress:
   117      offers:
   118        offer1:
   119          endpoints:
   120            - "some-endpoint"
   121  --- # overlay2.yaml
   122  applications:
   123    wordpress:
   124      offer:
   125        offer1:
   126          acl:
   127            admin: "admin"
   128            foo: "consume"
   129  `)
   130  
   131  	parts, err := parseBundleParts(r)
   132  	c.Assert(err, gc.IsNil)
   133  	c.Assert(parts, gc.HasLen, 3)
   134  	c.Assert(parts[0].UnmarshallError, gc.NotNil)
   135  	c.Assert(parts[0].UnmarshallError.Error(), gc.Matches, ""+
   136  		"unmarshal document 0: yaml: unmarshal errors:\n"+
   137  		"  line 5: field constrain not found in applications\n"+
   138  		"  line 8: field num_uns not found in applications")
   139  	c.Assert(parts[1].UnmarshallError, jc.ErrorIsNil)
   140  	c.Assert(parts[2].UnmarshallError, gc.NotNil)
   141  	c.Assert(parts[2].UnmarshallError.Error(), gc.Matches, ""+
   142  		"unmarshal document 2: yaml: unmarshal errors:\n"+
   143  		"  line 21: field offer not found in applications")
   144  }
   145  
   146  func (s *BundleDataSourceSuite) TestResolveAbsoluteFileInclude(c *gc.C) {
   147  	target, err := filepath.Abs(filepath.Join(c.MkDir(), "example"))
   148  	c.Assert(err, gc.IsNil)
   149  
   150  	expContent := "example content\n"
   151  	c.Assert(ioutil.WriteFile(target, []byte(expContent), os.ModePerm), gc.IsNil)
   152  
   153  	ds := new(resolvedBundleDataSource)
   154  
   155  	got, err := ds.ResolveInclude(target)
   156  	c.Assert(err, gc.IsNil)
   157  	c.Assert(string(got), gc.Equals, expContent)
   158  }
   159  
   160  func (s *BundleDataSourceSuite) TestResolveRelativeFileInclude(c *gc.C) {
   161  	relTo := c.MkDir()
   162  	target, err := filepath.Abs(filepath.Join(relTo, "example"))
   163  	c.Assert(err, gc.IsNil)
   164  
   165  	expContent := "example content\n"
   166  	c.Assert(ioutil.WriteFile(target, []byte(expContent), os.ModePerm), gc.IsNil)
   167  
   168  	ds := &resolvedBundleDataSource{
   169  		basePath: relTo,
   170  	}
   171  
   172  	got, err := ds.ResolveInclude("./example")
   173  	c.Assert(err, gc.IsNil)
   174  	c.Assert(string(got), gc.Equals, expContent)
   175  }
   176  
   177  func (s *BundleDataSourceSuite) TestResolveIncludeErrors(c *gc.C) {
   178  	cwd, err := os.Getwd()
   179  	c.Assert(err, gc.IsNil)
   180  
   181  	tmpDir := c.MkDir()
   182  	specs := []struct {
   183  		descr   string
   184  		incPath string
   185  		exp     string
   186  	}{
   187  		{
   188  			descr:   "abs path does not exist",
   189  			incPath: "/some/invalid/path",
   190  			exp:     `include file "/some/invalid/path" not found`,
   191  		},
   192  		{
   193  			descr:   "rel path does not exist",
   194  			incPath: "./missing",
   195  			exp:     `include file "` + cwd + `/missing" not found`,
   196  		},
   197  		{
   198  			descr:   "path points to directory",
   199  			incPath: tmpDir,
   200  			exp:     fmt.Sprintf("include path %q resolves to a folder", tmpDir),
   201  		},
   202  	}
   203  
   204  	ds := new(resolvedBundleDataSource)
   205  	for specIndex, spec := range specs {
   206  		c.Logf("[test %d] %s", specIndex, spec.descr)
   207  
   208  		_, err := ds.ResolveInclude(spec.incPath)
   209  		c.Assert(err, gc.Not(gc.IsNil))
   210  
   211  		c.Assert(err.Error(), gc.Equals, spec.exp)
   212  	}
   213  }
   214  
   215  func bundleDirPath(c *gc.C, name string) string {
   216  	path := filepath.Join("internal/test-charm-repo/bundle", name)
   217  	assertIsDir(c, path)
   218  	return path
   219  }
   220  
   221  func assertIsDir(c *gc.C, path string) {
   222  	info, err := os.Stat(path)
   223  	c.Assert(err, gc.IsNil)
   224  	c.Assert(info.IsDir(), gc.Equals, true)
   225  }
   226  
   227  func archiveBundleDirPath(c *gc.C, name string) string {
   228  	src := filepath.Join("internal/test-charm-repo/bundle", name, "bundle.yaml")
   229  	srcYaml, err := ioutil.ReadFile(src)
   230  	c.Assert(err, gc.IsNil)
   231  
   232  	dstPath := filepath.Join(c.MkDir(), "bundle.zip")
   233  	f, err := os.Create(dstPath)
   234  	c.Assert(err, gc.IsNil)
   235  	defer func() { c.Assert(f.Close(), gc.IsNil) }()
   236  
   237  	zw := zip.NewWriter(f)
   238  	defer func() { c.Assert(zw.Close(), gc.IsNil) }()
   239  	w, err := zw.Create("bundle.yaml")
   240  	c.Assert(err, gc.IsNil)
   241  	_, err = w.Write(srcYaml)
   242  	c.Assert(err, gc.IsNil)
   243  
   244  	return dstPath
   245  }