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  }