func (s *StoreSuite) TestLockUpdatesExpires(c *C) { urlA := charm.MustParseURL("cs:oneiric/wordpress-a") urlB := charm.MustParseURL("cs:oneiric/wordpress-b") urls := []*charm.URL{urlA, urlB} // Initiate an update of B only to force a partial conflict. lock1, err := s.store.LockUpdates(urls[1:]) c.Assert(err, IsNil) // Hack time to force an expiration. locks := s.Session.DB("juju").C("locks") selector := bson.M{"_id": urlB.String()} update := bson.M{"time": bson.Now().Add(-store.UpdateTimeout - 10e9)} err = locks.Update(selector, update) c.Check(err, IsNil) // Works due to expiration of previous lock. lock2, err := s.store.LockUpdates(urls) c.Assert(err, IsNil) defer lock2.Unlock() // The expired lock was forcefully killed. Unlocking it must // not interfere with lock2 which is still alive. lock1.Unlock() // The above statement was a NOOP and lock2 is still in effect, // so attempting another lock must necessarily fail. lock3, err := s.store.LockUpdates(urls) c.Check(err, Equals, store.ErrUpdateConflict) c.Check(lock3, IsNil) }
func (s *StoreSuite) TestRedundantUpdate(c *C) { urlA := charm.MustParseURL("cs:oneiric/wordpress-a") urlB := charm.MustParseURL("cs:oneiric/wordpress-b") urls := []*charm.URL{urlA, urlB} pub, err := s.store.CharmPublisher(urls, "digest-0") c.Assert(err, IsNil) c.Assert(pub.Revision(), Equals, 0) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, IsNil) // All charms are already on digest-0. pub, err = s.store.CharmPublisher(urls, "digest-0") c.Assert(err, ErrorMatches, "charm is up-to-date") c.Assert(err, Equals, store.ErrRedundantUpdate) c.Assert(pub, IsNil) // Now add a second revision just for wordpress-b. pub, err = s.store.CharmPublisher(urls[1:], "digest-1") c.Assert(err, IsNil) c.Assert(pub.Revision(), Equals, 1) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, IsNil) // Same digest bumps revision because one of them was old. pub, err = s.store.CharmPublisher(urls, "digest-1") c.Assert(err, IsNil) c.Assert(pub.Revision(), Equals, 2) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, IsNil) }
func (s *DeployerSuite) TestInstall(c *C) { // Install. d := charm.NewDeployer(filepath.Join(c.MkDir(), "deployer")) bun := s.bundle(c, func(path string) { err := ioutil.WriteFile(filepath.Join(path, "some-file"), []byte("hello"), 0644) c.Assert(err, IsNil) }) err := d.Stage(bun, corecharm.MustParseURL("cs:s/c-1")) c.Assert(err, IsNil) target := charm.NewGitDir(filepath.Join(c.MkDir(), "target")) err = d.Deploy(target) c.Assert(err, IsNil) // Check content. data, err := ioutil.ReadFile(filepath.Join(target.Path(), "some-file")) c.Assert(err, IsNil) c.Assert(string(data), Equals, "hello") url, err := charm.ReadCharmURL(target) c.Assert(err, IsNil) c.Assert(url, DeepEquals, corecharm.MustParseURL("cs:s/c-1")) lines, err := target.Log() c.Assert(err, IsNil) c.Assert(lines, HasLen, 2) c.Assert(lines[0], Matches, `[0-9a-f]{7} Deployed charm "cs:s/c-1".`) c.Assert(lines[1], Matches, `[0-9a-f]{7} Imported charm "cs:s/c-1" from ".*".`) }
func (s *StoreSuite) TestBranchLocation(c *C) { charmURL := charm.MustParseURL("cs:series/name") location := s.store.BranchLocation(charmURL) c.Assert(location, Equals, "lp:charms/series/name") charmURL = charm.MustParseURL("cs:~user/series/name") location = s.store.BranchLocation(charmURL) c.Assert(location, Equals, "lp:~user/charms/series/name/trunk") }
func (s *LocalRepoSuite) TestMissingCharm(c *C) { _, err := s.repo.Latest(charm.MustParseURL("local:series/zebra")) c.Assert(err, ErrorMatches, `no charms found matching "local:series/zebra"`) _, err = s.repo.Get(charm.MustParseURL("local:series/zebra")) c.Assert(err, ErrorMatches, `no charms found matching "local:series/zebra"`) _, err = s.repo.Latest(charm.MustParseURL("local:badseries/zebra")) c.Assert(err, ErrorMatches, `no charms found matching "local:badseries/zebra"`) _, err = s.repo.Get(charm.MustParseURL("local:badseries/zebra")) c.Assert(err, ErrorMatches, `no charms found matching "local:badseries/zebra"`) }
func (s *StoreSuite) TestGetCacheImplicitRevision(c *C) { base := "cs:series/good" charmURL := charm.MustParseURL(base) revCharmURL := charm.MustParseURL(base + "-23") ch, err := s.store.Get(charmURL) c.Assert(err, IsNil) c.Assert(ch, NotNil) c.Assert(s.server.downloads, DeepEquals, []*charm.URL{revCharmURL}) s.assertCached(c, charmURL) s.assertCached(c, revCharmURL) }
func (s *StoreSuite) TestGetCacheImplicitRevision(c *C) { os.RemoveAll(s.cache) base := "cs:series/blah" curl := charm.MustParseURL(base) revCurl := charm.MustParseURL(base + "-23") ch, err := s.store.Get(curl) c.Assert(err, IsNil) c.Assert(ch, NotNil) c.Assert(s.server.downloads, DeepEquals, []*charm.URL{revCurl}) s.assertCached(c, curl) s.assertCached(c, revCurl) }
func (s *LocalRepoSuite) TestMissingRepo(c *C) { c.Assert(os.RemoveAll(s.repo.Path), IsNil) _, err := s.repo.Latest(charm.MustParseURL("local:series/zebra")) c.Assert(err, ErrorMatches, `no repository found at ".*"`) _, err = s.repo.Get(charm.MustParseURL("local:series/zebra")) c.Assert(err, ErrorMatches, `no repository found at ".*"`) c.Assert(ioutil.WriteFile(s.repo.Path, nil, 0666), IsNil) _, err = s.repo.Latest(charm.MustParseURL("local:series/zebra")) c.Assert(err, ErrorMatches, `no repository found at ".*"`) _, err = s.repo.Get(charm.MustParseURL("local:series/zebra")) c.Assert(err, ErrorMatches, `no repository found at ".*"`) }
func (s *StoreSuite) TestPublishCharmDistro(c *C) { branch := s.dummyBranch(c, "~joe/charms/oneiric/dummy/trunk") // The Distro call will look for bare /charms, first. testing.Server.Response(200, jsonType, []byte("{}")) // And then it picks up the tips. data := fmt.Sprintf(`[`+ `["file://%s", "rev1", ["oneiric", "precise"]],`+ `["file://%s", "%s", []],`+ `["file:///non-existent/~jeff/charms/precise/bad/trunk", "rev2", []],`+ `["file:///non-existent/~jeff/charms/precise/bad/skip-me", "rev3", []]`+ `]`, branch.path(), branch.path(), branch.digest()) testing.Server.Response(200, jsonType, []byte(data)) apiBase := lpad.APIBase(testing.Server.URL) err := store.PublishCharmsDistro(s.store, apiBase) // Should have a single failure from the trunk branch that doesn't // exist. The redundant update with the known digest should be // ignored, and skip-me isn't a supported branch name so it's // ignored as well. c.Assert(err, ErrorMatches, `1 branch\(es\) failed to be published`) berr := err.(store.PublishBranchErrors)[0] c.Assert(berr.URL, Equals, "file:///non-existent/~jeff/charms/precise/bad/trunk") c.Assert(berr.Err, ErrorMatches, "(?s).*bzr: ERROR: Not a branch.*") for _, url := range []string{"cs:oneiric/dummy", "cs:precise/dummy-0", "cs:~joe/oneiric/dummy-0"} { dummy, err := s.store.CharmInfo(charm.MustParseURL(url)) c.Assert(err, IsNil) c.Assert(dummy.Meta().Name, Equals, "dummy") } // The known digest should have been ignored, so revision is still at 0. _, err = s.store.CharmInfo(charm.MustParseURL("cs:~joe/oneiric/dummy-1")) c.Assert(err, Equals, store.ErrNotFound) // bare /charms lookup req := testing.Server.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/charms") // tips request req = testing.Server.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/charms") c.Assert(req.Form["ws.op"], DeepEquals, []string{"getBranchTips"}) c.Assert(req.Form["since"], IsNil) // Request must be signed by juju. c.Assert(req.Header.Get("Authorization"), Matches, `.*oauth_consumer_key="juju".*`) }
func (s *StoreSuite) TestGetBadCache(c *C) { base := "cs:series/blah" curl := charm.MustParseURL(base) revCurl := charm.MustParseURL(base + "-23") name := charm.Quote(revCurl.String()) + ".charm" err := ioutil.WriteFile(filepath.Join(s.cache, name), nil, 0666) c.Assert(err, IsNil) ch, err := s.store.Get(curl) c.Assert(err, IsNil) c.Assert(ch, NotNil) c.Assert(s.server.downloads, DeepEquals, []*charm.URL{revCurl}) s.assertCached(c, curl) s.assertCached(c, revCurl) }
func (s *StoreSuite) TestGetBadCache(c *C) { c.Assert(os.Mkdir(filepath.Join(charm.CacheDir, "cache"), 0777), IsNil) base := "cs:series/good" charmURL := charm.MustParseURL(base) revCharmURL := charm.MustParseURL(base + "-23") name := charm.Quote(revCharmURL.String()) + ".charm" err := ioutil.WriteFile(filepath.Join(charm.CacheDir, "cache", name), nil, 0666) c.Assert(err, IsNil) ch, err := s.store.Get(charmURL) c.Assert(err, IsNil) c.Assert(ch, NotNil) c.Assert(s.server.downloads, DeepEquals, []*charm.URL{revCharmURL}) s.assertCached(c, charmURL) s.assertCached(c, revCharmURL) }
func (s *DeploySuite) TestCharmDir(c *C) { coretesting.Charms.ClonedDirPath(s.SeriesPath, "dummy") err := runDeploy(c, "local:dummy") c.Assert(err, IsNil) curl := charm.MustParseURL("local:precise/dummy-1") s.AssertService(c, "dummy", curl, 1, 0) }
func (s *StoreSuite) TestCharmPublishError(c *C) { url := charm.MustParseURL("cs:oneiric/wordpress") urls := []*charm.URL{url} // Publish one successfully to bump the revision so we can // make sure it is being correctly set below. pub, err := s.store.CharmPublisher(urls, "one-digest") c.Assert(err, IsNil) c.Assert(pub.Revision(), Equals, 0) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, IsNil) pub, err = s.store.CharmPublisher(urls, "another-digest") c.Assert(err, IsNil) c.Assert(pub.Revision(), Equals, 1) err = pub.Publish(&FakeCharmDir{error: "beforeWrite"}) c.Assert(err, ErrorMatches, "beforeWrite") pub, err = s.store.CharmPublisher(urls, "another-digest") c.Assert(err, IsNil) c.Assert(pub.Revision(), Equals, 1) err = pub.Publish(&FakeCharmDir{error: "afterWrite"}) c.Assert(err, ErrorMatches, "afterWrite") // Still at the original charm revision that succeeded first. info, err := s.store.CharmInfo(url) c.Assert(err, IsNil) c.Assert(info.Revision(), Equals, 0) c.Assert(info.Digest(), Equals, "one-digest") }
func (s *DeploySuite) TestCharmBundle(c *C) { coretesting.Charms.BundlePath(s.SeriesPath, "dummy") err := runDeploy(c, "local:dummy", "some-service-name") c.Assert(err, IsNil) curl := charm.MustParseURL("local:precise/dummy-1") s.AssertService(c, "some-service-name", curl, 1, 0) }
func (s *DeployLocalSuite) SetUpTest(c *C) { s.JujuConnSuite.SetUpTest(c) curl := charm.MustParseURL("local:series/dummy") charm, err := s.Conn.PutCharm(curl, s.repo, false) c.Assert(err, IsNil) s.charm = charm }
func build() error { environ, err := environs.NewFromName("") if err != nil { return err } err = environs.Bootstrap(environ, true, nil) if err != nil { return err } conn, err := juju.NewConn(environ) if err != nil { return err } repo := &charm.LocalRepository{filepath.Dir(os.Args[0])} curl := charm.MustParseURL("local:precise/builddb") ch, err := conn.PutCharm(curl, repo, false) if err != nil { return err } service, err := conn.AddService("builddb", ch) if err != nil { return err } if err := service.SetExposed(); err != nil { return err } units, err := conn.AddUnits(service, 1) if err != nil { return err } log.Printf("builddb: Waiting for unit to reach %q status...", state.UnitStarted) unit := units[0] last, info, err := unit.Status() if err != nil { return err } logStatus(last, info) for last != state.UnitStarted { time.Sleep(2 * time.Second) if err := unit.Refresh(); err != nil { return err } status, info, err := unit.Status() if err != nil { return err } if status != last { logStatus(status, info) last = status } } addr, ok := unit.PublicAddress() if !ok { return fmt.Errorf("cannot retrieve files: build unit lacks a public-address") } log.Printf("builddb: Built files published at http://%s", addr) log.Printf("builddb: Remember to destroy the environment when you're done...") return nil }
func (s *StoreSuite) TestConflictingUpdate(c *C) { // This test checks that if for whatever reason the locking // safety-net fails, adding two charms in parallel still // results in a sane outcome. url := charm.MustParseURL("cs:oneiric/wordpress") urls := []*charm.URL{url} pub1, err := s.store.CharmPublisher(urls, "some-digest") c.Assert(err, IsNil) c.Assert(pub1.Revision(), Equals, 0) pub2, err := s.store.CharmPublisher(urls, "some-digest") c.Assert(err, IsNil) c.Assert(pub2.Revision(), Equals, 0) // The first publishing attempt should work. err = pub2.Publish(&FakeCharmDir{}) c.Assert(err, IsNil) // Attempting to finish the second attempt should break, // since it lost the race and the given revision is already // in place. err = pub1.Publish(&FakeCharmDir{}) c.Assert(err, Equals, store.ErrUpdateConflict) }
func (s *DeploySuite) TestNumUnits(c *C) { coretesting.Charms.BundlePath(s.SeriesPath, "dummy") err := runDeploy(c, "local:dummy", "-n", "13") c.Assert(err, IsNil) curl := charm.MustParseURL("local:precise/dummy-1") s.AssertService(c, "dummy", curl, 13, 0) }
func (s *FilterSuite) TestCharmErrorEvents(c *C) { f, err := newFilter(s.State, s.unit.Name()) c.Assert(err, IsNil) defer f.Stop() assertNoChange := func() { s.State.StartSync() select { case <-f.ConfigEvents(): c.Fatalf("unexpected config event") case <-time.After(coretesting.ShortWait): } } // Check setting an invalid charm URL does not send events. err = f.SetCharm(charm.MustParseURL("cs:missing/one-1")) c.Assert(err, Equals, tomb.ErrDying) assertNoChange() s.assertFilterDies(c, f) // Filter died after the error, so restart it. f, err = newFilter(s.State, s.unit.Name()) c.Assert(err, IsNil) defer f.Stop() // Check with a nil charm URL, again no changes. err = f.SetCharm(nil) c.Assert(err, Equals, tomb.ErrDying) assertNoChange() s.assertFilterDies(c, f) }
func (s *SSHSuite) TestSSHCommand(c *C) { m := s.makeMachines(3, c) ch := coretesting.Charms.Dir("series", "dummy") curl := charm.MustParseURL( fmt.Sprintf("local:series/%s-%d", ch.Meta().Name, ch.Revision()), ) bundleURL, err := url.Parse("http://bundles.example.com/dummy-1") c.Assert(err, IsNil) dummy, err := s.State.AddCharm(ch, curl, bundleURL, "dummy-1-sha256") c.Assert(err, IsNil) srv, err := s.State.AddService("mysql", dummy) c.Assert(err, IsNil) s.addUnit(srv, m[0], c) srv, err = s.State.AddService("mongodb", dummy) c.Assert(err, IsNil) s.addUnit(srv, m[1], c) s.addUnit(srv, m[2], c) for _, t := range sshTests { c.Logf("testing juju ssh %s", t.args) ctx := &cmd.Context{c.MkDir(), &bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{}} code := cmd.Main(&SSHCommand{}, ctx, t.args) c.Check(code, Equals, 0) c.Check(ctx.Stderr.(*bytes.Buffer).String(), Equals, "") c.Check(ctx.Stdout.(*bytes.Buffer).String(), Equals, t.result) } }
func (s *UnitSuite) TestUnitCharm(c *C) { preventUnitDestroyRemove(c, s.unit) curl, ok := s.unit.CharmURL() c.Assert(ok, Equals, false) c.Assert(curl, IsNil) err := s.unit.SetCharmURL(nil) c.Assert(err, ErrorMatches, "cannot set nil charm url") err = s.unit.SetCharmURL(charm.MustParseURL("cs:missing/one-1")) c.Assert(err, ErrorMatches, `unknown charm url "cs:missing/one-1"`) err = s.unit.SetCharmURL(s.charm.URL()) c.Assert(err, IsNil) curl, ok = s.unit.CharmURL() c.Assert(ok, Equals, true) c.Assert(curl, DeepEquals, s.charm.URL()) err = s.unit.Destroy() c.Assert(err, IsNil) err = s.unit.SetCharmURL(s.charm.URL()) c.Assert(err, IsNil) curl, ok = s.unit.CharmURL() c.Assert(ok, Equals, true) c.Assert(curl, DeepEquals, s.charm.URL()) err = s.unit.EnsureDead() c.Assert(err, IsNil) err = s.unit.SetCharmURL(s.charm.URL()) c.Assert(err, ErrorMatches, `unit "wordpress/0" is dead`) }
func (s *LocalRepoSuite) TestMultipleVersions(c *C) { curl := charm.MustParseURL("local:series/upgrade") s.addDir("upgrade1") rev, err := s.repo.Latest(curl) c.Assert(err, IsNil) c.Assert(rev, Equals, 1) ch, err := s.repo.Get(curl) c.Assert(err, IsNil) c.Assert(ch.Revision(), Equals, 1) s.addDir("upgrade2") rev, err = s.repo.Latest(curl) c.Assert(err, IsNil) c.Assert(rev, Equals, 2) ch, err = s.repo.Get(curl) c.Assert(err, IsNil) c.Assert(ch.Revision(), Equals, 2) revCurl := curl.WithRevision(1) rev, err = s.repo.Latest(revCurl) c.Assert(err, IsNil) c.Assert(rev, Equals, 2) ch, err = s.repo.Get(revCurl) c.Assert(err, IsNil) c.Assert(ch.Revision(), Equals, 1) badRevCurl := curl.WithRevision(33) rev, err = s.repo.Latest(badRevCurl) c.Assert(err, IsNil) c.Assert(rev, Equals, 2) ch, err = s.repo.Get(badRevCurl) c.Assert(err, ErrorMatches, `no charms found matching "local:series/upgrade-33"`) }
func (s *MockStore) ServeInfo(w http.ResponseWriter, r *http.Request) { r.ParseForm() response := map[string]*charm.InfoResponse{} for _, url := range r.Form["charms"] { cr := &charm.InfoResponse{} response[url] = cr charmURL := charm.MustParseURL(url) switch charmURL.Name { case "borken": cr.Errors = append(cr.Errors, "badness") case "unwise": cr.Warnings = append(cr.Warnings, "foolishness") fallthrough case "good": if charmURL.Revision == -1 { cr.Revision = 23 } else { cr.Revision = charmURL.Revision } cr.Sha256 = s.bundleSha256 default: cr.Errors = append(cr.Errors, "entry not found") } } data, err := json.Marshal(response) if err != nil { panic(err) } w.Header().Set("Content-Type", "application/json") _, err = w.Write(data) if err != nil { panic(err) } }
func (s *LocalRepoSuite) TestMultipleVersions(c *C) { charmURL := charm.MustParseURL("local:series/upgrade") s.addDir("upgrade1") rev, err := s.repo.Latest(charmURL) c.Assert(err, IsNil) c.Assert(rev, Equals, 1) ch, err := s.repo.Get(charmURL) c.Assert(err, IsNil) c.Assert(ch.Revision(), Equals, 1) s.addDir("upgrade2") rev, err = s.repo.Latest(charmURL) c.Assert(err, IsNil) c.Assert(rev, Equals, 2) ch, err = s.repo.Get(charmURL) c.Assert(err, IsNil) c.Assert(ch.Revision(), Equals, 2) revCharmURL := charmURL.WithRevision(1) rev, err = s.repo.Latest(revCharmURL) c.Assert(err, IsNil) c.Assert(rev, Equals, 2) ch, err = s.repo.Get(revCharmURL) c.Assert(err, IsNil) c.Assert(ch.Revision(), Equals, 1) badRevCharmURL := charmURL.WithRevision(33) rev, err = s.repo.Latest(badRevCharmURL) c.Assert(err, IsNil) c.Assert(rev, Equals, 2) _, err = s.repo.Get(badRevCharmURL) s.checkNotFoundErr(c, err, badRevCharmURL) }
func (s *DeploySuite) TestSubordinateCharm(c *C) { coretesting.Charms.BundlePath(s.SeriesPath, "logging") err := runDeploy(c, "local:logging") c.Assert(err, IsNil) curl := charm.MustParseURL("local:precise/logging-1") s.AssertService(c, "logging", curl, 0, 0) }
func (s *StoreSuite) TestInfo(c *C) { charmURL := charm.MustParseURL("cs:series/good") info, err := s.store.Info(charmURL) c.Assert(err, IsNil) c.Assert(info.Errors, IsNil) c.Assert(info.Revision, Equals, 23) }
func (s *AddUnitSuite) setupService(c *C) *charm.URL { testing.Charms.BundlePath(s.SeriesPath, "dummy") err := runDeploy(c, "local:dummy", "some-service-name") c.Assert(err, IsNil) curl := charm.MustParseURL("local:precise/dummy-1") s.AssertService(c, "some-service-name", curl, 1, 0) return curl }
func (s *StoreSuite) TestMissing(c *C) { charmURL := charm.MustParseURL("cs:series/missing") expect := `charm not found: cs:series/missing` _, err := s.store.Latest(charmURL) c.Assert(err, ErrorMatches, expect) _, err = s.store.Get(charmURL) c.Assert(err, ErrorMatches, expect) }
func (s *StoreSuite) TestEventWithDigest(c *C) { charmURL := charm.MustParseURL("cs:series/good") event, err := s.store.Event(charmURL, "the-digest") c.Assert(err, IsNil) c.Assert(event.Errors, IsNil) c.Assert(event.Revision, Equals, 23) c.Assert(event.Digest, Equals, "the-digest") }
func (s *StoreSuite) TestError(c *C) { charmURL := charm.MustParseURL("cs:series/borken") expect := `charm info errors for "cs:series/borken": badness` _, err := s.store.Latest(charmURL) c.Assert(err, ErrorMatches, expect) _, err = s.store.Get(charmURL) c.Assert(err, ErrorMatches, expect) }