func (s *GitDeployerSuite) TestInstall(c *gc.C) { // Prepare. info := s.bundles.AddCustomBundle(c, corecharm.MustParseURL("cs:s/c-1"), func(path string) { err := ioutil.WriteFile(filepath.Join(path, "some-file"), []byte("hello"), 0644) c.Assert(err, gc.IsNil) }) err := s.deployer.Stage(info, nil) c.Assert(err, gc.IsNil) checkCleanup(c, s.deployer) // Install. err = s.deployer.Deploy() c.Assert(err, gc.IsNil) checkCleanup(c, s.deployer) // Check content. data, err := ioutil.ReadFile(filepath.Join(s.targetPath, "some-file")) c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, "hello") target := charm.NewGitDir(s.targetPath) url, err := target.ReadCharmURL() c.Assert(err, gc.IsNil) c.Assert(url, gc.DeepEquals, corecharm.MustParseURL("cs:s/c-1")) lines, err := target.Log() c.Assert(err, gc.IsNil) c.Assert(lines, gc.HasLen, 2) c.Assert(lines[0], gc.Matches, `[0-9a-f]{7} Deployed charm "cs:s/c-1"\.`) c.Assert(lines[1], gc.Matches, `[0-9a-f]{7} Imported charm "cs:s/c-1"\.`) }
func (s *StoreSuite) TestLockUpdatesExpires(c *gc.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, gc.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, gc.IsNil) // Works due to expiration of previous lock. lock2, err := s.store.LockUpdates(urls) c.Assert(err, gc.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, gc.Equals, store.ErrUpdateConflict) c.Check(lock3, gc.IsNil) }
func (s *StoreSuite) TestRedundantUpdate(c *gc.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, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 0) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) // All charms are already on digest-0. pub, err = s.store.CharmPublisher(urls, "digest-0") c.Assert(err, gc.ErrorMatches, "charm is up-to-date") c.Assert(err, gc.Equals, store.ErrRedundantUpdate) c.Assert(pub, gc.IsNil) // Now add a second revision just for wordpress-b. pub, err = s.store.CharmPublisher(urls[1:], "digest-1") c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 1) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) // Same digest bumps revision because one of them was old. pub, err = s.store.CharmPublisher(urls, "digest-1") c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 2) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) }
func (s *StoreSuite) TestCharmPublishError(c *gc.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, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 0) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) pub, err = s.store.CharmPublisher(urls, "another-digest") c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 1) err = pub.Publish(&FakeCharmDir{error: "beforeWrite"}) c.Assert(err, gc.ErrorMatches, "beforeWrite") pub, err = s.store.CharmPublisher(urls, "another-digest") c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 1) err = pub.Publish(&FakeCharmDir{error: "afterWrite"}) c.Assert(err, gc.ErrorMatches, "afterWrite") // Still at the original charm revision that succeeded first. info, err := s.store.CharmInfo(url) c.Assert(err, gc.IsNil) c.Assert(info.Revision(), gc.Equals, 0) c.Assert(info.Digest(), gc.Equals, "one-digest") }
func (s *FilterSuite) TestCharmErrorEvents(c *gc.C) { f, err := newFilter(s.uniter, s.unit.Tag()) c.Assert(err, gc.IsNil) defer f.Stop() // no AssertStop, we test for an error below assertNoChange := func() { s.BackingState.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, gc.Equals, tomb.ErrDying) assertNoChange() s.assertFilterDies(c, f) // Filter died after the error, so restart it. f, err = newFilter(s.uniter, s.unit.Tag()) c.Assert(err, gc.IsNil) defer f.Stop() // no AssertStop, we test for an error below // Check with a nil charm URL, again no changes. err = f.SetCharm(nil) c.Assert(err, gc.Equals, tomb.ErrDying) assertNoChange() s.assertFilterDies(c, f) }
func (s *StoreSuite) TestConflictingUpdate(c *gc.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, gc.IsNil) c.Assert(pub1.Revision(), gc.Equals, 0) pub2, err := s.store.CharmPublisher(urls, "some-digest") c.Assert(err, gc.IsNil) c.Assert(pub2.Revision(), gc.Equals, 0) // The first publishing attempt should work. err = pub2.Publish(&FakeCharmDir{}) c.Assert(err, gc.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, gc.Equals, store.ErrUpdateConflict) }
func (s *SSHSuite) TestSSHCommand(c *gc.C) { m := s.makeMachines(3, c, true) ch := coretesting.Charms.Dir("dummy") curl := charm.MustParseURL( fmt.Sprintf("local:quantal/%s-%d", ch.Meta().Name, ch.Revision()), ) bundleURL, err := url.Parse("http://bundles.testing.invalid/dummy-1") c.Assert(err, gc.IsNil) dummy, err := s.State.AddCharm(ch, curl, bundleURL, "dummy-1-sha256") c.Assert(err, gc.IsNil) srv := s.AddTestingService(c, "mysql", dummy) s.addUnit(srv, m[0], c) srv = s.AddTestingService(c, "mongodb", dummy) s.addUnit(srv, m[1], c) s.addUnit(srv, m[2], c) for i, t := range sshTests { c.Logf("test %d: %s -> %s\n", i, t.about, t.args) ctx := coretesting.Context(c) jujucmd := cmd.NewSuperCommand(cmd.SuperCommandParams{}) jujucmd.Register(envcmd.Wrap(&SSHCommand{})) code := cmd.Main(jujucmd, ctx, t.args) c.Check(code, gc.Equals, 0) c.Check(ctx.Stderr.(*bytes.Buffer).String(), gc.Equals, "") c.Check(ctx.Stdout.(*bytes.Buffer).String(), gc.Equals, t.result) } }
func (s *DeploySuite) TestCharmDir(c *gc.C) { coretesting.Charms.ClonedDirPath(s.SeriesPath, "dummy") err := runDeploy(c, "local:dummy") c.Assert(err, gc.IsNil) curl := charm.MustParseURL("local:precise/dummy-1") s.AssertService(c, "dummy", curl, 1, 0) }
func (s *DeploySuite) TestNumUnits(c *gc.C) { coretesting.Charms.BundlePath(s.SeriesPath, "dummy") err := runDeploy(c, "local:dummy", "-n", "13") c.Assert(err, gc.IsNil) curl := charm.MustParseURL("local:precise/dummy-1") s.AssertService(c, "dummy", curl, 13, 0) }
func (s *DeploySuite) TestSubordinateCharm(c *gc.C) { coretesting.Charms.BundlePath(s.SeriesPath, "logging") err := runDeploy(c, "local:logging") c.Assert(err, gc.IsNil) curl := charm.MustParseURL("local:precise/logging-1") s.AssertService(c, "logging", curl, 0, 0) }
func addCharm(c *gc.C, st *State, series string, ch charm.Charm) *Charm { ident := fmt.Sprintf("%s-%s-%d", series, ch.Meta().Name, ch.Revision()) curl := charm.MustParseURL("local:" + series + "/" + ident) bundleURL, err := url.Parse("http://bundles.testing.invalid/" + ident) c.Assert(err, gc.IsNil) sch, err := st.AddCharm(ch, curl, bundleURL, ident+"-sha256") c.Assert(err, gc.IsNil) return sch }
func (s *GitDeployerSuite) TestUpgrade(c *gc.C) { // Install. info1 := s.bundles.AddCustomBundle(c, corecharm.MustParseURL("cs:s/c-1"), func(path string) { err := ioutil.WriteFile(filepath.Join(path, "some-file"), []byte("hello"), 0644) c.Assert(err, gc.IsNil) err = os.Symlink("./some-file", filepath.Join(path, "a-symlink")) c.Assert(err, gc.IsNil) }) err := s.deployer.Stage(info1, nil) c.Assert(err, gc.IsNil) err = s.deployer.Deploy() c.Assert(err, gc.IsNil) // Upgrade. info2 := s.bundles.AddCustomBundle(c, corecharm.MustParseURL("cs:s/c-2"), func(path string) { err := ioutil.WriteFile(filepath.Join(path, "some-file"), []byte("goodbye"), 0644) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(filepath.Join(path, "a-symlink"), []byte("not any more!"), 0644) c.Assert(err, gc.IsNil) }) err = s.deployer.Stage(info2, nil) c.Assert(err, gc.IsNil) checkCleanup(c, s.deployer) err = s.deployer.Deploy() c.Assert(err, gc.IsNil) checkCleanup(c, s.deployer) // Check content. data, err := ioutil.ReadFile(filepath.Join(s.targetPath, "some-file")) c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, "goodbye") data, err = ioutil.ReadFile(filepath.Join(s.targetPath, "a-symlink")) c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, "not any more!") target := charm.NewGitDir(s.targetPath) url, err := target.ReadCharmURL() c.Assert(err, gc.IsNil) c.Assert(url, gc.DeepEquals, corecharm.MustParseURL("cs:s/c-2")) lines, err := target.Log() c.Assert(err, gc.IsNil) c.Assert(lines, gc.HasLen, 5) c.Assert(lines[0], gc.Matches, `[0-9a-f]{7} Upgraded charm to "cs:s/c-2".`) }
func (s *StoreSuite) TestRevisioning(c *gc.C) { urlA := charm.MustParseURL("cs:oneiric/wordpress-a") urlB := charm.MustParseURL("cs:oneiric/wordpress-b") urls := []*charm.URL{urlA, urlB} tests := []struct { urls []*charm.URL data string }{ {urls[0:], "charm-revision-0"}, {urls[1:], "charm-revision-1"}, {urls[0:], "charm-revision-2"}, } for i, t := range tests { pub, err := s.store.CharmPublisher(t.urls, fmt.Sprintf("digest-%d", i)) c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, i) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) } for i, t := range tests { for _, url := range t.urls { url = url.WithRevision(i) info, rc, err := s.store.OpenCharm(url) c.Assert(err, gc.IsNil) data, err := ioutil.ReadAll(rc) cerr := rc.Close() c.Assert(info.Revision(), gc.Equals, i) c.Assert(url.Revision, gc.Equals, i) // Untouched. c.Assert(cerr, gc.IsNil) c.Assert(string(data), gc.Equals, string(t.data)) c.Assert(err, gc.IsNil) } } info, rc, err := s.store.OpenCharm(urlA.WithRevision(1)) c.Assert(err, gc.Equals, store.ErrNotFound) c.Assert(info, gc.IsNil) c.Assert(rc, gc.IsNil) }
func (s *DeploySuite) TestConstraints(c *gc.C) { coretesting.Charms.BundlePath(s.SeriesPath, "dummy") err := runDeploy(c, "local:dummy", "--constraints", "mem=2G cpu-cores=2") c.Assert(err, gc.IsNil) curl := charm.MustParseURL("local:precise/dummy-1") service, _ := s.AssertService(c, "dummy", curl, 1, 0) cons, err := service.Constraints() c.Assert(err, gc.IsNil) c.Assert(cons, gc.DeepEquals, constraints.MustParse("mem=2G cpu-cores=2")) }
func (s *StoreSuite) prepareServer(c *gc.C) (*store.Server, *charm.URL) { curl := charm.MustParseURL("cs:precise/wordpress") pub, err := s.store.CharmPublisher([]*charm.URL{curl}, "some-digest") c.Assert(err, gc.IsNil) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) server, err := store.NewServer(s.store) c.Assert(err, gc.IsNil) return server, curl }
func (s *DeploySuite) TestNetworks(c *gc.C) { coretesting.Charms.BundlePath(s.SeriesPath, "dummy") err := runDeploy(c, "local:dummy", "--networks", ", net1, net2 , ") c.Assert(err, gc.IsNil) curl := charm.MustParseURL("local:precise/dummy-1") service, _ := s.AssertService(c, "dummy", curl, 1, 0) includeNetworks, excludeNetworks, err := service.Networks() c.Assert(err, gc.IsNil) c.Assert(includeNetworks, gc.DeepEquals, []string{"net1", "net2"}) c.Assert(excludeNetworks, gc.HasLen, 0) }
func (s *StoreSuite) TestLockUpdates(c *gc.C) { urlA := charm.MustParseURL("cs:oneiric/wordpress-a") urlB := charm.MustParseURL("cs:oneiric/wordpress-b") urls := []*charm.URL{urlA, urlB} // Lock update of just B to force a partial conflict. lock1, err := s.store.LockUpdates(urls[1:]) c.Assert(err, gc.IsNil) // Partially conflicts with locked update above. lock2, err := s.store.LockUpdates(urls) c.Check(err, gc.Equals, store.ErrUpdateConflict) c.Check(lock2, gc.IsNil) lock1.Unlock() // Trying again should work since lock1 was released. lock3, err := s.store.LockUpdates(urls) c.Assert(err, gc.IsNil) lock3.Unlock() }
func (s *StoreSuite) TestCharmPublisher(c *gc.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, "some-digest") c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 0) err = pub.Publish(testing.Charms.ClonedDir(c.MkDir(), "dummy")) c.Assert(err, gc.IsNil) for _, url := range urls { info, rc, err := s.store.OpenCharm(url) c.Assert(err, gc.IsNil) c.Assert(info.Revision(), gc.Equals, 0) c.Assert(info.Digest(), gc.Equals, "some-digest") data, err := ioutil.ReadAll(rc) c.Check(err, gc.IsNil) err = rc.Close() c.Assert(err, gc.IsNil) bundle, err := charm.ReadBundleBytes(data) c.Assert(err, gc.IsNil) // The same information must be available by reading the // full charm data... c.Assert(bundle.Meta().Name, gc.Equals, "dummy") c.Assert(bundle.Config().Options["title"].Default, gc.Equals, "My Title") // ... and the queriable details. c.Assert(info.Meta().Name, gc.Equals, "dummy") c.Assert(info.Config().Options["title"].Default, gc.Equals, "My Title") info2, err := s.store.CharmInfo(url) c.Assert(err, gc.IsNil) c.Assert(info2, gc.DeepEquals, info) } }
func (s *StoreSuite) TestDeleteCharm(c *gc.C) { url := charm.MustParseURL("cs:oneiric/wordpress") for i := 0; i < 4; i++ { pub, err := s.store.CharmPublisher([]*charm.URL{url}, fmt.Sprintf("some-digest-%d", i)) c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, i) err = pub.Publish(testing.Charms.ClonedDir(c.MkDir(), "dummy")) c.Assert(err, gc.IsNil) } // Verify charms were published info, rc, err := s.store.OpenCharm(url) c.Assert(err, gc.IsNil) err = rc.Close() c.Assert(err, gc.IsNil) c.Assert(info.Revision(), gc.Equals, 3) // Delete an arbitrary middle revision url1 := url.WithRevision(1) infos, err := s.store.DeleteCharm(url1) c.Assert(err, gc.IsNil) c.Assert(len(infos), gc.Equals, 1) // Verify still published info, rc, err = s.store.OpenCharm(url) c.Assert(err, gc.IsNil) err = rc.Close() c.Assert(err, gc.IsNil) c.Assert(info.Revision(), gc.Equals, 3) // Delete all revisions expectedRevs := map[int]bool{0: true, 2: true, 3: true} infos, err = s.store.DeleteCharm(url) c.Assert(err, gc.IsNil) c.Assert(len(infos), gc.Equals, 3) for _, deleted := range infos { // We deleted the charm we expected to c.Assert(info.Meta().Name, gc.Equals, deleted.Meta().Name) _, has := expectedRevs[deleted.Revision()] c.Assert(has, gc.Equals, true) delete(expectedRevs, deleted.Revision()) } c.Assert(len(expectedRevs), gc.Equals, 0) // The charm is all gone _, _, err = s.store.OpenCharm(url) c.Assert(err, gc.Not(gc.IsNil)) }
func (s *StoreSuite) TestLogCharmEventWithRevisionedURL(c *gc.C) { url := charm.MustParseURL("cs:oneiric/wordpress-0") event := &store.CharmEvent{ Kind: store.EventPublishError, Digest: "some-digest", URLs: []*charm.URL{url}, } err := s.store.LogCharmEvent(event) c.Assert(err, gc.ErrorMatches, "LogCharmEvent: got charm URL with revision: cs:oneiric/wordpress-0") // This may work in the future, but not now. event, err = s.store.CharmEvent(url, "some-digest") c.Assert(err, gc.ErrorMatches, "CharmEvent: got charm URL with revision: cs:oneiric/wordpress-0") c.Assert(event, gc.IsNil) }
func (s *MockStore) serveEvent(w http.ResponseWriter, r *http.Request) { r.ParseForm() response := map[string]*charm.EventResponse{} for _, url := range r.Form["charms"] { digest := "" if i := strings.Index(url, "@"); i >= 0 { digest = url[i+1:] url = url[:i] } er := &charm.EventResponse{} response[url] = er if digest != "" && digest != "the-digest" { er.Kind = "not-found" er.Errors = []string{"entry not found"} continue } charmURL := charm.MustParseURL(url) switch charmURL.Name { case "borken": er.Kind = "publish-error" er.Errors = append(er.Errors, "badness") case "unwise": er.Warnings = append(er.Warnings, "foolishness") fallthrough default: if rev, ok := s.charms[charmURL.WithRevision(-1).String()]; ok { er.Kind = "published" er.Revision = rev er.Digest = "the-digest" } else { er.Kind = "not-found" er.Errors = []string{"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 *StoreSuite) TestCharmBundleData(c *gc.C) { url := charm.MustParseURL("cs:oneiric/wordpress") urls := []*charm.URL{url} pub, err := s.store.CharmPublisher(urls, "key") c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 0) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) info, rc, err := s.store.OpenCharm(url) c.Assert(err, gc.IsNil) c.Check(info.BundleSha256(), gc.Equals, fakeRevZeroSha) c.Check(info.BundleSize(), gc.Equals, int64(len("charm-revision-0"))) err = rc.Close() c.Check(err, gc.IsNil) }
func (s *UnexposeSuite) TestUnexpose(c *gc.C) { testing.Charms.BundlePath(s.SeriesPath, "dummy") err := runDeploy(c, "local:dummy", "some-service-name") c.Assert(err, gc.IsNil) curl := charm.MustParseURL("local:precise/dummy-1") s.AssertService(c, "some-service-name", curl, 1, 0) err = runExpose(c, "some-service-name") c.Assert(err, gc.IsNil) s.assertExposed(c, "some-service-name", true) err = runUnexpose(c, "some-service-name") c.Assert(err, gc.IsNil) s.assertExposed(c, "some-service-name", false) err = runUnexpose(c, "nonexistent-service") c.Assert(err, gc.ErrorMatches, `service "nonexistent-service" not found`) }
func (s *StoreSuite) TestSeriesSolver(c *gc.C) { for _, t := range seriesSolverCharms { url := charm.MustParseURL(fmt.Sprintf("cs:%s/%s", t.series, t.name)) urls := []*charm.URL{url} pub, err := s.store.CharmPublisher(urls, fmt.Sprintf("some-%s-%s-digest", t.series, t.name)) c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 0) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) } // LTS, then non-LTS, reverse alphabetical order ref, _, err := charm.ParseReference("cs:wordpress") c.Assert(err, gc.IsNil) series, err := s.store.Series(ref) c.Assert(err, gc.IsNil) c.Assert(series, gc.HasLen, 5) c.Check(series[0], gc.Equals, "trusty") c.Check(series[1], gc.Equals, "precise") c.Check(series[2], gc.Equals, "volumetric") c.Check(series[3], gc.Equals, "quantal") c.Check(series[4], gc.Equals, "oneiric") // Ensure that the full charm name matches, not just prefix ref, _, err = charm.ParseReference("cs:mysql") c.Assert(err, gc.IsNil) series, err = s.store.Series(ref) c.Assert(err, gc.IsNil) c.Assert(series, gc.HasLen, 1) c.Check(series[0], gc.Equals, "precise") // No LTS, reverse alphabetical order ref, _, err = charm.ParseReference("cs:zebra") c.Assert(err, gc.IsNil) series, err = s.store.Series(ref) c.Assert(err, gc.IsNil) c.Assert(series, gc.HasLen, 2) c.Check(series[0], gc.Equals, "zef") c.Check(series[1], gc.Equals, "def") }
func (s *charmVersionSuite) TestUpdateRevisions(c *gc.C) { s.AddMachine(c, "0", state.JobManageEnviron) s.SetupScenario(c) curl := charm.MustParseURL("cs:quantal/mysql") _, err := s.State.LatestPlaceholderCharm(curl) c.Assert(err, jc.Satisfies, errors.IsNotFound) curl = charm.MustParseURL("cs:quantal/wordpress") _, err = s.State.LatestPlaceholderCharm(curl) c.Assert(err, jc.Satisfies, errors.IsNotFound) result, err := s.charmrevisionupdater.UpdateLatestRevisions() c.Assert(err, gc.IsNil) c.Assert(result.Error, gc.IsNil) curl = charm.MustParseURL("cs:quantal/mysql") pending, err := s.State.LatestPlaceholderCharm(curl) c.Assert(err, gc.IsNil) c.Assert(pending.String(), gc.Equals, "cs:quantal/mysql-23") // Latest wordpress is already deployed, so no pending charm. curl = charm.MustParseURL("cs:quantal/wordpress") _, err = s.State.LatestPlaceholderCharm(curl) c.Assert(err, jc.Satisfies, errors.IsNotFound) // Varnish has an error when updating, so no pending charm. curl = charm.MustParseURL("cs:quantal/varnish") _, err = s.State.LatestPlaceholderCharm(curl) c.Assert(err, jc.Satisfies, errors.IsNotFound) // Update mysql version and run update again. svc, err := s.State.Service("mysql") c.Assert(err, gc.IsNil) ch := s.AddCharmWithRevision(c, "mysql", 23) err = svc.SetCharm(ch, true) c.Assert(err, gc.IsNil) result, err = s.charmrevisionupdater.UpdateLatestRevisions() c.Assert(err, gc.IsNil) c.Assert(result.Error, gc.IsNil) // Latest mysql is now deployed, so no pending charm. curl = charm.MustParseURL("cs:quantal/mysql") _, err = s.State.LatestPlaceholderCharm(curl) c.Assert(err, jc.Satisfies, errors.IsNotFound) }
func (s *DeleteCharmSuite) TestRun(c *gc.C) { // Derive config file from test mongo port confDir := c.MkDir() f, err := os.Create(path.Join(confDir, "charmd.conf")) c.Assert(err, gc.IsNil) configPath := f.Name() { defer f.Close() fmt.Fprintf(f, "mongo-url: %s\n", testing.MgoServer.Addr()) } // Delete charm that does not exist, not found error. config := &DeleteCharmCommand{} out, err := testing.RunCommand(c, config, "--config", configPath, "--url", "cs:unreleased/foo") fmt.Println(out) c.Assert(err, gc.NotNil) // Publish that charm now url := charm.MustParseURL("cs:unreleased/foo") { s, err := store.Open(testing.MgoServer.Addr()) defer s.Close() c.Assert(err, gc.IsNil) pub, err := s.CharmPublisher([]*charm.URL{url}, "such-digest-much-unique") c.Assert(err, gc.IsNil) err = pub.Publish(testing.Charms.ClonedDir(c.MkDir(), "dummy")) c.Assert(err, gc.IsNil) } // Delete charm, should now succeed _, err = testing.RunCommand(c, config, "--config", configPath, "--url", "cs:unreleased/foo") c.Assert(err, gc.IsNil) c.Assert(config.Config, gc.NotNil) // Confirm that the charm is gone { s, err := store.Open(testing.MgoServer.Addr()) defer s.Close() c.Assert(err, gc.IsNil) _, err = s.CharmInfo(url) c.Assert(err, gc.NotNil) } }
func (s *MockStore) serveCharm(w http.ResponseWriter, r *http.Request) { charmURL := charm.MustParseURL("cs:" + r.URL.Path[len("/charm/"):]) r.ParseForm() if r.Form.Get("stats") == "0" { s.DownloadsNoStats = append(s.DownloadsNoStats, charmURL) } else { s.Downloads = append(s.Downloads, charmURL) } if auth := r.Header.Get("Authorization"); auth != "" { s.Authorizations = append(s.Authorizations, auth) } w.Header().Set("Connection", "close") w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Content-Length", strconv.Itoa(len(s.bundleBytes))) _, err := w.Write(s.bundleBytes) if err != nil { panic(err) } }
func (s *StoreSuite) TestMysqlSeriesSolver(c *gc.C) { for _, t := range mysqlSeriesCharms { var urls []*charm.URL for _, url := range t.urls { urls = append(urls, charm.MustParseURL(url)) } pub, err := s.store.CharmPublisher(urls, t.fakeDigest) c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 0) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) } ref, _, err := charm.ParseReference("cs:mysql") c.Assert(err, gc.IsNil) series, err := s.store.Series(ref) c.Assert(err, gc.IsNil) c.Assert(series, gc.HasLen, 2) c.Check(series[0], gc.Equals, "precise") c.Check(series[1], gc.Equals, "oneiric") }
func (s *MachineWithCharmsSuite) TestManageEnvironRunsCharmRevisionUpdater(c *gc.C) { m, _, _ := s.primeAgent(c, version.Current, state.JobManageEnviron) s.SetupScenario(c) a := s.newAgent(c, m) go func() { c.Check(a.Run(nil), gc.IsNil) }() defer func() { c.Check(a.Stop(), gc.IsNil) }() checkRevision := func() bool { curl := charm.MustParseURL("cs:quantal/mysql") placeholder, err := s.State.LatestPlaceholderCharm(curl) return err == nil && placeholder.String() == curl.WithRevision(23).String() } success := false for attempt := coretesting.LongAttempt.Start(); attempt.Next(); { if success = checkRevision(); success { break } } c.Assert(success, gc.Equals, true) }
func (s *StoreSuite) TestServerCharmEvent(c *gc.C) { server, _ := s.prepareServer(c) req, err := http.NewRequest("GET", "/charm-event", nil) c.Assert(err, gc.IsNil) url1 := charm.MustParseURL("cs:oneiric/wordpress") url2 := charm.MustParseURL("cs:oneiric/mysql") urls := []*charm.URL{url1, url2} event1 := &store.CharmEvent{ Kind: store.EventPublished, Revision: 42, Digest: "revKey1", URLs: urls, Warnings: []string{"A warning."}, Time: time.Unix(1, 0), } event2 := &store.CharmEvent{ Kind: store.EventPublished, Revision: 43, Digest: "revKey2", URLs: urls, Time: time.Unix(2, 0), } event3 := &store.CharmEvent{ Kind: store.EventPublishError, Digest: "revKey3", Errors: []string{"An error."}, URLs: urls[:1], Time: time.Unix(3, 0), } for _, event := range []*store.CharmEvent{event1, event2, event3} { err := s.store.LogCharmEvent(event) c.Assert(err, gc.IsNil) } var tests = []struct { query string kind, digest string err, warn string time string revision int }{ { query: url1.String(), digest: "revKey3", kind: "publish-error", err: "An error.", time: "1970-01-01T00:00:03Z", }, { query: url2.String(), digest: "revKey2", kind: "published", revision: 43, time: "1970-01-01T00:00:02Z", }, { query: url1.String() + "@revKey1", digest: "revKey1", kind: "published", revision: 42, warn: "A warning.", time: "1970-01-01T00:00:01Z", }, { query: "cs:non/existent", revision: 0, err: "entry not found", }, } for _, t := range tests { req.Form = url.Values{"charms": []string{t.query}} rec := httptest.NewRecorder() server.ServeHTTP(rec, req) url := t.query if i := strings.Index(url, "@"); i >= 0 { url = url[:i] } info := map[string]interface{}{ "kind": "", "revision": float64(0), } if t.kind != "" { info["kind"] = t.kind info["revision"] = float64(t.revision) info["digest"] = t.digest info["time"] = t.time } if t.err != "" { info["errors"] = []interface{}{t.err} } if t.warn != "" { info["warnings"] = []interface{}{t.warn} } expected := map[string]interface{}{url: info} obtained := map[string]interface{}{} err = json.NewDecoder(rec.Body).Decode(&obtained) c.Assert(err, gc.IsNil) c.Assert(obtained, gc.DeepEquals, expected) c.Assert(rec.Header().Get("Content-Type"), gc.Equals, "application/json") } s.checkCounterSum(c, []string{"charm-event", "oneiric", "wordpress"}, false, 2) s.checkCounterSum(c, []string{"charm-event", "oneiric", "mysql"}, false, 1) query1 := url1.String() + "@" + event1.Digest query3 := url1.String() + "@" + event3.Digest event1Info := map[string]interface{}{ "kind": "published", "revision": float64(42), "digest": "revKey1", "warnings": []interface{}{"A warning."}, "time": "1970-01-01T00:00:01Z"} event3Info := map[string]interface{}{ "kind": "publish-error", "revision": float64(0), "digest": "revKey3", "errors": []interface{}{"An error."}, "time": "1970-01-01T00:00:03Z"} req.Form = url.Values{"charms": []string{query1, query3}} rec := httptest.NewRecorder() server.ServeHTTP(rec, req) expected := map[string]interface{}{url1.String(): event3Info} obtained := map[string]interface{}{} err = json.NewDecoder(rec.Body).Decode(&obtained) c.Assert(err, gc.IsNil) c.Assert(obtained, jc.DeepEquals, expected) req.Form = url.Values{"charms": []string{query1, query3}, "long_keys": []string{"1"}} rec = httptest.NewRecorder() server.ServeHTTP(rec, req) expected = map[string]interface{}{query1: event1Info, query3: event3Info} obtained = map[string]interface{}{} err = json.NewDecoder(rec.Body).Decode(&obtained) c.Assert(err, gc.IsNil) c.Assert(obtained, jc.DeepEquals, expected) }