// upgradeCharm upgrades the charm for the given service to the given charm id. // If the service 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(service string, chID charmstore.CharmID, csMac *macaroon.Macaroon, resources map[string]string) error { id := chID.URL.String() existing, err := h.serviceClient.GetCharmURL(service) if err != nil { return errors.Annotatef(err, "cannot retrieve info for service %q", service) } if existing.String() == id { h.log.Infof("reusing service %s (charm: %s)", service, 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) } filtered, err := getUpgradeResources(h.serviceDeployer.api, service, url, h.client, resources) if err != nil { return errors.Trace(err) } var resNames2IDs map[string]string if len(filtered) != 0 { resNames2IDs, err = handleResources(h.serviceDeployer.api, resources, service, chID, csMac, filtered) if err != nil { return errors.Trace(err) } } cfg := apiservice.SetCharmConfig{ ServiceName: service, CharmID: chID, ResourceIDs: resNames2IDs, } if err := h.serviceClient.SetCharm(cfg); err != nil { return errors.Annotatef(err, "cannot upgrade charm to %q", id) } h.log.Infof("upgraded charm for existing service %s (from %s to %s)", service, existing, id) for resName := range resNames2IDs { h.log.Infof("added resource %s", resName) } return nil }
// addCharm interprets the new charmRef and adds the specified charm if the new charm is different // to what's already deployed as specified by oldURL. func (c *upgradeCharmCommand) addCharm( oldURL *charm.URL, charmRef string, client *api.Client, resolver *charmURLResolver, ) (charmstore.CharmID, *macaroon.Macaroon, error) { var id charmstore.CharmID // Charm may have been supplied via a path reference. ch, newURL, err := charmrepo.NewCharmAtPathForceSeries(charmRef, oldURL.Series, c.ForceSeries) if err == nil { _, newName := filepath.Split(charmRef) if newName != oldURL.Name { return id, nil, fmt.Errorf("cannot upgrade %q to %q", oldURL.Name, newName) } addedURL, err := client.AddLocalCharm(newURL, ch) id.URL = addedURL return id, nil, err } if _, ok := err.(*charmrepo.NotFoundError); ok { return id, nil, errors.Errorf("no charm found at %q", charmRef) } // If we get a "not exists" or invalid path error then we attempt to interpret // the supplied charm reference as a URL below, otherwise we return the error. if err != os.ErrNotExist && !charmrepo.IsInvalidPathError(err) { return id, nil, err } // Charm has been supplied as a URL so we resolve and deploy using the store. newURL, channel, supportedSeries, repo, err := resolver.resolve(charmRef) if err != nil { return id, nil, errors.Trace(err) } id.Channel = channel if !c.ForceSeries && oldURL.Series != "" && newURL.Series == "" && !isSeriesSupported(oldURL.Series, supportedSeries) { series := []string{"no series"} if len(supportedSeries) > 0 { series = supportedSeries } return id, nil, errors.Errorf( "cannot upgrade from single series %q charm to a charm supporting %q. Use --force-series to override.", oldURL.Series, series, ) } // If no explicit revision was set with either SwitchURL // or Revision flags, discover the latest. if *newURL == *oldURL { newRef, _ := charm.ParseURL(charmRef) if newRef.Revision != -1 { return id, nil, fmt.Errorf("already running specified charm %q", newURL) } 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 id, nil, fmt.Errorf("already running latest charm %q", newURL) } } curl, csMac, err := addCharmFromURL(client, newURL, channel, repo) if err != nil { return id, nil, errors.Trace(err) } id.URL = curl return id, csMac, 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 }