func (c *DeployCommand) handleResources(serviceName string, metaResources map[string]charmresource.Meta) (map[string]string, error) { if len(c.Resources) == 0 && len(metaResources) == 0 { return nil, nil } api, err := c.NewAPIRoot() if err != nil { return nil, errors.Trace(err) } ids, err := resourceadapters.DeployResources(serviceName, c.Resources, metaResources, api) if err != nil { return nil, errors.Trace(err) } return ids, nil }
func handleResources(c APICmd, resources map[string]string, serviceName string, chID charmstore.CharmID, csMac *macaroon.Macaroon, metaResources map[string]charmresource.Meta) (map[string]string, error) { if len(resources) == 0 && len(metaResources) == 0 { return nil, nil } api, err := c.NewAPIRoot() if err != nil { return nil, errors.Trace(err) } ids, err := resourceadapters.DeployResources(serviceName, chID, csMac, resources, metaResources, api) if err != nil { return nil, errors.Trace(err) } return ids, nil }
// upgradeCharm upgrades the charm for the given application to the given charm id. // If the application is already deployed using the given charm id, do nothing. // This function returns an error if the existing charm and the target one are // incompatible, meaning an upgrade from one to the other is not allowed. func (h *bundleHandler) upgradeCharm( api DeployAPI, applicationName string, chID charmstore.CharmID, csMac *macaroon.Macaroon, resources map[string]string, ) error { id := chID.URL.String() existing, err := h.api.GetCharmURL(applicationName) if err != nil { return errors.Annotatef(err, "cannot retrieve info for application %q", applicationName) } if existing.String() == id { h.log.Infof("reusing application %s (charm: %s)", applicationName, id) return nil } url, err := charm.ParseURL(id) if err != nil { return errors.Annotatef(err, "cannot parse charm URL %q", id) } chID.URL = url if url.WithRevision(-1).Path() != existing.WithRevision(-1).Path() { return errors.Errorf("bundle charm %q is incompatible with existing charm %q", id, existing) } charmsClient := charms.NewClient(api) resourceLister, err := resourceadapters.NewAPIClient(api) if err != nil { return errors.Trace(err) } filtered, err := getUpgradeResources(charmsClient, resourceLister, applicationName, url, resources) if err != nil { return errors.Trace(err) } var resNames2IDs map[string]string if len(filtered) != 0 { resNames2IDs, err = resourceadapters.DeployResources( applicationName, chID, csMac, resources, filtered, api, ) if err != nil { return errors.Trace(err) } } cfg := application.SetCharmConfig{ ApplicationName: applicationName, CharmID: chID, ResourceIDs: resNames2IDs, } if err := h.api.SetCharm(cfg); err != nil { return errors.Annotatef(err, "cannot upgrade charm to %q", id) } h.log.Infof("upgraded charm for existing application %s (from %s to %s)", applicationName, existing, id) for resName := range resNames2IDs { h.log.Infof("added resource %s", resName) } return nil }
func (c *DeployCommand) deployCharm( id charmstore.CharmID, csMac *macaroon.Macaroon, series string, ctx *cmd.Context, apiRoot DeployAPI, ) (rErr error) { charmInfo, err := apiRoot.CharmInfo(id.URL.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 application") } if numUnits == 1 && c.PlacementSpec == "" { numUnits = 0 } else { return errors.New("cannot use --num-units or --to with subordinate application") } } serviceName := c.ApplicationName if serviceName == "" { serviceName = charmInfo.Meta.Name } var configYAML []byte if c.Config.Path != "" { configYAML, err = c.Config.Read(ctx) if err != nil { return errors.Trace(err) } } bakeryClient, err := c.BakeryClient() if err != nil { return errors.Trace(err) } uuid, ok := apiRoot.ModelUUID() if !ok { return errors.New("API connection is controller-only (should never happen)") } deployInfo := DeploymentInfo{ CharmID: id, ApplicationName: serviceName, ModelUUID: uuid, CharmInfo: charmInfo, } for _, step := range c.Steps { err = step.RunPre(apiRoot, bakeryClient, ctx, deployInfo) if err != nil { return errors.Trace(err) } } defer func() { for _, step := range c.Steps { err = errors.Trace(step.RunPost(apiRoot, bakeryClient, ctx, deployInfo, rErr)) if err != nil { rErr = err } } }() if id.URL != nil && id.URL.Schema != "local" && len(charmInfo.Meta.Terms) > 0 { ctx.Infof("Deployment under prior agreement to terms: %s", strings.Join(charmInfo.Meta.Terms, " ")) } ids, err := resourceadapters.DeployResources( serviceName, id, csMac, c.Resources, charmInfo.Meta.Resources, apiRoot, ) if err != nil { return errors.Trace(err) } return errors.Trace(apiRoot.Deploy(application.DeployArgs{ CharmID: id, Cons: c.Constraints, ApplicationName: serviceName, Series: series, NumUnits: numUnits, ConfigYAML: string(configYAML), Placement: c.Placement, Storage: c.Storage, Resources: ids, EndpointBindings: c.Bindings, })) }
// addService deploys or update an application with no units. Service options are // also set or updated. func (h *bundleHandler) addService( api DeployAPI, id string, p bundlechanges.AddApplicationParams, chID charmstore.CharmID, csMac *macaroon.Macaroon, ) error { h.results[id] = p.Application ch := chID.URL.String() // Handle application configuration. configYAML := "" if len(p.Options) > 0 { config, err := yaml.Marshal(map[string]map[string]interface{}{p.Application: p.Options}) if err != nil { return errors.Annotatef(err, "cannot marshal options for application %q", p.Application) } configYAML = string(config) } // Handle application constraints. cons, err := constraints.Parse(p.Constraints) if err != nil { // This should never happen, as the bundle is already verified. return errors.Annotate(err, "invalid constraints for application") } storageConstraints := h.bundleStorage[p.Application] if len(p.Storage) > 0 { if storageConstraints == nil { storageConstraints = make(map[string]storage.Constraints) } for k, v := range p.Storage { if _, ok := storageConstraints[k]; ok { // Storage constraints overridden // on the command line. continue } cons, err := storage.ParseConstraints(v) if err != nil { return errors.Annotate(err, "invalid storage constraints") } storageConstraints[k] = cons } } resources := make(map[string]string) for resName, revision := range p.Resources { resources[resName] = fmt.Sprint(revision) } charmInfo, err := h.api.CharmInfo(ch) if err != nil { return err } resNames2IDs, err := resourceadapters.DeployResources( p.Application, chID, csMac, resources, charmInfo.Meta.Resources, api, ) if err != nil { return errors.Trace(err) } // Figure out what series we need to deploy with. conf, err := getModelConfig(h.api) if err != nil { return err } supportedSeries := charmInfo.Meta.Series if len(supportedSeries) == 0 && chID.URL.Series != "" { supportedSeries = []string{chID.URL.Series} } selector := seriesSelector{ seriesFlag: p.Series, charmURLSeries: chID.URL.Series, supportedSeries: supportedSeries, conf: conf, fromBundle: true, } series, err := selector.charmSeries() if err != nil { return errors.Trace(err) } // Deploy the application. logger.Debugf("application %s is deploying (charm %s)", p.Application, ch) h.log.Infof("Deploying charm %q", ch) if err := api.Deploy(application.DeployArgs{ CharmID: chID, Cons: cons, ApplicationName: p.Application, Series: series, ConfigYAML: configYAML, Storage: storageConstraints, Resources: resNames2IDs, EndpointBindings: p.EndpointBindings, }); err == nil { for resName := range resNames2IDs { h.log.Infof("added resource %s", resName) } return nil } else if !isErrServiceExists(err) { return errors.Annotatef(err, "cannot deploy application %q", p.Application) } // The application is already deployed in the environment: check that its // charm is compatible with the one declared in the bundle. If it is, // reuse the existing application or upgrade to a specified revision. // Exit with an error otherwise. if err := h.upgradeCharm(api, p.Application, chID, csMac, resources); err != nil { return errors.Annotatef(err, "cannot upgrade application %q", p.Application) } // Update application configuration. if configYAML != "" { if err := h.api.Update(params.ApplicationUpdate{ ApplicationName: p.Application, SettingsYAML: configYAML, }); err != nil { // This should never happen as possible errors are already returned // by the application Deploy call above. return errors.Annotatef(err, "cannot update options for application %q", p.Application) } h.log.Infof("configuration updated for application %s", p.Application) } // Update application constraints. if p.Constraints != "" { if err := h.api.SetConstraints(p.Application, cons); err != nil { // This should never happen, as the bundle is already verified. return errors.Annotatef(err, "cannot update constraints for application %q", p.Application) } h.log.Infof("constraints applied for application %s", p.Application) } return nil }