Пример #1
0
// gitManifest returns every file path in the supplied directory, *except* for:
//    * paths below .git, because we don't need to track every file: we just
//      want them all gone
//    * charmURLPath, because we don't ever want to remove that: that's how
//      the manifestDeployer keeps track of what version it's upgrading from.
// All paths are slash-separated, to match the bundle manifest format.
func gitManifest(linkPath string) (set.Strings, error) {
	dirPath, err := os.Readlink(linkPath)
	if err != nil {
		return set.NewStrings(), err
	}
	manifest := set.NewStrings()
	err = filepath.Walk(dirPath, func(path string, fileInfo os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		relPath, err := filepath.Rel(dirPath, path)
		if err != nil {
			return err
		}
		switch relPath {
		case ".", charmURLPath:
			return nil
		case ".git":
			err = filepath.SkipDir
		}
		manifest.Add(filepath.ToSlash(relPath))
		return err
	})
	if err != nil {
		return set.NewStrings(), err
	}
	return manifest, nil
}
Пример #2
0
// RegisterConflicts is defined on Validator.
func (v *validator) RegisterConflicts(reds, blues []string) {
	for _, red := range reds {
		v.conflicts[red] = set.NewStrings(blues...)
	}
	for _, blue := range blues {
		v.conflicts[blue] = set.NewStrings(reds...)
	}
}
Пример #3
0
// loadManifest loads, from dataPath, the manifest for the charm identified by the
// identity file at the supplied path within the charm directory.
func (d *manifestDeployer) loadManifest(urlFilePath string) (*charm.URL, set.Strings, error) {
	url, err := ReadCharmURL(d.CharmPath(urlFilePath))
	if err != nil {
		return nil, set.NewStrings(), err
	}
	name := charm.Quote(url.String())
	path := filepath.Join(d.DataPath(manifestsDataPath), name)
	manifest := []string{}
	err = utils.ReadYaml(path, &manifest)
	if os.IsNotExist(err) {
		logger.Warningf("manifest not found at %q: files from charm %q may be left unremoved", path, url)
		err = nil
	}
	return url, set.NewStrings(manifest...), err
}
Пример #4
0
func (s *BundleSuite) TestManifest(c *gc.C) {
	bundle, err := charm.ReadBundle(s.bundlePath)
	c.Assert(err, gc.IsNil)
	manifest, err := bundle.Manifest()
	c.Assert(err, gc.IsNil)
	c.Assert(manifest, jc.DeepEquals, set.NewStrings(dummyManifest...))
}
Пример #5
0
// SetUp is defined on the worker.NotifyWatchHandler interface.
func (kw *keyupdaterWorker) SetUp() (watcher.NotifyWatcher, error) {
	// Record the keys Juju knows about.
	jujuKeys, err := kw.st.AuthorisedKeys(kw.tag)
	if err != nil {
		return nil, errors.LoggedErrorf(logger, "reading Juju ssh keys for %q: %v", kw.tag, err)
	}
	kw.jujuKeys = set.NewStrings(jujuKeys...)

	// Read the keys currently in ~/.ssh/authorised_keys.
	sshKeys, err := ssh.ListKeys(SSHUser, ssh.FullKeys)
	if err != nil {
		return nil, errors.LoggedErrorf(logger, "reading ssh authorized keys for %q: %v", kw.tag, err)
	}
	// Record any keys not added by Juju.
	for _, key := range sshKeys {
		_, comment, err := ssh.KeyFingerprint(key)
		// Also record keys which we cannot parse.
		if err != nil || !strings.HasPrefix(comment, ssh.JujuCommentPrefix) {
			kw.nonJujuKeys = append(kw.nonJujuKeys, key)
		}
	}
	// Write out the ssh authorised keys file to match the current state of the world.
	if err := kw.writeSSHKeys(jujuKeys); err != nil {
		return nil, errors.LoggedErrorf(logger, "adding current Juju keys to ssh authorised keys: %v", err)
	}

	w, err := kw.st.WatchAuthorisedKeys(kw.tag)
	if err != nil {
		return nil, errors.LoggedErrorf(logger, "starting key updater worker: %v", err)
	}
	logger.Infof("%q key updater worker started", kw.tag)
	return w, nil
}
Пример #6
0
// publicKeyFiles returns the filenames of public SSH keys
// in the specified directory (all the files ending with .pub).
func publicKeyFiles(clientKeysDir string) ([]string, error) {
	if clientKeysDir == "" {
		return nil, nil
	}
	var keys []string
	dir, err := os.Open(clientKeysDir)
	if err != nil {
		return nil, err
	}
	names, err := dir.Readdirnames(-1)
	dir.Close()
	if err != nil {
		return nil, err
	}
	candidates := set.NewStrings(names...)
	for _, name := range names {
		if !strings.HasSuffix(name, PublicKeySuffix) {
			continue
		}
		// If the private key filename also exists, add the file.
		priv := name[:len(name)-len(PublicKeySuffix)]
		if candidates.Contains(priv) {
			keys = append(keys, filepath.Join(dir.Name(), name))
		}
	}
	return keys, nil
}
Пример #7
0
func (s *BundleSuite) TestManifestSymlink(c *gc.C) {
	srcPath := testing.Charms.ClonedDirPath(c.MkDir(), "dummy")
	if err := os.Symlink("../target", filepath.Join(srcPath, "hooks/symlink")); err != nil {
		c.Skip("cannot symlink")
	}
	expected := append([]string{"hooks/symlink"}, dummyManifest...)

	bundle := bundleDir(c, srcPath)
	manifest, err := bundle.Manifest()
	c.Assert(err, gc.IsNil)
	c.Assert(manifest, gc.DeepEquals, set.NewStrings(expected...))
}
Пример #8
0
// SeriesToUpload returns the supplied series with duplicates removed if
// non-empty; otherwise it returns a default list of series we should
// probably upload, based on cfg.
func SeriesToUpload(cfg *config.Config, series []string) []string {
	unique := set.NewStrings(series...)
	if unique.IsEmpty() {
		unique.Add(version.Current.Series)
		for _, toolsSeries := range ToolsLtsSeries {
			unique.Add(toolsSeries)
		}
		if series, ok := cfg.DefaultSeries(); ok {
			unique.Add(series)
		}
	}
	return unique.Values()
}
Пример #9
0
func uploadFakeTools(stor storage.Storage) error {
	toolsSeries := set.NewStrings(bootstrap.ToolsLtsSeries...)
	toolsSeries.Add(version.Current.Series)
	var versions []version.Binary
	for _, series := range toolsSeries.Values() {
		vers := version.Current
		vers.Series = series
		versions = append(versions, vers)
	}
	if _, err := UploadFakeToolsVersions(stor, versions...); err != nil {
		return err
	}
	return nil
}
Пример #10
0
func (s *BundleSuite) TestManifestNoRevision(c *gc.C) {
	bundle, err := charm.ReadBundle(s.bundlePath)
	c.Assert(err, gc.IsNil)
	dirPath := c.MkDir()
	err = bundle.ExpandTo(dirPath)
	c.Assert(err, gc.IsNil)
	err = os.Remove(filepath.Join(dirPath, "revision"))
	c.Assert(err, gc.IsNil)

	bundle = extBundleDir(c, dirPath)
	manifest, err := bundle.Manifest()
	c.Assert(err, gc.IsNil)
	c.Assert(manifest, gc.DeepEquals, set.NewStrings(dummyManifest...))
}
Пример #11
0
// checkStopSomeInstances checks that instancesToStop are stopped while instancesToKeep are not.
func (s *CommonProvisionerSuite) checkStopSomeInstances(c *gc.C,
	instancesToStop []instance.Instance, instancesToKeep []instance.Instance) {

	s.BackingState.StartSync()
	instanceIdsToStop := set.NewStrings()
	for _, instance := range instancesToStop {
		instanceIdsToStop.Add(string(instance.Id()))
	}
	instanceIdsToKeep := set.NewStrings()
	for _, instance := range instancesToKeep {
		instanceIdsToKeep.Add(string(instance.Id()))
	}
	// Continue checking for stop instance calls until all the instances we
	// are waiting on to finish, actually finish, or we time out.
	for !instanceIdsToStop.IsEmpty() {
		select {
		case o := <-s.op:
			switch o := o.(type) {
			case dummy.OpStopInstances:
				for _, id := range o.Ids {
					instId := string(id)
					instanceIdsToStop.Remove(instId)
					if instanceIdsToKeep.Contains(instId) {
						c.Errorf("provisioner unexpectedly stopped instance %s", instId)
					}
				}
			default:
				c.Fatalf("unexpected operation %#v", o)
				return
			}
		case <-time.After(2 * time.Second):
			c.Fatalf("provisioner did not stop an instance")
			return
		}
	}
}
Пример #12
0
func (s *ManifestDeployerSuite) TestUpgradeConflictResolveRetrySameCharm(c *gc.C) {
	// Create base install.
	s.deployCharm(c, 1,
		ft.File{"shared-file", "old", 0755},
		ft.File{"old-file", "old", 0644},
	)

	// Create mock upgrade charm that can (claim to) fail to expand...
	failDeploy := true
	upgradeContent := ft.Entries{
		ft.File{"shared-file", "new", 0755},
		ft.File{"new-file", "new", 0644},
	}
	mockCharm := mockBundle{
		paths: set.NewStrings(upgradeContent.Paths()...),
		expand: func(targetPath string) error {
			upgradeContent.Create(c, targetPath)
			if failDeploy {
				return fmt.Errorf("oh noes")
			}
			return nil
		},
	}
	info := s.addMockCharm(c, 2, mockCharm)
	err := s.deployer.Stage(info, nil)
	c.Assert(err, gc.IsNil)

	// ...and see it fail to expand. We're not too bothered about the actual
	// content of the target dir at this stage, but we do want to check it's
	// still marked as based on the original charm...
	err = s.deployer.Deploy()
	c.Assert(err, gc.Equals, charm.ErrConflict)
	s.assertCharm(c, 1)

	// ...and we want to verify that if we "fix the errors" and redeploy the
	// same charm...
	failDeploy = false
	err = s.deployer.NotifyResolved()
	c.Assert(err, gc.IsNil)
	err = s.deployer.Deploy()
	c.Assert(err, gc.IsNil)

	// ...we end up with the right stuff in play.
	s.assertCharm(c, 2, upgradeContent...)
	ft.Removed{"old-file"}.Check(c, s.targetPath)
}
Пример #13
0
func (s *ManifestDeployerSuite) TestUpgradeConflictRevertRetryDifferentCharm(c *gc.C) {
	// Create base install and add a user file.
	s.deployCharm(c, 1,
		ft.File{"shared-file", "old", 0755},
		ft.File{"old-file", "old", 0644},
	)
	userFile := ft.File{"user-file", "user", 0644}.Create(c, s.targetPath)

	// Create a charm upgrade that never works (but still writes a bunch of files),
	// and deploy it.
	badUpgradeContent := ft.Entries{
		ft.File{"shared-file", "bad", 0644},
		ft.File{"bad-file", "bad", 0644},
	}
	badCharm := mockBundle{
		paths: set.NewStrings(badUpgradeContent.Paths()...),
		expand: func(targetPath string) error {
			badUpgradeContent.Create(c, targetPath)
			return fmt.Errorf("oh noes")
		},
	}
	badInfo := s.addMockCharm(c, 2, badCharm)
	err := s.deployer.Stage(badInfo, nil)
	c.Assert(err, gc.IsNil)
	err = s.deployer.Deploy()
	c.Assert(err, gc.Equals, charm.ErrConflict)

	// Notify the Deployer that it'll be expected to revert the changes from
	// the last attempt.
	err = s.deployer.NotifyRevert()
	c.Assert(err, gc.IsNil)

	// Create a charm upgrade that creates a bunch of different files, without
	// error, and deploy it; check user files are preserved, and nothing from
	// charm 1 or 2 is.
	s.deployCharm(c, 3,
		ft.File{"shared-file", "new", 0755},
		ft.File{"new-file", "new", 0644},
	)
	userFile.Check(c, s.targetPath)
	ft.Removed{"old-file"}.Check(c, s.targetPath)
	ft.Removed{"bad-file"}.Check(c, s.targetPath)
}
Пример #14
0
// checkConflicts returns an error if the constraints Value contains conflicting attributes.
func (v *validator) checkConflicts(cons Value) error {
	attrValues := cons.attributesWithValues()
	attrSet := set.NewStrings()
	for attrTag := range attrValues {
		attrSet.Add(attrTag)
	}
	for _, attrTag := range attrSet.SortedValues() {
		conflicts, ok := v.conflicts[attrTag]
		if !ok {
			continue
		}
		for _, conflict := range conflicts.SortedValues() {
			if attrSet.Contains(conflict) {
				return fmt.Errorf("ambiguous constraints: %q overlaps with %q", attrTag, conflict)
			}
		}
	}
	return nil
}
Пример #15
0
// Handle is defined on the worker.NotifyWatchHandler interface.
func (kw *keyupdaterWorker) Handle() error {
	// Read the keys that Juju has.
	newKeys, err := kw.st.AuthorisedKeys(kw.tag)
	if err != nil {
		return errors.LoggedErrorf(logger, "reading Juju ssh keys for %q: %v", kw.tag, err)
	}
	// Figure out if any keys have been added or deleted.
	newJujuKeys := set.NewStrings(newKeys...)
	deleted := kw.jujuKeys.Difference(newJujuKeys)
	added := newJujuKeys.Difference(kw.jujuKeys)
	if added.Size() > 0 || deleted.Size() > 0 {
		logger.Debugf("adding ssh keys to authorised keys: %v", added)
		logger.Debugf("deleting ssh keys from authorised keys: %v", deleted)
		if err = kw.writeSSHKeys(newKeys); err != nil {
			return errors.LoggedErrorf(logger, "updating ssh keys: %v", err)
		}
	}
	kw.jujuKeys = newJujuKeys
	return nil
}
Пример #16
0
func (b mockBundle) Manifest() (set.Strings, error) {
	return set.NewStrings(b.paths.Values()...), nil
}
Пример #17
0
// RegisterUnsupported is defined on Validator.
func (v *validator) RegisterUnsupported(unsupported []string) {
	v.unsupported = set.NewStrings(unsupported...)
}
Пример #18
0
	"github.com/juju/core/testing"
	"github.com/juju/core/utils/set"
	"github.com/juju/core/worker/uniter/jujuc"
)

type PortsSuite struct {
	ContextSuite
}

var _ = gc.Suite(&PortsSuite{})

var portsTests = []struct {
	cmd    []string
	expect set.Strings
}{
	{[]string{"open-port", "80"}, set.NewStrings("80/tcp")},
	{[]string{"open-port", "99/tcp"}, set.NewStrings("80/tcp", "99/tcp")},
	{[]string{"close-port", "80/TCP"}, set.NewStrings("99/tcp")},
	{[]string{"open-port", "123/udp"}, set.NewStrings("99/tcp", "123/udp")},
	{[]string{"close-port", "9999/UDP"}, set.NewStrings("99/tcp", "123/udp")},
}

func (s *PortsSuite) TestOpenClose(c *gc.C) {
	hctx := s.GetHookContext(c, -1, "")
	for _, t := range portsTests {
		com, err := jujuc.NewCommand(hctx, t.cmd[0])
		c.Assert(err, gc.IsNil)
		ctx := testing.Context(c)
		code := cmd.Main(com, ctx, t.cmd[1:])
		c.Assert(code, gc.Equals, 0)
		c.Assert(bufferString(ctx.Stdout), gc.Equals, "")