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  }