Example #1
0
// Set implements gnuflag.Value.Set.
func (f storageFlag) Set(s string) error {
	fields := strings.SplitN(s, "=", 2)
	if len(fields) < 2 {
		return errors.New("expected [<service>:]<store>=<constraints>")
	}
	var serviceName, storageName string
	if colon := strings.IndexRune(fields[0], ':'); colon >= 0 {
		serviceName = fields[0][:colon]
		storageName = fields[0][colon+1:]
	} else {
		storageName = fields[0]
	}
	cons, err := storage.ParseConstraints(fields[1])
	if err != nil {
		return errors.Annotate(err, "cannot parse disk constraints")
	}
	var stores map[string]storage.Constraints
	if serviceName != "" {
		if *f.bundleStores == nil {
			*f.bundleStores = make(map[string]map[string]storage.Constraints)
		}
		stores = (*f.bundleStores)[serviceName]
		if stores == nil {
			stores = make(map[string]storage.Constraints)
			(*f.bundleStores)[serviceName] = stores
		}
	} else {
		if *f.stores == nil {
			*f.stores = make(map[string]storage.Constraints)
		}
		stores = *f.stores
	}
	stores[storageName] = cons
	return nil
}
Example #2
0
// Set implements gnuflag.Value.Set.
func (f disksFlag) Set(s string) error {
	for _, field := range strings.Fields(s) {
		cons, err := storage.ParseConstraints(field)
		if err != nil {
			return errors.Annotate(err, "cannot parse disk constraints")
		}
		*f.disks = append(*f.disks, cons)
	}
	return nil
}
Example #3
0
// Set implements gnuflag.Value.Set.
func (f storageFlag) Set(s string) error {
	fields := strings.SplitN(s, "=", 2)
	if len(fields) < 2 {
		return errors.New("expected <store>=<constraints>")
	}
	cons, err := storage.ParseConstraints(fields[1])
	if err != nil {
		return errors.Annotate(err, "cannot parse disk constraints")
	}
	if *f.stores == nil {
		*f.stores = make(map[string]storage.Constraints)
	}
	(*f.stores)[fields[0]] = cons
	return nil
}
Example #4
0
// GetBundleChanges returns the list of changes required to deploy the given
// bundle data. The changes are sorted by requirements, so that they can be
// applied in order.
func (c *Client) GetBundleChanges(args params.GetBundleChangesParams) (params.GetBundleChangesResults, error) {
	if err := c.checkCanRead(); err != nil {
		return params.GetBundleChangesResults{}, err
	}

	var results params.GetBundleChangesResults
	data, err := charm.ReadBundleData(strings.NewReader(args.BundleDataYAML))
	if err != nil {
		return results, errors.Annotate(err, "cannot read bundle YAML")
	}
	verifyConstraints := func(s string) error {
		_, err := constraints.Parse(s)
		return err
	}
	verifyStorage := func(s string) error {
		_, err := storage.ParseConstraints(s)
		return err
	}
	if err := data.Verify(verifyConstraints, verifyStorage); err != nil {
		if err, ok := err.(*charm.VerificationError); ok {
			results.Errors = make([]string, len(err.Errors))
			for i, e := range err.Errors {
				results.Errors[i] = e.Error()
			}
			return results, nil
		}
		// This should never happen as Verify only returns verification errors.
		return results, errors.Annotate(err, "cannot verify bundle")
	}
	changes := bundlechanges.FromData(data)
	results.Changes = make([]*params.BundleChangesChange, len(changes))
	for i, c := range changes {
		results.Changes[i] = &params.BundleChangesChange{
			Id:       c.Id(),
			Method:   c.Method(),
			Args:     c.GUIArgs(),
			Requires: c.Requires(),
		}
	}
	return results, nil
}
Example #5
0
File: bundle.go Project: makyo/juju
// deployBundle deploys the given bundle data using the given API client and
// charm store client. The deployment is not transactional, and its progress is
// notified using the given deployment logger.
func deployBundle(
	bundleFilePath string,
	data *charm.BundleData,
	channel csparams.Channel,
	client *api.Client,
	serviceDeployer *serviceDeployer,
	resolver *charmURLResolver,
	log deploymentLogger,
	bundleStorage map[string]map[string]storage.Constraints,
) (map[*charm.URL]*macaroon.Macaroon, error) {
	verifyConstraints := func(s string) error {
		_, err := constraints.Parse(s)
		return err
	}
	verifyStorage := func(s string) error {
		_, err := storage.ParseConstraints(s)
		return err
	}
	var verifyError error
	if bundleFilePath == "" {
		verifyError = data.Verify(verifyConstraints, verifyStorage)
	} else {
		verifyError = data.VerifyLocal(bundleFilePath, verifyConstraints, verifyStorage)
	}
	if verifyError != nil {
		if verr, ok := verifyError.(*charm.VerificationError); ok {
			errs := make([]string, len(verr.Errors))
			for i, err := range verr.Errors {
				errs[i] = err.Error()
			}
			return nil, errors.New("the provided bundle has the following errors:\n" + strings.Join(errs, "\n"))
		}
		return nil, errors.Annotate(verifyError, "cannot deploy bundle")
	}

	// Retrieve bundle changes.
	changes := bundlechanges.FromData(data)
	numChanges := len(changes)

	// Initialize the unit status.
	status, err := client.Status(nil)
	if err != nil {
		return nil, errors.Annotate(err, "cannot get model status")
	}
	unitStatus := make(map[string]string, numChanges)
	for _, serviceData := range status.Services {
		for unit, unitData := range serviceData.Units {
			unitStatus[unit] = unitData.Machine
		}
	}

	// Instantiate a watcher used to follow the deployment progress.
	watcher, err := watchAll(client)
	if err != nil {
		return nil, errors.Annotate(err, "cannot watch model")
	}
	defer watcher.Stop()

	serviceClient, err := serviceDeployer.newServiceAPIClient()
	if err != nil {
		return nil, errors.Annotate(err, "cannot get service client")
	}

	annotationsClient, err := serviceDeployer.newAnnotationsAPIClient()
	if err != nil {
		return nil, errors.Annotate(err, "cannot get annotations client")
	}

	// Instantiate the bundle handler.
	h := &bundleHandler{
		bundleDir:         bundleFilePath,
		changes:           changes,
		results:           make(map[string]string, numChanges),
		channel:           channel,
		client:            client,
		serviceClient:     serviceClient,
		annotationsClient: annotationsClient,
		serviceDeployer:   serviceDeployer,
		bundleStorage:     bundleStorage,
		resolver:          resolver,
		log:               log,
		data:              data,
		unitStatus:        unitStatus,
		ignoredMachines:   make(map[string]bool, len(data.Services)),
		ignoredUnits:      make(map[string]bool, len(data.Services)),
		watcher:           watcher,
	}

	// Deploy the bundle.
	csMacs := make(map[*charm.URL]*macaroon.Macaroon)
	channels := make(map[*charm.URL]csparams.Channel)
	for _, change := range changes {
		switch change := change.(type) {
		case *bundlechanges.AddCharmChange:
			cURL, channel, csMac, err2 := h.addCharm(change.Id(), change.Params)
			if err2 == nil {
				csMacs[cURL] = csMac
				channels[cURL] = channel
			}
			err = err2
		case *bundlechanges.AddMachineChange:
			err = h.addMachine(change.Id(), change.Params)
		case *bundlechanges.AddRelationChange:
			err = h.addRelation(change.Id(), change.Params)
		case *bundlechanges.AddServiceChange:
			var cURL *charm.URL
			cURL, err = charm.ParseURL(resolve(change.Params.Charm, h.results))
			if err == nil {
				chID := charmstore.CharmID{
					URL:     cURL,
					Channel: channels[cURL],
				}
				csMac := csMacs[cURL]
				err = h.addService(change.Id(), change.Params, chID, csMac)
			}
		case *bundlechanges.AddUnitChange:
			err = h.addUnit(change.Id(), change.Params)
		case *bundlechanges.ExposeChange:
			err = h.exposeService(change.Id(), change.Params)
		case *bundlechanges.SetAnnotationsChange:
			err = h.setAnnotations(change.Id(), change.Params)
		default:
			return nil, errors.Errorf("unknown change type: %T", change)
		}
		if err != nil {
			return nil, errors.Annotate(err, "cannot deploy bundle")
		}
	}
	return csMacs, nil
}
Example #6
0
File: bundle.go Project: makyo/juju
// addService deploys or update a service with no units. Service options are
// also set or updated.
func (h *bundleHandler) addService(id string, p bundlechanges.AddServiceParams, chID charmstore.CharmID, csMac *macaroon.Macaroon) error {
	h.results[id] = p.Service
	ch := chID.URL.String()
	// Handle service configuration.
	configYAML := ""
	if len(p.Options) > 0 {
		config, err := yaml.Marshal(map[string]map[string]interface{}{p.Service: p.Options})
		if err != nil {
			return errors.Annotatef(err, "cannot marshal options for service %q", p.Service)
		}
		configYAML = string(config)
	}
	// Handle service 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 service")
	}
	storageConstraints := h.bundleStorage[p.Service]
	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.client.CharmInfo(ch)
	if err != nil {
		return err
	}
	resNames2IDs, err := handleResources(h.serviceDeployer.api, resources, p.Service, chID, csMac, charmInfo.Meta.Resources)
	if err != nil {
		return errors.Trace(err)
	}

	// Figure out what series we need to deploy with.
	conf, err := getClientConfig(h.client)
	if err != nil {
		return err
	}
	supportedSeries := charmInfo.Meta.Series
	series, message, err := charmSeries(p.Series, chID.URL.Series, supportedSeries, false, conf, deployFromBundle)
	if err != nil {
		return errors.Trace(err)
	}

	// Deploy the service.
	if err := h.serviceDeployer.serviceDeploy(serviceDeployParams{
		charmID:       chID,
		serviceName:   p.Service,
		series:        series,
		configYAML:    configYAML,
		constraints:   cons,
		storage:       storageConstraints,
		spaceBindings: p.EndpointBindings,
		resources:     resNames2IDs,
	}); err == nil {
		h.log.Infof("service %s deployed (charm %s %v)", p.Service, ch, fmt.Sprintf(message, series))
		for resName := range resNames2IDs {
			h.log.Infof("added resource %s", resName)
		}
		return nil
	} else if !isErrServiceExists(err) {
		return errors.Annotatef(err, "cannot deploy service %q", p.Service)
	}
	// The service 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 service or upgrade to a specified revision.
	// Exit with an error otherwise.
	if err := h.upgradeCharm(p.Service, chID, csMac, resources); err != nil {
		return errors.Annotatef(err, "cannot upgrade service %q", p.Service)
	}
	// Update service configuration.
	if configYAML != "" {
		if err := h.serviceClient.Update(params.ServiceUpdate{
			ServiceName:  p.Service,
			SettingsYAML: configYAML,
		}); err != nil {
			// This should never happen as possible errors are already returned
			// by the service Deploy call above.
			return errors.Annotatef(err, "cannot update options for service %q", p.Service)
		}
		h.log.Infof("configuration updated for service %s", p.Service)
	}
	// Update service constraints.
	if p.Constraints != "" {
		if err := h.serviceClient.SetConstraints(p.Service, cons); err != nil {
			// This should never happen, as the bundle is already verified.
			return errors.Annotatef(err, "cannot update constraints for service %q", p.Service)
		}
		h.log.Infof("constraints applied for service %s", p.Service)
	}
	return nil
}
Example #7
0
File: bundle.go Project: OSBI/juju
// deployBundle deploys the given bundle data using the given API client and
// charm store client. The deployment is not transactional, and its progress is
// notified using the given deployment logger.
func deployBundle(
	data *charm.BundleData, client *api.Client, serviceDeployer *serviceDeployer,
	csclient *csClient, repoPath string, conf *config.Config, log deploymentLogger,
	bundleStorage map[string]map[string]storage.Constraints,
) error {
	verifyConstraints := func(s string) error {
		_, err := constraints.Parse(s)
		return err
	}
	verifyStorage := func(s string) error {
		_, err := storage.ParseConstraints(s)
		return err
	}
	if err := data.Verify(verifyConstraints, verifyStorage); err != nil {
		return errors.Annotate(err, "cannot deploy bundle")
	}

	// Retrieve bundle changes.
	changes := bundlechanges.FromData(data)
	numChanges := len(changes)

	// Initialize the unit status.
	status, err := client.Status(nil)
	if err != nil {
		return errors.Annotate(err, "cannot get model status")
	}
	unitStatus := make(map[string]string, numChanges)
	for _, serviceData := range status.Services {
		for unit, unitData := range serviceData.Units {
			unitStatus[unit] = unitData.Machine
		}
	}

	// Instantiate a watcher used to follow the deployment progress.
	watcher, err := watchAll(client)
	if err != nil {
		return errors.Annotate(err, "cannot watch model")
	}
	defer watcher.Stop()

	serviceClient, err := serviceDeployer.newServiceAPIClient()
	if err != nil {
		return errors.Annotate(err, "cannot get service client")
	}

	annotationsClient, err := serviceDeployer.newAnnotationsAPIClient()
	if err != nil {
		return errors.Annotate(err, "cannot get annotations client")
	}

	// Instantiate the bundle handler.
	h := &bundleHandler{
		changes:           changes,
		results:           make(map[string]string, numChanges),
		client:            client,
		serviceClient:     serviceClient,
		annotationsClient: annotationsClient,
		serviceDeployer:   serviceDeployer,
		bundleStorage:     bundleStorage,
		csclient:          csclient,
		repoPath:          repoPath,
		conf:              conf,
		log:               log,
		data:              data,
		unitStatus:        unitStatus,
		ignoredMachines:   make(map[string]bool, len(data.Services)),
		ignoredUnits:      make(map[string]bool, len(data.Services)),
		watcher:           watcher,
	}

	// Deploy the bundle.
	for _, change := range changes {
		switch change := change.(type) {
		case *bundlechanges.AddCharmChange:
			err = h.addCharm(change.Id(), change.Params)
		case *bundlechanges.AddMachineChange:
			err = h.addMachine(change.Id(), change.Params)
		case *bundlechanges.AddRelationChange:
			err = h.addRelation(change.Id(), change.Params)
		case *bundlechanges.AddServiceChange:
			err = h.addService(change.Id(), change.Params)
		case *bundlechanges.AddUnitChange:
			err = h.addUnit(change.Id(), change.Params)
		case *bundlechanges.ExposeChange:
			err = h.exposeService(change.Id(), change.Params)
		case *bundlechanges.SetAnnotationsChange:
			err = h.setAnnotations(change.Id(), change.Params)
		default:
			return errors.Errorf("unknown change type: %T", change)
		}
		if err != nil {
			return errors.Annotate(err, "cannot deploy bundle")
		}
	}
	return nil
}
Example #8
0
File: bundle.go Project: OSBI/juju
// addService deploys or update a service with no units. Service options are
// also set or updated.
func (h *bundleHandler) addService(id string, p bundlechanges.AddServiceParams) error {
	h.results[id] = p.Service
	ch := resolve(p.Charm, h.results)
	// Handle service configuration.
	configYAML := ""
	if len(p.Options) > 0 {
		config, err := yaml.Marshal(map[string]map[string]interface{}{p.Service: p.Options})
		if err != nil {
			return errors.Annotatef(err, "cannot marshal options for service %q", p.Service)
		}
		configYAML = string(config)
	}
	// Handle service 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 service")
	}
	storageConstraints := h.bundleStorage[p.Service]
	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
		}
	}
	// Deploy the service.
	if err := h.serviceDeployer.serviceDeploy(serviceDeployParams{
		charmURL:      ch,
		serviceName:   p.Service,
		configYAML:    configYAML,
		constraints:   cons,
		storage:       storageConstraints,
		spaceBindings: p.EndpointBindings,
	}); err == nil {
		h.log.Infof("service %s deployed (charm: %s)", p.Service, ch)
		return nil
	} else if !isErrServiceExists(err) {
		return errors.Annotatef(err, "cannot deploy service %q", p.Service)
	}
	// The service 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 service or upgrade to a specified revision.
	// Exit with an error otherwise.
	if err := upgradeCharm(h.serviceClient, h.log, p.Service, ch); err != nil {
		return errors.Annotatef(err, "cannot upgrade service %q", p.Service)
	}
	// Update service configuration.
	if configYAML != "" {
		if err := h.serviceClient.Update(params.ServiceUpdate{
			ServiceName:  p.Service,
			SettingsYAML: configYAML,
		}); err != nil {
			// This should never happen as possible errors are already returned
			// by the service Deploy call above.
			return errors.Annotatef(err, "cannot update options for service %q", p.Service)
		}
		h.log.Infof("configuration updated for service %s", p.Service)
	}
	// Update service constraints.
	if p.Constraints != "" {
		if err := h.serviceClient.SetConstraints(p.Service, cons); err != nil {
			// This should never happen, as the bundle is already verified.
			return errors.Annotatef(err, "cannot update constraints for service %q", p.Service)
		}
		h.log.Infof("constraints applied for service %s", p.Service)
	}
	return nil
}
Example #9
0
File: bundle.go Project: bac/juju
// 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
}
Example #10
0
func (*ConstraintsSuite) testParseError(c *gc.C, s, expectErr string) {
	_, err := storage.ParseConstraints(s)
	c.Check(err, gc.ErrorMatches, expectErr)
}
Example #11
0
func (*ConstraintsSuite) testParse(c *gc.C, s string, expect storage.Constraints) {
	cons, err := storage.ParseConstraints(s)
	c.Check(err, jc.ErrorIsNil)
	c.Check(cons, gc.DeepEquals, expect)
}