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