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 }