// SetCharm sets the charm for a given for the application. func (api *API) SetCharm(args params.ApplicationSetCharm) error { if err := api.checkCanWrite(); err != nil { return err } // when forced units in error, don't block if !args.ForceUnits { if err := api.check.ChangeAllowed(); err != nil { return errors.Trace(err) } } application, err := api.backend.Application(args.ApplicationName) if err != nil { return errors.Trace(err) } channel := csparams.Channel(args.Channel) return api.applicationSetCharm( args.ApplicationName, application, args.CharmURL, channel, args.ConfigSettings, args.ConfigSettingsYAML, args.ForceSeries, args.ForceUnits, args.ResourceIDs, args.StorageConstraints, ) }
// Run implements cmd.Command. func (c *ListCharmResourcesCommand) Run(ctx *cmd.Context) error { // TODO(ericsnow) Adjust this to the charm store. apiclient, err := c.Connect(ctx) if err != nil { // TODO(ericsnow) Return a more user-friendly error? return errors.Trace(err) } defer apiclient.Close() charmURLs, err := resolveCharms([]string{c.charm}) if err != nil { return errors.Trace(err) } charms := make([]charmstore.CharmID, len(charmURLs)) for i, id := range charmURLs { charms[i] = charmstore.CharmID{URL: id, Channel: csparams.Channel(c.channel)} } resources, err := apiclient.ListResources(charms) if err != nil { return errors.Trace(err) } if len(resources) != 1 { return errors.New("got bad data from charm store") } // Note that we do not worry about c.CompatVersion // for show-charm-resources... formatter := newCharmResourcesFormatter(resources[0]) formatted := formatter.format() return c.out.Write(ctx, formatted) }
// SetCharm sets the charm for a given service. func (api *API) SetCharm(args params.ServiceSetCharm) error { // when forced units in error, don't block if !args.ForceUnits { if err := api.check.ChangeAllowed(); err != nil { return errors.Trace(err) } } service, err := api.state.Service(args.ServiceName) if err != nil { return errors.Trace(err) } channel := csparams.Channel(args.Channel) return api.serviceSetCharm(service, args.CharmUrl, channel, args.ForceSeries, args.ForceUnits, args.ResourceIDs) }
// AddPendingResources adds the provided resources (info) to the Juju // model in a pending state, meaning they are not available until // resolved. func (f Facade) AddPendingResources(args api.AddPendingResourcesArgs) (api.AddPendingResourcesResult, error) { var result api.AddPendingResourcesResult tag, apiErr := parseApplicationTag(args.Tag) if apiErr != nil { result.Error = apiErr return result, nil } applicationID := tag.Id() channel := csparams.Channel(args.Channel) ids, err := f.addPendingResources(applicationID, args.URL, channel, args.CharmStoreMacaroon, args.Resources) if err != nil { result.Error = common.ServerError(err) return result, nil } result.PendingIDs = ids return result, nil }
func openCSClient(args params.AddCharmWithAuthorization) (*csclient.Client, error) { csURL, err := url.Parse(csclient.ServerURL) if err != nil { return nil, err } csParams := csclient.Params{ URL: csURL.String(), HTTPClient: httpbakery.NewHTTPClient(), } if args.CharmStoreMacaroon != nil { // Set the provided charmstore authorizing macaroon // as a cookie in the HTTP client. // TODO(cmars) discharge any third party caveats in the macaroon. ms := []*macaroon.Macaroon{args.CharmStoreMacaroon} httpbakery.SetCookie(csParams.HTTPClient.Jar, csURL, ms) } csClient := csclient.New(csParams) channel := csparams.Channel(args.Channel) if channel != csparams.NoChannel { csClient = csClient.WithChannel(channel) } return csClient, nil }
// deployApplication fetches the charm from the charm store and deploys it. // The logic has been factored out into a common function which is called by // both the legacy API on the client facade, as well as the new application facade. func deployApplication( backend Backend, stateCharm func(Charm) *state.Charm, args params.ApplicationDeploy, ) error { curl, err := charm.ParseURL(args.CharmURL) if err != nil { return errors.Trace(err) } if curl.Revision < 0 { return errors.Errorf("charm url must include revision") } // Do a quick but not complete validation check before going any further. for _, p := range args.Placement { if p.Scope != instance.MachineScope { continue } _, err = backend.Machine(p.Directive) if err != nil { return errors.Annotatef(err, `cannot deploy "%v" to machine %v`, args.ApplicationName, p.Directive) } } // Try to find the charm URL in state first. ch, err := backend.Charm(curl) if err != nil { return errors.Trace(err) } if err := checkMinVersion(ch); err != nil { return errors.Trace(err) } var settings charm.Settings if len(args.ConfigYAML) > 0 { settings, err = ch.Config().ParseSettingsYAML([]byte(args.ConfigYAML), args.ApplicationName) } else if len(args.Config) > 0 { // Parse config in a compatible way (see function comment). settings, err = parseSettingsCompatible(ch.Config(), args.Config) } if err != nil { return errors.Trace(err) } channel := csparams.Channel(args.Channel) _, err = jjj.DeployApplication(backend, jjj.DeployApplicationParams{ ApplicationName: args.ApplicationName, Series: args.Series, Charm: stateCharm(ch), Channel: channel, NumUnits: args.NumUnits, ConfigSettings: settings, Constraints: args.Constraints, Placement: args.Placement, Storage: args.Storage, EndpointBindings: args.EndpointBindings, Resources: args.Resources, }) return errors.Trace(err) }
// DeployService fetches the charm from the charm store and deploys it. // The logic has been factored out into a common function which is called by // both the legacy API on the client facade, as well as the new service facade. func deployService(st *state.State, owner string, args params.ServiceDeploy) error { curl, err := charm.ParseURL(args.CharmUrl) if err != nil { return errors.Trace(err) } if curl.Revision < 0 { return errors.Errorf("charm url must include revision") } // Do a quick but not complete validation check before going any further. for _, p := range args.Placement { if p.Scope != instance.MachineScope { continue } _, err = st.Machine(p.Directive) if err != nil { return errors.Annotatef(err, `cannot deploy "%v" to machine %v`, args.ServiceName, p.Directive) } } // Try to find the charm URL in state first. ch, err := st.Charm(curl) // TODO(wallyworld) - remove for 2.0 beta4 if errors.IsNotFound(err) { // Clients written to expect 1.16 compatibility require this next block. if curl.Schema != "cs" { return errors.Errorf(`charm url has unsupported schema %q`, curl.Schema) } if err = AddCharmWithAuthorization(st, params.AddCharmWithAuthorization{ URL: args.CharmUrl, }); err == nil { ch, err = st.Charm(curl) } } if err != nil { return errors.Trace(err) } if err := checkMinVersion(ch); err != nil { return errors.Trace(err) } var settings charm.Settings if len(args.ConfigYAML) > 0 { settings, err = ch.Config().ParseSettingsYAML([]byte(args.ConfigYAML), args.ServiceName) } else if len(args.Config) > 0 { // Parse config in a compatible way (see function comment). settings, err = parseSettingsCompatible(ch, args.Config) } if err != nil { return errors.Trace(err) } channel := csparams.Channel(args.Channel) _, err = jjj.DeployService(st, jjj.DeployServiceParams{ ServiceName: args.ServiceName, Series: args.Series, // TODO(dfc) ServiceOwner should be a tag ServiceOwner: owner, Charm: ch, Channel: channel, NumUnits: args.NumUnits, ConfigSettings: settings, Constraints: args.Constraints, Placement: args.Placement, Storage: args.Storage, EndpointBindings: args.EndpointBindings, Resources: args.Resources, }) return errors.Trace(err) }