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 }