github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/application/bundle.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package application
     5  
     6  import (
     7  	"encoding/base64"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"os"
    11  	"path/filepath"
    12  	"reflect"
    13  	"strings"
    14  	"time"
    15  
    16  	"github.com/juju/bundlechanges"
    17  	"github.com/juju/cmd"
    18  	"github.com/juju/collections/set"
    19  	"github.com/juju/errors"
    20  	"github.com/juju/utils"
    21  	"github.com/kr/pretty"
    22  	"gopkg.in/juju/charm.v6"
    23  	"gopkg.in/juju/charmrepo.v3"
    24  	csparams "gopkg.in/juju/charmrepo.v3/csclient/params"
    25  	"gopkg.in/juju/names.v2"
    26  	"gopkg.in/macaroon.v2-unstable"
    27  	"gopkg.in/yaml.v2"
    28  
    29  	"github.com/juju/juju/api"
    30  	"github.com/juju/juju/api/application"
    31  	"github.com/juju/juju/apiserver/params"
    32  	"github.com/juju/juju/charmstore"
    33  	"github.com/juju/juju/core/constraints"
    34  	"github.com/juju/juju/core/devices"
    35  	"github.com/juju/juju/core/instance"
    36  	"github.com/juju/juju/core/lxdprofile"
    37  	"github.com/juju/juju/core/model"
    38  	"github.com/juju/juju/environs/config"
    39  	"github.com/juju/juju/resource/resourceadapters"
    40  	"github.com/juju/juju/state/multiwatcher"
    41  	"github.com/juju/juju/state/watcher"
    42  	"github.com/juju/juju/storage"
    43  )
    44  
    45  var watchAll = func(c *api.Client) (allWatcher, error) {
    46  	return c.WatchAll()
    47  }
    48  
    49  type allWatcher interface {
    50  	Next() ([]multiwatcher.Delta, error)
    51  	Stop() error
    52  }
    53  
    54  // deploymentLogger is used to notify clients about the bundle deployment
    55  // progress.
    56  type deploymentLogger interface {
    57  	// Infof formats and logs the given message.
    58  	Infof(string, ...interface{})
    59  }
    60  
    61  // composeBundle adds the overlays and bundle includes into the passed bundle
    62  // data struct.
    63  func composeBundle(data *charm.BundleData, ctx *cmd.Context, bundleDir string, overlayFileNames []string) error {
    64  	if err := processBundleOverlay(data, overlayFileNames...); err != nil {
    65  		return errors.Annotate(err, "unable to process overlays")
    66  	}
    67  	if bundleDir == "" {
    68  		bundleDir = ctx.Dir
    69  	}
    70  	if err := processBundleIncludes(bundleDir, data); err != nil {
    71  		return errors.Annotate(err, "unable to process includes")
    72  	}
    73  	return nil
    74  }
    75  
    76  func verifyBundle(data *charm.BundleData, bundleDir string) error {
    77  	verifyConstraints := func(s string) error {
    78  		_, err := constraints.Parse(s)
    79  		return err
    80  	}
    81  	verifyStorage := func(s string) error {
    82  		_, err := storage.ParseConstraints(s)
    83  		return err
    84  	}
    85  	verifyDevices := func(s string) error {
    86  		_, err := devices.ParseConstraints(s)
    87  		return err
    88  	}
    89  	var verifyError error
    90  	if bundleDir == "" {
    91  		verifyError = data.Verify(verifyConstraints, verifyStorage, verifyDevices)
    92  	} else {
    93  		verifyError = data.VerifyLocal(bundleDir, verifyConstraints, verifyStorage, verifyDevices)
    94  	}
    95  
    96  	if verr, ok := errors.Cause(verifyError).(*charm.VerificationError); ok {
    97  		errs := make([]string, len(verr.Errors))
    98  		for i, err := range verr.Errors {
    99  			errs[i] = err.Error()
   100  		}
   101  		return errors.New("the provided bundle has the following errors:\n" + strings.Join(errs, "\n"))
   102  	}
   103  	return errors.Trace(verifyError)
   104  }
   105  
   106  // deployBundle deploys the given bundle data using the given API client and
   107  // charm store client. The deployment is not transactional, and its progress is
   108  // notified using the given deployment logger.
   109  func deployBundle(
   110  	bundleDir string,
   111  	data *charm.BundleData,
   112  	bundleURL *charm.URL,
   113  	bundleOverlayFile []string,
   114  	channel csparams.Channel,
   115  	apiRoot DeployAPI,
   116  	ctx *cmd.Context,
   117  	bundleStorage map[string]map[string]storage.Constraints,
   118  	bundleDevices map[string]map[string]devices.Constraints,
   119  	dryRun bool,
   120  	useExistingMachines bool,
   121  	bundleMachines map[string]string,
   122  ) (map[*charm.URL]*macaroon.Macaroon, error) {
   123  
   124  	if err := composeBundle(data, ctx, bundleDir, bundleOverlayFile); err != nil {
   125  		return nil, errors.Trace(err)
   126  	}
   127  	if err := verifyBundle(data, bundleDir); err != nil {
   128  		return nil, errors.Trace(err)
   129  	}
   130  
   131  	// TODO: move bundle parsing and checking into the handler.
   132  	h := makeBundleHandler(dryRun, bundleDir, channel, apiRoot, ctx, data, bundleURL, bundleStorage, bundleDevices)
   133  	if err := h.makeModel(useExistingMachines, bundleMachines); err != nil {
   134  		return nil, errors.Trace(err)
   135  	}
   136  	if err := h.resolveCharmsAndEndpoints(); err != nil {
   137  		return nil, errors.Trace(err)
   138  	}
   139  	if err := h.getChanges(); err != nil {
   140  		return nil, errors.Trace(err)
   141  	}
   142  	if err := h.handleChanges(); err != nil {
   143  		return nil, errors.Trace(err)
   144  	}
   145  	return h.macaroons, nil
   146  
   147  }
   148  
   149  // bundleHandler provides helpers and the state required to deploy a bundle.
   150  type bundleHandler struct {
   151  	dryRun bool
   152  
   153  	// bundleDir is the path where the bundle file is located for local bundles.
   154  	bundleDir string
   155  	// changes holds the changes to be applied in order to deploy the bundle.
   156  	changes []bundlechanges.Change
   157  
   158  	// applications are all the applications defined in the bundle.
   159  	// Used primarily for iterating over sorted values.
   160  	applications set.Strings
   161  
   162  	// results collects data resulting from applying changes. Keys identify
   163  	// changes, values result from interacting with the environment, and are
   164  	// stored so that they can be potentially reused later, for instance for
   165  	// resolving a dynamic placeholder included in a change. Specifically, the
   166  	// following values are stored:
   167  	// - when adding a charm, the fully resolved charm is stored;
   168  	// - when deploying an application, the application name is stored;
   169  	// - when adding a machine, the resulting machine id is stored;
   170  	// - when adding a unit, either the id of the machine holding the unit or
   171  	//   the unit name can be stored. The latter happens when a machine is
   172  	//   implicitly created by adding a unit without a machine spec.
   173  	results map[string]string
   174  
   175  	// channel identifies the default channel to use for the bundle.
   176  	channel csparams.Channel
   177  
   178  	// api is used to interact with the environment.
   179  	api DeployAPI
   180  
   181  	// bundleStorage contains a mapping of application-specific storage
   182  	// constraints. For each application, the storage constraints in the
   183  	// map will replace or augment the storage constraints specified
   184  	// in the bundle itself.
   185  	bundleStorage map[string]map[string]storage.Constraints
   186  
   187  	// bundleDevices contains a mapping of application-specific device
   188  	// constraints. For each application, the device constraints in the
   189  	// map will replace or augment the device constraints specified
   190  	// in the bundle itself.
   191  	bundleDevices map[string]map[string]devices.Constraints
   192  
   193  	// ctx is the command context, which is used to output messages to the
   194  	// user, so that the user can keep track of the bundle deployment
   195  	// progress.
   196  	ctx *cmd.Context
   197  
   198  	// data is the original bundle data that we want to deploy.
   199  	data *charm.BundleData
   200  
   201  	// bundleURL is the URL of the bundle when deploying a bundle from the
   202  	// charmstore, nil otherwise.
   203  	bundleURL *charm.URL
   204  
   205  	// unitStatus reflects the environment status and maps unit names to their
   206  	// corresponding machine identifiers. This is kept updated by both change
   207  	// handlers (addCharm, addApplication etc.) and by updateUnitStatus.
   208  	unitStatus map[string]string
   209  
   210  	modelConfig *config.Config
   211  
   212  	model *bundlechanges.Model
   213  
   214  	macaroons map[*charm.URL]*macaroon.Macaroon
   215  	channels  map[*charm.URL]csparams.Channel
   216  
   217  	// watcher holds an environment mega-watcher used to keep the environment
   218  	// status up to date.
   219  	watcher allWatcher
   220  
   221  	// warnedLXC indicates whether or not we have warned the user that the
   222  	// bundle they're deploying uses lxc containers, which will be treated as
   223  	// LXD.  This flag keeps us from writing the warning more than once per
   224  	// bundle.
   225  	warnedLXC bool
   226  }
   227  
   228  func makeBundleHandler(
   229  	dryRun bool,
   230  	bundleDir string,
   231  	channel csparams.Channel,
   232  	api DeployAPI,
   233  	ctx *cmd.Context,
   234  	data *charm.BundleData,
   235  	bundleURL *charm.URL,
   236  	bundleStorage map[string]map[string]storage.Constraints,
   237  	bundleDevices map[string]map[string]devices.Constraints,
   238  ) *bundleHandler {
   239  	applications := set.NewStrings()
   240  	for name := range data.Applications {
   241  		applications.Add(name)
   242  	}
   243  	return &bundleHandler{
   244  		dryRun:        dryRun,
   245  		bundleDir:     bundleDir,
   246  		applications:  applications,
   247  		results:       make(map[string]string),
   248  		channel:       channel,
   249  		api:           api,
   250  		bundleStorage: bundleStorage,
   251  		bundleDevices: bundleDevices,
   252  		ctx:           ctx,
   253  		data:          data,
   254  		bundleURL:     bundleURL,
   255  		unitStatus:    make(map[string]string),
   256  		macaroons:     make(map[*charm.URL]*macaroon.Macaroon),
   257  		channels:      make(map[*charm.URL]csparams.Channel),
   258  	}
   259  }
   260  
   261  func (h *bundleHandler) makeModel(
   262  	useExistingMachines bool,
   263  	bundleMachines map[string]string,
   264  ) error {
   265  	// Initialize the unit status.
   266  	status, err := h.api.Status(nil)
   267  	if err != nil {
   268  		return errors.Annotate(err, "cannot get model status")
   269  	}
   270  	h.model, err = buildModelRepresentation(status, h.api, useExistingMachines, bundleMachines)
   271  	if err != nil {
   272  		return errors.Trace(err)
   273  	}
   274  	logger.Debugf("model: %s", pretty.Sprint(h.model))
   275  
   276  	for _, appData := range status.Applications {
   277  		for unit, unitData := range appData.Units {
   278  			h.unitStatus[unit] = unitData.Machine
   279  		}
   280  	}
   281  
   282  	h.modelConfig, err = getModelConfig(h.api)
   283  	if err != nil {
   284  		return err
   285  	}
   286  	return nil
   287  }
   288  
   289  // resolveCharmsAndEndpoints will go through the bundle and
   290  // resolve the charm URLs. From the model the charm names are
   291  // fully qualified, meaning they have a source and revision id.
   292  // Effectively the logic this method follows is:
   293  //   * if the bundle specifies a local charm, and the application
   294  //     exists already, then override the charm URL in the bundle
   295  //     spec to match the charm name from the model. We don't
   296  //     upgrade local charms as part of a bundle deploy.
   297  //   * the charm URL is resolved and the bundle spec is replaced
   298  //     with the fully resolved charm URL - i.e.: with rev id.
   299  //   * check all endpoints, and if any of them have implicit endpoints,
   300  //     and if they do, resolve the implicitness in order to compare
   301  //     with relations in the model.
   302  func (h *bundleHandler) resolveCharmsAndEndpoints() error {
   303  
   304  	deployedApps := set.NewStrings()
   305  
   306  	for _, name := range h.applications.SortedValues() {
   307  		spec := h.data.Applications[name]
   308  		app := h.model.GetApplication(name)
   309  		if app != nil {
   310  			deployedApps.Add(name)
   311  
   312  			if h.isLocalCharm(spec.Charm) {
   313  				logger.Debugf("%s exists in model uses a local charm, replacing with %q", name, app.Charm)
   314  				// Replace with charm from model
   315  				spec.Charm = app.Charm
   316  				continue
   317  			}
   318  			// If the charm matches, don't bother resolving.
   319  			if spec.Charm == app.Charm {
   320  				continue
   321  			}
   322  		}
   323  
   324  		if h.isLocalCharm(spec.Charm) {
   325  			continue
   326  		}
   327  
   328  		h.ctx.Infof("Resolving charm: %s", spec.Charm)
   329  		ch, err := charm.ParseURL(spec.Charm)
   330  		if err != nil {
   331  			return errors.Trace(err)
   332  		}
   333  		url, _, _, err := resolveCharm(h.api.ResolveWithChannel, ch)
   334  		if err != nil {
   335  			return errors.Annotatef(err, "cannot resolve URL %q", spec.Charm)
   336  		}
   337  		if url.Series == "bundle" {
   338  			return errors.Errorf("expected charm URL, got bundle URL %q", spec.Charm)
   339  		}
   340  
   341  		spec.Charm = url.String()
   342  	}
   343  
   344  	// TODO(thumper): the InferEndpoints code is deeply wedged in the
   345  	// persistence layer and needs to be extracted. This is a multi-day
   346  	// effort, so for now the bundle handling is doing no implicit endpoint
   347  	// handling.
   348  	return nil
   349  }
   350  
   351  func (h *bundleHandler) getChanges() error {
   352  	bundleURL := ""
   353  	if h.bundleURL != nil {
   354  		bundleURL = h.bundleURL.String()
   355  	}
   356  	changes, err := bundlechanges.FromData(
   357  		bundlechanges.ChangesConfig{
   358  			Bundle:    h.data,
   359  			BundleURL: bundleURL,
   360  			Model:     h.model,
   361  			Logger:    logger,
   362  		})
   363  	if err != nil {
   364  		return errors.Trace(err)
   365  	}
   366  
   367  	h.changes = changes
   368  	return nil
   369  }
   370  
   371  func (h *bundleHandler) handleChanges() error {
   372  	var err error
   373  	// Instantiate a watcher used to follow the deployment progress.
   374  	h.watcher, err = h.api.WatchAll()
   375  	if err != nil {
   376  		return errors.Annotate(err, "cannot watch model")
   377  	}
   378  	defer h.watcher.Stop()
   379  
   380  	if len(h.changes) == 0 {
   381  		h.ctx.Infof("No changes to apply.")
   382  		return nil
   383  	}
   384  
   385  	if h.dryRun {
   386  		fmt.Fprintf(h.ctx.Stdout, "Changes to deploy bundle:\n")
   387  	} else {
   388  		fmt.Fprintf(h.ctx.Stdout, "Executing changes:\n")
   389  	}
   390  
   391  	// Deploy the bundle.
   392  	for i, change := range h.changes {
   393  		fmt.Fprintf(h.ctx.Stdout, "- %s\n", change.Description())
   394  		logger.Tracef("%d: change %s", i, pretty.Sprint(change))
   395  		switch change := change.(type) {
   396  		case *bundlechanges.AddCharmChange:
   397  			err = h.addCharm(change)
   398  		case *bundlechanges.AddMachineChange:
   399  			err = h.addMachine(change)
   400  		case *bundlechanges.AddRelationChange:
   401  			err = h.addRelation(change)
   402  		case *bundlechanges.AddApplicationChange:
   403  			err = h.addApplication(change)
   404  		case *bundlechanges.ScaleChange:
   405  			err = h.scaleApplication(change)
   406  		case *bundlechanges.AddUnitChange:
   407  			err = h.addUnit(change)
   408  		case *bundlechanges.ExposeChange:
   409  			err = h.exposeApplication(change)
   410  		case *bundlechanges.SetAnnotationsChange:
   411  			err = h.setAnnotations(change)
   412  		case *bundlechanges.UpgradeCharmChange:
   413  			err = h.upgradeCharm(change)
   414  		case *bundlechanges.SetOptionsChange:
   415  			err = h.setOptions(change)
   416  		case *bundlechanges.SetConstraintsChange:
   417  			err = h.setConstraints(change)
   418  		default:
   419  			return errors.Errorf("unknown change type: %T", change)
   420  		}
   421  		if err != nil {
   422  			return errors.Trace(err)
   423  		}
   424  	}
   425  
   426  	if !h.dryRun {
   427  		h.ctx.Infof("Deploy of bundle completed.")
   428  	}
   429  
   430  	return nil
   431  }
   432  
   433  func (h *bundleHandler) isLocalCharm(name string) bool {
   434  	return strings.HasPrefix(name, ".") || filepath.IsAbs(name)
   435  }
   436  
   437  // addCharm adds a charm to the environment.
   438  func (h *bundleHandler) addCharm(change *bundlechanges.AddCharmChange) error {
   439  	if h.dryRun {
   440  		return nil
   441  	}
   442  	id := change.Id()
   443  	p := change.Params
   444  	// First attempt to interpret as a local path.
   445  	if h.isLocalCharm(p.Charm) {
   446  		charmPath := p.Charm
   447  		if !filepath.IsAbs(charmPath) {
   448  			charmPath = filepath.Join(h.bundleDir, charmPath)
   449  		}
   450  
   451  		series := p.Series
   452  		if series == "" {
   453  			series = h.data.Series
   454  		}
   455  		ch, curl, err := charmrepo.NewCharmAtPath(charmPath, series)
   456  		if err != nil && !os.IsNotExist(err) {
   457  			return errors.Annotatef(err, "cannot deploy local charm at %q", charmPath)
   458  		}
   459  		if err == nil {
   460  			if err := lxdprofile.ValidateCharmLXDProfile(ch); err != nil {
   461  				return errors.Annotatef(err, "cannot deploy local charm at %q", charmPath)
   462  			}
   463  			if curl, err = h.api.AddLocalCharm(curl, ch, false); err != nil {
   464  				return err
   465  			}
   466  			logger.Debugf("added charm %s", curl)
   467  			h.results[id] = curl.String()
   468  			return nil
   469  		}
   470  	}
   471  
   472  	// Not a local charm, so grab from the store.
   473  	ch, err := charm.ParseURL(p.Charm)
   474  	if err != nil {
   475  		return errors.Trace(err)
   476  	}
   477  
   478  	url, channel, _, err := resolveCharm(h.api.ResolveWithChannel, ch)
   479  	if err != nil {
   480  		return errors.Annotatef(err, "cannot resolve URL %q", p.Charm)
   481  	}
   482  	if url.Series == "bundle" {
   483  		return errors.Errorf("expected charm URL, got bundle URL %q", p.Charm)
   484  	}
   485  	var macaroon *macaroon.Macaroon
   486  	url, macaroon, err = addCharmFromURL(h.api, url, channel, false)
   487  	if err != nil {
   488  		return errors.Annotatef(err, "cannot add charm %q", p.Charm)
   489  	}
   490  	logger.Debugf("added charm %s", url)
   491  	h.results[id] = url.String()
   492  	h.macaroons[url] = macaroon
   493  	h.channels[url] = channel
   494  	return nil
   495  }
   496  
   497  func (h *bundleHandler) makeResourceMap(storeResources map[string]int, localResources map[string]string) map[string]string {
   498  	resources := make(map[string]string)
   499  	for resName, path := range localResources {
   500  		if !filepath.IsAbs(path) {
   501  			path = filepath.Clean(filepath.Join(h.bundleDir, path))
   502  		}
   503  		resources[resName] = path
   504  	}
   505  	for resName, revision := range storeResources {
   506  		resources[resName] = fmt.Sprint(revision)
   507  	}
   508  	return resources
   509  }
   510  
   511  // addApplication deploys an application with no units.
   512  func (h *bundleHandler) addApplication(change *bundlechanges.AddApplicationChange) error {
   513  	// TODO: add verbose output for details
   514  	if h.dryRun {
   515  		return nil
   516  	}
   517  
   518  	p := change.Params
   519  	cURL, err := charm.ParseURL(resolve(p.Charm, h.results))
   520  	if err != nil {
   521  		return errors.Trace(err)
   522  	}
   523  
   524  	chID := charmstore.CharmID{
   525  		URL:     cURL,
   526  		Channel: h.channels[cURL],
   527  	}
   528  	macaroon := h.macaroons[cURL]
   529  
   530  	h.results[change.Id()] = p.Application
   531  	ch := chID.URL.String()
   532  
   533  	// Handle application configuration.
   534  	configYAML := ""
   535  	if len(p.Options) > 0 {
   536  		config, err := yaml.Marshal(map[string]map[string]interface{}{p.Application: p.Options})
   537  		if err != nil {
   538  			return errors.Annotatef(err, "cannot marshal options for application %q", p.Application)
   539  		}
   540  		configYAML = string(config)
   541  	}
   542  	// Handle application constraints.
   543  	cons, err := constraints.Parse(p.Constraints)
   544  	if err != nil {
   545  		// This should never happen, as the bundle is already verified.
   546  		return errors.Annotate(err, "invalid constraints for application")
   547  	}
   548  	storageConstraints := h.bundleStorage[p.Application]
   549  	if len(p.Storage) > 0 {
   550  		if storageConstraints == nil {
   551  			storageConstraints = make(map[string]storage.Constraints)
   552  		}
   553  		for k, v := range p.Storage {
   554  			if _, ok := storageConstraints[k]; ok {
   555  				// Storage constraints overridden
   556  				// on the command line.
   557  				continue
   558  			}
   559  			cons, err := storage.ParseConstraints(v)
   560  			if err != nil {
   561  				return errors.Annotate(err, "invalid storage constraints")
   562  			}
   563  			storageConstraints[k] = cons
   564  		}
   565  	}
   566  	deviceConstraints := h.bundleDevices[p.Application]
   567  	if len(p.Devices) > 0 {
   568  		if deviceConstraints == nil {
   569  			deviceConstraints = make(map[string]devices.Constraints)
   570  		}
   571  		for k, v := range p.Devices {
   572  			if _, ok := deviceConstraints[k]; ok {
   573  				// Device constraints overridden
   574  				// on the command line.
   575  				continue
   576  			}
   577  			cons, err := devices.ParseConstraints(v)
   578  			if err != nil {
   579  				return errors.Annotate(err, "invalid device constraints")
   580  			}
   581  			deviceConstraints[k] = cons
   582  		}
   583  	}
   584  	resources := h.makeResourceMap(p.Resources, p.LocalResources)
   585  	charmInfo, err := h.api.CharmInfo(ch)
   586  	if err != nil {
   587  		return errors.Trace(err)
   588  	}
   589  
   590  	if err := lxdprofile.ValidateCharmInfoLXDProfile(charmInfo); err != nil {
   591  		return errors.Trace(err)
   592  	}
   593  
   594  	resNames2IDs, err := resourceadapters.DeployResources(
   595  		p.Application,
   596  		chID,
   597  		macaroon,
   598  		resources,
   599  		charmInfo.Meta.Resources,
   600  		h.api,
   601  	)
   602  	if err != nil {
   603  		return errors.Trace(err)
   604  	}
   605  
   606  	// Figure out what series we need to deploy with.
   607  	supportedSeries := charmInfo.Meta.Series
   608  	if len(supportedSeries) == 0 && chID.URL.Series != "" {
   609  		supportedSeries = []string{chID.URL.Series}
   610  	}
   611  	selector := seriesSelector{
   612  		seriesFlag:      p.Series,
   613  		charmURLSeries:  chID.URL.Series,
   614  		supportedSeries: supportedSeries,
   615  		conf:            h.modelConfig,
   616  		fromBundle:      true,
   617  	}
   618  	series, err := selector.charmSeries()
   619  	if err != nil {
   620  		return errors.Trace(err)
   621  	}
   622  
   623  	// Only Kubernetes bundles send the unit count and placement with the deploy API call.
   624  	numUnits := 0
   625  	var placement []*instance.Placement
   626  	if h.data.Type == "kubernetes" {
   627  		numUnits = p.NumUnits
   628  		if p.Placement != "" {
   629  			p := &instance.Placement{
   630  				Scope:     h.modelConfig.UUID(),
   631  				Directive: p.Placement,
   632  			}
   633  			placement = []*instance.Placement{p}
   634  		}
   635  	}
   636  	// Deploy the application.
   637  	if err := h.api.Deploy(application.DeployArgs{
   638  		CharmID:          chID,
   639  		Cons:             cons,
   640  		ApplicationName:  p.Application,
   641  		Series:           series,
   642  		NumUnits:         numUnits,
   643  		Placement:        placement,
   644  		ConfigYAML:       configYAML,
   645  		Storage:          storageConstraints,
   646  		Devices:          deviceConstraints,
   647  		Resources:        resNames2IDs,
   648  		EndpointBindings: p.EndpointBindings,
   649  	}); err != nil {
   650  		return errors.Annotatef(err, "cannot deploy application %q", p.Application)
   651  	}
   652  	h.writeAddedResources(resNames2IDs)
   653  
   654  	return nil
   655  }
   656  
   657  func (h *bundleHandler) writeAddedResources(resNames2IDs map[string]string) {
   658  	// Make sure the resources are output in a defined order.
   659  	names := set.NewStrings()
   660  	for resName := range resNames2IDs {
   661  		names.Add(resName)
   662  	}
   663  	for _, name := range names.SortedValues() {
   664  		h.ctx.Infof("  added resource %s", name)
   665  	}
   666  }
   667  
   668  // scaleApplication updates the number of units for an application.
   669  func (h *bundleHandler) scaleApplication(change *bundlechanges.ScaleChange) error {
   670  	if h.dryRun {
   671  		return nil
   672  	}
   673  
   674  	p := change.Params
   675  
   676  	result, err := h.api.ScaleApplication(application.ScaleApplicationParams{
   677  		ApplicationName: p.Application,
   678  		Scale:           p.Scale,
   679  	})
   680  	if err == nil && result.Error != nil {
   681  		err = result.Error
   682  	}
   683  	if err != nil {
   684  		return errors.Annotatef(err, "cannot scale application %q", p.Application)
   685  	}
   686  	return nil
   687  }
   688  
   689  // addMachine creates a new top-level machine or container in the environment.
   690  func (h *bundleHandler) addMachine(change *bundlechanges.AddMachineChange) error {
   691  	p := change.Params
   692  	var verbose []string
   693  	if p.Series != "" {
   694  		verbose = append(verbose, fmt.Sprintf("with series %q", p.Series))
   695  	}
   696  	if p.Constraints != "" {
   697  		verbose = append(verbose, fmt.Sprintf("with constraints %q", p.Constraints))
   698  	}
   699  	if output := strings.Join(verbose, ", "); output != "" {
   700  		h.ctx.Verbosef("  %s", output)
   701  	}
   702  	if h.dryRun {
   703  		return nil
   704  	}
   705  
   706  	deployedApps := func() string {
   707  		apps := h.applicationsForMachineChange(change.Id())
   708  		// Note that we *should* always have at least one application
   709  		// that justifies the creation of this machine. But just in
   710  		// case, check (see https://pad.lv/1773357).
   711  		if len(apps) == 0 {
   712  			h.ctx.Warningf("no applications found for machine change %q", change.Id())
   713  			return "nothing"
   714  		}
   715  		msg := apps[0] + " unit"
   716  
   717  		if count := len(apps); count != 1 {
   718  			msg = strings.Join(apps[:count-1], ", ") + " and " + apps[count-1] + " units"
   719  		}
   720  		return msg
   721  	}
   722  
   723  	cons, err := constraints.Parse(p.Constraints)
   724  	if err != nil {
   725  		// This should never happen, as the bundle is already verified.
   726  		return errors.Annotate(err, "invalid constraints for machine")
   727  	}
   728  	machineParams := params.AddMachineParams{
   729  		Constraints: cons,
   730  		Series:      p.Series,
   731  		Jobs:        []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
   732  	}
   733  	if ct := p.ContainerType; ct != "" {
   734  		// TODO(thumper): move the warning and translation into the bundle reading code.
   735  
   736  		// for backwards compatibility with 1.x bundles, we treat lxc
   737  		// placement directives as lxd.
   738  		if ct == "lxc" {
   739  			if !h.warnedLXC {
   740  				h.ctx.Infof("Bundle has one or more containers specified as lxc. lxc containers are deprecated in Juju 2.0. lxd containers will be deployed instead.")
   741  				h.warnedLXC = true
   742  			}
   743  			ct = string(instance.LXD)
   744  		}
   745  		containerType, err := instance.ParseContainerType(ct)
   746  		if err != nil {
   747  			return errors.Annotatef(err, "cannot create machine for holding %s", deployedApps())
   748  		}
   749  		machineParams.ContainerType = containerType
   750  		if p.ParentId != "" {
   751  			logger.Debugf("p.ParentId: %q", p.ParentId)
   752  			id, err := h.resolveMachine(p.ParentId)
   753  			if err != nil {
   754  				return errors.Annotatef(err, "cannot retrieve parent placement for %s", deployedApps())
   755  			}
   756  			// Never create nested containers for deployment.
   757  			machineParams.ParentId = h.topLevelMachine(id)
   758  		}
   759  	}
   760  	logger.Debugf("machineParams: %s", pretty.Sprint(machineParams))
   761  	r, err := h.api.AddMachines([]params.AddMachineParams{machineParams})
   762  	if err != nil {
   763  		return errors.Annotatef(err, "cannot create machine for holding %s", deployedApps())
   764  	}
   765  	if r[0].Error != nil {
   766  		return errors.Annotatef(r[0].Error, "cannot create machine for holding %s", deployedApps())
   767  	}
   768  	machine := r[0].Machine
   769  	if p.ContainerType == "" {
   770  		logger.Debugf("created new machine %s for holding %s", machine, deployedApps())
   771  	} else if p.ParentId == "" {
   772  		logger.Debugf("created %s container in new machine for holding %s", machine, deployedApps())
   773  	} else {
   774  		logger.Debugf("created %s container in machine %s for holding %s", machine, machineParams.ParentId, deployedApps())
   775  	}
   776  	h.results[change.Id()] = machine
   777  	return nil
   778  }
   779  
   780  // addRelation creates a relationship between two applications.
   781  func (h *bundleHandler) addRelation(change *bundlechanges.AddRelationChange) error {
   782  	if h.dryRun {
   783  		return nil
   784  	}
   785  	p := change.Params
   786  	ep1 := resolveRelation(p.Endpoint1, h.results)
   787  	ep2 := resolveRelation(p.Endpoint2, h.results)
   788  	// TODO(wallyworld) - CMR support in bundles
   789  	_, err := h.api.AddRelation([]string{ep1, ep2}, nil)
   790  	if err != nil {
   791  		// TODO(thumper): remove this error check when we add resolving
   792  		// implicit relations.
   793  		if params.IsCodeAlreadyExists(err) {
   794  			return nil
   795  		}
   796  		return errors.Annotatef(err, "cannot add relation between %q and %q", ep1, ep2)
   797  
   798  	}
   799  	return nil
   800  }
   801  
   802  // addUnit adds a single unit to an application already present in the environment.
   803  func (h *bundleHandler) addUnit(change *bundlechanges.AddUnitChange) error {
   804  	if h.dryRun {
   805  		return nil
   806  	}
   807  
   808  	p := change.Params
   809  	applicationName := resolve(p.Application, h.results)
   810  	var err error
   811  	var placementArg []*instance.Placement
   812  	targetMachine := p.To
   813  	if targetMachine != "" {
   814  		logger.Debugf("addUnit: placement %q", targetMachine)
   815  		// The placement maybe "container:machine"
   816  		container := ""
   817  		if parts := strings.Split(targetMachine, ":"); len(parts) > 1 {
   818  			container = parts[0]
   819  			targetMachine = parts[1]
   820  		}
   821  		targetMachine, err = h.resolveMachine(targetMachine)
   822  		if err != nil {
   823  			// Should never happen.
   824  			return errors.Annotatef(err, "cannot retrieve placement for %q unit", applicationName)
   825  		}
   826  		directive := targetMachine
   827  		if container != "" {
   828  			directive = container + ":" + directive
   829  		}
   830  		placement, err := parsePlacement(directive)
   831  		if err != nil {
   832  			return errors.Errorf("invalid --to parameter %q", directive)
   833  		}
   834  		logger.Debugf("  resolved: placement %q", directive)
   835  		placementArg = append(placementArg, placement)
   836  	}
   837  	r, err := h.api.AddUnits(application.AddUnitsParams{
   838  		ApplicationName: applicationName,
   839  		NumUnits:        1,
   840  		Placement:       placementArg,
   841  	})
   842  	if err != nil {
   843  		return errors.Annotatef(err, "cannot add unit for application %q", applicationName)
   844  	}
   845  	unit := r[0]
   846  	if targetMachine == "" {
   847  		logger.Debugf("added %s unit to new machine", unit)
   848  		// In this case, the unit name is stored in results instead of the
   849  		// machine id, which is lazily evaluated later only if required.
   850  		// This way we avoid waiting for watcher updates.
   851  		h.results[change.Id()] = unit
   852  	} else {
   853  		logger.Debugf("added %s unit to new machine", unit)
   854  		h.results[change.Id()] = targetMachine
   855  	}
   856  
   857  	// Note that the targetMachine can be empty for now, resulting in a partially
   858  	// incomplete unit status. That's ok as the missing info is provided later
   859  	// when it is required.
   860  	h.unitStatus[unit] = targetMachine
   861  	return nil
   862  }
   863  
   864  // upgradeCharm will get the application to use the new charm.
   865  func (h *bundleHandler) upgradeCharm(change *bundlechanges.UpgradeCharmChange) error {
   866  	if h.dryRun {
   867  		return nil
   868  	}
   869  
   870  	p := change.Params
   871  	cURL, err := charm.ParseURL(resolve(p.Charm, h.results))
   872  	if err != nil {
   873  		return errors.Trace(err)
   874  	}
   875  
   876  	chID := charmstore.CharmID{
   877  		URL:     cURL,
   878  		Channel: h.channels[cURL],
   879  	}
   880  	macaroon := h.macaroons[cURL]
   881  
   882  	resources := h.makeResourceMap(p.Resources, p.LocalResources)
   883  
   884  	resourceLister, err := resourceadapters.NewAPIClient(h.api)
   885  	if err != nil {
   886  		return errors.Trace(err)
   887  	}
   888  	filtered, err := getUpgradeResources(h.api, resourceLister, p.Application, cURL, resources)
   889  	if err != nil {
   890  		return errors.Trace(err)
   891  	}
   892  	var resNames2IDs map[string]string
   893  	if len(filtered) != 0 {
   894  		resNames2IDs, err = resourceadapters.DeployResources(
   895  			p.Application,
   896  			chID,
   897  			macaroon,
   898  			resources,
   899  			filtered,
   900  			h.api,
   901  		)
   902  		if err != nil {
   903  			return errors.Trace(err)
   904  		}
   905  	}
   906  
   907  	cfg := application.SetCharmConfig{
   908  		ApplicationName: p.Application,
   909  		CharmID:         chID,
   910  		ResourceIDs:     resNames2IDs,
   911  	}
   912  	// Bundles only ever deal with the current generation.
   913  	if err := h.api.SetCharm(model.GenerationCurrent, cfg); err != nil {
   914  		return errors.Trace(err)
   915  	}
   916  	h.writeAddedResources(resNames2IDs)
   917  
   918  	return nil
   919  }
   920  
   921  // setOptions updates application configuration settings.
   922  func (h *bundleHandler) setOptions(change *bundlechanges.SetOptionsChange) error {
   923  	p := change.Params
   924  	h.ctx.Verbosef("  setting options:")
   925  	for key, value := range p.Options {
   926  		switch value.(type) {
   927  		case string:
   928  			h.ctx.Verbosef("    %s: %q", key, value)
   929  		default:
   930  			h.ctx.Verbosef("    %s: %v", key, value)
   931  		}
   932  	}
   933  	if h.dryRun {
   934  		return nil
   935  	}
   936  
   937  	// We know that there wouldn't be any setOptions if there were no options.
   938  	cfg, err := yaml.Marshal(map[string]map[string]interface{}{p.Application: p.Options})
   939  	if err != nil {
   940  		return errors.Annotatef(err, "cannot marshal options for application %q", p.Application)
   941  	}
   942  
   943  	if err := h.api.Update(params.ApplicationUpdate{
   944  		ApplicationName: p.Application,
   945  		SettingsYAML:    string(cfg),
   946  		Generation:      model.GenerationCurrent,
   947  	}); err != nil {
   948  		return errors.Annotatef(err, "cannot update options for application %q", p.Application)
   949  	}
   950  
   951  	return nil
   952  }
   953  
   954  // setConstraints updates application constraints.
   955  func (h *bundleHandler) setConstraints(change *bundlechanges.SetConstraintsChange) error {
   956  	if h.dryRun {
   957  		return nil
   958  	}
   959  	p := change.Params
   960  	// We know that p.Constraints is a valid constraints type due to the validation.
   961  	cons, _ := constraints.Parse(p.Constraints)
   962  	if err := h.api.SetConstraints(p.Application, cons); err != nil {
   963  		// This should never happen, as the bundle is already verified.
   964  		return errors.Annotatef(err, "cannot update constraints for application %q", p.Application)
   965  	}
   966  
   967  	return nil
   968  }
   969  
   970  // exposeApplication exposes an application.
   971  func (h *bundleHandler) exposeApplication(change *bundlechanges.ExposeChange) error {
   972  	if h.dryRun {
   973  		return nil
   974  	}
   975  
   976  	application := resolve(change.Params.Application, h.results)
   977  	if err := h.api.Expose(application); err != nil {
   978  		return errors.Annotatef(err, "cannot expose application %s", application)
   979  	}
   980  	return nil
   981  }
   982  
   983  // setAnnotations sets annotations for an application or a machine.
   984  func (h *bundleHandler) setAnnotations(change *bundlechanges.SetAnnotationsChange) error {
   985  	p := change.Params
   986  	h.ctx.Verbosef("  setting annotations:")
   987  	for key, value := range p.Annotations {
   988  		h.ctx.Verbosef("    %s: %q", key, value)
   989  	}
   990  	if h.dryRun {
   991  		return nil
   992  	}
   993  	eid := resolve(p.Id, h.results)
   994  	var tag string
   995  	switch p.EntityType {
   996  	case bundlechanges.MachineType:
   997  		tag = names.NewMachineTag(eid).String()
   998  	case bundlechanges.ApplicationType:
   999  		tag = names.NewApplicationTag(eid).String()
  1000  	default:
  1001  		return errors.Errorf("unexpected annotation entity type %q", p.EntityType)
  1002  	}
  1003  	result, err := h.api.SetAnnotation(map[string]map[string]string{tag: p.Annotations})
  1004  	if err == nil && len(result) > 0 {
  1005  		err = result[0].Error
  1006  	}
  1007  	if err != nil {
  1008  		return errors.Annotatef(err, "cannot set annotations for %s %q", p.EntityType, eid)
  1009  	}
  1010  	return nil
  1011  }
  1012  
  1013  // applicationsForMachineChange returns the names of the applications for which an
  1014  // "addMachine" change is required, as adding machines is required to place
  1015  // units, and units belong to applications.
  1016  // Receive the id of the "addMachine" change.
  1017  func (h *bundleHandler) applicationsForMachineChange(changeId string) []string {
  1018  	applications := set.NewStrings()
  1019  mainloop:
  1020  	for _, change := range h.changes {
  1021  		for _, required := range change.Requires() {
  1022  			if required != changeId {
  1023  				continue
  1024  			}
  1025  			switch change := change.(type) {
  1026  			case *bundlechanges.AddMachineChange:
  1027  				// The original machine is a container, and its parent is
  1028  				// another "addMachines" change. Search again using the
  1029  				// parent id.
  1030  				for _, application := range h.applicationsForMachineChange(change.Id()) {
  1031  					applications.Add(application)
  1032  				}
  1033  				continue mainloop
  1034  			case *bundlechanges.AddUnitChange:
  1035  				// We have found the "addUnit" change, which refers to a
  1036  				// application: now resolve the application holding the unit.
  1037  				application := resolve(change.Params.Application, h.results)
  1038  				applications.Add(application)
  1039  				continue mainloop
  1040  			case *bundlechanges.SetAnnotationsChange:
  1041  				// A machine change is always required to set machine
  1042  				// annotations, but this isn't the interesting change here.
  1043  				continue mainloop
  1044  			default:
  1045  				// Should never happen.
  1046  				panic(fmt.Sprintf("unexpected change %T", change))
  1047  			}
  1048  		}
  1049  	}
  1050  	return applications.SortedValues()
  1051  }
  1052  
  1053  // updateUnitStatusPeriod is the time duration used to wait for a mega-watcher
  1054  // change to be available.
  1055  var updateUnitStatusPeriod = watcher.Period + 5*time.Second
  1056  
  1057  // updateUnitStatus uses the mega-watcher to update units and machines info
  1058  // (h.unitStatus) so that it reflects the current environment status.
  1059  // This function must be called assuming new delta changes are available or
  1060  // will be available within the watcher time period. Otherwise, the function
  1061  // unblocks and an error is returned.
  1062  func (h *bundleHandler) updateUnitStatus() error {
  1063  	var delta []multiwatcher.Delta
  1064  	var err error
  1065  	ch := make(chan struct{})
  1066  	go func() {
  1067  		delta, err = h.watcher.Next()
  1068  		close(ch)
  1069  	}()
  1070  	select {
  1071  	case <-ch:
  1072  		if err != nil {
  1073  			return errors.Annotate(err, "cannot update model status")
  1074  		}
  1075  		for _, d := range delta {
  1076  			switch entityInfo := d.Entity.(type) {
  1077  			case *multiwatcher.UnitInfo:
  1078  				h.unitStatus[entityInfo.Name] = entityInfo.MachineId
  1079  			}
  1080  		}
  1081  	case <-time.After(updateUnitStatusPeriod):
  1082  		// TODO(fwereade): 2016-03-17 lp:1558657
  1083  		return errors.New("timeout while trying to get new changes from the watcher")
  1084  	}
  1085  	return nil
  1086  }
  1087  
  1088  // resolveMachine returns the machine id resolving the given unit or machine
  1089  // placeholder.
  1090  func (h *bundleHandler) resolveMachine(placeholder string) (string, error) {
  1091  	logger.Debugf("resolveMachine(%q)", placeholder)
  1092  	machineOrUnit := resolve(placeholder, h.results)
  1093  	if !names.IsValidUnit(machineOrUnit) {
  1094  		return machineOrUnit, nil
  1095  	}
  1096  	for h.unitStatus[machineOrUnit] == "" {
  1097  		if err := h.updateUnitStatus(); err != nil {
  1098  			return "", errors.Annotate(err, "cannot resolve machine")
  1099  		}
  1100  	}
  1101  	return h.unitStatus[machineOrUnit], nil
  1102  }
  1103  
  1104  func (h *bundleHandler) topLevelMachine(id string) string {
  1105  	if !names.IsContainerMachine(id) {
  1106  		return id
  1107  	}
  1108  	tag := names.NewMachineTag(id)
  1109  	return tag.Parent().Id()
  1110  }
  1111  
  1112  // resolveRelation returns the relation name resolving the included application
  1113  // placeholder.
  1114  func resolveRelation(e string, results map[string]string) string {
  1115  	parts := strings.SplitN(e, ":", 2)
  1116  	application := resolve(parts[0], results)
  1117  	if len(parts) == 1 {
  1118  		return application
  1119  	}
  1120  	return fmt.Sprintf("%s:%s", application, parts[1])
  1121  }
  1122  
  1123  // resolve returns the real entity name for the bundle entity (for instance a
  1124  // application or a machine) with the given placeholder id.
  1125  // A placeholder id is a string like "$deploy-42" or "$addCharm-2", indicating
  1126  // the results of a previously applied change. It always starts with a dollar
  1127  // sign, followed by the identifier of the referred change. A change id is a
  1128  // string indicating the action type ("deploy", "addRelation" etc.), followed
  1129  // by a unique incremental number.
  1130  //
  1131  // Now that the bundlechanges library understands the existing model, if the
  1132  // entity already existed in the model, the placeholder value is the actual
  1133  // entity from the model, and in these situations the placeholder value doesn't
  1134  // start with the '$'.
  1135  func resolve(placeholder string, results map[string]string) string {
  1136  	logger.Debugf("resolve %q from %s", placeholder, pretty.Sprint(results))
  1137  	if !strings.HasPrefix(placeholder, "$") {
  1138  		return placeholder
  1139  	}
  1140  	id := placeholder[1:]
  1141  	return results[id]
  1142  }
  1143  
  1144  func processBundleIncludes(baseDir string, data *charm.BundleData) error {
  1145  	for app, appData := range data.Applications {
  1146  		// A bundle isn't valid if there are no applications, and applications must
  1147  		// specify a charm at least, so we know appData must be non-nil.
  1148  		for key, value := range appData.Options {
  1149  			result, processed, err := processValue(baseDir, value)
  1150  			if err != nil {
  1151  				return errors.Annotatef(err, "processing options value %s for application %s", key, app)
  1152  			}
  1153  			if processed {
  1154  				appData.Options[key] = result
  1155  			}
  1156  		}
  1157  		for key, value := range appData.Annotations {
  1158  			result, processed, err := processValue(baseDir, value)
  1159  			if err != nil {
  1160  				return errors.Annotatef(err, "processing annotation value %s for application %s", key, app)
  1161  			}
  1162  			if processed {
  1163  				appData.Annotations[key] = result.(string)
  1164  			}
  1165  		}
  1166  	}
  1167  
  1168  	for machine, machineData := range data.Machines {
  1169  		if machineData == nil {
  1170  			continue
  1171  		}
  1172  		for key, value := range machineData.Annotations {
  1173  			result, processed, err := processValue(baseDir, value)
  1174  			if err != nil {
  1175  				return errors.Annotatef(err, "processing annotation value %s for machine %s", key, machine)
  1176  			}
  1177  			if processed {
  1178  				machineData.Annotations[key] = result.(string)
  1179  			}
  1180  		}
  1181  	}
  1182  	return nil
  1183  }
  1184  
  1185  func processValue(baseDir string, v interface{}) (interface{}, bool, error) {
  1186  
  1187  	const (
  1188  		includeFile   = "include-file://"
  1189  		includeBase64 = "include-base64://"
  1190  	)
  1191  
  1192  	value, ok := v.(string)
  1193  	if !ok {
  1194  		// Not a string, just return it unchanged.
  1195  		return v, false, nil
  1196  	}
  1197  
  1198  	encode := false
  1199  	readFile := false
  1200  	filename := ""
  1201  
  1202  	if strings.HasPrefix(value, includeFile) {
  1203  		readFile = true
  1204  		filename = value[len(includeFile):]
  1205  	} else if strings.HasPrefix(value, includeBase64) {
  1206  		encode = true
  1207  		readFile = true
  1208  		filename = value[len(includeBase64):]
  1209  	}
  1210  
  1211  	if !readFile {
  1212  		// Unchanged, just return it.
  1213  		return v, false, nil
  1214  	}
  1215  
  1216  	if !filepath.IsAbs(filename) {
  1217  		filename = filepath.Clean(filepath.Join(baseDir, filename))
  1218  	}
  1219  
  1220  	bytes, err := ioutil.ReadFile(filename)
  1221  	if err != nil {
  1222  		return nil, false, errors.Annotate(err, "unable to read file")
  1223  	}
  1224  
  1225  	var result string
  1226  	if encode {
  1227  		result = base64.StdEncoding.EncodeToString(bytes)
  1228  	} else {
  1229  		result = string(bytes)
  1230  	}
  1231  
  1232  	return result, true, nil
  1233  }
  1234  
  1235  type bundleOverlayValueExists struct {
  1236  	Applications map[string]map[string]interface{} `yaml:"applications"`
  1237  }
  1238  
  1239  func processBundleOverlay(data *charm.BundleData, bundleOverlayFiles ...string) error {
  1240  	for _, filename := range bundleOverlayFiles {
  1241  		bundleOverlayFile, err := utils.NormalizePath(filename)
  1242  		if err != nil {
  1243  			return errors.Annotate(err, "unable to normalise bundle overlay file")
  1244  		}
  1245  		// Make sure the filename is absolute.
  1246  		if !filepath.IsAbs(bundleOverlayFile) {
  1247  			// TODO(babbageclunk): pass in ctx.Dir rather than using os.Getwd.
  1248  			cwd, err := os.Getwd()
  1249  			if err != nil {
  1250  				return errors.Trace(err)
  1251  			}
  1252  			bundleOverlayFile = filepath.Clean(filepath.Join(cwd, bundleOverlayFile))
  1253  		}
  1254  		if err := processSingleBundleOverlay(data, bundleOverlayFile); err != nil {
  1255  			return errors.Trace(err)
  1256  		}
  1257  	}
  1258  	return nil
  1259  }
  1260  
  1261  func processSingleBundleOverlay(data *charm.BundleData, bundleOverlayFile string) error {
  1262  	config, err := charmrepo.ReadBundleFile(bundleOverlayFile)
  1263  	if err != nil {
  1264  		return errors.Annotatef(err, "unable to read bundle overlay file %q", bundleOverlayFile)
  1265  	}
  1266  
  1267  	// From here we walk through the new bundleData, and override values
  1268  	// in the current bundle.
  1269  
  1270  	// If a new application is added, the content is added.
  1271  	// If an application is defined but with no content, that means
  1272  	// remove the application from the parent bundle.
  1273  	//   - if this is happening, all related relations are also removed.
  1274  	// If the application exists in both, values here override values there.
  1275  	// If machines are defined, they override the entire machines section.
  1276  
  1277  	content, err := ioutil.ReadFile(bundleOverlayFile)
  1278  	if err != nil {
  1279  		return errors.Annotate(err, "unable to open bundle overlay file")
  1280  	}
  1281  	baseDir := filepath.Dir(bundleOverlayFile)
  1282  
  1283  	// If this works, then this deserialisation should certainly succeed.
  1284  	// Since we are only looking to overwrite the values in the underlying bundle
  1285  	// for config values that are set, we need to know if they were actually set,
  1286  	// and not just zero. The configCheck structure is a map that allows us to check
  1287  	// if the fields were actually in the underlying YAML.
  1288  	var configCheck bundleOverlayValueExists
  1289  	if err := yaml.Unmarshal(content, &configCheck); err != nil {
  1290  		return errors.Annotate(err, "unable to deserialize config structure")
  1291  	}
  1292  	// Here there is a possibility that the config file uses top level
  1293  	// applications, whereas the bundleOverlayValueExists only defines the newer
  1294  	// applications. If that is the case, we error out and tell the user.
  1295  	if len(configCheck.Applications) == 0 && len(config.Applications) > 0 {
  1296  		return errors.Errorf("bundle overlay file %q used deprecated 'services' key, this is not valid for bundle overlay files", bundleOverlayFile)
  1297  	}
  1298  
  1299  	// We want to confirm that all the applications mentioned in the config
  1300  	// actually exist in the bundle data.
  1301  	for appName, bc := range config.Applications {
  1302  		app, found := data.Applications[appName]
  1303  		// If bc is nil, that means to remove it from data.
  1304  		if bc == nil {
  1305  			delete(data.Applications, appName)
  1306  			data.Relations = removeRelations(data.Relations, appName)
  1307  			continue
  1308  		}
  1309  		if !found {
  1310  			// Add it in.
  1311  			data.Applications[appName] = bc
  1312  			continue
  1313  		}
  1314  
  1315  		fieldCheck := configCheck.Applications[appName]
  1316  
  1317  		if _, set := fieldCheck["charm"]; set {
  1318  			app.Charm = bc.Charm
  1319  		}
  1320  		if _, set := fieldCheck["series"]; set {
  1321  			app.Series = bc.Series
  1322  		}
  1323  		if _, set := fieldCheck["resources"]; set {
  1324  			if app.Resources == nil {
  1325  				app.Resources = make(map[string]interface{})
  1326  			}
  1327  			for key, value := range bc.Resources {
  1328  				app.Resources[key] = value
  1329  			}
  1330  		}
  1331  		if _, set := fieldCheck["num_units"]; set {
  1332  			app.NumUnits = bc.NumUnits
  1333  		}
  1334  		if _, set := fieldCheck["to"]; set {
  1335  			app.To = bc.To
  1336  		}
  1337  		if _, set := fieldCheck["expose"]; set {
  1338  			app.Expose = bc.Expose
  1339  		}
  1340  		if _, set := fieldCheck["options"]; set {
  1341  			if app.Options == nil {
  1342  				app.Options = make(map[string]interface{})
  1343  			}
  1344  			for key, value := range bc.Options {
  1345  				result, _, err := processValue(baseDir, value)
  1346  				if err != nil {
  1347  					return errors.Annotatef(err, "processing config options value %s for application %s", key, appName)
  1348  				}
  1349  				app.Options[key] = result
  1350  			}
  1351  		}
  1352  		if _, set := fieldCheck["annotations"]; set {
  1353  			if app.Annotations == nil {
  1354  				app.Annotations = make(map[string]string)
  1355  			}
  1356  			for key, value := range bc.Annotations {
  1357  				result, _, err := processValue(baseDir, value)
  1358  				if err != nil {
  1359  					return errors.Annotatef(err, "processing config annotations value %s for application %s", key, appName)
  1360  				}
  1361  				app.Annotations[key] = result.(string)
  1362  			}
  1363  		}
  1364  		if _, set := fieldCheck["constraints"]; set {
  1365  			app.Constraints = bc.Constraints
  1366  		}
  1367  		if _, set := fieldCheck["storage"]; set {
  1368  			if app.Storage == nil {
  1369  				app.Storage = make(map[string]string)
  1370  			}
  1371  			for key, value := range bc.Storage {
  1372  				app.Storage[key] = value
  1373  			}
  1374  		}
  1375  		if _, set := fieldCheck["devices"]; set {
  1376  			if app.Devices == nil {
  1377  				app.Devices = make(map[string]string)
  1378  			}
  1379  			for key, value := range bc.Devices {
  1380  				app.Devices[key] = value
  1381  			}
  1382  		}
  1383  		if _, set := fieldCheck["bindings"]; set {
  1384  			if app.EndpointBindings == nil {
  1385  				app.EndpointBindings = make(map[string]string)
  1386  			}
  1387  			for key, value := range bc.EndpointBindings {
  1388  				app.EndpointBindings[key] = value
  1389  			}
  1390  		}
  1391  	}
  1392  
  1393  	// If series is set in the config, it overrides the bundle.
  1394  	if config.Series != "" {
  1395  		data.Series = config.Series
  1396  	}
  1397  
  1398  	// Next process relations.
  1399  	for _, relation := range config.Relations {
  1400  		data.Relations = append(data.Relations, relation)
  1401  	}
  1402  
  1403  	// Finally, if the bundle overlay overrode the machines definition use
  1404  	// that.
  1405  	if config.Machines != nil {
  1406  		data.Machines = config.Machines
  1407  	}
  1408  
  1409  	return nil
  1410  }
  1411  
  1412  // removeRelations removes any relation defined in data that references
  1413  // the application appName.
  1414  func removeRelations(data [][]string, appName string) [][]string {
  1415  	var result [][]string
  1416  	for _, relation := range data {
  1417  		// Keep the dud relation in the set, it will be caught by the bundle
  1418  		// verify code.
  1419  		if len(relation) == 2 {
  1420  			left, right := relation[0], relation[1]
  1421  			if left == appName || strings.HasPrefix(left, appName+":") ||
  1422  				right == appName || strings.HasPrefix(right, appName+":") {
  1423  				continue
  1424  			}
  1425  		}
  1426  		result = append(result, relation)
  1427  	}
  1428  	return result
  1429  }
  1430  
  1431  // ModelExtractor provides everything we need to build a
  1432  // bundlechanges.Model from a model API connection.
  1433  type ModelExtractor interface {
  1434  	GetAnnotations(tags []string) ([]params.AnnotationsGetResult, error)
  1435  	GetConstraints(applications ...string) ([]constraints.Value, error)
  1436  	GetConfig(generation model.GenerationVersion, applications ...string) ([]map[string]interface{}, error)
  1437  	Sequences() (map[string]int, error)
  1438  }
  1439  
  1440  func buildModelRepresentation(
  1441  	status *params.FullStatus,
  1442  	apiRoot ModelExtractor,
  1443  	useExistingMachines bool,
  1444  	bundleMachines map[string]string,
  1445  ) (*bundlechanges.Model, error) {
  1446  	var (
  1447  		annotationTags []string
  1448  		appNames       []string
  1449  		principalApps  []string
  1450  	)
  1451  	machineMap := make(map[string]string)
  1452  	machines := make(map[string]*bundlechanges.Machine)
  1453  	for id, machineStatus := range status.Machines {
  1454  		machines[id] = &bundlechanges.Machine{
  1455  			ID:     id,
  1456  			Series: machineStatus.Series,
  1457  		}
  1458  		tag := names.NewMachineTag(id)
  1459  		annotationTags = append(annotationTags, tag.String())
  1460  		if useExistingMachines && tag.ContainerType() == "" {
  1461  			machineMap[id] = id
  1462  		}
  1463  	}
  1464  	// Now iterate over the bundleMachines that the user specified.
  1465  	for bundleMachine, modelMachine := range bundleMachines {
  1466  		machineMap[bundleMachine] = modelMachine
  1467  	}
  1468  	applications := make(map[string]*bundlechanges.Application)
  1469  	for name, appStatus := range status.Applications {
  1470  		app := &bundlechanges.Application{
  1471  			Name:          name,
  1472  			Charm:         appStatus.Charm,
  1473  			Scale:         appStatus.Scale,
  1474  			Exposed:       appStatus.Exposed,
  1475  			Series:        appStatus.Series,
  1476  			Placement:     appStatus.Placement,
  1477  			SubordinateTo: appStatus.SubordinateTo,
  1478  		}
  1479  		for unitName, unit := range appStatus.Units {
  1480  			app.Units = append(app.Units, bundlechanges.Unit{
  1481  				Name:    unitName,
  1482  				Machine: unit.Machine,
  1483  			})
  1484  		}
  1485  		applications[name] = app
  1486  		annotationTags = append(annotationTags, names.NewApplicationTag(name).String())
  1487  		appNames = append(appNames, name)
  1488  		if len(appStatus.Units) > 0 {
  1489  			// While this isn't entirely accurate, because an application
  1490  			// without any units is still a principal, it is less bad than
  1491  			// just using 'SubordinateTo' as a subordinate charm that isn't
  1492  			// related to anything has that empty too.
  1493  			principalApps = append(principalApps, name)
  1494  		}
  1495  	}
  1496  	mod := &bundlechanges.Model{
  1497  		Applications: applications,
  1498  		Machines:     machines,
  1499  		MachineMap:   machineMap,
  1500  	}
  1501  	for _, relation := range status.Relations {
  1502  		// All relations have two endpoints except peers.
  1503  		if len(relation.Endpoints) != 2 {
  1504  			continue
  1505  		}
  1506  		mod.Relations = append(mod.Relations, bundlechanges.Relation{
  1507  			App1:      relation.Endpoints[0].ApplicationName,
  1508  			Endpoint1: relation.Endpoints[0].Name,
  1509  			App2:      relation.Endpoints[1].ApplicationName,
  1510  			Endpoint2: relation.Endpoints[1].Name,
  1511  		})
  1512  	}
  1513  	// Get all the annotations.
  1514  	annotations, err := apiRoot.GetAnnotations(annotationTags)
  1515  	if err != nil {
  1516  		return nil, errors.Trace(err)
  1517  	}
  1518  	for _, result := range annotations {
  1519  		if result.Error.Error != nil {
  1520  			return nil, errors.Trace(result.Error.Error)
  1521  		}
  1522  		tag, err := names.ParseTag(result.EntityTag)
  1523  		if err != nil {
  1524  			return nil, errors.Trace(err) // This should never happen.
  1525  		}
  1526  		switch kind := tag.Kind(); kind {
  1527  		case names.ApplicationTagKind:
  1528  			mod.Applications[tag.Id()].Annotations = result.Annotations
  1529  		case names.MachineTagKind:
  1530  			mod.Machines[tag.Id()].Annotations = result.Annotations
  1531  		default:
  1532  			return nil, errors.Errorf("unexpected tag kind for annotations: %q", kind)
  1533  		}
  1534  	}
  1535  	// Add in the model sequences.
  1536  	sequences, err := apiRoot.Sequences()
  1537  	if err == nil {
  1538  		mod.Sequence = sequences
  1539  	} else if !errors.IsNotSupported(err) {
  1540  		return nil, errors.Annotate(err, "getting model sequences")
  1541  	}
  1542  
  1543  	// When dealing with bundles the current model generation is always used.
  1544  	configValues, err := apiRoot.GetConfig(model.GenerationCurrent, appNames...)
  1545  	if err != nil {
  1546  		return nil, errors.Annotate(err, "getting application options")
  1547  	}
  1548  	for i, cfg := range configValues {
  1549  		options := make(map[string]interface{})
  1550  		// The config map has values that looks like this:
  1551  		//  map[string]interface {}{
  1552  		//        "value":       "",
  1553  		//        "source":     "user", // or "unset" or "default"
  1554  		//        "description": "Where to gather metrics from.\nExamples:\n  host1.maas:9090\n  host1.maas:9090, host2.maas:9090\n",
  1555  		//        "type":        "string",
  1556  		//    },
  1557  		// We want the value iff default is false.
  1558  		for key, valueMap := range cfg {
  1559  			value, err := applicationConfigValue(key, valueMap)
  1560  			if err != nil {
  1561  				return nil, errors.Annotatef(err, "bad application config for %q", appNames[i])
  1562  			}
  1563  			if value != nil {
  1564  				options[key] = value
  1565  			}
  1566  		}
  1567  		mod.Applications[appNames[i]].Options = options
  1568  	}
  1569  	// Lastly get all the application constraints.
  1570  	constraintValues, err := apiRoot.GetConstraints(principalApps...)
  1571  	if err != nil {
  1572  		return nil, errors.Annotate(err, "getting application constraints")
  1573  	}
  1574  	for i, value := range constraintValues {
  1575  		mod.Applications[principalApps[i]].Constraints = value.String()
  1576  	}
  1577  
  1578  	mod.ConstraintsEqual = func(a, b string) bool {
  1579  		// Since the constraints have already been validated, we don't
  1580  		// even bother checking the error response here.
  1581  		ac, _ := constraints.Parse(a)
  1582  		bc, _ := constraints.Parse(b)
  1583  		return reflect.DeepEqual(ac, bc)
  1584  	}
  1585  
  1586  	return mod, nil
  1587  }
  1588  
  1589  // applicationConfigValue returns the value if it is not a default value.
  1590  // If the value is a default value, nil is returned.
  1591  // If there was issue determining the type or value, an error is returned.
  1592  func applicationConfigValue(key string, valueMap interface{}) (interface{}, error) {
  1593  	vm, ok := valueMap.(map[string]interface{})
  1594  	if !ok {
  1595  		return nil, errors.Errorf("unexpected application config value type %T for key %q", valueMap, key)
  1596  	}
  1597  	source, found := vm["source"]
  1598  	if !found {
  1599  		return nil, errors.Errorf("missing application config value 'source' for key %q", key)
  1600  	}
  1601  	if source != "user" {
  1602  		return nil, nil
  1603  	}
  1604  	value, found := vm["value"]
  1605  	if !found {
  1606  		return nil, errors.Errorf("missing application config value 'value'")
  1607  	}
  1608  	return value, nil
  1609  }