launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/charm/dir_test.go (about) 1 // Copyright 2011, 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package charm_test 5 6 import ( 7 "archive/zip" 8 "bytes" 9 "fmt" 10 "io/ioutil" 11 "os" 12 "path/filepath" 13 "strings" 14 "syscall" 15 16 gc "launchpad.net/gocheck" 17 18 "launchpad.net/juju-core/charm" 19 "launchpad.net/juju-core/testing" 20 "launchpad.net/juju-core/testing/testbase" 21 ) 22 23 type DirSuite struct { 24 testbase.LoggingSuite 25 } 26 27 var _ = gc.Suite(&DirSuite{}) 28 29 func (s *DirSuite) TestReadDir(c *gc.C) { 30 path := testing.Charms.DirPath("dummy") 31 dir, err := charm.ReadDir(path) 32 c.Assert(err, gc.IsNil) 33 checkDummy(c, dir, path) 34 } 35 36 func (s *DirSuite) TestReadDirWithoutConfig(c *gc.C) { 37 path := testing.Charms.DirPath("varnish") 38 dir, err := charm.ReadDir(path) 39 c.Assert(err, gc.IsNil) 40 41 // A lacking config.yaml file still causes a proper 42 // Config value to be returned. 43 c.Assert(dir.Config().Options, gc.HasLen, 0) 44 } 45 46 func (s *DirSuite) TestBundleTo(c *gc.C) { 47 baseDir := c.MkDir() 48 charmDir := testing.Charms.ClonedDirPath(baseDir, "dummy") 49 var haveSymlinks = true 50 if err := os.Symlink("../target", filepath.Join(charmDir, "hooks/symlink")); err != nil { 51 haveSymlinks = false 52 } 53 dir, err := charm.ReadDir(charmDir) 54 c.Assert(err, gc.IsNil) 55 path := filepath.Join(baseDir, "bundle.charm") 56 file, err := os.Create(path) 57 c.Assert(err, gc.IsNil) 58 err = dir.BundleTo(file) 59 file.Close() 60 c.Assert(err, gc.IsNil) 61 62 zipr, err := zip.OpenReader(path) 63 c.Assert(err, gc.IsNil) 64 defer zipr.Close() 65 66 var metaf, instf, emptyf, revf, symf *zip.File 67 for _, f := range zipr.File { 68 c.Logf("Bundled file: %s", f.Name) 69 switch f.Name { 70 case "revision": 71 revf = f 72 case "metadata.yaml": 73 metaf = f 74 case "hooks/install": 75 instf = f 76 case "hooks/symlink": 77 symf = f 78 case "empty/": 79 emptyf = f 80 case "build/ignored": 81 c.Errorf("bundle includes build/*: %s", f.Name) 82 case ".ignored", ".dir/ignored": 83 c.Errorf("bundle includes .* entries: %s", f.Name) 84 } 85 } 86 87 c.Assert(revf, gc.NotNil) 88 reader, err := revf.Open() 89 c.Assert(err, gc.IsNil) 90 data, err := ioutil.ReadAll(reader) 91 reader.Close() 92 c.Assert(err, gc.IsNil) 93 c.Assert(string(data), gc.Equals, "1") 94 95 c.Assert(metaf, gc.NotNil) 96 reader, err = metaf.Open() 97 c.Assert(err, gc.IsNil) 98 meta, err := charm.ReadMeta(reader) 99 reader.Close() 100 c.Assert(err, gc.IsNil) 101 c.Assert(meta.Name, gc.Equals, "dummy") 102 103 c.Assert(instf, gc.NotNil) 104 // Despite it being 0751, we pack and unpack it as 0755. 105 c.Assert(instf.Mode()&0777, gc.Equals, os.FileMode(0755)) 106 107 if haveSymlinks { 108 c.Assert(symf, gc.NotNil) 109 c.Assert(symf.Mode()&0777, gc.Equals, os.FileMode(0777)) 110 reader, err = symf.Open() 111 c.Assert(err, gc.IsNil) 112 data, err = ioutil.ReadAll(reader) 113 reader.Close() 114 c.Assert(err, gc.IsNil) 115 c.Assert(string(data), gc.Equals, "../target") 116 } else { 117 c.Assert(symf, gc.IsNil) 118 } 119 120 c.Assert(emptyf, gc.NotNil) 121 c.Assert(emptyf.Mode()&os.ModeType, gc.Equals, os.ModeDir) 122 // Despite it being 0750, we pack and unpack it as 0755. 123 c.Assert(emptyf.Mode()&0777, gc.Equals, os.FileMode(0755)) 124 } 125 126 // Bug #864164: Must complain if charm hooks aren't executable 127 func (s *DirSuite) TestBundleToWithNonExecutableHooks(c *gc.C) { 128 hooks := []string{"install", "start", "config-changed", "upgrade-charm", "stop"} 129 for _, relName := range []string{"foo", "bar", "self"} { 130 for _, kind := range []string{"joined", "changed", "departed", "broken"} { 131 hooks = append(hooks, relName+"-relation-"+kind) 132 } 133 } 134 135 dir := testing.Charms.Dir("all-hooks") 136 path := filepath.Join(c.MkDir(), "bundle.charm") 137 file, err := os.Create(path) 138 c.Assert(err, gc.IsNil) 139 err = dir.BundleTo(file) 140 file.Close() 141 c.Assert(err, gc.IsNil) 142 143 tlog := c.GetTestLog() 144 for _, hook := range hooks { 145 fullpath := filepath.Join(dir.Path, "hooks", hook) 146 exp := fmt.Sprintf(`^(.|\n)*WARNING juju charm: making "%s" executable in charm(.|\n)*$`, fullpath) 147 c.Assert(tlog, gc.Matches, exp, gc.Commentf("hook %q was not made executable", fullpath)) 148 } 149 150 // Expand it and check the hooks' permissions 151 // (But do not use ExpandTo(), just use the raw zip) 152 f, err := os.Open(path) 153 c.Assert(err, gc.IsNil) 154 defer f.Close() 155 fi, err := f.Stat() 156 c.Assert(err, gc.IsNil) 157 size := fi.Size() 158 zipr, err := zip.NewReader(f, size) 159 c.Assert(err, gc.IsNil) 160 allhooks := dir.Meta().Hooks() 161 for _, zfile := range zipr.File { 162 cleanName := filepath.Clean(zfile.Name) 163 if strings.HasPrefix(cleanName, "hooks") { 164 hookName := filepath.Base(cleanName) 165 if _, ok := allhooks[hookName]; ok { 166 perms := zfile.Mode() 167 c.Assert(perms&0100 != 0, gc.Equals, true, gc.Commentf("hook %q is not executable", hookName)) 168 } 169 } 170 } 171 } 172 173 func (s *DirSuite) TestBundleToWithBadType(c *gc.C) { 174 charmDir := testing.Charms.ClonedDirPath(c.MkDir(), "dummy") 175 badFile := filepath.Join(charmDir, "hooks", "badfile") 176 177 // Symlink targeting a path outside of the charm. 178 err := os.Symlink("../../target", badFile) 179 c.Assert(err, gc.IsNil) 180 181 dir, err := charm.ReadDir(charmDir) 182 c.Assert(err, gc.IsNil) 183 184 err = dir.BundleTo(&bytes.Buffer{}) 185 c.Assert(err, gc.ErrorMatches, `symlink "hooks/badfile" links out of charm: "../../target"`) 186 187 // Symlink targeting an absolute path. 188 os.Remove(badFile) 189 err = os.Symlink("/target", badFile) 190 c.Assert(err, gc.IsNil) 191 192 dir, err = charm.ReadDir(charmDir) 193 c.Assert(err, gc.IsNil) 194 195 err = dir.BundleTo(&bytes.Buffer{}) 196 c.Assert(err, gc.ErrorMatches, `symlink "hooks/badfile" is absolute: "/target"`) 197 198 // Can't bundle special files either. 199 os.Remove(badFile) 200 err = syscall.Mkfifo(badFile, 0644) 201 c.Assert(err, gc.IsNil) 202 203 dir, err = charm.ReadDir(charmDir) 204 c.Assert(err, gc.IsNil) 205 206 err = dir.BundleTo(&bytes.Buffer{}) 207 c.Assert(err, gc.ErrorMatches, `file is a named pipe: "hooks/badfile"`) 208 } 209 210 func (s *DirSuite) TestDirRevisionFile(c *gc.C) { 211 charmDir := testing.Charms.ClonedDirPath(c.MkDir(), "dummy") 212 revPath := filepath.Join(charmDir, "revision") 213 214 // Missing revision file 215 err := os.Remove(revPath) 216 c.Assert(err, gc.IsNil) 217 218 dir, err := charm.ReadDir(charmDir) 219 c.Assert(err, gc.IsNil) 220 c.Assert(dir.Revision(), gc.Equals, 0) 221 222 // Missing revision file with old revision in metadata 223 file, err := os.OpenFile(filepath.Join(charmDir, "metadata.yaml"), os.O_WRONLY|os.O_APPEND, 0) 224 c.Assert(err, gc.IsNil) 225 _, err = file.Write([]byte("\nrevision: 1234\n")) 226 c.Assert(err, gc.IsNil) 227 228 dir, err = charm.ReadDir(charmDir) 229 c.Assert(err, gc.IsNil) 230 c.Assert(dir.Revision(), gc.Equals, 1234) 231 232 // Revision file with bad content 233 err = ioutil.WriteFile(revPath, []byte("garbage"), 0666) 234 c.Assert(err, gc.IsNil) 235 236 dir, err = charm.ReadDir(charmDir) 237 c.Assert(err, gc.ErrorMatches, "invalid revision file") 238 c.Assert(dir, gc.IsNil) 239 } 240 241 func (s *DirSuite) TestDirSetRevision(c *gc.C) { 242 dir := testing.Charms.ClonedDir(c.MkDir(), "dummy") 243 c.Assert(dir.Revision(), gc.Equals, 1) 244 dir.SetRevision(42) 245 c.Assert(dir.Revision(), gc.Equals, 42) 246 247 var b bytes.Buffer 248 err := dir.BundleTo(&b) 249 c.Assert(err, gc.IsNil) 250 251 bundle, err := charm.ReadBundleBytes(b.Bytes()) 252 c.Assert(bundle.Revision(), gc.Equals, 42) 253 } 254 255 func (s *DirSuite) TestDirSetDiskRevision(c *gc.C) { 256 charmDir := testing.Charms.ClonedDirPath(c.MkDir(), "dummy") 257 dir, err := charm.ReadDir(charmDir) 258 c.Assert(err, gc.IsNil) 259 260 c.Assert(dir.Revision(), gc.Equals, 1) 261 dir.SetDiskRevision(42) 262 c.Assert(dir.Revision(), gc.Equals, 42) 263 264 dir, err = charm.ReadDir(charmDir) 265 c.Assert(err, gc.IsNil) 266 c.Assert(dir.Revision(), gc.Equals, 42) 267 }