github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/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  	"github.com/juju/juju/constraints"
    11  	"github.com/juju/juju/environs"
    12  	"github.com/juju/juju/environs/bootstrap"
    13  	envtools "github.com/juju/juju/environs/tools"
    14  	"github.com/juju/juju/instance"
    15  	"github.com/juju/juju/provider/common"
    16  	"github.com/juju/juju/tools"
    17  	"github.com/juju/juju/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  		},
    95  	)
    96  	if err != nil {
    97  		return err
    98  	}
    99  	defer func() {
   100  		if err != nil {
   101  			logger.Errorf("bootstrapping failed, removing state file: %v", err)
   102  			bootstrapStorage.Remove(bootstrap.StateFile)
   103  		}
   104  	}()
   105  
   106  	// If the tools are on the machine already, get a file:// scheme tools URL.
   107  	tools := *possibleTools[0]
   108  	storageDir := args.Environ.StorageDir()
   109  	toolsStorageName := envtools.StorageName(tools.Version)
   110  	if url, _ := bootstrapStorage.URL(toolsStorageName); url == tools.URL {
   111  		tools.URL = fmt.Sprintf("file://%s/%s", storageDir, toolsStorageName)
   112  	}
   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  	mcfg := environs.NewBootstrapMachineConfig(privateKey)
   127  	mcfg.InstanceId = BootstrapInstanceId
   128  	mcfg.HardwareCharacteristics = args.HardwareCharacteristics
   129  	if args.DataDir != "" {
   130  		mcfg.DataDir = args.DataDir
   131  	}
   132  	mcfg.Tools = &tools
   133  	err = environs.FinishMachineConfig(mcfg, args.Environ.Config(), constraints.Value{})
   134  	if err != nil {
   135  		return err
   136  	}
   137  	for k, v := range agentEnv {
   138  		mcfg.AgentEnvironment[k] = v
   139  	}
   140  	return provisionMachineAgent(args.Host, mcfg, args.Context.GetStderr())
   141  }