func (s *JujuConnSuite) AddTestingCharm(c *gc.C, name string) *state.Charm { ch := testing.Charms.Dir(name) ident := fmt.Sprintf("%s-%d", ch.Meta().Name, ch.Revision()) curl := charm.MustParseURL("local:quantal/" + ident) repo, err := charm.InferRepository(curl.Reference, testing.Charms.Path()) c.Assert(err, gc.IsNil) sch, err := s.Conn.PutCharm(curl, repo, false) c.Assert(err, gc.IsNil) return sch }
func (s *CharmSuite) TestInferRepository(c *gc.C) { for i, t := range inferRepoTests { c.Logf("test %d", i) curl, err := charm.InferURL(t.url, "precise") c.Assert(err, gc.IsNil) repo, err := charm.InferRepository(curl.Reference, "/some/path") c.Assert(err, gc.IsNil) switch repo := repo.(type) { case *charm.LocalRepository: c.Assert(repo.Path, gc.Equals, t.path) default: c.Assert(repo, gc.Equals, charm.Store) } } curl, err := charm.InferURL("local:whatever", "precise") c.Assert(err, gc.IsNil) _, err = charm.InferRepository(curl.Reference, "") c.Assert(err, gc.ErrorMatches, "path to local repository not specified") curl.Schema = "foo" _, err = charm.InferRepository(curl.Reference, "") c.Assert(err, gc.ErrorMatches, "unknown schema for charm reference.*") }
func (c *DeployCommand) Run(ctx *cmd.Context) error { client, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer client.Close() attrs, err := client.EnvironmentGet() if err != nil { return err } conf, err := config.New(config.NoDefaults, attrs) if err != nil { return err } curl, err := resolveCharmURL(c.CharmName, client, conf) if err != nil { return err } repo, err := charm.InferRepository(curl.Reference, ctx.AbsPath(c.RepoPath)) if err != nil { return err } repo = config.SpecializeCharmRepo(repo, conf) curl, err = addCharmViaAPI(client, ctx, curl, repo) if err != nil { return err } if c.BumpRevision { ctx.Infof("--upgrade (or -u) is deprecated and ignored; charms are always deployed with a unique revision.") } var includeNetworks []string var excludeNetworks []string haveNetworks := false if c.Networks != "" { includeNetworks = parseNetworks(c.Networks) haveNetworks = true } if c.ExcludeNetworks != "" { excludeNetworks = parseNetworks(c.ExcludeNetworks) haveNetworks = true } if haveNetworks { env, err := environs.New(conf) if err != nil { return err } if !env.SupportNetworks() { return errors.New("cannot use --networks/--exclude-networks: not supported by the environment") } } charmInfo, err := client.CharmInfo(curl.String()) if err != nil { return err } numUnits := c.NumUnits if charmInfo.Meta.Subordinate { if !constraints.IsEmpty(&c.Constraints) { 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 = charmInfo.Meta.Name } var configYAML []byte if c.Config.Path != "" { configYAML, err = c.Config.Read(ctx) if err != nil { return err } } err = client.ServiceDeployWithNetworks( curl.String(), serviceName, numUnits, string(configYAML), c.Constraints, c.ToMachineSpec, includeNetworks, excludeNetworks, ) if params.IsCodeNotImplemented(err) { if haveNetworks { return errors.New("cannot use --networks/--exclude-networks: not supported by the API server") } err = client.ServiceDeploy( curl.String(), serviceName, numUnits, string(configYAML), c.Constraints, c.ToMachineSpec) } return err }
// Run connects to the specified environment and starts the charm // upgrade process. func (c *UpgradeCharmCommand) Run(ctx *cmd.Context) error { client, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer client.Close() oldURL, err := client.ServiceGetCharmURL(c.ServiceName) if err != nil { return err } attrs, err := client.EnvironmentGet() if err != nil { return err } conf, err := config.New(config.NoDefaults, attrs) if err != nil { return err } var newURL *charm.URL if c.SwitchURL != "" { newURL, err = resolveCharmURL(c.SwitchURL, client, conf) 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.Reference, ctx.AbsPath(c.RepoPath)) if err != nil { return err } repo = config.SpecializeCharmRepo(repo, conf) // 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 := charm.Latest(repo, newURL) if err != nil { return err } newURL = newURL.WithRevision(latest) } if *newURL == *oldURL { if explicitRevision { return fmt.Errorf("already running specified charm %q", newURL) } else if newURL.Schema == "cs" { // No point in trying to upgrade a charm store charm when // we just determined that's the latest revision // available. return fmt.Errorf("already running latest charm %q", newURL) } } addedURL, err := addCharmViaAPI(client, ctx, newURL, repo) if err != nil { return err } return client.ServiceSetCharm(c.ServiceName, addedURL.String(), c.Force) }
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.Reference, "/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) } logger.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 { logger.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) } logger.Infof("sending charm to the charm store...") err = branch.Push(&bzr.PushAttr{Location: pushLocation, Remember: true}) if err != nil { return err } logger.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) } }