func (s *URLSuite) TestInferURL(c *C) { for _, t := range inferTests { comment := Commentf("InferURL(%q, %q)", t.vague, "defseries") inferred, ierr := charm.InferURL(t.vague, "defseries") parsed, perr := charm.ParseURL(t.exact) if parsed != nil { c.Check(inferred, DeepEquals, parsed, comment) } else { expect := perr.Error() if t.vague != t.exact { expect = fmt.Sprintf("%s (URL inferred from %q)", expect, t.vague) } c.Check(ierr.Error(), Equals, expect, comment) } } u, err := charm.InferURL("~blah", "defseries") c.Assert(u, IsNil) c.Assert(err, ErrorMatches, "cannot infer charm URL with user but no schema: .*") }
func (s *URLSuite) TestInferURLNoDefaultSeries(c *C) { for _, t := range inferNoDefaultSeriesTests { inferred, err := charm.InferURL(t.vague, "") if t.exact == "" { c.Assert(err, ErrorMatches, fmt.Sprintf("cannot infer charm URL for %q: no series provided", t.vague)) } else { parsed, err := charm.ParseURL(t.exact) c.Assert(err, IsNil) c.Assert(inferred, DeepEquals, parsed, Commentf(`InferURL(%q, "")`, t.vague)) } } }
func (s *CharmSuite) TestInferRepository(c *C) { for i, t := range inferRepoTests { c.Logf("test %d", i) curl, err := charm.InferURL(t.url, "precise") c.Assert(err, IsNil) repo, err := charm.InferRepository(curl, "/some/path") c.Assert(err, IsNil) switch repo := repo.(type) { case *charm.LocalRepository: c.Assert(repo.Path, Equals, t.path) default: c.Assert(repo, FitsTypeOf, charm.Store()) } } curl, err := charm.InferURL("local:whatever", "precise") c.Assert(err, IsNil) _, err = charm.InferRepository(curl, "") c.Assert(err, ErrorMatches, "path to local repository not specified") curl.Schema = "foo" _, err = charm.InferRepository(curl, "") c.Assert(err, ErrorMatches, "unknown schema for charm URL.*") }
func (c *DeployCommand) Init(args []string) error { switch len(args) { case 2: if !state.IsServiceName(args[1]) { return fmt.Errorf("invalid service name %q", args[1]) } c.ServiceName = args[1] fallthrough case 1: if _, err := charm.InferURL(args[0], "fake"); err != nil { return fmt.Errorf("invalid charm name %q", args[0]) } c.CharmName = args[0] case 0: return errors.New("no charm specified") default: return cmd.CheckEmpty(args[2:]) } return c.UnitCommandBase.Init(args) }
func (c *PublishCommand) Run(ctx *cmd.Context) (err error) { branch := bzr.New(ctx.AbsPath(c.CharmPath)) if _, err := os.Stat(branch.Join(".bzr")); err != nil { return fmt.Errorf("not a charm branch: %s", branch.Location()) } if err := branch.CheckClean(); err != nil { return err } var curl *charm.URL if c.URL == "" { if err == nil { loc, err := branch.PushLocation() if err != nil { return fmt.Errorf("no charm URL provided and cannot infer from current directory (no push location)") } curl, err = charm.Store.CharmURL(loc) if err != nil { return fmt.Errorf("cannot infer charm URL from branch location: %q", loc) } } } else { curl, err = charm.InferURL(c.URL, "") if err != nil { return err } } pushLocation := charm.Store.BranchLocation(curl) if c.changePushLocation != nil { pushLocation = c.changePushLocation(pushLocation) } repo, err := charm.InferRepository(curl, "/not/important") if err != nil { return err } if repo != charm.Store { return fmt.Errorf("charm URL must reference the juju charm store") } localDigest, err := branch.RevisionId() if err != nil { return fmt.Errorf("cannot obtain local digest: %v", err) } log.Infof("local digest is %s", localDigest) ch, err := charm.ReadDir(branch.Location()) if err != nil { return err } if ch.Meta().Name != curl.Name { return fmt.Errorf("charm name in metadata must match name in URL: %q != %q", ch.Meta().Name, curl.Name) } oldEvent, err := charm.Store.Event(curl, localDigest) if _, ok := err.(*charm.NotFoundError); ok { oldEvent, err = charm.Store.Event(curl, "") if _, ok := err.(*charm.NotFoundError); ok { log.Infof("charm %s is not yet in the store", curl) err = nil } } if err != nil { return fmt.Errorf("cannot obtain event details from the store: %s", err) } if oldEvent != nil && oldEvent.Digest == localDigest { return handleEvent(ctx, curl, oldEvent) } log.Infof("sending charm to the charm store...") err = branch.Push(&bzr.PushAttr{Location: pushLocation, Remember: true}) if err != nil { return err } log.Infof("charm sent; waiting for it to be published...") for { time.Sleep(c.pollDelay) newEvent, err := charm.Store.Event(curl, "") if _, ok := err.(*charm.NotFoundError); ok { continue } if err != nil { return fmt.Errorf("cannot obtain event details from the store: %s", err) } if oldEvent != nil && oldEvent.Digest == newEvent.Digest { continue } if newEvent.Digest != localDigest { // TODO Check if the published digest is in the local history. return fmt.Errorf("charm changed but not to local charm digest; publishing race?") } return handleEvent(ctx, curl, newEvent) } return nil }
// Run connects to the specified environment and starts the charm // upgrade process. func (c *UpgradeCharmCommand) Run(ctx *cmd.Context) error { conn, err := juju.NewConnFromName(c.EnvName) if err != nil { return err } defer conn.Close() service, err := conn.State.Service(c.ServiceName) if err != nil { return err } oldURL, _ := service.CharmURL() var newURL *charm.URL if c.SwitchURL != "" { // A new charm URL was explicitly specified. conf, err := conn.State.EnvironConfig() if err != nil { return err } newURL, err = charm.InferURL(c.SwitchURL, conf.DefaultSeries()) if err != nil { return err } } else { // No new URL specified, but revision might have been. newURL = oldURL.WithRevision(c.Revision) } repo, err := charm.InferRepository(newURL, ctx.AbsPath(c.RepoPath)) if err != nil { return err } // If no explicit revision was set with either SwitchURL // or Revision flags, discover the latest. explicitRevision := true if newURL.Revision == -1 { explicitRevision = false latest, err := repo.Latest(newURL) if err != nil { return err } newURL = newURL.WithRevision(latest) } bumpRevision := false if *newURL == *oldURL { if explicitRevision { return fmt.Errorf("already running specified charm %q", newURL) } // Only try bumping the revision when necessary (local dir charm). if _, isLocal := repo.(*charm.LocalRepository); !isLocal { // TODO(dimitern): If the --force flag is set to something // different to before, we might actually want to allow this // case (and the other error below). LP bug #1174287 return fmt.Errorf("already running latest charm %q", newURL) } // This is a local repository. if ch, err := repo.Get(newURL); err != nil { return err } else if _, bumpRevision = ch.(*charm.Dir); !bumpRevision { // Only bump the revision when it's a directory. return fmt.Errorf("cannot increment revision of charm %q: not a directory", newURL) } } sch, err := conn.PutCharm(newURL, repo, bumpRevision) if err != nil { return err } return service.SetCharm(sch, c.Force) }
func (c *DeployCommand) Run(ctx *cmd.Context) error { conn, err := juju.NewConnFromName(c.EnvName) if err != nil { return err } defer conn.Close() conf, err := conn.State.EnvironConfig() if err != nil { return err } curl, err := charm.InferURL(c.CharmName, conf.DefaultSeries()) if err != nil { return err } repo, err := charm.InferRepository(curl, ctx.AbsPath(c.RepoPath)) if err != nil { return err } // TODO(fwereade) it's annoying to roundtrip the bytes through the client // here, but it's the original behaviour and not convenient to change. // PutCharm will always be required in some form for local charms; and we // will need an EnsureStoreCharm method somewhere that gets the state.Charm // for use in the following checks. ch, err := conn.PutCharm(curl, repo, c.BumpRevision) if err != nil { return err } numUnits := c.NumUnits if ch.Meta().Subordinate { empty := constraints.Value{} if c.Constraints != empty { return errors.New("cannot use --constraints with subordinate service") } if numUnits == 1 && c.ToMachineSpec == "" { numUnits = 0 } else { return errors.New("cannot use --num-units or --to with subordinate service") } } serviceName := c.ServiceName if serviceName == "" { serviceName = ch.Meta().Name } var settings charm.Settings if c.Config.Path != "" { configYAML, err := c.Config.Read(ctx) if err != nil { return err } settings, err = ch.Config().ParseSettingsYAML(configYAML, serviceName) if err != nil { return err } } _, err = conn.DeployService(juju.DeployServiceParams{ ServiceName: serviceName, Charm: ch, NumUnits: numUnits, ConfigSettings: settings, Constraints: c.Constraints, ToMachineSpec: c.ToMachineSpec, }) return err }