github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/worker/uniter/charm/deployer_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package charm_test 5 6 import ( 7 "fmt" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 12 gc "launchpad.net/gocheck" 13 14 corecharm "launchpad.net/juju-core/charm" 15 "launchpad.net/juju-core/testing" 16 "launchpad.net/juju-core/worker/uniter/charm" 17 "launchpad.net/juju-core/utils" 18 ) 19 20 type DeployerSuite struct { 21 testing.GitSuite 22 bundles *bundleReader 23 targetPath string 24 deployer charm.Deployer 25 } 26 27 var _ = gc.Suite(&DeployerSuite{}) 28 29 func (s *DeployerSuite) SetUpTest(c *gc.C) { 30 s.GitSuite.SetUpTest(c) 31 s.bundles = &bundleReader{} 32 s.targetPath = filepath.Join(c.MkDir(), "target") 33 deployerPath := filepath.Join(c.MkDir(), "deployer") 34 s.deployer = charm.NewGitDeployer(s.targetPath, deployerPath, s.bundles) 35 } 36 37 func (s *DeployerSuite) TestUnsetCharm(c *gc.C) { 38 err := s.deployer.Deploy() 39 c.Assert(err, gc.ErrorMatches, "charm deployment failed: no charm set") 40 } 41 42 func (s *DeployerSuite) TestInstall(c *gc.C) { 43 // Prepare. 44 info := s.bundles.Add(c, corecharm.MustParseURL("cs:s/c-1"), func(path string) { 45 err := ioutil.WriteFile(filepath.Join(path, "some-file"), []byte("hello"), 0644) 46 c.Assert(err, gc.IsNil) 47 }) 48 err := s.deployer.Stage(info, nil) 49 c.Assert(err, gc.IsNil) 50 checkCleanup(c, s.deployer) 51 52 // Install. 53 err = s.deployer.Deploy() 54 c.Assert(err, gc.IsNil) 55 checkCleanup(c, s.deployer) 56 57 // Check content. 58 data, err := ioutil.ReadFile(filepath.Join(s.targetPath, "some-file")) 59 c.Assert(err, gc.IsNil) 60 c.Assert(string(data), gc.Equals, "hello") 61 62 target := charm.NewGitDir(s.targetPath) 63 url, err := charm.ReadCharmURL(target) 64 c.Assert(err, gc.IsNil) 65 c.Assert(url, gc.DeepEquals, corecharm.MustParseURL("cs:s/c-1")) 66 lines, err := target.Log() 67 c.Assert(err, gc.IsNil) 68 c.Assert(lines, gc.HasLen, 2) 69 c.Assert(lines[0], gc.Matches, `[0-9a-f]{7} Deployed charm "cs:s/c-1".`) 70 c.Assert(lines[1], gc.Matches, `[0-9a-f]{7} Imported charm "cs:s/c-1" from ".*".`) 71 } 72 73 func (s *DeployerSuite) TestUpgrade(c *gc.C) { 74 // Install. 75 info1 := s.bundles.Add(c, corecharm.MustParseURL("cs:s/c-1"), func(path string) { 76 err := ioutil.WriteFile(filepath.Join(path, "some-file"), []byte("hello"), 0644) 77 c.Assert(err, gc.IsNil) 78 err = utils.Symlink("./some-file", filepath.Join(path, "a-symlink")) 79 c.Assert(err, gc.IsNil) 80 }) 81 err := s.deployer.Stage(info1, nil) 82 c.Assert(err, gc.IsNil) 83 err = s.deployer.Deploy() 84 c.Assert(err, gc.IsNil) 85 86 // Upgrade. 87 info2 := s.bundles.Add(c, corecharm.MustParseURL("cs:s/c-2"), func(path string) { 88 err := ioutil.WriteFile(filepath.Join(path, "some-file"), []byte("goodbye"), 0644) 89 c.Assert(err, gc.IsNil) 90 err = ioutil.WriteFile(filepath.Join(path, "a-symlink"), []byte("not any more!"), 0644) 91 c.Assert(err, gc.IsNil) 92 }) 93 err = s.deployer.Stage(info2, nil) 94 c.Assert(err, gc.IsNil) 95 checkCleanup(c, s.deployer) 96 err = s.deployer.Deploy() 97 c.Assert(err, gc.IsNil) 98 checkCleanup(c, s.deployer) 99 100 // Check content. 101 data, err := ioutil.ReadFile(filepath.Join(s.targetPath, "some-file")) 102 c.Assert(err, gc.IsNil) 103 c.Assert(string(data), gc.Equals, "goodbye") 104 data, err = ioutil.ReadFile(filepath.Join(s.targetPath, "a-symlink")) 105 c.Assert(err, gc.IsNil) 106 c.Assert(string(data), gc.Equals, "not any more!") 107 108 target := charm.NewGitDir(s.targetPath) 109 url, err := charm.ReadCharmURL(target) 110 c.Assert(err, gc.IsNil) 111 c.Assert(url, gc.DeepEquals, corecharm.MustParseURL("cs:s/c-2")) 112 lines, err := target.Log() 113 c.Assert(err, gc.IsNil) 114 c.Assert(lines, gc.HasLen, 5) 115 c.Assert(lines[0], gc.Matches, `[0-9a-f]{7} Upgraded charm to "cs:s/c-2".`) 116 } 117 118 func (s *DeployerSuite) TestConflictRevertResolve(c *gc.C) { 119 // Install. 120 info1 := s.bundles.Add(c, corecharm.MustParseURL("cs:s/c-1"), func(path string) { 121 err := ioutil.WriteFile(filepath.Join(path, "some-file"), []byte("hello"), 0644) 122 c.Assert(err, gc.IsNil) 123 }) 124 err := s.deployer.Stage(info1, nil) 125 c.Assert(err, gc.IsNil) 126 err = s.deployer.Deploy() 127 c.Assert(err, gc.IsNil) 128 129 // Mess up target. 130 err = ioutil.WriteFile(filepath.Join(s.targetPath, "some-file"), []byte("mu!"), 0644) 131 c.Assert(err, gc.IsNil) 132 133 // Upgrade. 134 info2 := s.bundles.Add(c, corecharm.MustParseURL("cs:s/c-2"), func(path string) { 135 err := ioutil.WriteFile(filepath.Join(path, "some-file"), []byte("goodbye"), 0644) 136 c.Assert(err, gc.IsNil) 137 }) 138 err = s.deployer.Stage(info2, nil) 139 c.Assert(err, gc.IsNil) 140 err = s.deployer.Deploy() 141 c.Assert(err, gc.Equals, charm.ErrConflict) 142 checkCleanup(c, s.deployer) 143 144 // Check state. 145 target := charm.NewGitDir(s.targetPath) 146 conflicted, err := target.Conflicted() 147 c.Assert(err, gc.IsNil) 148 c.Assert(conflicted, gc.Equals, true) 149 150 // Revert and check initial content. 151 err = s.deployer.NotifyRevert() 152 c.Assert(err, gc.IsNil) 153 data, err := ioutil.ReadFile(filepath.Join(s.targetPath, "some-file")) 154 c.Assert(err, gc.IsNil) 155 c.Assert(string(data), gc.Equals, "mu!") 156 conflicted, err = target.Conflicted() 157 c.Assert(err, gc.IsNil) 158 c.Assert(conflicted, gc.Equals, false) 159 160 // Try to upgrade again. 161 err = s.deployer.Deploy() 162 c.Assert(err, gc.Equals, charm.ErrConflict) 163 conflicted, err = target.Conflicted() 164 c.Assert(err, gc.IsNil) 165 c.Assert(conflicted, gc.Equals, true) 166 checkCleanup(c, s.deployer) 167 168 // And again. 169 err = s.deployer.Deploy() 170 c.Assert(err, gc.Equals, charm.ErrConflict) 171 conflicted, err = target.Conflicted() 172 c.Assert(err, gc.IsNil) 173 c.Assert(conflicted, gc.Equals, true) 174 checkCleanup(c, s.deployer) 175 176 // Manually resolve, and commit. 177 err = ioutil.WriteFile(filepath.Join(target.Path(), "some-file"), []byte("nu!"), 0644) 178 c.Assert(err, gc.IsNil) 179 err = s.deployer.NotifyResolved() 180 c.Assert(err, gc.IsNil) 181 conflicted, err = target.Conflicted() 182 c.Assert(err, gc.IsNil) 183 c.Assert(conflicted, gc.Equals, false) 184 185 // Try a final upgrade to the same charm and check it doesn't write anything 186 // except the upgrade log line. 187 err = s.deployer.Deploy() 188 c.Assert(err, gc.IsNil) 189 checkCleanup(c, s.deployer) 190 191 data, err = ioutil.ReadFile(filepath.Join(target.Path(), "some-file")) 192 c.Assert(err, gc.IsNil) 193 c.Assert(string(data), gc.Equals, "nu!") 194 conflicted, err = target.Conflicted() 195 c.Assert(err, gc.IsNil) 196 c.Assert(conflicted, gc.Equals, false) 197 lines, err := target.Log() 198 c.Assert(err, gc.IsNil) 199 c.Assert(lines[0], gc.Matches, `[0-9a-f]{7} Upgraded charm to "cs:s/c-2".`) 200 } 201 202 func bundle(c *gc.C, customize func(path string)) *corecharm.Bundle { 203 base := c.MkDir() 204 dirpath := testing.Charms.ClonedDirPath(base, "dummy") 205 customize(dirpath) 206 dir, err := corecharm.ReadDir(dirpath) 207 c.Assert(err, gc.IsNil) 208 bunpath := filepath.Join(base, "bundle") 209 file, err := os.Create(bunpath) 210 c.Assert(err, gc.IsNil) 211 defer file.Close() 212 err = dir.BundleTo(file) 213 c.Assert(err, gc.IsNil) 214 bundle, err := corecharm.ReadBundle(bunpath) 215 c.Assert(err, gc.IsNil) 216 return bundle 217 } 218 219 func checkCleanup(c *gc.C, d charm.Deployer) { 220 // Only one update dir should exist and be pointed to by the 'current' 221 // symlink since extra ones should have been cleaned up by 222 // cleanupOrphans. 223 deployerPath := charm.GitDeployerDataPath(d) 224 updateDirs, err := filepath.Glob(filepath.Join(deployerPath, "update-*")) 225 c.Assert(err, gc.IsNil) 226 c.Assert(updateDirs, gc.HasLen, 1) 227 deployerCurrent := charm.GitDeployerCurrent(d) 228 current, err := os.Readlink(deployerCurrent.Path()) 229 c.Assert(err, gc.IsNil) 230 c.Assert(updateDirs[0], gc.Equals, current) 231 232 // No install dirs should be left behind since the one created is 233 // renamed to the target path. 234 installDirs, err := filepath.Glob(filepath.Join(deployerPath, "install-*")) 235 c.Assert(err, gc.IsNil) 236 c.Assert(installDirs, gc.HasLen, 0) 237 } 238 239 type bundleReader struct { 240 bundles map[string]*corecharm.Bundle 241 } 242 243 // Read implements the BundleReader interface. 244 func (br *bundleReader) Read(info charm.BundleInfo, abort <-chan struct{}) (*corecharm.Bundle, error) { 245 bundle, ok := br.bundles[info.URL().String()] 246 if !ok { 247 return nil, fmt.Errorf("no such charm!") 248 } 249 return bundle, nil 250 } 251 252 func (br *bundleReader) Add(c *gc.C, url *corecharm.URL, customize func(path string)) charm.BundleInfo { 253 bundle := bundle(c, customize) 254 if br.bundles == nil { 255 br.bundles = map[string]*corecharm.Bundle{} 256 } 257 br.bundles[url.String()] = bundle 258 return &bundleInfo{nil, url} 259 } 260 261 type bundleInfo struct { 262 charm.BundleInfo 263 url *corecharm.URL 264 } 265 266 func (info *bundleInfo) URL() *corecharm.URL { 267 return info.url 268 }