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