github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/uniter/charm/manifest_deployer_test.go (about) 1 // Copyright 2012-2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package charm_test 5 6 import ( 7 "fmt" 8 "path/filepath" 9 10 jc "github.com/juju/testing/checkers" 11 ft "github.com/juju/testing/filetesting" 12 "github.com/juju/utils/set" 13 gc "gopkg.in/check.v1" 14 15 "github.com/juju/juju/testing" 16 "github.com/juju/juju/worker/uniter/charm" 17 ) 18 19 type ManifestDeployerSuite struct { 20 testing.BaseSuite 21 bundles *bundleReader 22 targetPath string 23 deployer charm.Deployer 24 } 25 26 var _ = gc.Suite(&ManifestDeployerSuite{}) 27 28 // because we generally use real charm bundles for testing, and charm bundling 29 // sets every file mode to 0755 or 0644, all our input data uses those modes as 30 // well. 31 32 func (s *ManifestDeployerSuite) SetUpTest(c *gc.C) { 33 s.BaseSuite.SetUpTest(c) 34 s.bundles = &bundleReader{} 35 s.targetPath = filepath.Join(c.MkDir(), "target") 36 deployerPath := filepath.Join(c.MkDir(), "deployer") 37 s.deployer = charm.NewManifestDeployer(s.targetPath, deployerPath, s.bundles) 38 } 39 40 func (s *ManifestDeployerSuite) addMockCharm(c *gc.C, revision int, bundle charm.Bundle) charm.BundleInfo { 41 return s.bundles.AddBundle(c, charmURL(revision), bundle) 42 } 43 44 func (s *ManifestDeployerSuite) addCharm(c *gc.C, revision int, content ...ft.Entry) charm.BundleInfo { 45 return s.bundles.AddCustomBundle(c, charmURL(revision), func(path string) { 46 ft.Entries(content).Create(c, path) 47 }) 48 } 49 50 func (s *ManifestDeployerSuite) deployCharm(c *gc.C, revision int, content ...ft.Entry) charm.BundleInfo { 51 info := s.addCharm(c, revision, content...) 52 err := s.deployer.Stage(info, nil) 53 c.Assert(err, jc.ErrorIsNil) 54 err = s.deployer.Deploy() 55 c.Assert(err, jc.ErrorIsNil) 56 s.assertCharm(c, revision, content...) 57 return info 58 } 59 60 func (s *ManifestDeployerSuite) assertCharm(c *gc.C, revision int, content ...ft.Entry) { 61 url, err := charm.ReadCharmURL(filepath.Join(s.targetPath, ".juju-charm")) 62 c.Assert(err, jc.ErrorIsNil) 63 c.Assert(url, gc.DeepEquals, charmURL(revision)) 64 ft.Entries(content).Check(c, s.targetPath) 65 } 66 67 func (s *ManifestDeployerSuite) TestAbortStageWhenClosed(c *gc.C) { 68 info := s.addMockCharm(c, 1, mockBundle{}) 69 abort := make(chan struct{}) 70 errors := make(chan error) 71 s.bundles.EnableWaitForAbort() 72 go func() { 73 errors <- s.deployer.Stage(info, abort) 74 }() 75 close(abort) 76 err := <-errors 77 c.Assert(err, gc.ErrorMatches, "charm read aborted") 78 } 79 80 func (s *ManifestDeployerSuite) TestDontAbortStageWhenNotClosed(c *gc.C) { 81 info := s.addMockCharm(c, 1, mockBundle{}) 82 abort := make(chan struct{}) 83 errors := make(chan error) 84 stopWaiting := s.bundles.EnableWaitForAbort() 85 go func() { 86 errors <- s.deployer.Stage(info, abort) 87 }() 88 close(stopWaiting) 89 err := <-errors 90 c.Assert(err, jc.ErrorIsNil) 91 } 92 93 func (s *ManifestDeployerSuite) TestDeployWithoutStage(c *gc.C) { 94 err := s.deployer.Deploy() 95 c.Assert(err, gc.ErrorMatches, "charm deployment failed: no charm set") 96 } 97 98 func (s *ManifestDeployerSuite) TestInstall(c *gc.C) { 99 s.deployCharm(c, 1, 100 ft.File{"some-file", "hello", 0644}, 101 ft.Dir{"some-dir", 0755}, 102 ft.Symlink{"some-dir/some-link", "../some-file"}, 103 ) 104 } 105 106 func (s *ManifestDeployerSuite) TestUpgradeOverwrite(c *gc.C) { 107 s.deployCharm(c, 1, 108 ft.File{"some-file", "hello", 0644}, 109 ft.Dir{"some-dir", 0755}, 110 ft.File{"some-dir/another-file", "to be removed", 0755}, 111 ft.Dir{"another-dir", 0755}, 112 ft.Symlink{"another-dir/some-link", "../some-file"}, 113 ) 114 // Replace each of file, dir, and symlink with a different entry; in 115 // the case of dir, checking that contained files are also removed. 116 s.deployCharm(c, 2, 117 ft.Symlink{"some-file", "no-longer-a-file"}, 118 ft.File{"some-dir", "no-longer-a-dir", 0644}, 119 ft.Dir{"another-dir", 0755}, 120 ft.Dir{"another-dir/some-link", 0755}, 121 ) 122 } 123 124 func (s *ManifestDeployerSuite) TestUpgradePreserveUserFiles(c *gc.C) { 125 originalCharmContent := ft.Entries{ 126 ft.File{"charm-file", "to-be-removed", 0644}, 127 ft.Dir{"charm-dir", 0755}, 128 } 129 s.deployCharm(c, 1, originalCharmContent...) 130 131 // Add user files we expect to keep to the target dir. 132 preserveUserContent := ft.Entries{ 133 ft.File{"user-file", "to-be-preserved", 0644}, 134 ft.Dir{"user-dir", 0755}, 135 ft.File{"user-dir/user-file", "also-preserved", 0644}, 136 }.Create(c, s.targetPath) 137 138 // Add some user files we expect to be removed. 139 removeUserContent := ft.Entries{ 140 ft.File{"charm-dir/user-file", "whoops-removed", 0755}, 141 }.Create(c, s.targetPath) 142 143 // Add some user files we expect to be replaced. 144 ft.Entries{ 145 ft.File{"replace-file", "original", 0644}, 146 ft.Dir{"replace-dir", 0755}, 147 ft.Symlink{"replace-symlink", "replace-file"}, 148 }.Create(c, s.targetPath) 149 150 // Deploy an upgrade; all new content overwrites the old... 151 s.deployCharm(c, 2, 152 ft.File{"replace-file", "updated", 0644}, 153 ft.Dir{"replace-dir", 0755}, 154 ft.Symlink{"replace-symlink", "replace-dir"}, 155 ) 156 157 // ...and other files are preserved or removed according to 158 // source and location. 159 preserveUserContent.Check(c, s.targetPath) 160 removeUserContent.AsRemoveds().Check(c, s.targetPath) 161 originalCharmContent.AsRemoveds().Check(c, s.targetPath) 162 } 163 164 func (s *ManifestDeployerSuite) TestUpgradeConflictResolveRetrySameCharm(c *gc.C) { 165 // Create base install. 166 s.deployCharm(c, 1, 167 ft.File{"shared-file", "old", 0755}, 168 ft.File{"old-file", "old", 0644}, 169 ) 170 171 // Create mock upgrade charm that can (claim to) fail to expand... 172 failDeploy := true 173 upgradeContent := ft.Entries{ 174 ft.File{"shared-file", "new", 0755}, 175 ft.File{"new-file", "new", 0644}, 176 } 177 mockCharm := mockBundle{ 178 paths: set.NewStrings(upgradeContent.Paths()...), 179 expand: func(targetPath string) error { 180 upgradeContent.Create(c, targetPath) 181 if failDeploy { 182 return fmt.Errorf("oh noes") 183 } 184 return nil 185 }, 186 } 187 info := s.addMockCharm(c, 2, mockCharm) 188 err := s.deployer.Stage(info, nil) 189 c.Assert(err, jc.ErrorIsNil) 190 191 // ...and see it fail to expand. We're not too bothered about the actual 192 // content of the target dir at this stage, but we do want to check it's 193 // still marked as based on the original charm... 194 err = s.deployer.Deploy() 195 c.Assert(err, gc.Equals, charm.ErrConflict) 196 s.assertCharm(c, 1) 197 198 // ...and we want to verify that if we "fix the errors" and redeploy the 199 // same charm... 200 failDeploy = false 201 err = s.deployer.NotifyResolved() 202 c.Assert(err, jc.ErrorIsNil) 203 err = s.deployer.Deploy() 204 c.Assert(err, jc.ErrorIsNil) 205 206 // ...we end up with the right stuff in play. 207 s.assertCharm(c, 2, upgradeContent...) 208 ft.Removed{"old-file"}.Check(c, s.targetPath) 209 } 210 211 func (s *ManifestDeployerSuite) TestUpgradeConflictRevertRetryDifferentCharm(c *gc.C) { 212 // Create base install and add a user file. 213 s.deployCharm(c, 1, 214 ft.File{"shared-file", "old", 0755}, 215 ft.File{"old-file", "old", 0644}, 216 ) 217 userFile := ft.File{"user-file", "user", 0644}.Create(c, s.targetPath) 218 219 // Create a charm upgrade that never works (but still writes a bunch of files), 220 // and deploy it. 221 badUpgradeContent := ft.Entries{ 222 ft.File{"shared-file", "bad", 0644}, 223 ft.File{"bad-file", "bad", 0644}, 224 } 225 badCharm := mockBundle{ 226 paths: set.NewStrings(badUpgradeContent.Paths()...), 227 expand: func(targetPath string) error { 228 badUpgradeContent.Create(c, targetPath) 229 return fmt.Errorf("oh noes") 230 }, 231 } 232 badInfo := s.addMockCharm(c, 2, badCharm) 233 err := s.deployer.Stage(badInfo, nil) 234 c.Assert(err, jc.ErrorIsNil) 235 err = s.deployer.Deploy() 236 c.Assert(err, gc.Equals, charm.ErrConflict) 237 238 // Notify the Deployer that it'll be expected to revert the changes from 239 // the last attempt. 240 err = s.deployer.NotifyRevert() 241 c.Assert(err, jc.ErrorIsNil) 242 243 // Create a charm upgrade that creates a bunch of different files, without 244 // error, and deploy it; check user files are preserved, and nothing from 245 // charm 1 or 2 is. 246 s.deployCharm(c, 3, 247 ft.File{"shared-file", "new", 0755}, 248 ft.File{"new-file", "new", 0644}, 249 ) 250 userFile.Check(c, s.targetPath) 251 ft.Removed{"old-file"}.Check(c, s.targetPath) 252 ft.Removed{"bad-file"}.Check(c, s.targetPath) 253 }