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

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package commands
     5  
     6  import (
     7  	"bufio"
     8  	"fmt"
     9  	"os"
    10  	"sort"
    11  	"strings"
    12  
    13  	"github.com/juju/cmd"
    14  	"github.com/juju/errors"
    15  	"github.com/juju/gnuflag"
    16  	"github.com/juju/schema"
    17  	"github.com/juju/utils"
    18  	"github.com/juju/utils/featureflag"
    19  	"github.com/juju/version"
    20  	"gopkg.in/juju/charm.v6"
    21  	"gopkg.in/juju/names.v2"
    22  
    23  	jujucloud "github.com/juju/juju/cloud"
    24  	jujucmd "github.com/juju/juju/cmd"
    25  	"github.com/juju/juju/cmd/juju/common"
    26  	"github.com/juju/juju/cmd/modelcmd"
    27  	"github.com/juju/juju/controller"
    28  	"github.com/juju/juju/core/constraints"
    29  	"github.com/juju/juju/core/instance"
    30  	"github.com/juju/juju/core/model"
    31  	"github.com/juju/juju/environs"
    32  	"github.com/juju/juju/environs/bootstrap"
    33  	"github.com/juju/juju/environs/config"
    34  	"github.com/juju/juju/environs/context"
    35  	"github.com/juju/juju/environs/sync"
    36  	"github.com/juju/juju/feature"
    37  	"github.com/juju/juju/juju"
    38  	"github.com/juju/juju/juju/osenv"
    39  	"github.com/juju/juju/jujuclient"
    40  	"github.com/juju/juju/network"
    41  	"github.com/juju/juju/provider/lxd/lxdnames"
    42  	jujuversion "github.com/juju/juju/version"
    43  )
    44  
    45  // provisionalProviders is the names of providers that are hidden behind
    46  // feature flags.
    47  var provisionalProviders = map[string]string{}
    48  
    49  var usageBootstrapSummary = `
    50  Initializes a cloud environment.`[1:]
    51  
    52  var usageBootstrapDetails = `
    53  Used without arguments, bootstrap will step you through the process of
    54  initializing a Juju cloud environment. Initialization consists of creating
    55  a 'controller' model and provisioning a machine to act as controller.
    56  
    57  We recommend you call your controller ‘username-region’ e.g. ‘fred-us-east-1’
    58  See --clouds for a list of clouds and credentials.
    59  See --regions <cloud> for a list of available regions for a given cloud.
    60  
    61  Credentials are set beforehand and are distinct from any other
    62  configuration (see `[1:] + "`juju add-credential`" + `).
    63  The 'controller' model typically does not run workloads. It should remain
    64  pristine to run and manage Juju's own infrastructure for the corresponding
    65  cloud. Additional (hosted) models should be created with ` + "`juju create-\nmodel`" + ` for workload purposes.
    66  Note that a 'default' model is also created and becomes the current model
    67  of the environment once the command completes. It can be discarded if
    68  other models are created.
    69  
    70  If '--bootstrap-constraints' is used, its values will also apply to any
    71  future controllers provisioned for high availability (HA).
    72  
    73  If '--constraints' is used, its values will be set as the default
    74  constraints for all future workload machines in the model, exactly as if
    75  the constraints were set with ` + "`juju set-model-constraints`" + `.
    76  
    77  It is possible to override constraints and the automatic machine selection
    78  algorithm by assigning a "placement directive" via the '--to' option. This
    79  dictates what machine to use for the controller. This would typically be
    80  used with the MAAS provider ('--to <host>.maas').
    81  
    82  Available keys for use with --config can be found here:
    83      https://jujucharms.com/stable/controllers-config
    84      https://jujucharms.com/stable/models-config
    85  
    86  You can change the default timeout and retry delays used during the
    87  bootstrap by changing the following settings in your configuration
    88  (all values represent number of seconds):
    89      # How long to wait for a connection to the controller
    90      bootstrap-timeout: 600 # default: 10 minutes
    91      # How long to wait between connection attempts to a controller
    92  address.
    93      bootstrap-retry-delay: 5 # default: 5 seconds
    94      # How often to refresh controller addresses from the API server.
    95      bootstrap-addresses-delay: 10 # default: 10 seconds
    96  
    97  Private clouds may need to specify their own custom image metadata and
    98  tools/agent. Use '--metadata-source' whose value is a local directory.
    99  
   100  By default, the Juju version of the agent binary that is downloaded and
   101  installed on all models for the new controller will be the same as that
   102  of the Juju client used to perform the bootstrap.
   103  However, a user can specify a different agent version via '--agent-version'
   104  option to bootstrap command. Juju will use this version for models' agents
   105  as long as the client's version is from the same Juju release series.
   106  In other words, a 2.2.1 client can bootstrap any 2.2.x agents but cannot
   107  bootstrap any 2.0.x or 2.1.x agents.
   108  The agent version can be specified a simple numeric version, e.g. 2.2.4.
   109  
   110  For example, at the time when 2.3.0, 2.3.1 and 2.3.2 are released and your
   111  agent stream is 'released' (default), then a 2.3.1 client can bootstrap:
   112      * 2.3.0 controller by running '... bootstrap --agent-version=2.3.0 ...';
   113      * 2.3.1 controller by running '... bootstrap ...';
   114      * 2.3.2 controller by running 'bootstrap --auto-upgrade'.
   115  However, if this client has a copy of codebase, then a local copy of Juju
   116  will be built and bootstrapped - 2.3.1.1.
   117  
   118  Examples:
   119      juju bootstrap
   120      juju bootstrap --clouds
   121      juju bootstrap --regions aws
   122      juju bootstrap aws
   123      juju bootstrap aws/us-east-1
   124      juju bootstrap google joe-us-east1
   125      juju bootstrap --config=~/config-rs.yaml rackspace joe-syd
   126      juju bootstrap --agent-version=2.2.4 aws joe-us-east-1
   127      juju bootstrap --config bootstrap-timeout=1200 azure joe-eastus
   128  
   129  See also:
   130      add-credentials
   131      add-model
   132      controller-config
   133      model-config
   134      set-constraints
   135      show-cloud`
   136  
   137  // defaultHostedModelName is the name of the hosted model created in each
   138  // controller for deploying workloads to, in addition to the "controller" model.
   139  const defaultHostedModelName = "default"
   140  
   141  func newBootstrapCommand() cmd.Command {
   142  	command := &bootstrapCommand{}
   143  	command.CanClearCurrentModel = true
   144  	return modelcmd.Wrap(command,
   145  		modelcmd.WrapSkipModelFlags,
   146  		modelcmd.WrapSkipDefaultModel,
   147  	)
   148  }
   149  
   150  // bootstrapCommand is responsible for launching the first machine in a juju
   151  // environment, and setting up everything necessary to continue working.
   152  type bootstrapCommand struct {
   153  	modelcmd.ModelCommandBase
   154  
   155  	Constraints             constraints.Value
   156  	ConstraintsStr          string
   157  	BootstrapConstraints    constraints.Value
   158  	BootstrapConstraintsStr string
   159  	BootstrapSeries         string
   160  	BootstrapImage          string
   161  	BuildAgent              bool
   162  	MetadataSource          string
   163  	Placement               string
   164  	KeepBrokenEnvironment   bool
   165  	AutoUpgrade             bool
   166  	AgentVersionParam       string
   167  	AgentVersion            *version.Number
   168  	config                  common.ConfigFlag
   169  	modelDefaults           common.ConfigFlag
   170  
   171  	showClouds          bool
   172  	showRegionsForCloud string
   173  	controllerName      string
   174  	hostedModelName     string
   175  	CredentialName      string
   176  	Cloud               string
   177  	Region              string
   178  	noGUI               bool
   179  	noSwitch            bool
   180  	interactive         bool
   181  }
   182  
   183  func (c *bootstrapCommand) Info() *cmd.Info {
   184  	return jujucmd.Info(&cmd.Info{
   185  		Name:    "bootstrap",
   186  		Args:    "[<cloud name>[/region] [<controller name>]]",
   187  		Purpose: usageBootstrapSummary,
   188  		Doc:     usageBootstrapDetails,
   189  	})
   190  }
   191  
   192  func (c *bootstrapCommand) SetFlags(f *gnuflag.FlagSet) {
   193  	c.ModelCommandBase.SetFlags(f)
   194  	f.StringVar(&c.ConstraintsStr, "constraints", "", "Set model constraints")
   195  	f.StringVar(&c.BootstrapConstraintsStr, "bootstrap-constraints", "", "Specify bootstrap machine constraints")
   196  	f.StringVar(&c.BootstrapSeries, "bootstrap-series", "", "Specify the series of the bootstrap machine")
   197  	if featureflag.Enabled(feature.ImageMetadata) {
   198  		f.StringVar(&c.BootstrapImage, "bootstrap-image", "", "Specify the image of the bootstrap machine")
   199  	}
   200  	f.BoolVar(&c.BuildAgent, "build-agent", false, "Build local version of agent binary before bootstrapping")
   201  	f.StringVar(&c.MetadataSource, "metadata-source", "", "Local path to use as agent and/or image metadata source")
   202  	f.StringVar(&c.Placement, "to", "", "Placement directive indicating an instance to bootstrap")
   203  	f.BoolVar(&c.KeepBrokenEnvironment, "keep-broken", false, "Do not destroy the model if bootstrap fails")
   204  	f.BoolVar(&c.AutoUpgrade, "auto-upgrade", false, "After bootstrap, upgrade to the latest patch release")
   205  	f.StringVar(&c.AgentVersionParam, "agent-version", "", "Version of agent binaries to use for Juju agents")
   206  	f.StringVar(&c.CredentialName, "credential", "", "Credentials to use when bootstrapping")
   207  	f.Var(&c.config, "config", "Specify a controller configuration file, or one or more configuration\n    options\n    (--config config.yaml [--config key=value ...])")
   208  	f.Var(&c.modelDefaults, "model-default", "Specify a configuration file, or one or more configuration\n    options to be set for all models, unless otherwise specified\n    (--model-default config.yaml [--model-default key=value ...])")
   209  	f.StringVar(&c.hostedModelName, "d", defaultHostedModelName, "Name of the default hosted model for the controller")
   210  	f.StringVar(&c.hostedModelName, "default-model", defaultHostedModelName, "Name of the default hosted model for the controller")
   211  	f.BoolVar(&c.showClouds, "clouds", false, "Print the available clouds which can be used to bootstrap a Juju environment")
   212  	f.StringVar(&c.showRegionsForCloud, "regions", "", "Print the available regions for the specified cloud")
   213  	f.BoolVar(&c.noGUI, "no-gui", false, "Do not install the Juju GUI in the controller when bootstrapping")
   214  	f.BoolVar(&c.noSwitch, "no-switch", false, "Do not switch to the newly created controller")
   215  }
   216  
   217  func (c *bootstrapCommand) Init(args []string) (err error) {
   218  	if c.showClouds && c.showRegionsForCloud != "" {
   219  		return errors.New("--clouds and --regions can't be used together")
   220  	}
   221  	if c.showClouds {
   222  		return cmd.CheckEmpty(args)
   223  	}
   224  	if c.showRegionsForCloud != "" {
   225  		return cmd.CheckEmpty(args)
   226  	}
   227  	if c.AgentVersionParam != "" && c.BuildAgent {
   228  		return errors.New("--agent-version and --build-agent can't be used together")
   229  	}
   230  	if c.BootstrapSeries != "" && !charm.IsValidSeries(c.BootstrapSeries) {
   231  		return errors.NotValidf("series %q", c.BootstrapSeries)
   232  	}
   233  
   234  	/* controller is the name of controller created for internal juju management */
   235  	if c.hostedModelName == "controller" {
   236  		return errors.New(" 'controller' name is already assigned to juju internal management model")
   237  	}
   238  
   239  	// Parse the placement directive. Bootstrap currently only
   240  	// supports provider-specific placement directives.
   241  	if c.Placement != "" {
   242  		_, err = instance.ParsePlacement(c.Placement)
   243  		if err != instance.ErrPlacementScopeMissing {
   244  			// We only support unscoped placement directives for bootstrap.
   245  			return errors.Errorf("unsupported bootstrap placement directive %q", c.Placement)
   246  		}
   247  	}
   248  	if !c.AutoUpgrade {
   249  		// With no auto upgrade chosen, we default to the version matching the bootstrap client.
   250  		vers := jujuversion.Current
   251  		c.AgentVersion = &vers
   252  	}
   253  	if c.AgentVersionParam != "" {
   254  		if vers, err := version.ParseBinary(c.AgentVersionParam); err == nil {
   255  			c.AgentVersion = &vers.Number
   256  		} else if vers, err := version.Parse(c.AgentVersionParam); err == nil {
   257  			c.AgentVersion = &vers
   258  		} else {
   259  			return err
   260  		}
   261  	}
   262  	if c.AgentVersion != nil && (c.AgentVersion.Major != jujuversion.Current.Major || c.AgentVersion.Minor != jujuversion.Current.Minor) {
   263  		return errors.Errorf("this client can only bootstrap %v.%v agents", jujuversion.Current.Major, jujuversion.Current.Minor)
   264  	}
   265  
   266  	switch len(args) {
   267  	case 0:
   268  		// no args or flags, go interactive.
   269  		c.interactive = true
   270  		return nil
   271  	}
   272  	c.Cloud = args[0]
   273  	if i := strings.IndexRune(c.Cloud, '/'); i > 0 {
   274  		c.Cloud, c.Region = c.Cloud[:i], c.Cloud[i+1:]
   275  	}
   276  	if ok := names.IsValidCloud(c.Cloud); !ok {
   277  		return errors.NotValidf("cloud name %q", c.Cloud)
   278  	}
   279  	if len(args) > 1 {
   280  		c.controllerName = args[1]
   281  		return cmd.CheckEmpty(args[2:])
   282  	}
   283  	return nil
   284  }
   285  
   286  // BootstrapInterface provides bootstrap functionality that Run calls to support cleaner testing.
   287  type BootstrapInterface interface {
   288  	// Bootstrap bootstraps a controller.
   289  	Bootstrap(ctx environs.BootstrapContext, environ environs.BootstrapEnviron, callCtx context.ProviderCallContext, args bootstrap.BootstrapParams) error
   290  
   291  	// CloudDetector returns a CloudDetector for the given provider,
   292  	// if the provider supports it.
   293  	CloudDetector(environs.EnvironProvider) (environs.CloudDetector, bool)
   294  
   295  	// CloudRegionDetector returns a CloudRegionDetector for the given provider,
   296  	// if the provider supports it.
   297  	CloudRegionDetector(environs.EnvironProvider) (environs.CloudRegionDetector, bool)
   298  
   299  	// CloudFinalizer returns a CloudFinalizer for the given provider,
   300  	// if the provider supports it.
   301  	CloudFinalizer(environs.EnvironProvider) (environs.CloudFinalizer, bool)
   302  }
   303  
   304  type bootstrapFuncs struct{}
   305  
   306  func (b bootstrapFuncs) Bootstrap(ctx environs.BootstrapContext, env environs.BootstrapEnviron, callCtx context.ProviderCallContext, args bootstrap.BootstrapParams) error {
   307  	return bootstrap.Bootstrap(ctx, env, callCtx, args)
   308  }
   309  
   310  func (b bootstrapFuncs) CloudDetector(provider environs.EnvironProvider) (environs.CloudDetector, bool) {
   311  	detector, ok := provider.(environs.CloudDetector)
   312  	return detector, ok
   313  }
   314  
   315  func (b bootstrapFuncs) CloudRegionDetector(provider environs.EnvironProvider) (environs.CloudRegionDetector, bool) {
   316  	detector, ok := provider.(environs.CloudRegionDetector)
   317  	return detector, ok
   318  }
   319  
   320  func (b bootstrapFuncs) CloudFinalizer(provider environs.EnvironProvider) (environs.CloudFinalizer, bool) {
   321  	finalizer, ok := provider.(environs.CloudFinalizer)
   322  	return finalizer, ok
   323  }
   324  
   325  var getBootstrapFuncs = func() BootstrapInterface {
   326  	return &bootstrapFuncs{}
   327  }
   328  
   329  var (
   330  	bootstrapPrepareController = bootstrap.PrepareController
   331  	environsDestroy            = environs.Destroy
   332  	waitForAgentInitialisation = common.WaitForAgentInitialisation
   333  )
   334  
   335  var ambiguousDetectedCredentialError = errors.New(`
   336  more than one credential detected
   337  run juju autoload-credentials and specify a credential using the --credential argument`[1:],
   338  )
   339  
   340  var ambiguousCredentialError = errors.New(`
   341  more than one credential is available
   342  specify a credential using the --credential argument`[1:],
   343  )
   344  
   345  func (c *bootstrapCommand) parseConstraints(ctx *cmd.Context) (err error) {
   346  	allAliases := map[string]string{}
   347  	defer common.WarnConstraintAliases(ctx, allAliases)
   348  	if c.ConstraintsStr != "" {
   349  		cons, aliases, err := constraints.ParseWithAliases(c.ConstraintsStr)
   350  		for k, v := range aliases {
   351  			allAliases[k] = v
   352  		}
   353  		if err != nil {
   354  			return err
   355  		}
   356  		c.Constraints = cons
   357  	}
   358  	if c.BootstrapConstraintsStr != "" {
   359  		cons, aliases, err := constraints.ParseWithAliases(c.BootstrapConstraintsStr)
   360  		for k, v := range aliases {
   361  			allAliases[k] = v
   362  		}
   363  		if err != nil {
   364  			return err
   365  		}
   366  		c.BootstrapConstraints = cons
   367  	}
   368  	return nil
   369  }
   370  
   371  // Run connects to the environment specified on the command line and bootstraps
   372  // a juju in that environment if none already exists. If there is as yet no environments.yaml file,
   373  // the user is informed how to create one.
   374  func (c *bootstrapCommand) Run(ctx *cmd.Context) (resultErr error) {
   375  	defer func() {
   376  		resultErr = handleChooseCloudRegionError(ctx, resultErr)
   377  	}()
   378  
   379  	if err := c.parseConstraints(ctx); err != nil {
   380  		return err
   381  	}
   382  
   383  	// Start by checking for usage errors, requests for information
   384  	finished, err := c.handleCommandLineErrorsAndInfoRequests(ctx)
   385  	if err != nil {
   386  		return errors.Trace(err)
   387  	}
   388  	if finished {
   389  		return nil
   390  	}
   391  
   392  	// Run interactive bootstrap if needed/asked for
   393  	if c.interactive {
   394  		if err := c.runInteractive(ctx); err != nil {
   395  			return errors.Trace(err)
   396  		}
   397  		// now run normal bootstrap using info gained above.
   398  	}
   399  
   400  	cloud, provider, err := c.cloud(ctx)
   401  	if err != nil {
   402  		return errors.Trace(err)
   403  	}
   404  
   405  	isCAASController := jujucloud.CloudIsCAAS(cloud)
   406  
   407  	if isCAASController && !featureflag.Enabled(feature.DeveloperMode) {
   408  		return errors.NotSupportedf("bootstrap to kubernetes cluster")
   409  	}
   410  
   411  	// Custom clouds may not have explicitly declared support for any auth-
   412  	// types, in which case we'll assume that they support everything that
   413  	// the provider supports.
   414  	if len(cloud.AuthTypes) == 0 {
   415  		for authType := range provider.CredentialSchemas() {
   416  			cloud.AuthTypes = append(cloud.AuthTypes, authType)
   417  		}
   418  	}
   419  
   420  	credentials, regionName, err := c.credentialsAndRegionName(ctx, provider, cloud)
   421  	if err != nil {
   422  		if errors.IsNotFound(err) {
   423  			err = errors.NewNotFound(nil, fmt.Sprintf("%v\nSee `juju add-credential %s --help` for instructions", err, cloud.Name))
   424  		}
   425  
   426  		if err == cmd.ErrSilent {
   427  			return err
   428  		}
   429  		return errors.Trace(err)
   430  	}
   431  
   432  	cloudCallCtx := context.NewCloudCallContext()
   433  	// At this stage, the credential we intend to use is not yet stored
   434  	// server-side. So, if the credential is not accepted by the provider,
   435  	// we cannot mark it as invalid, just log it as an informative message.
   436  	cloudCallCtx.InvalidateCredentialFunc = func(reason string) error {
   437  		ctx.Infof("Cloud credential %q is not accepted by cloud provider: %v", credentials.name, reason)
   438  		return nil
   439  	}
   440  
   441  	region, err := common.ChooseCloudRegion(cloud, regionName)
   442  	if err != nil {
   443  		return errors.Trace(err)
   444  	}
   445  	if c.controllerName == "" {
   446  		c.controllerName = defaultControllerName(cloud.Name, region.Name)
   447  	}
   448  
   449  	// set a Region so it's config can be found below.
   450  	if c.Region == "" {
   451  		c.Region = region.Name
   452  	}
   453  
   454  	config, err := c.bootstrapConfigs(ctx, cloud, provider)
   455  	if err != nil {
   456  		return errors.Trace(err)
   457  	}
   458  
   459  	// Read existing current controller so we can clean up on error.
   460  	var oldCurrentController string
   461  	store := c.ClientStore()
   462  	oldCurrentController, err = store.CurrentController()
   463  	if errors.IsNotFound(err) {
   464  		oldCurrentController = ""
   465  	} else if err != nil {
   466  		return errors.Annotate(err, "error reading current controller")
   467  	}
   468  
   469  	defer func() {
   470  		if resultErr == nil || errors.IsAlreadyExists(resultErr) {
   471  			return
   472  		}
   473  		if oldCurrentController != "" {
   474  			if err := store.SetCurrentController(oldCurrentController); err != nil {
   475  				logger.Errorf(
   476  					"cannot reset current controller to %q: %v",
   477  					oldCurrentController, err,
   478  				)
   479  			}
   480  		}
   481  		if err := store.RemoveController(c.controllerName); err != nil {
   482  			logger.Errorf(
   483  				"cannot destroy newly created controller %q details: %v",
   484  				c.controllerName, err,
   485  			)
   486  		}
   487  	}()
   488  
   489  	bootstrapCtx := modelcmd.BootstrapContext(ctx)
   490  	bootstrapPrepareParams := bootstrap.PrepareParams{
   491  		ModelConfig:      config.bootstrapModel,
   492  		ControllerConfig: config.controller,
   493  		ControllerName:   c.controllerName,
   494  		Cloud: environs.CloudSpec{
   495  			Type:             cloud.Type,
   496  			Name:             cloud.Name,
   497  			Region:           region.Name,
   498  			Endpoint:         region.Endpoint,
   499  			IdentityEndpoint: region.IdentityEndpoint,
   500  			StorageEndpoint:  region.StorageEndpoint,
   501  			Credential:       credentials.credential,
   502  			CACertificates:   cloud.CACertificates,
   503  		},
   504  		CredentialName: credentials.name,
   505  		AdminSecret:    config.bootstrap.AdminSecret,
   506  	}
   507  	bootstrapParams := bootstrap.BootstrapParams{
   508  		BootstrapSeries:           c.BootstrapSeries,
   509  		BootstrapImage:            c.BootstrapImage,
   510  		Placement:                 c.Placement,
   511  		BuildAgent:                c.BuildAgent,
   512  		BuildAgentTarball:         sync.BuildAgentTarball,
   513  		AgentVersion:              c.AgentVersion,
   514  		Cloud:                     cloud,
   515  		CloudRegion:               region.Name,
   516  		ControllerConfig:          config.controller,
   517  		ControllerInheritedConfig: config.inheritedControllerAttrs,
   518  		RegionInheritedConfig:     cloud.RegionConfig,
   519  		AdminSecret:               config.bootstrap.AdminSecret,
   520  		CAPrivateKey:              config.bootstrap.CAPrivateKey,
   521  		DialOpts: environs.BootstrapDialOpts{
   522  			Timeout:        config.bootstrap.BootstrapTimeout,
   523  			RetryDelay:     config.bootstrap.BootstrapRetryDelay,
   524  			AddressesDelay: config.bootstrap.BootstrapAddressesDelay,
   525  		},
   526  	}
   527  
   528  	environ, err := bootstrapPrepareController(
   529  		isCAASController, bootstrapCtx, store, bootstrapPrepareParams,
   530  	)
   531  	if err != nil {
   532  		return errors.Trace(err)
   533  	}
   534  
   535  	if isCAASController {
   536  		if !c.noSwitch {
   537  			if err := store.SetCurrentController(c.controllerName); err != nil {
   538  				return errors.Trace(err)
   539  			}
   540  		}
   541  	} else {
   542  
   543  		// only IAAS has hosted model.
   544  		hostedModelUUID, err := utils.NewUUID()
   545  		if err != nil {
   546  			return errors.Trace(err)
   547  		}
   548  
   549  		// Set the current model to the initial hosted model.
   550  		modelDetails := jujuclient.ModelDetails{
   551  			ModelUUID: hostedModelUUID.String(),
   552  			ModelType: model.IAAS,
   553  		}
   554  		if featureflag.Enabled(feature.Generations) {
   555  			modelDetails.ModelGeneration = model.GenerationCurrent
   556  		}
   557  		if err := store.UpdateModel(
   558  			c.controllerName,
   559  			c.hostedModelName,
   560  			modelDetails,
   561  		); err != nil {
   562  			return errors.Trace(err)
   563  		}
   564  
   565  		if !c.noSwitch {
   566  			if err := store.SetCurrentModel(c.controllerName, c.hostedModelName); err != nil {
   567  				return errors.Trace(err)
   568  			}
   569  			if err := store.SetCurrentController(c.controllerName); err != nil {
   570  				return errors.Trace(err)
   571  			}
   572  		}
   573  
   574  		bootstrapParams.HostedModelConfig = c.hostedModelConfig(
   575  			hostedModelUUID, config.inheritedControllerAttrs, config.userConfigAttrs, environ,
   576  		)
   577  	}
   578  
   579  	cloudRegion := c.Cloud
   580  	if region.Name != "" {
   581  		cloudRegion = fmt.Sprintf("%s/%s", cloudRegion, region.Name)
   582  	}
   583  	ctx.Infof(
   584  		"Creating Juju controller %q on %s",
   585  		c.controllerName, cloudRegion,
   586  	)
   587  
   588  	// If we error out for any reason, clean up the environment.
   589  	defer func() {
   590  		if resultErr != nil {
   591  			if c.KeepBrokenEnvironment {
   592  				ctx.Infof(`
   593  bootstrap failed but --keep-broken was specified.
   594  This means that cloud resources are left behind, but not registered to
   595  your local client, as the controller was not successfully created.
   596  However, you should be able to ssh into the machine using the user "ubuntu" and
   597  their IP address for diagnosis and investigation.
   598  When you are ready to clean up the failed controller, use your cloud console or
   599  equivalent CLI tools to terminate the instances and remove remaining resources.
   600  
   601  See `[1:] + "`juju kill-controller`" + `.`)
   602  			} else {
   603  				logger.Errorf("%v", resultErr)
   604  				logger.Debugf("(error details: %v)", errors.Details(resultErr))
   605  				// Set resultErr to cmd.ErrSilent to prevent
   606  				// logging the error twice.
   607  				resultErr = cmd.ErrSilent
   608  				handleBootstrapError(ctx, func() error {
   609  					return environsDestroy(
   610  						c.controllerName, environ, cloudCallCtx, store,
   611  					)
   612  				})
   613  			}
   614  		}
   615  	}()
   616  
   617  	// Block interruption during bootstrap. Providers may also
   618  	// register for interrupt notification so they can exit early.
   619  	interrupted := make(chan os.Signal, 1)
   620  	defer close(interrupted)
   621  	ctx.InterruptNotify(interrupted)
   622  	defer ctx.StopInterruptNotify(interrupted)
   623  	go func() {
   624  		for range interrupted {
   625  			ctx.Infof("Interrupt signalled: waiting for bootstrap to exit")
   626  		}
   627  	}()
   628  
   629  	// If --metadata-source is specified, override the default tools metadata source so
   630  	// SyncTools can use it, and also upload any image metadata.
   631  	if c.MetadataSource != "" {
   632  		bootstrapParams.MetadataDir = ctx.AbsPath(c.MetadataSource)
   633  	}
   634  
   635  	constraintsValidator, err := environ.ConstraintsValidator(cloudCallCtx)
   636  	if err != nil {
   637  		return errors.Trace(err)
   638  	}
   639  
   640  	// Merge in any space constraints that should be implied from controller
   641  	// space config.
   642  	// Do it before calling merge, because the constraints will be validated
   643  	// there.
   644  	constraints := c.Constraints
   645  	constraints.Spaces = config.controller.AsSpaceConstraints(constraints.Spaces)
   646  
   647  	// Merge environ and bootstrap-specific constraints.
   648  	bootstrapParams.BootstrapConstraints, err = constraintsValidator.Merge(constraints, c.BootstrapConstraints)
   649  	if err != nil {
   650  		return errors.Trace(err)
   651  	}
   652  	logger.Infof("combined bootstrap constraints: %v", bootstrapParams.BootstrapConstraints)
   653  
   654  	bootstrapParams.ModelConstraints = c.Constraints
   655  
   656  	// Check whether the Juju GUI must be installed in the controller.
   657  	// Leaving this value empty means no GUI will be installed.
   658  	if !c.noGUI {
   659  		bootstrapParams.GUIDataSourceBaseURL = common.GUIDataSourceBaseURL()
   660  	}
   661  
   662  	if credentials.name == "" {
   663  		// credentialName will be empty if the credential was detected.
   664  		// We must supply a name for the credential in the database,
   665  		// so choose one.
   666  		credentials.name = credentials.detectedName
   667  	}
   668  	bootstrapParams.CloudCredential = credentials.credential
   669  	bootstrapParams.CloudCredentialName = credentials.name
   670  
   671  	bootstrapFuncs := getBootstrapFuncs()
   672  	if err = bootstrapFuncs.Bootstrap(
   673  		modelcmd.BootstrapContext(ctx),
   674  		environ,
   675  		cloudCallCtx,
   676  		bootstrapParams,
   677  	); err != nil {
   678  		return errors.Annotate(err, "failed to bootstrap model")
   679  	}
   680  
   681  	if isCAASController {
   682  		// TODO(caas): wait and fetch controller public endpoint then update juju home
   683  		return nil
   684  	}
   685  
   686  	if err = c.SetModelName(modelcmd.JoinModelName(c.controllerName, c.hostedModelName), false); err != nil {
   687  		return errors.Trace(err)
   688  	}
   689  
   690  	agentVersion := jujuversion.Current
   691  	if c.AgentVersion != nil {
   692  		agentVersion = *c.AgentVersion
   693  	}
   694  	var addrs []network.Address
   695  	if env, ok := environ.(environs.InstanceBroker); ok {
   696  		addrs, err = common.BootstrapEndpointAddresses(env, cloudCallCtx)
   697  		if err != nil {
   698  			return errors.Trace(err)
   699  		}
   700  	} else {
   701  		// TODO(caas): this should never happen. but we need enhance here with the above TODO solved together
   702  		return errors.NewNotValid(nil, "unexpected error happened, IAAS mode should have environs.Environ implemented.")
   703  	}
   704  	if err := juju.UpdateControllerDetailsFromLogin(
   705  		c.ClientStore(),
   706  		c.controllerName,
   707  		juju.UpdateControllerParams{
   708  			AgentVersion:           agentVersion.String(),
   709  			CurrentHostPorts:       [][]network.HostPort{network.AddressesWithPort(addrs, config.controller.APIPort())},
   710  			PublicDNSName:          newStringIfNonEmpty(config.controller.AutocertDNSName()),
   711  			MachineCount:           newInt(1),
   712  			ControllerMachineCount: newInt(1),
   713  		}); err != nil {
   714  		return errors.Annotate(err, "saving bootstrap endpoint address")
   715  	}
   716  
   717  	// To avoid race conditions when running scripted bootstraps, wait
   718  	// for the controller's machine agent to be ready to accept commands
   719  	// before exiting this bootstrap command.
   720  	return waitForAgentInitialisation(ctx, &c.ModelCommandBase, c.controllerName, c.hostedModelName)
   721  }
   722  
   723  func (c *bootstrapCommand) handleCommandLineErrorsAndInfoRequests(ctx *cmd.Context) (bool, error) {
   724  	if c.BootstrapImage != "" {
   725  		if c.BootstrapSeries == "" {
   726  			return true, errors.Errorf("--bootstrap-image must be used with --bootstrap-series")
   727  		}
   728  		cons, err := constraints.Merge(c.Constraints, c.BootstrapConstraints)
   729  		if err != nil {
   730  			return true, errors.Trace(err)
   731  		}
   732  		if !cons.HasArch() {
   733  			return true, errors.Errorf("--bootstrap-image must be used with --bootstrap-constraints, specifying architecture")
   734  		}
   735  	}
   736  	if c.showClouds {
   737  		return true, printClouds(ctx, c.ClientStore())
   738  	}
   739  	if c.showRegionsForCloud != "" {
   740  		return true, printCloudRegions(ctx, c.showRegionsForCloud)
   741  	}
   742  
   743  	return false, nil
   744  }
   745  
   746  func (c *bootstrapCommand) cloud(ctx *cmd.Context) (jujucloud.Cloud, environs.EnvironProvider, error) {
   747  	bootstrapFuncs := getBootstrapFuncs()
   748  	fail := func(err error) (jujucloud.Cloud, environs.EnvironProvider, error) {
   749  		return jujucloud.Cloud{}, nil, err
   750  	}
   751  
   752  	// Get the cloud definition identified by c.Cloud. If c.Cloud does not
   753  	// identify a cloud in clouds.yaml, then we check if any of the
   754  	// providers can detect a cloud with the given name. Otherwise, if the
   755  	// cloud name identifies a provider *type* (e.g. "openstack"), then we
   756  	// check if that provider can detect cloud regions, and synthesise a
   757  	// cloud with those regions.
   758  	var provider environs.EnvironProvider
   759  	var cloud jujucloud.Cloud
   760  	cloudptr, err := jujucloud.CloudByName(c.Cloud)
   761  	if errors.IsNotFound(err) {
   762  		cloud, provider, err = c.detectCloud(ctx, bootstrapFuncs)
   763  		if err != nil {
   764  			return fail(errors.Trace(err))
   765  		}
   766  	} else if err != nil {
   767  		return fail(errors.Trace(err))
   768  	} else {
   769  		cloud = *cloudptr
   770  		if err := checkProviderType(cloud.Type); err != nil {
   771  			return fail(errors.Trace(err))
   772  		}
   773  		provider, err = environs.Provider(cloud.Type)
   774  		if err != nil {
   775  			return fail(errors.Trace(err))
   776  		}
   777  	}
   778  
   779  	if finalizer, ok := bootstrapFuncs.CloudFinalizer(provider); ok {
   780  		cloud, err = finalizer.FinalizeCloud(ctx, cloud)
   781  		if err != nil {
   782  			return fail(errors.Trace(err))
   783  		}
   784  	}
   785  
   786  	return cloud, provider, nil
   787  }
   788  
   789  func (c *bootstrapCommand) detectCloud(
   790  	ctx *cmd.Context,
   791  	bootstrapFuncs BootstrapInterface,
   792  ) (jujucloud.Cloud, environs.EnvironProvider, error) {
   793  	fail := func(err error) (jujucloud.Cloud, environs.EnvironProvider, error) {
   794  		return jujucloud.Cloud{}, nil, err
   795  	}
   796  
   797  	// Check if any of the registered providers can give us a cloud with
   798  	// the specified name. The first one wins.
   799  	for _, providerType := range environs.RegisteredProviders() {
   800  		provider, err := environs.Provider(providerType)
   801  		if err != nil {
   802  			return fail(errors.Trace(err))
   803  		}
   804  		cloudDetector, ok := bootstrapFuncs.CloudDetector(provider)
   805  		if !ok {
   806  			continue
   807  		}
   808  		cloud, err := cloudDetector.DetectCloud(c.Cloud)
   809  		if errors.IsNotFound(err) {
   810  			continue
   811  		} else if err != nil {
   812  			return fail(errors.Trace(err))
   813  		}
   814  		return cloud, provider, nil
   815  	}
   816  
   817  	ctx.Verbosef("cloud %q not found, trying as a provider name", c.Cloud)
   818  	provider, err := environs.Provider(c.Cloud)
   819  	if errors.IsNotFound(err) {
   820  		return fail(errors.NewNotFound(nil, fmt.Sprintf("unknown cloud %q, please try %q", c.Cloud, "juju update-clouds")))
   821  	} else if err != nil {
   822  		return fail(errors.Trace(err))
   823  	}
   824  	regionDetector, ok := bootstrapFuncs.CloudRegionDetector(provider)
   825  	if !ok {
   826  		ctx.Verbosef(
   827  			"provider %q does not support detecting regions",
   828  			c.Cloud,
   829  		)
   830  		return fail(errors.NewNotFound(nil, fmt.Sprintf("unknown cloud %q, please try %q", c.Cloud, "juju update-clouds")))
   831  	}
   832  
   833  	var cloudEndpoint string
   834  	regions, err := regionDetector.DetectRegions()
   835  	if errors.IsNotFound(err) {
   836  		// It's not an error to have no regions. If the
   837  		// provider does not support regions, then we
   838  		// reinterpret the supplied region name as the
   839  		// cloud's endpoint. This enables the user to
   840  		// supply, for example, maas/<IP> or manual/<IP>.
   841  		if c.Region != "" {
   842  			ctx.Verbosef("interpreting %q as the cloud endpoint", c.Region)
   843  			cloudEndpoint = c.Region
   844  			c.Region = ""
   845  		}
   846  	} else if err != nil {
   847  		return fail(errors.Annotatef(err,
   848  			"detecting regions for %q cloud provider",
   849  			c.Cloud,
   850  		))
   851  	}
   852  	schemas := provider.CredentialSchemas()
   853  	authTypes := make([]jujucloud.AuthType, 0, len(schemas))
   854  	for authType := range schemas {
   855  		authTypes = append(authTypes, authType)
   856  	}
   857  
   858  	// Since we are iterating over a map, lets sort the authTypes so
   859  	// they are always in a consistent order.
   860  	sort.Sort(jujucloud.AuthTypes(authTypes))
   861  	return jujucloud.Cloud{
   862  		Name:      c.Cloud,
   863  		Type:      c.Cloud,
   864  		AuthTypes: authTypes,
   865  		Endpoint:  cloudEndpoint,
   866  		Regions:   regions,
   867  	}, provider, nil
   868  }
   869  
   870  type bootstrapCredentials struct {
   871  	credential   *jujucloud.Credential
   872  	name         string
   873  	detectedName string
   874  }
   875  
   876  // Get the credentials and region name.
   877  func (c *bootstrapCommand) credentialsAndRegionName(
   878  	ctx *cmd.Context,
   879  	provider environs.EnvironProvider,
   880  	cloud jujucloud.Cloud,
   881  ) (
   882  	creds bootstrapCredentials,
   883  	regionName string,
   884  	err error,
   885  ) {
   886  
   887  	store := c.ClientStore()
   888  
   889  	// When looking for credentials, we should attempt to see if there are any
   890  	// credentials that should be registered, before we get or detect them
   891  	err = common.RegisterCredentials(ctx, store, provider, modelcmd.RegisterCredentialsParams{
   892  		Cloud: cloud,
   893  	})
   894  	if err != nil {
   895  		logger.Errorf("registering credentials errored %s", err)
   896  	}
   897  
   898  	var detected bool
   899  	creds.credential, creds.name, regionName, detected, err = common.GetOrDetectCredential(
   900  		ctx, store, provider, modelcmd.GetCredentialsParams{
   901  			Cloud:          cloud,
   902  			CloudRegion:    c.Region,
   903  			CredentialName: c.CredentialName,
   904  		},
   905  	)
   906  	switch errors.Cause(err) {
   907  	case nil:
   908  	case modelcmd.ErrMultipleCredentials:
   909  		return bootstrapCredentials{}, "", ambiguousCredentialError
   910  	case common.ErrMultipleDetectedCredentials:
   911  		return bootstrapCredentials{}, "", ambiguousDetectedCredentialError
   912  	default:
   913  		return bootstrapCredentials{}, "", errors.Trace(err)
   914  	}
   915  	logger.Debugf(
   916  		"authenticating with region %q and credential %q (%v)",
   917  		regionName, creds.name, creds.credential.Label,
   918  	)
   919  	if detected {
   920  		creds.detectedName = creds.name
   921  		creds.name = ""
   922  	}
   923  	logger.Tracef("credential: %v", creds.credential)
   924  	return creds, regionName, nil
   925  }
   926  
   927  type bootstrapConfigs struct {
   928  	bootstrapModel           map[string]interface{}
   929  	controller               controller.Config
   930  	bootstrap                bootstrap.Config
   931  	inheritedControllerAttrs map[string]interface{}
   932  	userConfigAttrs          map[string]interface{}
   933  }
   934  
   935  func (c *bootstrapCommand) bootstrapConfigs(
   936  	ctx *cmd.Context,
   937  	cloud jujucloud.Cloud,
   938  	provider environs.EnvironProvider,
   939  ) (
   940  	bootstrapConfigs,
   941  	error,
   942  ) {
   943  
   944  	controllerModelUUID, err := utils.NewUUID()
   945  	if err != nil {
   946  		return bootstrapConfigs{}, errors.Trace(err)
   947  	}
   948  	controllerUUID, err := utils.NewUUID()
   949  	if err != nil {
   950  		return bootstrapConfigs{}, errors.Trace(err)
   951  	}
   952  
   953  	// Create a model config, and split out any controller
   954  	// and bootstrap config attributes.
   955  	combinedConfig := map[string]interface{}{
   956  		"type":         cloud.Type,
   957  		"name":         bootstrap.ControllerModelName,
   958  		config.UUIDKey: controllerModelUUID.String(),
   959  	}
   960  
   961  	userConfigAttrs, err := c.config.ReadAttrs(ctx)
   962  	if err != nil {
   963  		return bootstrapConfigs{}, errors.Trace(err)
   964  	}
   965  	modelDefaultConfigAttrs, err := c.modelDefaults.ReadAttrs(ctx)
   966  	if err != nil {
   967  		return bootstrapConfigs{}, errors.Trace(err)
   968  	}
   969  	// The provider may define some custom attributes specific
   970  	// to the provider. These will be added to the model config.
   971  	providerAttrs := make(map[string]interface{})
   972  	if ps, ok := provider.(config.ConfigSchemaSource); ok {
   973  		for attr := range ps.ConfigSchema() {
   974  			// Start with the model defaults, and if also specified
   975  			// in the user config attrs, they override the model default.
   976  			if v, ok := modelDefaultConfigAttrs[attr]; ok {
   977  				providerAttrs[attr] = v
   978  			}
   979  			if v, ok := userConfigAttrs[attr]; ok {
   980  				providerAttrs[attr] = v
   981  			}
   982  		}
   983  		fields := schema.FieldMap(ps.ConfigSchema(), ps.ConfigDefaults())
   984  		if coercedAttrs, err := fields.Coerce(providerAttrs, nil); err != nil {
   985  			return bootstrapConfigs{},
   986  				errors.Annotatef(err, "invalid attribute value(s) for %v cloud", cloud.Type)
   987  		} else {
   988  			providerAttrs = coercedAttrs.(map[string]interface{})
   989  		}
   990  	}
   991  
   992  	bootstrapConfigAttrs := make(map[string]interface{})
   993  	controllerConfigAttrs := make(map[string]interface{})
   994  	// Based on the attribute names in clouds.yaml, create
   995  	// a map of shared config for all models on this cloud.
   996  	inheritedControllerAttrs := make(map[string]interface{})
   997  	for k, v := range cloud.Config {
   998  		switch {
   999  		case bootstrap.IsBootstrapAttribute(k):
  1000  			bootstrapConfigAttrs[k] = v
  1001  			continue
  1002  		case controller.ControllerOnlyAttribute(k):
  1003  			controllerConfigAttrs[k] = v
  1004  			continue
  1005  		}
  1006  		inheritedControllerAttrs[k] = v
  1007  	}
  1008  	// Region config values, for the region to be bootstrapped, from clouds.yaml
  1009  	// override what is in the cloud config.
  1010  	for k, v := range cloud.RegionConfig[c.Region] {
  1011  		switch {
  1012  		case bootstrap.IsBootstrapAttribute(k):
  1013  			bootstrapConfigAttrs[k] = v
  1014  			continue
  1015  		case controller.ControllerOnlyAttribute(k):
  1016  			controllerConfigAttrs[k] = v
  1017  			continue
  1018  		}
  1019  		inheritedControllerAttrs[k] = v
  1020  	}
  1021  	// Model defaults are added to the inherited controller attributes.
  1022  	// Any command line set model defaults override what is in the cloud config.
  1023  	for k, v := range modelDefaultConfigAttrs {
  1024  		switch {
  1025  		case bootstrap.IsBootstrapAttribute(k):
  1026  			return bootstrapConfigs{},
  1027  				errors.Errorf("%q is a bootstrap only attribute, and cannot be set as a model-default", k)
  1028  		case controller.ControllerOnlyAttribute(k):
  1029  			return bootstrapConfigs{},
  1030  				errors.Errorf("%q is a controller attribute, and cannot be set as a model-default", k)
  1031  		}
  1032  		inheritedControllerAttrs[k] = v
  1033  	}
  1034  
  1035  	// Start with the model defaults, then add in user config attributes.
  1036  	for k, v := range modelDefaultConfigAttrs {
  1037  		combinedConfig[k] = v
  1038  	}
  1039  
  1040  	// Provider specific attributes are either already specified in model
  1041  	// config (but may have been coerced), or were not present. Either way,
  1042  	// copy them in.
  1043  	logger.Debugf("provider attrs: %v", providerAttrs)
  1044  	for k, v := range providerAttrs {
  1045  		combinedConfig[k] = v
  1046  	}
  1047  
  1048  	for k, v := range inheritedControllerAttrs {
  1049  		combinedConfig[k] = v
  1050  	}
  1051  
  1052  	for k, v := range userConfigAttrs {
  1053  		combinedConfig[k] = v
  1054  	}
  1055  
  1056  	// Add in any default attribute values if not already
  1057  	// specified, making the recorded bootstrap config
  1058  	// immutable to changes in Juju.
  1059  	for k, v := range config.ConfigDefaults() {
  1060  		if _, ok := combinedConfig[k]; !ok {
  1061  			combinedConfig[k] = v
  1062  		}
  1063  	}
  1064  
  1065  	bootstrapModelConfig := make(map[string]interface{})
  1066  	for k, v := range combinedConfig {
  1067  		switch {
  1068  		case bootstrap.IsBootstrapAttribute(k):
  1069  			bootstrapConfigAttrs[k] = v
  1070  		case controller.ControllerOnlyAttribute(k):
  1071  			controllerConfigAttrs[k] = v
  1072  		default:
  1073  			bootstrapModelConfig[k] = v
  1074  		}
  1075  	}
  1076  
  1077  	bootstrapConfig, err := bootstrap.NewConfig(bootstrapConfigAttrs)
  1078  	if err != nil {
  1079  		return bootstrapConfigs{}, errors.Annotate(err, "constructing bootstrap config")
  1080  	}
  1081  	controllerConfig, err := controller.NewConfig(
  1082  		controllerUUID.String(), bootstrapConfig.CACert, controllerConfigAttrs,
  1083  	)
  1084  	if err != nil {
  1085  		return bootstrapConfigs{}, errors.Annotate(err, "constructing controller config")
  1086  	}
  1087  	if controllerConfig.AutocertDNSName() != "" {
  1088  		if _, ok := controllerConfigAttrs[controller.APIPort]; !ok {
  1089  			// The configuration did not explicitly mention the API port,
  1090  			// so default to 443 because it is not usually possible to
  1091  			// obtain autocert certificates without listening on port 443.
  1092  			controllerConfig[controller.APIPort] = 443
  1093  		}
  1094  	}
  1095  
  1096  	if err := common.FinalizeAuthorizedKeys(ctx, bootstrapModelConfig); err != nil {
  1097  		return bootstrapConfigs{}, errors.Annotate(err, "finalizing authorized-keys")
  1098  	}
  1099  	logger.Debugf("preparing controller with config: %v", bootstrapModelConfig)
  1100  
  1101  	configs := bootstrapConfigs{
  1102  		bootstrapModel:           bootstrapModelConfig,
  1103  		controller:               controllerConfig,
  1104  		bootstrap:                bootstrapConfig,
  1105  		inheritedControllerAttrs: inheritedControllerAttrs,
  1106  		userConfigAttrs:          userConfigAttrs,
  1107  	}
  1108  	return configs, nil
  1109  }
  1110  
  1111  func (c *bootstrapCommand) hostedModelConfig(
  1112  	hostedModelUUID utils.UUID,
  1113  	inheritedControllerAttrs,
  1114  	userConfigAttrs map[string]interface{},
  1115  	environ environs.ConfigGetter,
  1116  ) map[string]interface{} {
  1117  
  1118  	hostedModelConfig := map[string]interface{}{
  1119  		"name":         c.hostedModelName,
  1120  		config.UUIDKey: hostedModelUUID.String(),
  1121  	}
  1122  	for k, v := range inheritedControllerAttrs {
  1123  		hostedModelConfig[k] = v
  1124  	}
  1125  
  1126  	// We copy across any user supplied attributes to the hosted model config.
  1127  	// But only if the attributes have not been removed from the controller
  1128  	// model config as part of preparing the controller model.
  1129  	controllerModelConfigAttrs := environ.Config().AllAttrs()
  1130  	for k, v := range userConfigAttrs {
  1131  		if _, ok := controllerModelConfigAttrs[k]; ok {
  1132  			hostedModelConfig[k] = v
  1133  		}
  1134  	}
  1135  	// Ensure that certain config attributes are not included in the hosted
  1136  	// model config. These attributes may be modified during bootstrap; by
  1137  	// removing them from this map, we ensure the modified values are
  1138  	// inherited.
  1139  	delete(hostedModelConfig, config.AuthorizedKeysKey)
  1140  	delete(hostedModelConfig, config.AgentVersionKey)
  1141  
  1142  	return hostedModelConfig
  1143  }
  1144  
  1145  // runInteractive queries the user about bootstrap config interactively at the
  1146  // command prompt.
  1147  func (c *bootstrapCommand) runInteractive(ctx *cmd.Context) error {
  1148  	scanner := bufio.NewScanner(ctx.Stdin)
  1149  	clouds, err := assembleClouds()
  1150  	if err != nil {
  1151  		return errors.Trace(err)
  1152  	}
  1153  	c.Cloud, err = queryCloud(clouds, lxdnames.DefaultCloud, scanner, ctx.Stdout)
  1154  	if err != nil {
  1155  		return errors.Trace(err)
  1156  	}
  1157  	cloud, err := common.CloudByName(c.Cloud)
  1158  	if err != nil {
  1159  		return errors.Trace(err)
  1160  	}
  1161  
  1162  	switch len(cloud.Regions) {
  1163  	case 0:
  1164  		// No region to choose, nothing to do.
  1165  	case 1:
  1166  		// If there's just one, don't prompt, just use it.
  1167  		c.Region = cloud.Regions[0].Name
  1168  	default:
  1169  		c.Region, err = queryRegion(c.Cloud, cloud.Regions, scanner, ctx.Stdout)
  1170  		if err != nil {
  1171  			return errors.Trace(err)
  1172  		}
  1173  	}
  1174  
  1175  	defName := defaultControllerName(c.Cloud, c.Region)
  1176  
  1177  	c.controllerName, err = queryName(defName, scanner, ctx.Stdout)
  1178  	if err != nil {
  1179  		return errors.Trace(err)
  1180  	}
  1181  	return nil
  1182  }
  1183  
  1184  // checkProviderType ensures the provider type is okay.
  1185  func checkProviderType(envType string) error {
  1186  	featureflag.SetFlagsFromEnvironment(osenv.JujuFeatureFlagEnvKey)
  1187  	flag, ok := provisionalProviders[envType]
  1188  	if ok && !featureflag.Enabled(flag) {
  1189  		msg := `the %q provider is provisional in this version of Juju. To use it anyway, set JUJU_DEV_FEATURE_FLAGS="%s" in your shell model`
  1190  		return errors.Errorf(msg, envType, flag)
  1191  	}
  1192  	return nil
  1193  }
  1194  
  1195  // handleBootstrapError is called to clean up if bootstrap fails.
  1196  func handleBootstrapError(ctx *cmd.Context, cleanup func() error) {
  1197  	ch := make(chan os.Signal, 1)
  1198  	ctx.InterruptNotify(ch)
  1199  	defer ctx.StopInterruptNotify(ch)
  1200  	defer close(ch)
  1201  	go func() {
  1202  		for range ch {
  1203  			fmt.Fprintln(ctx.GetStderr(), "Cleaning up failed bootstrap")
  1204  		}
  1205  	}()
  1206  	logger.Debugf("cleaning up after failed bootstrap")
  1207  	if err := cleanup(); err != nil {
  1208  		logger.Errorf("error cleaning up: %v", err)
  1209  	}
  1210  }
  1211  
  1212  func handleChooseCloudRegionError(ctx *cmd.Context, err error) error {
  1213  	if !common.IsChooseCloudRegionError(err) {
  1214  		return err
  1215  	}
  1216  	fmt.Fprintf(ctx.GetStderr(),
  1217  		"%s\n\nSpecify an alternative region, or try %q.\n",
  1218  		err, "juju update-clouds",
  1219  	)
  1220  	return cmd.ErrSilent
  1221  }
  1222  
  1223  func newInt(i int) *int {
  1224  	return &i
  1225  }
  1226  
  1227  func newStringIfNonEmpty(s string) *string {
  1228  	if s == "" {
  1229  		return nil
  1230  	}
  1231  	return &s
  1232  }