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 }