// 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 }
// 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...) } }
// 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 }
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...)) }
// 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 }
// 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 }
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...)) }
// 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() }
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 }
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...)) }
// 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 } } }
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) }
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) }
// 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 }
// 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 }
func (b mockBundle) Manifest() (set.Strings, error) { return set.NewStrings(b.paths.Values()...), nil }
// RegisterUnsupported is defined on Validator. func (v *validator) RegisterUnsupported(unsupported []string) { v.unsupported = set.NewStrings(unsupported...) }
"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, "")