Exemple #1
0
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
}
Exemple #2
0
// 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
}
Exemple #3
0
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)
}
Exemple #4
0
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
}
Exemple #5
0
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)
}
Exemple #6
0
// 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
}
Exemple #7
0
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
}
Exemple #8
0
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
}
Exemple #9
0
// 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
}
Exemple #10
0
// 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
}
Exemple #11
0
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)

}
Exemple #12
0
// 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)
}
Exemple #13
0
func (s *apiCharmsSuite) SetUpTest(c *gc.C) {
	s.JujuConnSuite.SetUpTest(c)
	s.charmsClient = charms.NewClient(s.APIState)
	c.Assert(s.charmsClient, gc.NotNil)
}
Exemple #14
0
	"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)
Exemple #15
0
// 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
}