launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/environs/manual/bootstrap.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package manual
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  
    10  	"launchpad.net/juju-core/constraints"
    11  	"launchpad.net/juju-core/environs"
    12  	"launchpad.net/juju-core/environs/bootstrap"
    13  	envtools "launchpad.net/juju-core/environs/tools"
    14  	"launchpad.net/juju-core/instance"
    15  	"launchpad.net/juju-core/provider/common"
    16  	"launchpad.net/juju-core/tools"
    17  	"launchpad.net/juju-core/worker/localstorage"
    18  )
    19  
    20  const BootstrapInstanceId = instance.Id(manualInstancePrefix)
    21  
    22  // LocalStorageEnviron is an Environ where the bootstrap node
    23  // manages its own local storage.
    24  type LocalStorageEnviron interface {
    25  	environs.Environ
    26  	localstorage.LocalStorageConfig
    27  }
    28  
    29  type BootstrapArgs struct {
    30  	Host                    string
    31  	DataDir                 string
    32  	Environ                 LocalStorageEnviron
    33  	PossibleTools           tools.List
    34  	Context                 environs.BootstrapContext
    35  	Series                  string
    36  	HardwareCharacteristics *instance.HardwareCharacteristics
    37  }
    38  
    39  func errMachineIdInvalid(machineId string) error {
    40  	return fmt.Errorf("%q is not a valid machine ID", machineId)
    41  }
    42  
    43  // NewManualBootstrapEnviron wraps a LocalStorageEnviron with another which
    44  // overrides the Bootstrap method; when Bootstrap is invoked, the specified
    45  // host will be manually bootstrapped.
    46  //
    47  // InitUbuntuUser is expected to have been executed successfully against
    48  // the host being bootstrapped.
    49  func Bootstrap(args BootstrapArgs) (err error) {
    50  	if args.Host == "" {
    51  		return errors.New("host argument is empty")
    52  	}
    53  	if args.Environ == nil {
    54  		return errors.New("environ argument is nil")
    55  	}
    56  	if args.DataDir == "" {
    57  		return errors.New("data-dir argument is empty")
    58  	}
    59  	if args.Series == "" {
    60  		return errors.New("series argument is empty")
    61  	}
    62  	if args.HardwareCharacteristics == nil {
    63  		return errors.New("hardware characteristics argument is empty")
    64  	}
    65  	if len(args.PossibleTools) == 0 {
    66  		return errors.New("possible tools is empty")
    67  	}
    68  
    69  	provisioned, err := checkProvisioned(args.Host)
    70  	if err != nil {
    71  		return fmt.Errorf("failed to check provisioned status: %v", err)
    72  	}
    73  	if provisioned {
    74  		return ErrProvisioned
    75  	}
    76  
    77  	// Filter tools based on detected series/arch.
    78  	logger.Infof("Filtering possible tools: %v", args.PossibleTools)
    79  	possibleTools, err := args.PossibleTools.Match(tools.Filter{
    80  		Arch:   *args.HardwareCharacteristics.Arch,
    81  		Series: args.Series,
    82  	})
    83  	if err != nil {
    84  		return err
    85  	}
    86  
    87  	// Store the state file. If provisioning fails, we'll remove the file.
    88  	logger.Infof("Saving bootstrap state file to bootstrap storage")
    89  	bootstrapStorage := args.Environ.Storage()
    90  	err = bootstrap.SaveState(
    91  		bootstrapStorage,
    92  		&bootstrap.BootstrapState{
    93  			StateInstances:  []instance.Id{BootstrapInstanceId},
    94  			Characteristics: []instance.HardwareCharacteristics{*args.HardwareCharacteristics},
    95  		},
    96  	)
    97  	if err != nil {
    98  		return err
    99  	}
   100  	defer func() {
   101  		if err != nil {
   102  			logger.Errorf("bootstrapping failed, removing state file: %v", err)
   103  			bootstrapStorage.Remove(bootstrap.StateFile)
   104  		}
   105  	}()
   106  
   107  	// Get a file:// scheme tools URL for the tools, which will have been
   108  	// copied to the remote machine's storage directory.
   109  	tools := *possibleTools[0]
   110  	storageDir := args.Environ.StorageDir()
   111  	toolsStorageName := envtools.StorageName(tools.Version)
   112  	tools.URL = fmt.Sprintf("file://%s/%s", storageDir, toolsStorageName)
   113  
   114  	// Add the local storage configuration.
   115  	agentEnv, err := localstorage.StoreConfig(args.Environ)
   116  	if err != nil {
   117  		return err
   118  	}
   119  
   120  	privateKey, err := common.GenerateSystemSSHKey(args.Environ)
   121  	if err != nil {
   122  		return err
   123  	}
   124  
   125  	// Finally, provision the machine agent.
   126  	stateFileURL := fmt.Sprintf("file://%s/%s", storageDir, bootstrap.StateFile)
   127  	mcfg := environs.NewBootstrapMachineConfig(stateFileURL, privateKey)
   128  	if args.DataDir != "" {
   129  		mcfg.DataDir = args.DataDir
   130  	}
   131  	mcfg.Tools = &tools
   132  	err = environs.FinishMachineConfig(mcfg, args.Environ.Config(), constraints.Value{})
   133  	if err != nil {
   134  		return err
   135  	}
   136  	for k, v := range agentEnv {
   137  		mcfg.AgentEnvironment[k] = v
   138  	}
   139  	return provisionMachineAgent(args.Host, mcfg, args.Context.Stderr())
   140  }