github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/cmd/jujud/bootstrap.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "encoding/base64" 8 "fmt" 9 "io/ioutil" 10 "strings" 11 12 "launchpad.net/gnuflag" 13 "launchpad.net/goyaml" 14 15 "launchpad.net/juju-core/agent" 16 "launchpad.net/juju-core/cmd" 17 "launchpad.net/juju-core/constraints" 18 "launchpad.net/juju-core/environs" 19 "launchpad.net/juju-core/environs/bootstrap" 20 "launchpad.net/juju-core/environs/cloudinit" 21 "launchpad.net/juju-core/environs/config" 22 "launchpad.net/juju-core/instance" 23 "launchpad.net/juju-core/state" 24 "launchpad.net/juju-core/state/api/params" 25 ) 26 27 // Cloud-init write the URL to be used to load the bootstrap state into this file. 28 // A variable is used here to allow tests to override. 29 var providerStateURLFile = cloudinit.BootstrapStateURLFile 30 31 type BootstrapCommand struct { 32 cmd.CommandBase 33 Conf AgentConf 34 EnvConfig map[string]interface{} 35 Constraints constraints.Value 36 } 37 38 // Info returns a decription of the command. 39 func (c *BootstrapCommand) Info() *cmd.Info { 40 return &cmd.Info{ 41 Name: "bootstrap-state", 42 Purpose: "initialize juju state", 43 } 44 } 45 46 func (c *BootstrapCommand) SetFlags(f *gnuflag.FlagSet) { 47 c.Conf.addFlags(f) 48 yamlBase64Var(f, &c.EnvConfig, "env-config", "", "initial environment configuration (yaml, base64 encoded)") 49 f.Var(constraints.ConstraintsValue{&c.Constraints}, "constraints", "initial environment constraints (space-separated strings)") 50 } 51 52 // Init initializes the command for running. 53 func (c *BootstrapCommand) Init(args []string) error { 54 if len(c.EnvConfig) == 0 { 55 return requiredError("env-config") 56 } 57 return c.Conf.checkArgs(args) 58 } 59 60 // Run initializes state for an environment. 61 func (c *BootstrapCommand) Run(_ *cmd.Context) error { 62 data, err := ioutil.ReadFile(providerStateURLFile) 63 if err != nil { 64 return fmt.Errorf("cannot read provider-state-url file: %v", err) 65 } 66 envCfg, err := config.New(config.NoDefaults, c.EnvConfig) 67 if err != nil { 68 return err 69 } 70 stateInfoURL := strings.Split(string(data), "\n")[0] 71 bsState, err := bootstrap.LoadStateFromURL(stateInfoURL, !envCfg.SSLHostnameVerification()) 72 if err != nil { 73 return fmt.Errorf("cannot load state from URL %q (read from %q): %v", stateInfoURL, providerStateURLFile, err) 74 } 75 err = c.Conf.read("machine-0") 76 if err != nil { 77 return err 78 } 79 // agent.Jobs is an optional field in the agent config, and was 80 // introduced after 1.17.2. We default to allowing units on 81 // machine-0 if missing. 82 jobs := c.Conf.config.Jobs() 83 if len(jobs) == 0 { 84 jobs = []params.MachineJob{ 85 params.JobManageEnviron, 86 params.JobHostUnits, 87 } 88 } 89 var characteristics instance.HardwareCharacteristics 90 if len(bsState.Characteristics) > 0 { 91 characteristics = bsState.Characteristics[0] 92 } 93 st, _, err := c.Conf.config.InitializeState(envCfg, agent.BootstrapMachineConfig{ 94 Constraints: c.Constraints, 95 Jobs: jobs, 96 InstanceId: bsState.StateInstances[0], 97 Characteristics: characteristics, 98 }, state.DefaultDialOpts(), environs.NewStatePolicy()) 99 if err != nil { 100 return err 101 } 102 st.Close() 103 return nil 104 } 105 106 // yamlBase64Value implements gnuflag.Value on a map[string]interface{}. 107 type yamlBase64Value map[string]interface{} 108 109 // Set decodes the base64 value into yaml then expands that into a map. 110 func (v *yamlBase64Value) Set(value string) error { 111 decoded, err := base64.StdEncoding.DecodeString(value) 112 if err != nil { 113 return err 114 } 115 return goyaml.Unmarshal(decoded, v) 116 } 117 118 func (v *yamlBase64Value) String() string { 119 return fmt.Sprintf("%v", *v) 120 } 121 122 // yamlBase64Var sets up a gnuflag flag analogous to the FlagSet.*Var methods. 123 func yamlBase64Var(fs *gnuflag.FlagSet, target *map[string]interface{}, name string, value string, usage string) { 124 fs.Var((*yamlBase64Value)(target), name, usage) 125 }