Merge lp:~axwalk/juju-core/lp1291292-jujud-bootstrap-nostateurl into lp:~go-bot/juju-core/trunk
- lp1291292-jujud-bootstrap-nostateurl
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Andrew Wilkins |
Approved revision: | no longer in the source branch. |
Merged at revision: | 2464 |
Proposed branch: | lp:~axwalk/juju-core/lp1291292-jujud-bootstrap-nostateurl |
Merge into: | lp:~go-bot/juju-core/trunk |
Diff against target: |
791 lines (+138/-188) 14 files modified
cloudinit/sshinit/configure_test.go (+2/-1) cmd/jujud/bootstrap.go (+10/-25) cmd/jujud/bootstrap_test.go (+72/-52) cmd/jujud/main_test.go (+1/-0) environs/bootstrap/state.go (+0/-20) environs/bootstrap/state_test.go (+0/-27) environs/cloudinit.go (+1/-3) environs/cloudinit/cloudinit.go (+20/-10) environs/cloudinit/cloudinit_test.go (+11/-8) environs/manual/bootstrap.go (+3/-2) instance/instance.go (+10/-0) provider/common/bootstrap.go (+3/-9) provider/common/bootstrap_test.go (+3/-26) provider/local/environ.go (+2/-5) |
To merge this branch: | bzr merge lp:~axwalk/juju-core/lp1291292-jujud-bootstrap-nostateurl |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju Engineering | Pending | ||
Review via email: mp+212172@code.launchpad.net |
Commit message
Pass instance-
The bootstrap-state agent currently loads its
instance ID and hardware characteristics by
fetching a URL that is written to a file on
disk. This is no longer necessary with
synchronous bootstrap.
I have changed jujud and bootstrap script
generation to pass the instance-id and hardware
characteristics directly to jujud as command
line arguments.
Fixes part of lp:1291292
Description of the change
Pass instance-
The bootstrap-state agent currently loads its
instance ID and hardware characteristics by
fetching a URL that is written to a file on
disk. This is no longer necessary with
synchronous bootstrap.
I have changed jujud and bootstrap script
generation to pass the instance-id and hardware
characteristics directly to jujud as command
line arguments.
Fixes part of lp:1291292
Andrew Wilkins (axwalk) wrote : | # |
Dimiter Naydenov (dimitern) wrote : | # |
This is great, LGTM, assuming you tested it live.
https:/
File cmd/jujud/
https:/
cmd/jujud/
{
s/=/:=/
Andrew Wilkins (axwalk) wrote : | # |
Please take a look.
https:/
File cmd/jujud/
https:/
cmd/jujud/
{
On 2014/03/21 15:03:24, dimitern wrote:
> s/=/:=/
Done.
Andrew Wilkins (axwalk) wrote : | # |
Please take a look.
Andrew Wilkins (axwalk) wrote : | # |
On 2014/03/21 15:03:23, dimitern wrote:
> This is great, LGTM, assuming you tested it live.
Thanks, yes I have tested with local, manual and openstack.
I realised I could remove LoadStateFromURL altogether, so I went ahead
and did that.
> https:/
> File cmd/jujud/
https:/
> cmd/jujud/
nil {
> s/=/:=/
Preview Diff
1 | === modified file 'cloudinit/sshinit/configure_test.go' | |||
2 | --- cloudinit/sshinit/configure_test.go 2014-03-06 12:14:26 +0000 | |||
3 | +++ cloudinit/sshinit/configure_test.go 2014-03-24 01:53:33 +0000 | |||
4 | @@ -55,7 +55,8 @@ | |||
5 | 55 | func (s *configureSuite) getCloudConfig(c *gc.C, stateServer bool, vers version.Binary) *cloudinit.Config { | 55 | func (s *configureSuite) getCloudConfig(c *gc.C, stateServer bool, vers version.Binary) *cloudinit.Config { |
6 | 56 | var mcfg *envcloudinit.MachineConfig | 56 | var mcfg *envcloudinit.MachineConfig |
7 | 57 | if stateServer { | 57 | if stateServer { |
9 | 58 | mcfg = environs.NewBootstrapMachineConfig("http://whatever/dotcom", "private-key") | 58 | mcfg = environs.NewBootstrapMachineConfig("private-key") |
10 | 59 | mcfg.InstanceId = "instance-id" | ||
11 | 59 | mcfg.Jobs = []params.MachineJob{params.JobManageEnviron, params.JobHostUnits} | 60 | mcfg.Jobs = []params.MachineJob{params.JobManageEnviron, params.JobHostUnits} |
12 | 60 | } else { | 61 | } else { |
13 | 61 | mcfg = environs.NewMachineConfig("0", "ya", nil, nil) | 62 | mcfg = environs.NewMachineConfig("0", "ya", nil, nil) |
14 | 62 | 63 | ||
15 | === modified file 'cmd/jujud/bootstrap.go' | |||
16 | --- cmd/jujud/bootstrap.go 2014-03-05 16:30:01 +0000 | |||
17 | +++ cmd/jujud/bootstrap.go 2014-03-24 01:53:33 +0000 | |||
18 | @@ -6,8 +6,6 @@ | |||
19 | 6 | import ( | 6 | import ( |
20 | 7 | "encoding/base64" | 7 | "encoding/base64" |
21 | 8 | "fmt" | 8 | "fmt" |
22 | 9 | "io/ioutil" | ||
23 | 10 | "strings" | ||
24 | 11 | 9 | ||
25 | 12 | "launchpad.net/gnuflag" | 10 | "launchpad.net/gnuflag" |
26 | 13 | "launchpad.net/goyaml" | 11 | "launchpad.net/goyaml" |
27 | @@ -16,23 +14,19 @@ | |||
28 | 16 | "launchpad.net/juju-core/cmd" | 14 | "launchpad.net/juju-core/cmd" |
29 | 17 | "launchpad.net/juju-core/constraints" | 15 | "launchpad.net/juju-core/constraints" |
30 | 18 | "launchpad.net/juju-core/environs" | 16 | "launchpad.net/juju-core/environs" |
31 | 19 | "launchpad.net/juju-core/environs/bootstrap" | ||
32 | 20 | "launchpad.net/juju-core/environs/cloudinit" | ||
33 | 21 | "launchpad.net/juju-core/environs/config" | 17 | "launchpad.net/juju-core/environs/config" |
34 | 22 | "launchpad.net/juju-core/instance" | 18 | "launchpad.net/juju-core/instance" |
35 | 23 | "launchpad.net/juju-core/state" | 19 | "launchpad.net/juju-core/state" |
36 | 24 | "launchpad.net/juju-core/state/api/params" | 20 | "launchpad.net/juju-core/state/api/params" |
37 | 25 | ) | 21 | ) |
38 | 26 | 22 | ||
39 | 27 | // Cloud-init write the URL to be used to load the bootstrap state into this file. | ||
40 | 28 | // A variable is used here to allow tests to override. | ||
41 | 29 | var providerStateURLFile = cloudinit.BootstrapStateURLFile | ||
42 | 30 | |||
43 | 31 | type BootstrapCommand struct { | 23 | type BootstrapCommand struct { |
44 | 32 | cmd.CommandBase | 24 | cmd.CommandBase |
45 | 33 | Conf AgentConf | 25 | Conf AgentConf |
46 | 34 | EnvConfig map[string]interface{} | 26 | EnvConfig map[string]interface{} |
47 | 35 | Constraints constraints.Value | 27 | Constraints constraints.Value |
48 | 28 | Hardware instance.HardwareCharacteristics | ||
49 | 29 | InstanceId string | ||
50 | 36 | } | 30 | } |
51 | 37 | 31 | ||
52 | 38 | // Info returns a decription of the command. | 32 | // Info returns a decription of the command. |
53 | @@ -47,6 +41,8 @@ | |||
54 | 47 | c.Conf.addFlags(f) | 41 | c.Conf.addFlags(f) |
55 | 48 | yamlBase64Var(f, &c.EnvConfig, "env-config", "", "initial environment configuration (yaml, base64 encoded)") | 42 | yamlBase64Var(f, &c.EnvConfig, "env-config", "", "initial environment configuration (yaml, base64 encoded)") |
56 | 49 | f.Var(constraints.ConstraintsValue{&c.Constraints}, "constraints", "initial environment constraints (space-separated strings)") | 43 | f.Var(constraints.ConstraintsValue{&c.Constraints}, "constraints", "initial environment constraints (space-separated strings)") |
57 | 44 | f.Var(&c.Hardware, "hardware", "hardware characteristics (space-separated strings)") | ||
58 | 45 | f.StringVar(&c.InstanceId, "instance-id", "", "unique instance-id for bootstrap machine") | ||
59 | 50 | } | 46 | } |
60 | 51 | 47 | ||
61 | 52 | // Init initializes the command for running. | 48 | // Init initializes the command for running. |
62 | @@ -54,26 +50,19 @@ | |||
63 | 54 | if len(c.EnvConfig) == 0 { | 50 | if len(c.EnvConfig) == 0 { |
64 | 55 | return requiredError("env-config") | 51 | return requiredError("env-config") |
65 | 56 | } | 52 | } |
66 | 53 | if c.InstanceId == "" { | ||
67 | 54 | return requiredError("instance-id") | ||
68 | 55 | } | ||
69 | 57 | return c.Conf.checkArgs(args) | 56 | return c.Conf.checkArgs(args) |
70 | 58 | } | 57 | } |
71 | 59 | 58 | ||
72 | 60 | // Run initializes state for an environment. | 59 | // Run initializes state for an environment. |
73 | 61 | func (c *BootstrapCommand) Run(_ *cmd.Context) error { | 60 | func (c *BootstrapCommand) Run(_ *cmd.Context) error { |
74 | 62 | data, err := ioutil.ReadFile(providerStateURLFile) | ||
75 | 63 | if err != nil { | ||
76 | 64 | return fmt.Errorf("cannot read provider-state-url file: %v", err) | ||
77 | 65 | } | ||
78 | 66 | envCfg, err := config.New(config.NoDefaults, c.EnvConfig) | 61 | envCfg, err := config.New(config.NoDefaults, c.EnvConfig) |
79 | 67 | if err != nil { | 62 | if err != nil { |
80 | 68 | return err | 63 | return err |
81 | 69 | } | 64 | } |
89 | 70 | stateInfoURL := strings.Split(string(data), "\n")[0] | 65 | if err := c.Conf.read("machine-0"); err != nil { |
83 | 71 | bsState, err := bootstrap.LoadStateFromURL(stateInfoURL, !envCfg.SSLHostnameVerification()) | ||
84 | 72 | if err != nil { | ||
85 | 73 | return fmt.Errorf("cannot load state from URL %q (read from %q): %v", stateInfoURL, providerStateURLFile, err) | ||
86 | 74 | } | ||
87 | 75 | err = c.Conf.read("machine-0") | ||
88 | 76 | if err != nil { | ||
90 | 77 | return err | 66 | return err |
91 | 78 | } | 67 | } |
92 | 79 | // agent.Jobs is an optional field in the agent config, and was | 68 | // agent.Jobs is an optional field in the agent config, and was |
93 | @@ -86,15 +75,11 @@ | |||
94 | 86 | params.JobHostUnits, | 75 | params.JobHostUnits, |
95 | 87 | } | 76 | } |
96 | 88 | } | 77 | } |
97 | 89 | var characteristics instance.HardwareCharacteristics | ||
98 | 90 | if len(bsState.Characteristics) > 0 { | ||
99 | 91 | characteristics = bsState.Characteristics[0] | ||
100 | 92 | } | ||
101 | 93 | st, _, err := c.Conf.config.InitializeState(envCfg, agent.BootstrapMachineConfig{ | 78 | st, _, err := c.Conf.config.InitializeState(envCfg, agent.BootstrapMachineConfig{ |
102 | 94 | Constraints: c.Constraints, | 79 | Constraints: c.Constraints, |
103 | 95 | Jobs: jobs, | 80 | Jobs: jobs, |
106 | 96 | InstanceId: bsState.StateInstances[0], | 81 | InstanceId: instance.Id(c.InstanceId), |
107 | 97 | Characteristics: characteristics, | 82 | Characteristics: c.Hardware, |
108 | 98 | }, state.DefaultDialOpts(), environs.NewStatePolicy()) | 83 | }, state.DefaultDialOpts(), environs.NewStatePolicy()) |
109 | 99 | if err != nil { | 84 | if err != nil { |
110 | 100 | return err | 85 | return err |
111 | 101 | 86 | ||
112 | === modified file 'cmd/jujud/bootstrap_test.go' | |||
113 | --- cmd/jujud/bootstrap_test.go 2014-03-13 07:54:56 +0000 | |||
114 | +++ cmd/jujud/bootstrap_test.go 2014-03-24 01:53:33 +0000 | |||
115 | @@ -5,8 +5,6 @@ | |||
116 | 5 | 5 | ||
117 | 6 | import ( | 6 | import ( |
118 | 7 | "encoding/base64" | 7 | "encoding/base64" |
119 | 8 | "io/ioutil" | ||
120 | 9 | "path/filepath" | ||
121 | 10 | 8 | ||
122 | 11 | jc "github.com/juju/testing/checkers" | 9 | jc "github.com/juju/testing/checkers" |
123 | 12 | gc "launchpad.net/gocheck" | 10 | gc "launchpad.net/gocheck" |
124 | @@ -15,8 +13,6 @@ | |||
125 | 15 | "launchpad.net/juju-core/agent" | 13 | "launchpad.net/juju-core/agent" |
126 | 16 | "launchpad.net/juju-core/constraints" | 14 | "launchpad.net/juju-core/constraints" |
127 | 17 | "launchpad.net/juju-core/environs" | 15 | "launchpad.net/juju-core/environs" |
128 | 18 | "launchpad.net/juju-core/environs/bootstrap" | ||
129 | 19 | "launchpad.net/juju-core/environs/jujutest" | ||
130 | 20 | "launchpad.net/juju-core/errors" | 16 | "launchpad.net/juju-core/errors" |
131 | 21 | "launchpad.net/juju-core/instance" | 17 | "launchpad.net/juju-core/instance" |
132 | 22 | "launchpad.net/juju-core/provider/dummy" | 18 | "launchpad.net/juju-core/provider/dummy" |
133 | @@ -33,32 +29,15 @@ | |||
134 | 33 | type BootstrapSuite struct { | 29 | type BootstrapSuite struct { |
135 | 34 | testbase.LoggingSuite | 30 | testbase.LoggingSuite |
136 | 35 | testing.MgoSuite | 31 | testing.MgoSuite |
140 | 36 | dataDir string | 32 | dataDir string |
141 | 37 | logDir string | 33 | logDir string |
139 | 38 | providerStateURLFile string | ||
142 | 39 | } | 34 | } |
143 | 40 | 35 | ||
144 | 41 | var _ = gc.Suite(&BootstrapSuite{}) | 36 | var _ = gc.Suite(&BootstrapSuite{}) |
145 | 42 | 37 | ||
146 | 43 | var testRoundTripper = &jujutest.ProxyRoundTripper{} | ||
147 | 44 | |||
148 | 45 | func init() { | ||
149 | 46 | // Prepare mock http transport for provider-state output in tests. | ||
150 | 47 | testRoundTripper.RegisterForScheme("test") | ||
151 | 48 | } | ||
152 | 49 | |||
153 | 50 | func (s *BootstrapSuite) SetUpSuite(c *gc.C) { | 38 | func (s *BootstrapSuite) SetUpSuite(c *gc.C) { |
154 | 51 | s.LoggingSuite.SetUpSuite(c) | 39 | s.LoggingSuite.SetUpSuite(c) |
155 | 52 | s.MgoSuite.SetUpSuite(c) | 40 | s.MgoSuite.SetUpSuite(c) |
156 | 53 | stateInfo := bootstrap.BootstrapState{ | ||
157 | 54 | StateInstances: []instance.Id{instance.Id("dummy.instance.id")}, | ||
158 | 55 | } | ||
159 | 56 | stateData, err := goyaml.Marshal(stateInfo) | ||
160 | 57 | c.Assert(err, gc.IsNil) | ||
161 | 58 | content := map[string]string{"/" + bootstrap.StateFile: string(stateData)} | ||
162 | 59 | testRoundTripper.Sub = jujutest.NewCannedRoundTripper(content, nil) | ||
163 | 60 | s.providerStateURLFile = filepath.Join(c.MkDir(), "provider-state-url") | ||
164 | 61 | providerStateURLFile = s.providerStateURLFile | ||
165 | 62 | } | 41 | } |
166 | 63 | 42 | ||
167 | 64 | func (s *BootstrapSuite) TearDownSuite(c *gc.C) { | 43 | func (s *BootstrapSuite) TearDownSuite(c *gc.C) { |
168 | @@ -85,7 +64,6 @@ | |||
169 | 85 | } | 64 | } |
170 | 86 | 65 | ||
171 | 87 | func (s *BootstrapSuite) initBootstrapCommand(c *gc.C, jobs []params.MachineJob, args ...string) (machineConf agent.Config, cmd *BootstrapCommand, err error) { | 66 | func (s *BootstrapSuite) initBootstrapCommand(c *gc.C, jobs []params.MachineJob, args ...string) (machineConf agent.Config, cmd *BootstrapCommand, err error) { |
172 | 88 | ioutil.WriteFile(s.providerStateURLFile, []byte("test://localhost/provider-state\n"), 0600) | ||
173 | 89 | if len(jobs) == 0 { | 67 | if len(jobs) == 0 { |
174 | 90 | // Add default jobs. | 68 | // Add default jobs. |
175 | 91 | jobs = []params.MachineJob{ | 69 | jobs = []params.MachineJob{ |
176 | @@ -123,7 +101,8 @@ | |||
177 | 123 | } | 101 | } |
178 | 124 | 102 | ||
179 | 125 | func (s *BootstrapSuite) TestInitializeEnvironment(c *gc.C) { | 103 | func (s *BootstrapSuite) TestInitializeEnvironment(c *gc.C) { |
181 | 126 | _, cmd, err := s.initBootstrapCommand(c, nil, "--env-config", testConfig) | 104 | hw := instance.MustParseHardware("arch=amd64 mem=8G") |
182 | 105 | _, cmd, err := s.initBootstrapCommand(c, nil, "--env-config", testConfig, "--instance-id", "anything", "--hardware", hw.String()) | ||
183 | 127 | c.Assert(err, gc.IsNil) | 106 | c.Assert(err, gc.IsNil) |
184 | 128 | err = cmd.Run(nil) | 107 | err = cmd.Run(nil) |
185 | 129 | c.Assert(err, gc.IsNil) | 108 | c.Assert(err, gc.IsNil) |
186 | @@ -141,7 +120,12 @@ | |||
187 | 141 | 120 | ||
188 | 142 | instid, err := machines[0].InstanceId() | 121 | instid, err := machines[0].InstanceId() |
189 | 143 | c.Assert(err, gc.IsNil) | 122 | c.Assert(err, gc.IsNil) |
191 | 144 | c.Assert(instid, gc.Equals, instance.Id("dummy.instance.id")) | 123 | c.Assert(instid, gc.Equals, instance.Id("anything")) |
192 | 124 | |||
193 | 125 | stateHw, err := machines[0].HardwareCharacteristics() | ||
194 | 126 | c.Assert(err, gc.IsNil) | ||
195 | 127 | c.Assert(stateHw, gc.NotNil) | ||
196 | 128 | c.Assert(*stateHw, gc.DeepEquals, hw) | ||
197 | 145 | 129 | ||
198 | 146 | cons, err := st.EnvironConstraints() | 130 | cons, err := st.EnvironConstraints() |
199 | 147 | c.Assert(err, gc.IsNil) | 131 | c.Assert(err, gc.IsNil) |
200 | @@ -150,7 +134,11 @@ | |||
201 | 150 | 134 | ||
202 | 151 | func (s *BootstrapSuite) TestSetConstraints(c *gc.C) { | 135 | func (s *BootstrapSuite) TestSetConstraints(c *gc.C) { |
203 | 152 | tcons := constraints.Value{Mem: uint64p(2048), CpuCores: uint64p(2)} | 136 | tcons := constraints.Value{Mem: uint64p(2048), CpuCores: uint64p(2)} |
205 | 153 | _, cmd, err := s.initBootstrapCommand(c, nil, "--env-config", testConfig, "--constraints", tcons.String()) | 137 | _, cmd, err := s.initBootstrapCommand(c, nil, |
206 | 138 | "--env-config", testConfig, | ||
207 | 139 | "--instance-id", "anything", | ||
208 | 140 | "--constraints", tcons.String(), | ||
209 | 141 | ) | ||
210 | 154 | c.Assert(err, gc.IsNil) | 142 | c.Assert(err, gc.IsNil) |
211 | 155 | err = cmd.Run(nil) | 143 | err = cmd.Run(nil) |
212 | 156 | c.Assert(err, gc.IsNil) | 144 | c.Assert(err, gc.IsNil) |
213 | @@ -182,7 +170,7 @@ | |||
214 | 182 | expectedJobs := []state.MachineJob{ | 170 | expectedJobs := []state.MachineJob{ |
215 | 183 | state.JobManageEnviron, state.JobHostUnits, | 171 | state.JobManageEnviron, state.JobHostUnits, |
216 | 184 | } | 172 | } |
218 | 185 | _, cmd, err := s.initBootstrapCommand(c, nil, "--env-config", testConfig) | 173 | _, cmd, err := s.initBootstrapCommand(c, nil, "--env-config", testConfig, "--instance-id", "anything") |
219 | 186 | c.Assert(err, gc.IsNil) | 174 | c.Assert(err, gc.IsNil) |
220 | 187 | err = cmd.Run(nil) | 175 | err = cmd.Run(nil) |
221 | 188 | c.Assert(err, gc.IsNil) | 176 | c.Assert(err, gc.IsNil) |
222 | @@ -201,7 +189,7 @@ | |||
223 | 201 | 189 | ||
224 | 202 | func (s *BootstrapSuite) TestConfiguredMachineJobs(c *gc.C) { | 190 | func (s *BootstrapSuite) TestConfiguredMachineJobs(c *gc.C) { |
225 | 203 | jobs := []params.MachineJob{params.JobManageEnviron} | 191 | jobs := []params.MachineJob{params.JobManageEnviron} |
227 | 204 | _, cmd, err := s.initBootstrapCommand(c, jobs, "--env-config", testConfig) | 192 | _, cmd, err := s.initBootstrapCommand(c, jobs, "--env-config", testConfig, "--instance-id", "anything") |
228 | 205 | c.Assert(err, gc.IsNil) | 193 | c.Assert(err, gc.IsNil) |
229 | 206 | err = cmd.Run(nil) | 194 | err = cmd.Run(nil) |
230 | 207 | c.Assert(err, gc.IsNil) | 195 | c.Assert(err, gc.IsNil) |
231 | @@ -231,7 +219,7 @@ | |||
232 | 231 | } | 219 | } |
233 | 232 | 220 | ||
234 | 233 | func (s *BootstrapSuite) TestInitialPassword(c *gc.C) { | 221 | func (s *BootstrapSuite) TestInitialPassword(c *gc.C) { |
236 | 234 | machineConf, cmd, err := s.initBootstrapCommand(c, nil, "--env-config", testConfig) | 222 | machineConf, cmd, err := s.initBootstrapCommand(c, nil, "--env-config", testConfig, "--instance-id", "anything") |
237 | 235 | c.Assert(err, gc.IsNil) | 223 | c.Assert(err, gc.IsNil) |
238 | 236 | 224 | ||
239 | 237 | err = cmd.Run(nil) | 225 | err = cmd.Run(nil) |
240 | @@ -270,35 +258,65 @@ | |||
241 | 270 | defer st.Close() | 258 | defer st.Close() |
242 | 271 | } | 259 | } |
243 | 272 | 260 | ||
248 | 273 | var base64ConfigTests = []struct { | 261 | var bootstrapArgTests = []struct { |
249 | 274 | input []string | 262 | input []string |
250 | 275 | err string | 263 | err string |
251 | 276 | expected map[string]interface{} | 264 | expectedInstanceId string |
252 | 265 | expectedHardware instance.HardwareCharacteristics | ||
253 | 266 | expectedConfig map[string]interface{} | ||
254 | 277 | }{ | 267 | }{ |
255 | 278 | { | 268 | { |
260 | 279 | // no value supplied | 269 | // no value supplied for env-config |
261 | 280 | nil, | 270 | err: "--env-config option must be set", |
258 | 281 | "--env-config option must be set", | ||
259 | 282 | nil, | ||
262 | 283 | }, { | 271 | }, { |
267 | 284 | // empty | 272 | // empty env-config |
268 | 285 | []string{"--env-config", ""}, | 273 | input: []string{"--env-config", ""}, |
269 | 286 | "--env-config option must be set", | 274 | err: "--env-config option must be set", |
266 | 287 | nil, | ||
270 | 288 | }, { | 275 | }, { |
271 | 289 | // wrong, should be base64 | 276 | // wrong, should be base64 |
279 | 290 | []string{"--env-config", "name: banana\n"}, | 277 | input: []string{"--env-config", "name: banana\n"}, |
280 | 291 | ".*illegal base64 data at input byte.*", | 278 | err: ".*illegal base64 data at input byte.*", |
281 | 292 | nil, | 279 | }, { |
282 | 293 | }, { | 280 | // no value supplied for instance-id |
283 | 294 | []string{"--env-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n"))}, | 281 | input: []string{ |
284 | 295 | "", | 282 | "--env-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n")), |
285 | 296 | map[string]interface{}{"name": "banana"}, | 283 | }, |
286 | 284 | err: "--instance-id option must be set", | ||
287 | 285 | }, { | ||
288 | 286 | // empty instance-id | ||
289 | 287 | input: []string{ | ||
290 | 288 | "--env-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n")), | ||
291 | 289 | "--instance-id", "", | ||
292 | 290 | }, | ||
293 | 291 | err: "--instance-id option must be set", | ||
294 | 292 | }, { | ||
295 | 293 | input: []string{ | ||
296 | 294 | "--env-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n")), | ||
297 | 295 | "--instance-id", "anything", | ||
298 | 296 | }, | ||
299 | 297 | expectedInstanceId: "anything", | ||
300 | 298 | expectedConfig: map[string]interface{}{"name": "banana"}, | ||
301 | 299 | }, { | ||
302 | 300 | input: []string{ | ||
303 | 301 | "--env-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n")), | ||
304 | 302 | "--instance-id", "anything", | ||
305 | 303 | "--hardware", "nonsense", | ||
306 | 304 | }, | ||
307 | 305 | err: `invalid value "nonsense" for flag --hardware: malformed characteristic "nonsense"`, | ||
308 | 306 | }, { | ||
309 | 307 | input: []string{ | ||
310 | 308 | "--env-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n")), | ||
311 | 309 | "--instance-id", "anything", | ||
312 | 310 | "--hardware", "arch=amd64 cpu-cores=4 root-disk=2T", | ||
313 | 311 | }, | ||
314 | 312 | expectedInstanceId: "anything", | ||
315 | 313 | expectedHardware: instance.MustParseHardware("arch=amd64 cpu-cores=4 root-disk=2T"), | ||
316 | 314 | expectedConfig: map[string]interface{}{"name": "banana"}, | ||
317 | 297 | }, | 315 | }, |
318 | 298 | } | 316 | } |
319 | 299 | 317 | ||
322 | 300 | func (s *BootstrapSuite) TestBase64Config(c *gc.C) { | 318 | func (s *BootstrapSuite) TestBootstrapArgs(c *gc.C) { |
323 | 301 | for i, t := range base64ConfigTests { | 319 | for i, t := range bootstrapArgTests { |
324 | 302 | c.Logf("test %d", i) | 320 | c.Logf("test %d", i) |
325 | 303 | var args []string | 321 | var args []string |
326 | 304 | args = append(args, t.input...) | 322 | args = append(args, t.input...) |
327 | @@ -306,7 +324,9 @@ | |||
328 | 306 | if t.err == "" { | 324 | if t.err == "" { |
329 | 307 | c.Assert(cmd, gc.NotNil) | 325 | c.Assert(cmd, gc.NotNil) |
330 | 308 | c.Assert(err, gc.IsNil) | 326 | c.Assert(err, gc.IsNil) |
332 | 309 | c.Assert(cmd.EnvConfig, gc.DeepEquals, t.expected) | 327 | c.Assert(cmd.EnvConfig, gc.DeepEquals, t.expectedConfig) |
333 | 328 | c.Assert(cmd.InstanceId, gc.Equals, t.expectedInstanceId) | ||
334 | 329 | c.Assert(cmd.Hardware, gc.DeepEquals, t.expectedHardware) | ||
335 | 310 | } else { | 330 | } else { |
336 | 311 | c.Assert(err, gc.ErrorMatches, t.err) | 331 | c.Assert(err, gc.ErrorMatches, t.err) |
337 | 312 | } | 332 | } |
338 | 313 | 333 | ||
339 | === modified file 'cmd/jujud/main_test.go' | |||
340 | --- cmd/jujud/main_test.go 2014-03-19 03:48:12 +0000 | |||
341 | +++ cmd/jujud/main_test.go 2014-03-24 01:53:33 +0000 | |||
342 | @@ -110,6 +110,7 @@ | |||
343 | 110 | checkMessage(c, msga, | 110 | checkMessage(c, msga, |
344 | 111 | "bootstrap-state", | 111 | "bootstrap-state", |
345 | 112 | "--env-config", b64yaml{"blah": "blah"}.encode(), | 112 | "--env-config", b64yaml{"blah": "blah"}.encode(), |
346 | 113 | "--instance-id", "inst", | ||
347 | 113 | "toastie") | 114 | "toastie") |
348 | 114 | checkMessage(c, msga, "unit", | 115 | checkMessage(c, msga, "unit", |
349 | 115 | "--unit-name", "un/0", | 116 | "--unit-name", "un/0", |
350 | 116 | 117 | ||
351 | === modified file 'environs/bootstrap/state.go' | |||
352 | --- environs/bootstrap/state.go 2014-03-12 10:59:17 +0000 | |||
353 | +++ environs/bootstrap/state.go 2014-03-24 01:53:33 +0000 | |||
354 | @@ -8,7 +8,6 @@ | |||
355 | 8 | "fmt" | 8 | "fmt" |
356 | 9 | "io" | 9 | "io" |
357 | 10 | "io/ioutil" | 10 | "io/ioutil" |
358 | 11 | "net/http" | ||
359 | 12 | 11 | ||
360 | 13 | "launchpad.net/goyaml" | 12 | "launchpad.net/goyaml" |
361 | 14 | 13 | ||
362 | @@ -16,7 +15,6 @@ | |||
363 | 16 | "launchpad.net/juju-core/environs/storage" | 15 | "launchpad.net/juju-core/environs/storage" |
364 | 17 | coreerrors "launchpad.net/juju-core/errors" | 16 | coreerrors "launchpad.net/juju-core/errors" |
365 | 18 | "launchpad.net/juju-core/instance" | 17 | "launchpad.net/juju-core/instance" |
366 | 19 | "launchpad.net/juju-core/utils" | ||
367 | 20 | ) | 18 | ) |
368 | 21 | 19 | ||
369 | 22 | // StateFile is the name of the file where the provider's state is stored. | 20 | // StateFile is the name of the file where the provider's state is stored. |
370 | @@ -68,24 +66,6 @@ | |||
371 | 68 | return putState(storage, data) | 66 | return putState(storage, data) |
372 | 69 | } | 67 | } |
373 | 70 | 68 | ||
374 | 71 | // LoadStateFromURL reads state from the given URL. | ||
375 | 72 | func LoadStateFromURL(url string, disableSSLHostnameVerification bool) (*BootstrapState, error) { | ||
376 | 73 | logger.Debugf("loading %q from %q", StateFile, url) | ||
377 | 74 | client := http.DefaultClient | ||
378 | 75 | if disableSSLHostnameVerification { | ||
379 | 76 | logger.Infof("hostname SSL verification disabled") | ||
380 | 77 | client = utils.GetNonValidatingHTTPClient() | ||
381 | 78 | } | ||
382 | 79 | resp, err := client.Get(url) | ||
383 | 80 | if err != nil { | ||
384 | 81 | return nil, err | ||
385 | 82 | } | ||
386 | 83 | if resp.StatusCode != http.StatusOK { | ||
387 | 84 | return nil, fmt.Errorf("could not load state from url: %v %s", url, resp.Status) | ||
388 | 85 | } | ||
389 | 86 | return loadState(resp.Body) | ||
390 | 87 | } | ||
391 | 88 | |||
392 | 89 | // LoadState reads state from the given storage. | 69 | // LoadState reads state from the given storage. |
393 | 90 | func LoadState(stor storage.StorageReader) (*BootstrapState, error) { | 70 | func LoadState(stor storage.StorageReader) (*BootstrapState, error) { |
394 | 91 | r, err := storage.Get(stor, StateFile) | 71 | r, err := storage.Get(stor, StateFile) |
395 | 92 | 72 | ||
396 | === modified file 'environs/bootstrap/state_test.go' | |||
397 | --- environs/bootstrap/state_test.go 2014-03-13 07:54:56 +0000 | |||
398 | +++ environs/bootstrap/state_test.go 2014-03-24 01:53:33 +0000 | |||
399 | @@ -125,33 +125,6 @@ | |||
400 | 125 | c.Check(*storedState, gc.DeepEquals, state) | 125 | c.Check(*storedState, gc.DeepEquals, state) |
401 | 126 | } | 126 | } |
402 | 127 | 127 | ||
403 | 128 | func (suite *StateSuite) TestLoadStateFromURLReadsStateFile(c *gc.C) { | ||
404 | 129 | storage, dataDir := suite.newStorageWithDataDir(c) | ||
405 | 130 | state := suite.setUpSavedState(c, dataDir) | ||
406 | 131 | url, err := storage.URL(bootstrap.StateFile) | ||
407 | 132 | c.Assert(err, gc.IsNil) | ||
408 | 133 | storedState, err := bootstrap.LoadStateFromURL(url, false) | ||
409 | 134 | c.Assert(err, gc.IsNil) | ||
410 | 135 | c.Check(*storedState, gc.DeepEquals, state) | ||
411 | 136 | } | ||
412 | 137 | |||
413 | 138 | func (suite *StateSuite) TestLoadStateFromURLBadCert(c *gc.C) { | ||
414 | 139 | baseURL, _ := suite.testingHTTPSServer(c) | ||
415 | 140 | url := baseURL + "/" + bootstrap.StateFile | ||
416 | 141 | storedState, err := bootstrap.LoadStateFromURL(url, false) | ||
417 | 142 | c.Assert(err, gc.ErrorMatches, ".*/provider-state:.* certificate signed by unknown authority") | ||
418 | 143 | c.Assert(storedState, gc.IsNil) | ||
419 | 144 | } | ||
420 | 145 | |||
421 | 146 | func (suite *StateSuite) TestLoadStateFromURLBadCertPermitted(c *gc.C) { | ||
422 | 147 | baseURL, dataDir := suite.testingHTTPSServer(c) | ||
423 | 148 | state := suite.setUpSavedState(c, dataDir) | ||
424 | 149 | url := baseURL + "/" + bootstrap.StateFile | ||
425 | 150 | storedState, err := bootstrap.LoadStateFromURL(url, true) | ||
426 | 151 | c.Assert(err, gc.IsNil) | ||
427 | 152 | c.Check(*storedState, gc.DeepEquals, state) | ||
428 | 153 | } | ||
429 | 154 | |||
430 | 155 | func (suite *StateSuite) TestLoadStateMissingFile(c *gc.C) { | 128 | func (suite *StateSuite) TestLoadStateMissingFile(c *gc.C) { |
431 | 156 | stor := suite.newStorage(c) | 129 | stor := suite.newStorage(c) |
432 | 157 | _, err := bootstrap.LoadState(stor) | 130 | _, err := bootstrap.LoadState(stor) |
433 | 158 | 131 | ||
434 | === modified file 'environs/cloudinit.go' | |||
435 | --- environs/cloudinit.go 2014-03-12 02:28:30 +0000 | |||
436 | +++ environs/cloudinit.go 2014-03-24 01:53:33 +0000 | |||
437 | @@ -57,13 +57,11 @@ | |||
438 | 57 | // NewBootstrapMachineConfig sets up a basic machine configuration for a | 57 | // NewBootstrapMachineConfig sets up a basic machine configuration for a |
439 | 58 | // bootstrap node. You'll still need to supply more information, but this | 58 | // bootstrap node. You'll still need to supply more information, but this |
440 | 59 | // takes care of the fixed entries and the ones that are always needed. | 59 | // takes care of the fixed entries and the ones that are always needed. |
443 | 60 | // stateInfoURL is the storage URL for the environment's state file. | 60 | func NewBootstrapMachineConfig(privateSystemSSHKey string) *cloudinit.MachineConfig { |
442 | 61 | func NewBootstrapMachineConfig(stateInfoURL string, privateSystemSSHKey string) *cloudinit.MachineConfig { | ||
444 | 62 | // For a bootstrap instance, FinishMachineConfig will provide the | 61 | // For a bootstrap instance, FinishMachineConfig will provide the |
445 | 63 | // state.Info and the api.Info. The machine id must *always* be "0". | 62 | // state.Info and the api.Info. The machine id must *always* be "0". |
446 | 64 | mcfg := NewMachineConfig("0", state.BootstrapNonce, nil, nil) | 63 | mcfg := NewMachineConfig("0", state.BootstrapNonce, nil, nil) |
447 | 65 | mcfg.StateServer = true | 64 | mcfg.StateServer = true |
448 | 66 | mcfg.StateInfoURL = stateInfoURL | ||
449 | 67 | mcfg.SystemPrivateSSHKey = privateSystemSSHKey | 65 | mcfg.SystemPrivateSSHKey = privateSystemSSHKey |
450 | 68 | mcfg.Jobs = []params.MachineJob{params.JobManageEnviron, params.JobHostUnits} | 66 | mcfg.Jobs = []params.MachineJob{params.JobManageEnviron, params.JobHostUnits} |
451 | 69 | return mcfg | 67 | return mcfg |
452 | 70 | 68 | ||
453 | === modified file 'environs/cloudinit/cloudinit.go' | |||
454 | --- environs/cloudinit/cloudinit.go 2014-03-18 22:20:40 +0000 | |||
455 | +++ environs/cloudinit/cloudinit.go 2014-03-24 01:53:33 +0000 | |||
456 | @@ -31,12 +31,6 @@ | |||
457 | 31 | "launchpad.net/juju-core/version" | 31 | "launchpad.net/juju-core/version" |
458 | 32 | ) | 32 | ) |
459 | 33 | 33 | ||
460 | 34 | // BootstrapStateURLFile is used to communicate to the first bootstrap node | ||
461 | 35 | // the URL from which to obtain important state information (instance id and | ||
462 | 36 | // hardware characteristics). It is a transient file, only used as the node | ||
463 | 37 | // is bootstrapping. | ||
464 | 38 | const BootstrapStateURLFile = "/tmp/provider-state-url" | ||
465 | 39 | |||
466 | 40 | // SystemIdentity is the name of the file where the environment SSH key is kept. | 34 | // SystemIdentity is the name of the file where the environment SSH key is kept. |
467 | 41 | const SystemIdentity = "system-identity" | 35 | const SystemIdentity = "system-identity" |
468 | 42 | 36 | ||
469 | @@ -79,6 +73,15 @@ | |||
470 | 79 | // or be empty when starting a state server. | 73 | // or be empty when starting a state server. |
471 | 80 | APIInfo *api.Info | 74 | APIInfo *api.Info |
472 | 81 | 75 | ||
473 | 76 | // InstanceId is the instance ID of the machine being initialised. | ||
474 | 77 | // This is required when bootstrapping, and ignored otherwise. | ||
475 | 78 | InstanceId instance.Id | ||
476 | 79 | |||
477 | 80 | // HardwareCharacteristics contains the harrdware characteristics of | ||
478 | 81 | // the machine being initialised. This optional, and is only used by | ||
479 | 82 | // the bootstrap agent during state initialisation. | ||
480 | 83 | HardwareCharacteristics *instance.HardwareCharacteristics | ||
481 | 84 | |||
482 | 82 | // MachineNonce is set at provisioning/bootstrap time and used to | 85 | // MachineNonce is set at provisioning/bootstrap time and used to |
483 | 83 | // ensure the agent is running on the correct instance. | 86 | // ensure the agent is running on the correct instance. |
484 | 84 | MachineNonce string | 87 | MachineNonce string |
485 | @@ -128,9 +131,6 @@ | |||
486 | 128 | // Constraints holds the initial environment constraints. | 131 | // Constraints holds the initial environment constraints. |
487 | 129 | Constraints constraints.Value | 132 | Constraints constraints.Value |
488 | 130 | 133 | ||
489 | 131 | // StateInfoURL is the URL of a file which contains information about the state server machines. | ||
490 | 132 | StateInfoURL string | ||
491 | 133 | |||
492 | 134 | // DisableSSLHostnameVerification can be set to true to tell cloud-init | 134 | // DisableSSLHostnameVerification can be set to true to tell cloud-init |
493 | 135 | // that it shouldn't verify SSL certificates | 135 | // that it shouldn't verify SSL certificates |
494 | 136 | DisableSSLHostnameVerification bool | 136 | DisableSSLHostnameVerification bool |
495 | @@ -393,13 +393,20 @@ | |||
496 | 393 | if cons != "" { | 393 | if cons != "" { |
497 | 394 | cons = " --constraints " + shquote(cons) | 394 | cons = " --constraints " + shquote(cons) |
498 | 395 | } | 395 | } |
499 | 396 | var hardware string | ||
500 | 397 | if cfg.HardwareCharacteristics != nil { | ||
501 | 398 | if hardware = cfg.HardwareCharacteristics.String(); hardware != "" { | ||
502 | 399 | hardware = " --hardware " + shquote(hardware) | ||
503 | 400 | } | ||
504 | 401 | } | ||
505 | 396 | c.AddRunCmd(cloudinit.LogProgressCmd("Bootstrapping Juju machine agent")) | 402 | c.AddRunCmd(cloudinit.LogProgressCmd("Bootstrapping Juju machine agent")) |
506 | 397 | c.AddScripts( | 403 | c.AddScripts( |
507 | 398 | fmt.Sprintf("echo %s > %s", shquote(cfg.StateInfoURL), BootstrapStateURLFile), | ||
508 | 399 | // The bootstrapping is always run with debug on. | 404 | // The bootstrapping is always run with debug on. |
509 | 400 | cfg.jujuTools()+"/jujud bootstrap-state"+ | 405 | cfg.jujuTools()+"/jujud bootstrap-state"+ |
510 | 401 | " --data-dir "+shquote(cfg.DataDir)+ | 406 | " --data-dir "+shquote(cfg.DataDir)+ |
511 | 402 | " --env-config "+shquote(base64yaml(cfg.Config))+ | 407 | " --env-config "+shquote(base64yaml(cfg.Config))+ |
512 | 408 | " --instance-id "+shquote(string(cfg.InstanceId))+ | ||
513 | 409 | hardware+ | ||
514 | 403 | cons+ | 410 | cons+ |
515 | 404 | " --debug", | 411 | " --debug", |
516 | 405 | "rm -rf "+shquote(acfg.Dir()), | 412 | "rm -rf "+shquote(acfg.Dir()), |
517 | @@ -701,6 +708,9 @@ | |||
518 | 701 | if cfg.SystemPrivateSSHKey == "" { | 708 | if cfg.SystemPrivateSSHKey == "" { |
519 | 702 | return fmt.Errorf("missing system ssh identity") | 709 | return fmt.Errorf("missing system ssh identity") |
520 | 703 | } | 710 | } |
521 | 711 | if cfg.InstanceId == "" { | ||
522 | 712 | return fmt.Errorf("missing instance-id") | ||
523 | 713 | } | ||
524 | 704 | } else { | 714 | } else { |
525 | 705 | if len(cfg.StateInfo.Addrs) == 0 { | 715 | if len(cfg.StateInfo.Addrs) == 0 { |
526 | 706 | return fmt.Errorf("missing state hosts") | 716 | return fmt.Errorf("missing state hosts") |
527 | 707 | 717 | ||
528 | === modified file 'environs/cloudinit/cloudinit_test.go' | |||
529 | --- environs/cloudinit/cloudinit_test.go 2014-03-19 02:27:03 +0000 | |||
530 | +++ environs/cloudinit/cloudinit_test.go 2014-03-24 01:53:33 +0000 | |||
531 | @@ -101,7 +101,7 @@ | |||
532 | 101 | LogDir: agent.DefaultLogDir, | 101 | LogDir: agent.DefaultLogDir, |
533 | 102 | Jobs: allMachineJobs, | 102 | Jobs: allMachineJobs, |
534 | 103 | CloudInitOutputLog: environs.CloudInitOutputLog, | 103 | CloudInitOutputLog: environs.CloudInitOutputLog, |
536 | 104 | StateInfoURL: "some-url", | 104 | InstanceId: "i-bootstrap", |
537 | 105 | SystemPrivateSSHKey: "private rsa key", | 105 | SystemPrivateSSHKey: "private rsa key", |
538 | 106 | MachineAgentServiceName: "jujud-machine-0", | 106 | MachineAgentServiceName: "jujud-machine-0", |
539 | 107 | MongoServiceName: "juju-db", | 107 | MongoServiceName: "juju-db", |
540 | @@ -147,8 +147,7 @@ | |||
541 | 147 | install -m 600 /dev/null '/var/lib/juju/agents/bootstrap/agent\.conf' | 147 | install -m 600 /dev/null '/var/lib/juju/agents/bootstrap/agent\.conf' |
542 | 148 | printf '%s\\n' '.*' > '/var/lib/juju/agents/bootstrap/agent\.conf' | 148 | printf '%s\\n' '.*' > '/var/lib/juju/agents/bootstrap/agent\.conf' |
543 | 149 | echo 'Bootstrapping Juju machine agent'.* | 149 | echo 'Bootstrapping Juju machine agent'.* |
546 | 150 | echo 'some-url' > /tmp/provider-state-url | 150 | /var/lib/juju/tools/1\.2\.3-precise-amd64/jujud bootstrap-state --data-dir '/var/lib/juju' --env-config '[^']*' --instance-id 'i-bootstrap' --constraints 'mem=2048M' --debug |
545 | 151 | /var/lib/juju/tools/1\.2\.3-precise-amd64/jujud bootstrap-state --data-dir '/var/lib/juju' --env-config '[^']*' --constraints 'mem=2048M' --debug | ||
547 | 152 | rm -rf '/var/lib/juju/agents/bootstrap' | 151 | rm -rf '/var/lib/juju/agents/bootstrap' |
548 | 153 | ln -s 1\.2\.3-precise-amd64 '/var/lib/juju/tools/machine-0' | 152 | ln -s 1\.2\.3-precise-amd64 '/var/lib/juju/tools/machine-0' |
549 | 154 | echo 'Starting Juju machine agent \(jujud-machine-0\)'.* | 153 | echo 'Starting Juju machine agent \(jujud-machine-0\)'.* |
550 | @@ -182,7 +181,7 @@ | |||
551 | 182 | LogDir: agent.DefaultLogDir, | 181 | LogDir: agent.DefaultLogDir, |
552 | 183 | Jobs: allMachineJobs, | 182 | Jobs: allMachineJobs, |
553 | 184 | CloudInitOutputLog: environs.CloudInitOutputLog, | 183 | CloudInitOutputLog: environs.CloudInitOutputLog, |
555 | 185 | StateInfoURL: "some-url", | 184 | InstanceId: "i-bootstrap", |
556 | 186 | SystemPrivateSSHKey: "private rsa key", | 185 | SystemPrivateSSHKey: "private rsa key", |
557 | 187 | MachineAgentServiceName: "jujud-machine-0", | 186 | MachineAgentServiceName: "jujud-machine-0", |
558 | 188 | MongoServiceName: "juju-db", | 187 | MongoServiceName: "juju-db", |
559 | @@ -196,7 +195,7 @@ | |||
560 | 196 | grep '1234' \$bin/juju1\.2\.3-raring-amd64.sha256 \|\| \(echo "Tools checksum mismatch"; exit 1\) | 195 | grep '1234' \$bin/juju1\.2\.3-raring-amd64.sha256 \|\| \(echo "Tools checksum mismatch"; exit 1\) |
561 | 197 | rm \$bin/tools\.tar\.gz && rm \$bin/juju1\.2\.3-raring-amd64\.sha256 | 196 | rm \$bin/tools\.tar\.gz && rm \$bin/juju1\.2\.3-raring-amd64\.sha256 |
562 | 198 | printf %s '{"version":"1\.2\.3-raring-amd64","url":"http://foo\.com/tools/releases/juju1\.2\.3-raring-amd64\.tgz","sha256":"1234","size":10}' > \$bin/downloaded-tools\.txt | 197 | printf %s '{"version":"1\.2\.3-raring-amd64","url":"http://foo\.com/tools/releases/juju1\.2\.3-raring-amd64\.tgz","sha256":"1234","size":10}' > \$bin/downloaded-tools\.txt |
564 | 199 | /var/lib/juju/tools/1\.2\.3-raring-amd64/jujud bootstrap-state --data-dir '/var/lib/juju' --env-config '[^']*' --constraints 'mem=2048M' --debug | 198 | /var/lib/juju/tools/1\.2\.3-raring-amd64/jujud bootstrap-state --data-dir '/var/lib/juju' --env-config '[^']*' --instance-id 'i-bootstrap' --constraints 'mem=2048M' --debug |
565 | 200 | rm -rf '/var/lib/juju/agents/bootstrap' | 199 | rm -rf '/var/lib/juju/agents/bootstrap' |
566 | 201 | ln -s 1\.2\.3-raring-amd64 '/var/lib/juju/tools/machine-0' | 200 | ln -s 1\.2\.3-raring-amd64 '/var/lib/juju/tools/machine-0' |
567 | 202 | `, | 201 | `, |
568 | @@ -227,7 +226,7 @@ | |||
569 | 227 | LogDir: agent.DefaultLogDir, | 226 | LogDir: agent.DefaultLogDir, |
570 | 228 | Jobs: allMachineJobs, | 227 | Jobs: allMachineJobs, |
571 | 229 | CloudInitOutputLog: environs.CloudInitOutputLog, | 228 | CloudInitOutputLog: environs.CloudInitOutputLog, |
573 | 230 | StateInfoURL: "some-url", | 229 | InstanceId: "i-bootstrap", |
574 | 231 | SystemPrivateSSHKey: "private rsa key", | 230 | SystemPrivateSSHKey: "private rsa key", |
575 | 232 | MachineAgentServiceName: "jujud-machine-0", | 231 | MachineAgentServiceName: "jujud-machine-0", |
576 | 233 | MongoServiceName: "juju-db", | 232 | MongoServiceName: "juju-db", |
577 | @@ -386,7 +385,7 @@ | |||
578 | 386 | LogDir: agent.DefaultLogDir, | 385 | LogDir: agent.DefaultLogDir, |
579 | 387 | Jobs: allMachineJobs, | 386 | Jobs: allMachineJobs, |
580 | 388 | CloudInitOutputLog: environs.CloudInitOutputLog, | 387 | CloudInitOutputLog: environs.CloudInitOutputLog, |
582 | 389 | StateInfoURL: "some-url", | 388 | InstanceId: "i-bootstrap", |
583 | 390 | SystemPrivateSSHKey: "private rsa key", | 389 | SystemPrivateSSHKey: "private rsa key", |
584 | 391 | MachineAgentServiceName: "jujud-machine-0", | 390 | MachineAgentServiceName: "jujud-machine-0", |
585 | 392 | MongoServiceName: "juju-db", | 391 | MongoServiceName: "juju-db", |
586 | @@ -394,7 +393,7 @@ | |||
587 | 394 | setEnvConfig: true, | 393 | setEnvConfig: true, |
588 | 395 | inexactMatch: true, | 394 | inexactMatch: true, |
589 | 396 | expectScripts: ` | 395 | expectScripts: ` |
591 | 397 | /var/lib/juju/tools/1\.2\.3-precise-amd64/jujud bootstrap-state --data-dir '/var/lib/juju' --env-config '[^']*' --debug | 396 | /var/lib/juju/tools/1\.2\.3-precise-amd64/jujud bootstrap-state --data-dir '/var/lib/juju' --env-config '[^']*' --instance-id 'i-bootstrap' --debug |
592 | 398 | `, | 397 | `, |
593 | 399 | }, | 398 | }, |
594 | 400 | } | 399 | } |
595 | @@ -783,6 +782,9 @@ | |||
596 | 783 | {"missing mongo service name", func(cfg *cloudinit.MachineConfig) { | 782 | {"missing mongo service name", func(cfg *cloudinit.MachineConfig) { |
597 | 784 | cfg.MongoServiceName = "" | 783 | cfg.MongoServiceName = "" |
598 | 785 | }}, | 784 | }}, |
599 | 785 | {"missing instance-id", func(cfg *cloudinit.MachineConfig) { | ||
600 | 786 | cfg.InstanceId = "" | ||
601 | 787 | }}, | ||
602 | 786 | } | 788 | } |
603 | 787 | 789 | ||
604 | 788 | // TestCloudInitVerify checks that required fields are appropriately | 790 | // TestCloudInitVerify checks that required fields are appropriately |
605 | @@ -812,6 +814,7 @@ | |||
606 | 812 | LogDir: agent.DefaultLogDir, | 814 | LogDir: agent.DefaultLogDir, |
607 | 813 | Jobs: normalMachineJobs, | 815 | Jobs: normalMachineJobs, |
608 | 814 | CloudInitOutputLog: environs.CloudInitOutputLog, | 816 | CloudInitOutputLog: environs.CloudInitOutputLog, |
609 | 817 | InstanceId: "i-bootstrap", | ||
610 | 815 | MachineNonce: "FAKE_NONCE", | 818 | MachineNonce: "FAKE_NONCE", |
611 | 816 | SystemPrivateSSHKey: "private rsa key", | 819 | SystemPrivateSSHKey: "private rsa key", |
612 | 817 | MachineAgentServiceName: "jujud-machine-99", | 820 | MachineAgentServiceName: "jujud-machine-99", |
613 | 818 | 821 | ||
614 | === modified file 'environs/manual/bootstrap.go' | |||
615 | --- environs/manual/bootstrap.go 2014-02-20 02:24:46 +0000 | |||
616 | +++ environs/manual/bootstrap.go 2014-03-24 01:53:33 +0000 | |||
617 | @@ -124,8 +124,9 @@ | |||
618 | 124 | } | 124 | } |
619 | 125 | 125 | ||
620 | 126 | // Finally, provision the machine agent. | 126 | // Finally, provision the machine agent. |
623 | 127 | stateFileURL := fmt.Sprintf("file://%s/%s", storageDir, bootstrap.StateFile) | 127 | mcfg := environs.NewBootstrapMachineConfig(privateKey) |
624 | 128 | mcfg := environs.NewBootstrapMachineConfig(stateFileURL, privateKey) | 128 | mcfg.InstanceId = BootstrapInstanceId |
625 | 129 | mcfg.HardwareCharacteristics = args.HardwareCharacteristics | ||
626 | 129 | if args.DataDir != "" { | 130 | if args.DataDir != "" { |
627 | 130 | mcfg.DataDir = args.DataDir | 131 | mcfg.DataDir = args.DataDir |
628 | 131 | } | 132 | } |
629 | 132 | 133 | ||
630 | === modified file 'instance/instance.go' | |||
631 | --- instance/instance.go 2014-03-21 11:00:41 +0000 | |||
632 | +++ instance/instance.go 2014-03-24 01:53:33 +0000 | |||
633 | @@ -112,6 +112,16 @@ | |||
634 | 112 | return strings.Join(strs, " ") | 112 | return strings.Join(strs, " ") |
635 | 113 | } | 113 | } |
636 | 114 | 114 | ||
637 | 115 | // Implement gnuflag.Value | ||
638 | 116 | func (hc *HardwareCharacteristics) Set(s string) error { | ||
639 | 117 | parsed, err := ParseHardware(s) | ||
640 | 118 | if err != nil { | ||
641 | 119 | return err | ||
642 | 120 | } | ||
643 | 121 | *hc = parsed | ||
644 | 122 | return nil | ||
645 | 123 | } | ||
646 | 124 | |||
647 | 115 | // MustParseHardware constructs a HardwareCharacteristics from the supplied arguments, | 125 | // MustParseHardware constructs a HardwareCharacteristics from the supplied arguments, |
648 | 116 | // as Parse, but panics on failure. | 126 | // as Parse, but panics on failure. |
649 | 117 | func MustParseHardware(args ...string) HardwareCharacteristics { | 127 | func MustParseHardware(args ...string) HardwareCharacteristics { |
650 | 118 | 128 | ||
651 | === modified file 'provider/common/bootstrap.go' | |||
652 | --- provider/common/bootstrap.go 2014-03-19 02:55:15 +0000 | |||
653 | +++ provider/common/bootstrap.go 2014-03-24 01:53:33 +0000 | |||
654 | @@ -56,19 +56,11 @@ | |||
655 | 56 | return fmt.Errorf("no SSH client available") | 56 | return fmt.Errorf("no SSH client available") |
656 | 57 | } | 57 | } |
657 | 58 | 58 | ||
658 | 59 | // Create an empty bootstrap state file so we can get its URL. | ||
659 | 60 | // It will be updated with the instance id and hardware characteristics | ||
660 | 61 | // after the bootstrap instance is started. | ||
661 | 62 | stateFileURL, err := bootstrap.CreateStateFile(env.Storage()) | ||
662 | 63 | if err != nil { | ||
663 | 64 | return err | ||
664 | 65 | } | ||
665 | 66 | |||
666 | 67 | privateKey, err := GenerateSystemSSHKey(env) | 59 | privateKey, err := GenerateSystemSSHKey(env) |
667 | 68 | if err != nil { | 60 | if err != nil { |
668 | 69 | return err | 61 | return err |
669 | 70 | } | 62 | } |
671 | 71 | machineConfig := environs.NewBootstrapMachineConfig(stateFileURL, privateKey) | 63 | machineConfig := environs.NewBootstrapMachineConfig(privateKey) |
672 | 72 | 64 | ||
673 | 73 | fmt.Fprintln(ctx.GetStderr(), "Launching instance") | 65 | fmt.Fprintln(ctx.GetStderr(), "Launching instance") |
674 | 74 | inst, hw, err := env.StartInstance(environs.StartInstanceParams{ | 66 | inst, hw, err := env.StartInstance(environs.StartInstanceParams{ |
675 | @@ -80,6 +72,8 @@ | |||
676 | 80 | return fmt.Errorf("cannot start bootstrap instance: %v", err) | 72 | return fmt.Errorf("cannot start bootstrap instance: %v", err) |
677 | 81 | } | 73 | } |
678 | 82 | fmt.Fprintf(ctx.GetStderr(), " - %s\n", inst.Id()) | 74 | fmt.Fprintf(ctx.GetStderr(), " - %s\n", inst.Id()) |
679 | 75 | machineConfig.InstanceId = inst.Id() | ||
680 | 76 | machineConfig.HardwareCharacteristics = hw | ||
681 | 83 | 77 | ||
682 | 84 | var characteristics []instance.HardwareCharacteristics | 78 | var characteristics []instance.HardwareCharacteristics |
683 | 85 | if hw != nil { | 79 | if hw != nil { |
684 | 86 | 80 | ||
685 | === modified file 'provider/common/bootstrap_test.go' | |||
686 | --- provider/common/bootstrap_test.go 2014-03-19 22:16:15 +0000 | |||
687 | +++ provider/common/bootstrap_test.go 2014-03-24 01:53:33 +0000 | |||
688 | @@ -15,7 +15,6 @@ | |||
689 | 15 | 15 | ||
690 | 16 | "launchpad.net/juju-core/constraints" | 16 | "launchpad.net/juju-core/constraints" |
691 | 17 | "launchpad.net/juju-core/environs" | 17 | "launchpad.net/juju-core/environs" |
692 | 18 | "launchpad.net/juju-core/environs/bootstrap" | ||
693 | 19 | "launchpad.net/juju-core/environs/cloudinit" | 18 | "launchpad.net/juju-core/environs/cloudinit" |
694 | 20 | "launchpad.net/juju-core/environs/config" | 19 | "launchpad.net/juju-core/environs/config" |
695 | 21 | "launchpad.net/juju-core/environs/storage" | 20 | "launchpad.net/juju-core/environs/storage" |
696 | @@ -76,21 +75,7 @@ | |||
697 | 76 | return func() *config.Config { return cfg } | 75 | return func() *config.Config { return cfg } |
698 | 77 | } | 76 | } |
699 | 78 | 77 | ||
700 | 79 | func (s *BootstrapSuite) TestCannotWriteStateFile(c *gc.C) { | ||
701 | 80 | brokenStorage := &mockStorage{ | ||
702 | 81 | Storage: newStorage(s, c), | ||
703 | 82 | putErr: fmt.Errorf("noes!"), | ||
704 | 83 | } | ||
705 | 84 | env := &mockEnviron{storage: brokenStorage, config: configGetter(c)} | ||
706 | 85 | ctx := coretesting.Context(c) | ||
707 | 86 | err := common.Bootstrap(ctx, env, constraints.Value{}) | ||
708 | 87 | c.Assert(err, gc.ErrorMatches, "cannot create initial state file: noes!") | ||
709 | 88 | } | ||
710 | 89 | |||
711 | 90 | func (s *BootstrapSuite) TestCannotStartInstance(c *gc.C) { | 78 | func (s *BootstrapSuite) TestCannotStartInstance(c *gc.C) { |
712 | 91 | stor := newStorage(s, c) | ||
713 | 92 | checkURL, err := stor.URL(bootstrap.StateFile) | ||
714 | 93 | c.Assert(err, gc.IsNil) | ||
715 | 94 | checkCons := constraints.MustParse("mem=8G") | 79 | checkCons := constraints.MustParse("mem=8G") |
716 | 95 | 80 | ||
717 | 96 | startInstance := func( | 81 | startInstance := func( |
718 | @@ -99,18 +84,18 @@ | |||
719 | 99 | instance.Instance, *instance.HardwareCharacteristics, error, | 84 | instance.Instance, *instance.HardwareCharacteristics, error, |
720 | 100 | ) { | 85 | ) { |
721 | 101 | c.Assert(cons, gc.DeepEquals, checkCons) | 86 | c.Assert(cons, gc.DeepEquals, checkCons) |
723 | 102 | c.Assert(mcfg, gc.DeepEquals, environs.NewBootstrapMachineConfig(checkURL, mcfg.SystemPrivateSSHKey)) | 87 | c.Assert(mcfg, gc.DeepEquals, environs.NewBootstrapMachineConfig(mcfg.SystemPrivateSSHKey)) |
724 | 103 | return nil, nil, fmt.Errorf("meh, not started") | 88 | return nil, nil, fmt.Errorf("meh, not started") |
725 | 104 | } | 89 | } |
726 | 105 | 90 | ||
727 | 106 | env := &mockEnviron{ | 91 | env := &mockEnviron{ |
729 | 107 | storage: stor, | 92 | storage: newStorage(s, c), |
730 | 108 | startInstance: startInstance, | 93 | startInstance: startInstance, |
731 | 109 | config: configGetter(c), | 94 | config: configGetter(c), |
732 | 110 | } | 95 | } |
733 | 111 | 96 | ||
734 | 112 | ctx := coretesting.Context(c) | 97 | ctx := coretesting.Context(c) |
736 | 113 | err = common.Bootstrap(ctx, env, checkCons) | 98 | err := common.Bootstrap(ctx, env, checkCons) |
737 | 114 | c.Assert(err, gc.ErrorMatches, "cannot start bootstrap instance: meh, not started") | 99 | c.Assert(err, gc.ErrorMatches, "cannot start bootstrap instance: meh, not started") |
738 | 115 | } | 100 | } |
739 | 116 | 101 | ||
740 | @@ -192,13 +177,11 @@ | |||
741 | 192 | checkInstanceId := "i-success" | 177 | checkInstanceId := "i-success" |
742 | 193 | checkHardware := instance.MustParseHardware("mem=2T") | 178 | checkHardware := instance.MustParseHardware("mem=2T") |
743 | 194 | 179 | ||
744 | 195 | checkURL := "" | ||
745 | 196 | startInstance := func( | 180 | startInstance := func( |
746 | 197 | _ constraints.Value, _ environs.Networks, _ tools.List, mcfg *cloudinit.MachineConfig, | 181 | _ constraints.Value, _ environs.Networks, _ tools.List, mcfg *cloudinit.MachineConfig, |
747 | 198 | ) ( | 182 | ) ( |
748 | 199 | instance.Instance, *instance.HardwareCharacteristics, error, | 183 | instance.Instance, *instance.HardwareCharacteristics, error, |
749 | 200 | ) { | 184 | ) { |
750 | 201 | checkURL = mcfg.StateInfoURL | ||
751 | 202 | return &mockInstance{id: checkInstanceId}, &checkHardware, nil | 185 | return &mockInstance{id: checkInstanceId}, &checkHardware, nil |
752 | 203 | } | 186 | } |
753 | 204 | var mocksConfig = minimalConfig(c) | 187 | var mocksConfig = minimalConfig(c) |
754 | @@ -226,12 +209,6 @@ | |||
755 | 226 | err := common.Bootstrap(ctx, env, constraints.Value{}) | 209 | err := common.Bootstrap(ctx, env, constraints.Value{}) |
756 | 227 | c.Assert(err, gc.IsNil) | 210 | c.Assert(err, gc.IsNil) |
757 | 228 | 211 | ||
758 | 229 | savedState, err := bootstrap.LoadStateFromURL(checkURL, false) | ||
759 | 230 | c.Assert(err, gc.IsNil) | ||
760 | 231 | c.Assert(savedState, gc.DeepEquals, &bootstrap.BootstrapState{ | ||
761 | 232 | StateInstances: []instance.Id{instance.Id(checkInstanceId)}, | ||
762 | 233 | Characteristics: []instance.HardwareCharacteristics{checkHardware}, | ||
763 | 234 | }) | ||
764 | 235 | authKeys := env.Config().AuthorizedKeys() | 212 | authKeys := env.Config().AuthorizedKeys() |
765 | 236 | c.Assert(authKeys, gc.Not(gc.Equals), originalAuthKeys) | 213 | c.Assert(authKeys, gc.Not(gc.Equals), originalAuthKeys) |
766 | 237 | c.Assert(authKeys, jc.HasSuffix, "juju-system-key\n") | 214 | c.Assert(authKeys, jc.HasSuffix, "juju-system-key\n") |
767 | 238 | 215 | ||
768 | === modified file 'provider/local/environ.go' | |||
769 | --- provider/local/environ.go 2014-03-19 21:08:58 +0000 | |||
770 | +++ provider/local/environ.go 2014-03-24 01:53:33 +0000 | |||
771 | @@ -109,10 +109,6 @@ | |||
772 | 109 | 109 | ||
773 | 110 | // Before we write the agent config file, we need to make sure the | 110 | // Before we write the agent config file, we need to make sure the |
774 | 111 | // instance is saved in the StateInfo. | 111 | // instance is saved in the StateInfo. |
775 | 112 | stateFileURL, err := bootstrap.CreateStateFile(env.Storage()) | ||
776 | 113 | if err != nil { | ||
777 | 114 | return err | ||
778 | 115 | } | ||
779 | 116 | if err := bootstrap.SaveState(env.Storage(), &bootstrap.BootstrapState{ | 112 | if err := bootstrap.SaveState(env.Storage(), &bootstrap.BootstrapState{ |
780 | 117 | StateInstances: []instance.Id{bootstrapInstanceId}, | 113 | StateInstances: []instance.Id{bootstrapInstanceId}, |
781 | 118 | }); err != nil { | 114 | }); err != nil { |
782 | @@ -138,7 +134,8 @@ | |||
783 | 138 | return err | 134 | return err |
784 | 139 | } | 135 | } |
785 | 140 | 136 | ||
787 | 141 | mcfg := environs.NewBootstrapMachineConfig(stateFileURL, privateKey) | 137 | mcfg := environs.NewBootstrapMachineConfig(privateKey) |
788 | 138 | mcfg.InstanceId = bootstrapInstanceId | ||
789 | 142 | mcfg.Tools = selectedTools[0] | 139 | mcfg.Tools = selectedTools[0] |
790 | 143 | mcfg.DataDir = env.config.rootDir() | 140 | mcfg.DataDir = env.config.rootDir() |
791 | 144 | mcfg.LogDir = fmt.Sprintf("/var/log/juju-%s", env.config.namespace()) | 141 | mcfg.LogDir = fmt.Sprintf("/var/log/juju-%s", env.config.namespace()) |
Reviewers: mp+212172_ code.launchpad. net,
Message:
Please take a look.
Description: id/hardware directly to jujud
Pass instance-
The bootstrap-state agent currently loads its
instance ID and hardware characteristics by
fetching a URL that is written to a file on
disk. This is no longer necessary with
synchronous bootstrap.
I have changed jujud and bootstrap script
generation to pass the instance-id and hardware
characteristics directly to jujud as command
line arguments.
Fixes part of lp:1291292
https:/ /code.launchpad .net/~axwalk/ juju-core/ lp1291292- jujud-bootstrap -nostateurl/ +merge/ 212172
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/78840044/
Affected files (+140, -141 lines): sshinit/ configure_ test.go bootstrap. go bootstrap_ test.go main_test. go cloudinit. go cloudinit/ cloudinit. go cloudinit/ cloudinit_ test.go manual/ bootstrap. go instance. go common/ bootstrap. go common/ bootstrap_ test.go local/environ. go
A [revision details]
M cloudinit/
M cmd/jujud/
M cmd/jujud/
M cmd/jujud/
M environs/
M environs/
M environs/
M environs/
M instance/
M provider/
M provider/
M provider/