func NewDeployCommandWithDefaultAPI(steps []DeployStep) cmd.Command { deployCmd := &DeployCommand{Steps: steps} cmd := modelcmd.Wrap(deployCmd) deployCmd.NewAPIRoot = func() (DeployAPI, error) { apiRoot, err := deployCmd.ModelCommandBase.NewAPIRoot() if err != nil { return nil, errors.Trace(err) } bakeryClient, err := deployCmd.BakeryClient() if err != nil { return nil, errors.Trace(err) } cstoreClient := newCharmStoreClient(bakeryClient).WithChannel(deployCmd.Channel) adapter := &deployAPIAdapter{ Connection: apiRoot, apiClient: &apiClient{Client: apiRoot.Client()}, charmsClient: &charmsClient{Client: apicharms.NewClient(apiRoot)}, applicationClient: &applicationClient{Client: application.NewClient(apiRoot)}, modelConfigClient: &modelConfigClient{Client: modelconfig.NewClient(apiRoot)}, charmstoreClient: &charmstoreClient{Client: cstoreClient}, annotationsClient: &annotationsClient{Client: annotations.NewClient(apiRoot)}, charmRepoClient: &charmRepoClient{CharmStore: charmrepo.NewCharmStoreFromClient(cstoreClient)}, } return adapter, nil } return cmd }
// RunPre obtains authorization to deploy this charm. The authorization, if received is not // sent to the controller, rather it is kept as an attribute on RegisterMeteredCharm. func (r *RegisterMeteredCharm) RunPre(state api.Connection, client *http.Client, deployInfo DeploymentInfo) error { charmsClient := charms.NewClient(state) defer charmsClient.Close() metered, err := charmsClient.IsMetered(deployInfo.CharmURL.String()) if err != nil { return err } if !metered { return nil } bakeryClient := httpbakery.Client{Client: client, VisitWebPage: httpbakery.OpenWebBrowser} if r.Plan == "" { r.Plan, err = r.getDefaultPlan(client, deployInfo.CharmURL.String()) if err != nil { if isNoDefaultPlanError(err) { options, err1 := r.getCharmPlans(client, deployInfo.CharmURL.String()) if err1 != nil { return err1 } charmUrl := deployInfo.CharmURL.String() return errors.Errorf(`%v has no default plan. Try "juju deploy --plan <plan-name> with one of %v"`, charmUrl, strings.Join(options, ", ")) } return err } } r.credentials, err = r.registerMetrics(deployInfo.ModelUUID, deployInfo.CharmURL.String(), deployInfo.ServiceName, &bakeryClient) if err != nil { logger.Infof("failed to obtain plan authorization: %v", err) return err } return nil }
func (s *charmsMockSuite) TestIsMeteredFalse(c *gc.C) { var called bool curl := "local:quantal/dummy-1" apiCaller := basetesting.APICallerFunc( func(objType string, version int, id, request string, a, result interface{}, ) error { called = true c.Check(objType, gc.Equals, "Charms") c.Check(id, gc.Equals, "") c.Check(request, gc.Equals, "IsMetered") args, ok := a.(params.CharmInfo) c.Assert(ok, jc.IsTrue) c.Assert(args.CharmURL, gc.DeepEquals, curl) if wanted, k := result.(*charms.CharmInfo); k { wanted.URL = curl } return nil }) charmsClient := charms.NewClient(apiCaller) _, err := charmsClient.IsMetered(curl) c.Assert(err, jc.ErrorIsNil) c.Assert(called, jc.IsTrue) }
func registerMeteredCharm(registrationURL string, state *api.State, jar *cookiejar.Jar, charmURL string, serviceName, environmentUUID string) error { charmsClient := charms.NewClient(state) defer charmsClient.Close() metered, err := charmsClient.IsMetered(charmURL) if err != nil { return err } if metered { httpClient := httpbakery.NewHTTPClient() httpClient.Jar = jar credentials, err := registerMetrics(registrationURL, environmentUUID, charmURL, serviceName, httpClient, openWebBrowser) if err != nil { logger.Infof("failed to register metrics: %v", err) return err } api, cerr := getMetricCredentialsAPI(state) if cerr != nil { logger.Infof("failed to get the metrics credentials setter: %v", cerr) } err = api.SetMetricCredentials(serviceName, credentials) if err != nil { logger.Infof("failed to set metric credentials: %v", err) return err } api.Close() } return nil }
func (s *charmsMockSuite) TestList(c *gc.C) { var called bool charmName := "dummy-1" curl := "local:quantal/dummy-1" apiCaller := basetesting.APICallerFunc( func(objType string, version int, id, request string, a, result interface{}, ) error { called = true c.Check(objType, gc.Equals, "Charms") c.Check(id, gc.Equals, "") c.Check(request, gc.Equals, "List") args, ok := a.(params.CharmsList) c.Assert(ok, jc.IsTrue) c.Assert(args.Names, gc.HasLen, 1) c.Assert(args.Names[0], gc.DeepEquals, charmName) if wanted, k := result.(*params.CharmsListResult); k { wanted.CharmURLs = []string{curl} } return nil }) charmsClient := charms.NewClient(apiCaller) listResult, err := charmsClient.List([]string{charmName}) c.Assert(err, jc.ErrorIsNil) c.Assert(called, jc.IsTrue) c.Assert(listResult, gc.HasLen, 1) c.Assert(listResult[0], gc.DeepEquals, curl) }
// RunPre obtains authorization to deploy this charm. The authorization, if received is not // sent to the controller, rather it is kept as an attribute on RegisterMeteredCharm. func (r *RegisterMeteredCharm) RunPre(state api.Connection, bakeryClient *httpbakery.Client, ctx *cmd.Context, deployInfo DeploymentInfo) error { if allocBudget, allocLimit, err := parseBudgetWithLimit(r.AllocationSpec); err == nil { // Make these available to registration if valid. r.budget, r.limit = allocBudget, allocLimit } else { return errors.Trace(err) } charmsClient := charms.NewClient(state) metered, err := charmsClient.IsMetered(deployInfo.CharmID.URL.String()) if err != nil { return err } if !metered { return nil } if r.Plan == "" && deployInfo.CharmID.URL.Schema == "cs" { r.Plan, err = r.getDefaultPlan(bakeryClient, deployInfo.CharmID.URL.String()) if err != nil { if isNoDefaultPlanError(err) { options, err1 := r.getCharmPlans(bakeryClient, deployInfo.CharmID.URL.String()) if err1 != nil { return err1 } charmUrl := deployInfo.CharmID.URL.String() return errors.Errorf(`%v has no default plan. Try "juju deploy --plan <plan-name> with one of %v"`, charmUrl, strings.Join(options, ", ")) } return err } } r.credentials, err = r.registerMetrics( deployInfo.ModelUUID, deployInfo.CharmID.URL.String(), deployInfo.ServiceName, r.budget, r.limit, bakeryClient) if err != nil { if deployInfo.CharmID.URL.Schema == "cs" { logger.Infof("failed to obtain plan authorization: %v", err) return err } logger.Debugf("no plan authorization: %v", err) } return nil }
func (c *removeServiceCommand) removeAllocation(ctx *cmd.Context) error { client, err := c.getAPI() if err != nil { return err } charmURL, err := client.GetCharmURL(c.ServiceName) if err != nil { return errors.Trace(err) } if charmURL.Schema == "local" { return nil } root, err := c.NewAPIRoot() if err != nil { return errors.Trace(err) } charmsClient := charms.NewClient(root) metered, err := charmsClient.IsMetered(charmURL.String()) if err != nil { return errors.Trace(err) } if !metered { return nil } modelUUID := client.ModelUUID() httpClient, err := c.HTTPClient() if err != nil { return errors.Trace(err) } bClient, err := getBudgetAPIClient(httpClient) if err != nil { return errors.Trace(err) } resp, err := bClient.DeleteAllocation(modelUUID, c.ServiceName) if wireformat.IsNotAvail(err) { fmt.Fprintf(ctx.Stdout, "WARNING: Allocation not removed - %s.\n", err.Error()) } else if err != nil { return err } if resp != "" { fmt.Fprintf(ctx.Stdout, "%s\n", resp) } return nil }
func (c *removeServiceCommand) removeAllocation(ctx *cmd.Context) error { client, err := c.getAPI() if err != nil { return err } charmURL, err := client.GetCharmURL(c.ApplicationName) if err != nil { return errors.Trace(err) } if charmURL.Schema == "local" { return nil } root, err := c.NewAPIRoot() if err != nil { return errors.Trace(err) } charmsClient := charms.NewClient(root) metered, err := charmsClient.IsMetered(charmURL.String()) if err != nil { return errors.Trace(err) } if !metered { return nil } modelUUID := client.ModelUUID() bakeryClient, err := c.BakeryClient() if err != nil { return errors.Trace(err) } budgetClient := getBudgetAPIClient(bakeryClient) resp, err := budgetClient.DeleteAllocation(modelUUID, c.ApplicationName) if wireformat.IsNotAvail(err) { logger.Warningf("allocation not removed: %v", err) } else if err != nil { return err } if resp != "" { logger.Infof(resp) } return nil }
// RunPre obtains authorization to deploy this charm. The authorization, if received is not // sent to the controller, rather it is kept as an attribute on RegisterMeteredCharm. func (r *RegisterMeteredCharm) RunPre(state api.Connection, client *http.Client, deployInfo DeploymentInfo) error { charmsClient := charms.NewClient(state) defer charmsClient.Close() metered, err := charmsClient.IsMetered(deployInfo.CharmURL.String()) if params.IsCodeNotImplemented(err) { // The state server is too old to support metering. Warn // the user, but don't return an error. logger.Tracef("current state server version does not support charm metering") return nil } else if err != nil { return err } if !metered { return nil } bakeryClient := httpbakery.Client{Client: client, VisitWebPage: httpbakery.OpenWebBrowser} if r.Plan == "" { r.Plan, err = r.getDefaultPlan(client, deployInfo.CharmURL.String()) if err != nil { if isNoDefaultPlanError(err) { options, err1 := r.getCharmPlans(client, deployInfo.CharmURL.String()) if err1 != nil { return err1 } charmUrl := deployInfo.CharmURL.String() return errors.Errorf(`%v has no default plan. Try "juju deploy --plan <plan-name> with one of %v"`, charmUrl, strings.Join(options, ", ")) } return err } } r.credentials, err = r.registerMetrics(deployInfo.EnvUUID, deployInfo.CharmURL.String(), deployInfo.ServiceName, &bakeryClient) if err != nil { logger.Infof("failed to obtain plan authorization: %v", err) return err } return nil }
// RunPre is part of the DeployStep interface. func (a *AllocateBudget) RunPre(state api.Connection, client *http.Client, ctx *cmd.Context, deployInfo DeploymentInfo) error { if deployInfo.CharmURL.Schema == "local" { return nil } charmsClient := charms.NewClient(state) metered, err := charmsClient.IsMetered(deployInfo.CharmURL.String()) if params.IsCodeNotImplemented(err) { // The state server is too old to support metering. Warn // the user, but don't return an error. logger.Tracef("current state server version does not support charm metering") return nil } else if err != nil { return errors.Annotate(err, "could not determine charm type") } if !metered { return nil } allocBudget, allocLimit, err := parseBudgetWithLimit(a.AllocationSpec) if err != nil { return errors.Trace(err) } a.APIClient, err = getApiClient(client) if err != nil { return errors.Annotate(err, "could not create API client") } resp, err := a.APIClient.CreateAllocation(allocBudget, allocLimit, deployInfo.ModelUUID, []string{deployInfo.ServiceName}) if err != nil { if wireformat.IsNotAvail(err) { fmt.Fprintf(ctx.Stdout, "WARNING: Allocation not created - %s.\n", err.Error()) return nil } return errors.Annotate(err, "could not create budget allocation") } a.allocated = true fmt.Fprintf(ctx.Stdout, "%s\n", resp) return nil }
func (s *RemoveCharmStoreCharmsSuite) SetUpTest(c *gc.C) { s.charmStoreSuite.SetUpTest(c) s.ctx = testing.Context(c) s.stub = &jutesting.Stub{} s.budgetAPIClient = &mockBudgetAPIClient{Stub: s.stub} s.PatchValue(&getBudgetAPIClient, func(*httpbakery.Client) budgetAPIClient { return s.budgetAPIClient }) testcharms.UploadCharm(c, s.client, "cs:quantal/metered-1", "metered") deployCmd := &DeployCommand{} cmd := modelcmd.Wrap(deployCmd) deployCmd.NewAPIRoot = func() (DeployAPI, error) { apiRoot, err := deployCmd.ModelCommandBase.NewAPIRoot() if err != nil { return nil, errors.Trace(err) } bakeryClient, err := deployCmd.BakeryClient() if err != nil { return nil, errors.Trace(err) } cstoreClient := newCharmStoreClient(bakeryClient).WithChannel(deployCmd.Channel) return &deployAPIAdapter{ Connection: apiRoot, apiClient: &apiClient{Client: apiRoot.Client()}, charmsClient: &charmsClient{Client: charms.NewClient(apiRoot)}, applicationClient: &applicationClient{Client: application.NewClient(apiRoot)}, modelConfigClient: &modelConfigClient{Client: modelconfig.NewClient(apiRoot)}, charmstoreClient: &charmstoreClient{Client: cstoreClient}, annotationsClient: &annotationsClient{Client: annotations.NewClient(apiRoot)}, charmRepoClient: &charmRepoClient{CharmStore: charmrepo.NewCharmStoreFromClient(cstoreClient)}, }, nil } _, err := testing.RunCommand(c, cmd, "cs:quantal/metered-1") c.Assert(err, jc.ErrorIsNil) }
// NewUpgradeCharmCommand returns a command which upgrades application's charm. func NewUpgradeCharmCommand() cmd.Command { cmd := &upgradeCharmCommand{ DeployResources: resourceadapters.DeployResources, ResolveCharm: resolveCharm, NewCharmAdder: newCharmAdder, NewCharmClient: func(conn api.Connection) CharmClient { return charms.NewClient(conn) }, NewCharmUpgradeClient: func(conn api.Connection) CharmUpgradeClient { return application.NewClient(conn) }, NewModelConfigGetter: func(conn api.Connection) ModelConfigGetter { return modelconfig.NewClient(conn) }, NewResourceLister: func(conn api.Connection) (ResourceLister, error) { resclient, err := resourceadapters.NewAPIClient(conn) if err != nil { return nil, err } return resclient, nil }, } return modelcmd.Wrap(cmd) }
func (s *apiCharmsSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.charmsClient = charms.NewClient(s.APIState) c.Assert(s.charmsClient, gc.NotNil) }
"github.com/juju/juju/api" "github.com/juju/juju/api/charms" ) var ( openWebBrowser = func(_ *url.URL) error { return nil } ) type metricRegistrationPost struct { EnvironmentUUID string `json:"env-uuid"` CharmURL string `json:"charm-url"` ServiceName string `json:"service-name"` } var registerMeteredCharm = func(registrationURL string, state api.Connection, jar *cookiejar.Jar, charmURL string, serviceName, environmentUUID string) error { charmsClient := charms.NewClient(state) defer charmsClient.Close() metered, err := charmsClient.IsMetered(charmURL) if err != nil { return err } if metered { httpClient := httpbakery.NewHTTPClient() httpClient.Jar = jar credentials, err := registerMetrics(registrationURL, environmentUUID, charmURL, serviceName, httpClient, openWebBrowser) if err != nil { logger.Infof("failed to register metrics: %v", err) return err } api, cerr := getMetricCredentialsAPI(state)
// 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 }