1
// Copyright 2012, 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
4
package agentbootstrap_test
11
"github.com/juju/names"
12
gitjujutesting "github.com/juju/testing"
13
jc "github.com/juju/testing/checkers"
14
"github.com/juju/utils"
15
"github.com/juju/utils/series"
16
gc "gopkg.in/check.v1"
18
"github.com/juju/juju/agent"
19
"github.com/juju/juju/agent/agentbootstrap"
20
"github.com/juju/juju/apiserver/params"
21
"github.com/juju/juju/constraints"
22
"github.com/juju/juju/environs"
23
"github.com/juju/juju/environs/config"
24
"github.com/juju/juju/instance"
25
"github.com/juju/juju/mongo"
26
"github.com/juju/juju/network"
27
"github.com/juju/juju/provider/dummy"
28
"github.com/juju/juju/state"
29
"github.com/juju/juju/state/multiwatcher"
30
"github.com/juju/juju/testing"
31
jujuversion "github.com/juju/juju/version"
34
type bootstrapSuite struct {
36
mgoInst gitjujutesting.MgoInstance
39
var _ = gc.Suite(&bootstrapSuite{})
41
func (s *bootstrapSuite) SetUpTest(c *gc.C) {
42
s.BaseSuite.SetUpTest(c)
43
// Don't use MgoSuite, because we need to ensure
44
// we have a fresh mongo for each test case.
45
s.mgoInst.EnableAuth = true
46
err := s.mgoInst.Start(testing.Certs)
47
c.Assert(err, jc.ErrorIsNil)
50
func (s *bootstrapSuite) TearDownTest(c *gc.C) {
52
s.BaseSuite.TearDownTest(c)
55
func (s *bootstrapSuite) TestInitializeState(c *gc.C) {
58
lxcFakeNetConfig := filepath.Join(c.MkDir(), "lxc-net")
63
LXC_BRIDGE="foobar" # detected
65
LXC_BRIDGE="ignored"`[1:])
66
err := ioutil.WriteFile(lxcFakeNetConfig, netConf, 0644)
67
c.Assert(err, jc.ErrorIsNil)
68
s.PatchValue(&network.InterfaceByNameAddrs, func(name string) ([]net.Addr, error) {
69
c.Assert(name, gc.Equals, "foobar")
71
&net.IPAddr{IP: net.IPv4(10, 0, 3, 1)},
72
&net.IPAddr{IP: net.IPv4(10, 0, 3, 4)},
75
s.PatchValue(&network.LXCNetDefaultConfig, lxcFakeNetConfig)
77
configParams := agent.AgentConfigParams{
78
Paths: agent.Paths{DataDir: dataDir},
79
Tag: names.NewMachineTag("0"),
80
UpgradedToVersion: jujuversion.Current,
81
StateAddresses: []string{s.mgoInst.Addr()},
82
CACert: testing.CACert,
83
Password: testing.DefaultMongoPassword,
84
Model: testing.ModelTag,
86
servingInfo := params.StateServingInfo{
87
Cert: testing.ServerCert,
88
PrivateKey: testing.ServerKey,
89
CAPrivateKey: testing.CAKey,
91
StatePort: s.mgoInst.Port(),
92
SystemIdentity: "def456",
95
cfg, err := agent.NewStateMachineConfig(configParams, servingInfo)
96
c.Assert(err, jc.ErrorIsNil)
98
_, available := cfg.StateServingInfo()
99
c.Assert(available, jc.IsTrue)
100
expectBootstrapConstraints := constraints.MustParse("mem=1024M")
101
expectModelConstraints := constraints.MustParse("mem=512M")
102
expectHW := instance.MustParseHardware("mem=2048M")
103
initialAddrs := network.NewAddresses(
106
"10.0.3.1", // lxc bridge address filtered.
107
"10.0.3.4", // lxc bridge address filtered (-"-).
108
"10.0.3.3", // not a lxc bridge address
110
mcfg := agentbootstrap.BootstrapMachineConfig{
111
Addresses: initialAddrs,
112
BootstrapConstraints: expectBootstrapConstraints,
113
ModelConstraints: expectModelConstraints,
114
Jobs: []multiwatcher.MachineJob{multiwatcher.JobManageModel},
115
InstanceId: "i-bootstrap",
116
Characteristics: expectHW,
117
SharedSecret: "abc123",
119
filteredAddrs := network.NewAddresses(
125
// Prepare bootstrap config, so we can use it in the state policy.
126
provider, err := environs.Provider("dummy")
127
c.Assert(err, jc.ErrorIsNil)
128
envAttrs := dummy.SampleConfig().Delete("admin-secret").Merge(testing.Attrs{
129
"agent-version": jujuversion.Current.String(),
130
"not-for-hosted": "foo",
132
envCfg, err := config.New(config.NoDefaults, envAttrs)
133
c.Assert(err, jc.ErrorIsNil)
134
envCfg, err = provider.BootstrapConfig(environs.BootstrapConfigParams{Config: envCfg})
135
c.Assert(err, jc.ErrorIsNil)
138
hostedModelUUID := utils.MustNewUUID().String()
139
hostedModelConfigAttrs := map[string]interface{}{
141
"uuid": hostedModelUUID,
144
adminUser := names.NewLocalUserTag("agent-admin")
145
st, m, err := agentbootstrap.InitializeState(
146
adminUser, cfg, envCfg, hostedModelConfigAttrs, mcfg,
147
mongo.DefaultDialOpts(), environs.NewStatePolicy(),
149
c.Assert(err, jc.ErrorIsNil)
153
c.Assert(err, jc.ErrorIsNil)
155
// Check that the environment has been set up.
156
env, err := st.Model()
157
c.Assert(err, jc.ErrorIsNil)
158
c.Assert(env.UUID(), gc.Equals, envCfg.UUID())
160
// Check that initial admin user has been set up correctly.
161
modelTag := env.Tag().(names.ModelTag)
162
s.assertCanLogInAsAdmin(c, modelTag, testing.DefaultMongoPassword)
163
user, err := st.User(env.Owner())
164
c.Assert(err, jc.ErrorIsNil)
165
c.Assert(user.PasswordValid(testing.DefaultMongoPassword), jc.IsTrue)
167
// Check that controller model configuration has been added, and
168
// model constraints set.
169
newEnvCfg, err := st.ModelConfig()
170
c.Assert(err, jc.ErrorIsNil)
171
c.Assert(newEnvCfg.AllAttrs(), gc.DeepEquals, envCfg.AllAttrs())
172
gotModelConstraints, err := st.ModelConstraints()
173
c.Assert(err, jc.ErrorIsNil)
174
c.Assert(gotModelConstraints, gc.DeepEquals, expectModelConstraints)
176
// Check that the hosted model has been added, and model constraints
178
hostedModelSt, err := st.ForModel(names.NewModelTag(hostedModelUUID))
179
c.Assert(err, jc.ErrorIsNil)
180
defer hostedModelSt.Close()
181
gotModelConstraints, err = hostedModelSt.ModelConstraints()
182
c.Assert(err, jc.ErrorIsNil)
183
c.Assert(gotModelConstraints, gc.DeepEquals, expectModelConstraints)
184
hostedModel, err := hostedModelSt.Model()
185
c.Assert(err, jc.ErrorIsNil)
186
c.Assert(hostedModel.Name(), gc.Equals, "hosted")
187
hostedCfg, err := hostedModelSt.ModelConfig()
188
c.Assert(err, jc.ErrorIsNil)
189
_, hasUnexpected := hostedCfg.AllAttrs()["not-for-hosted"]
190
c.Assert(hasUnexpected, jc.IsFalse)
192
// Check that the bootstrap machine looks correct.
193
c.Assert(m.Id(), gc.Equals, "0")
194
c.Assert(m.Jobs(), gc.DeepEquals, []state.MachineJob{state.JobManageModel})
195
c.Assert(m.Series(), gc.Equals, series.HostSeries())
196
c.Assert(m.CheckProvisioned(agent.BootstrapNonce), jc.IsTrue)
197
c.Assert(m.Addresses(), jc.DeepEquals, filteredAddrs)
198
gotBootstrapConstraints, err := m.Constraints()
199
c.Assert(err, jc.ErrorIsNil)
200
c.Assert(gotBootstrapConstraints, gc.DeepEquals, expectBootstrapConstraints)
201
c.Assert(err, jc.ErrorIsNil)
202
gotHW, err := m.HardwareCharacteristics()
203
c.Assert(err, jc.ErrorIsNil)
204
c.Assert(*gotHW, gc.DeepEquals, expectHW)
206
// Check that the API host ports are initialised correctly.
207
apiHostPorts, err := st.APIHostPorts()
208
c.Assert(err, jc.ErrorIsNil)
209
c.Assert(apiHostPorts, jc.DeepEquals, [][]network.HostPort{
210
network.AddressesWithPort(filteredAddrs, 1234),
213
// Check that the state serving info is initialised correctly.
214
stateServingInfo, err := st.StateServingInfo()
215
c.Assert(err, jc.ErrorIsNil)
216
c.Assert(stateServingInfo, jc.DeepEquals, state.StateServingInfo{
218
StatePort: s.mgoInst.Port(),
219
Cert: testing.ServerCert,
220
PrivateKey: testing.ServerKey,
221
CAPrivateKey: testing.CAKey,
222
SharedSecret: "abc123",
223
SystemIdentity: "def456",
226
// Check that the machine agent's config has been written
227
// and that we can use it to connect to the state.
228
machine0 := names.NewMachineTag("0")
229
newCfg, err := agent.ReadConfig(agent.ConfigPath(dataDir, machine0))
230
c.Assert(err, jc.ErrorIsNil)
231
c.Assert(newCfg.Tag(), gc.Equals, machine0)
232
info, ok := cfg.MongoInfo()
233
c.Assert(ok, jc.IsTrue)
234
c.Assert(info.Password, gc.Not(gc.Equals), testing.DefaultMongoPassword)
235
st1, err := state.Open(newCfg.Model(), info, mongo.DefaultDialOpts(), environs.NewStatePolicy())
236
c.Assert(err, jc.ErrorIsNil)
240
func (s *bootstrapSuite) TestInitializeStateWithStateServingInfoNotAvailable(c *gc.C) {
241
configParams := agent.AgentConfigParams{
242
Paths: agent.Paths{DataDir: c.MkDir()},
243
Tag: names.NewMachineTag("0"),
244
UpgradedToVersion: jujuversion.Current,
245
StateAddresses: []string{s.mgoInst.Addr()},
246
CACert: testing.CACert,
248
Model: testing.ModelTag,
250
cfg, err := agent.NewAgentConfig(configParams)
251
c.Assert(err, jc.ErrorIsNil)
253
_, available := cfg.StateServingInfo()
254
c.Assert(available, jc.IsFalse)
256
adminUser := names.NewLocalUserTag("agent-admin")
257
_, _, err = agentbootstrap.InitializeState(adminUser, cfg, nil, nil, agentbootstrap.BootstrapMachineConfig{}, mongo.DefaultDialOpts(), environs.NewStatePolicy())
258
// InitializeState will fail attempting to get the api port information
259
c.Assert(err, gc.ErrorMatches, "state serving information not available")
262
func (s *bootstrapSuite) TestInitializeStateFailsSecondTime(c *gc.C) {
265
configParams := agent.AgentConfigParams{
266
Paths: agent.Paths{DataDir: dataDir},
267
Tag: names.NewMachineTag("0"),
268
UpgradedToVersion: jujuversion.Current,
269
StateAddresses: []string{s.mgoInst.Addr()},
270
CACert: testing.CACert,
271
Password: testing.DefaultMongoPassword,
272
Model: testing.ModelTag,
274
cfg, err := agent.NewAgentConfig(configParams)
275
c.Assert(err, jc.ErrorIsNil)
276
cfg.SetStateServingInfo(params.StateServingInfo{
278
StatePort: s.mgoInst.Port(),
282
SystemIdentity: "qux",
284
expectHW := instance.MustParseHardware("mem=2048M")
285
mcfg := agentbootstrap.BootstrapMachineConfig{
286
BootstrapConstraints: constraints.MustParse("mem=1024M"),
287
Jobs: []multiwatcher.MachineJob{multiwatcher.JobManageModel},
288
InstanceId: "i-bootstrap",
289
Characteristics: expectHW,
291
envAttrs := dummy.SampleConfig().Delete("admin-secret").Merge(testing.Attrs{
292
"agent-version": jujuversion.Current.String(),
294
envCfg, err := config.New(config.NoDefaults, envAttrs)
295
c.Assert(err, jc.ErrorIsNil)
297
hostedModelConfigAttrs := map[string]interface{}{
299
"uuid": utils.MustNewUUID().String(),
302
adminUser := names.NewLocalUserTag("agent-admin")
303
st, _, err := agentbootstrap.InitializeState(
304
adminUser, cfg, envCfg, hostedModelConfigAttrs, mcfg,
305
mongo.DefaultDialOpts(), state.Policy(nil),
307
c.Assert(err, jc.ErrorIsNil)
310
st, _, err = agentbootstrap.InitializeState(adminUser, cfg, envCfg, nil, mcfg, mongo.DefaultDialOpts(), environs.NewStatePolicy())
314
c.Assert(err, gc.ErrorMatches, "failed to initialize mongo admin user: cannot set admin password: not authorized .*")
317
func (s *bootstrapSuite) TestMachineJobFromParams(c *gc.C) {
318
var tests = []struct {
319
name multiwatcher.MachineJob
320
want state.MachineJob
323
name: multiwatcher.JobHostUnits,
324
want: state.JobHostUnits,
326
name: multiwatcher.JobManageModel,
327
want: state.JobManageModel,
329
name: multiwatcher.JobManageNetworking,
330
want: state.JobManageNetworking,
334
err: `invalid machine job "invalid"`,
336
for _, test := range tests {
337
got, err := agentbootstrap.MachineJobFromParams(test.name)
339
c.Check(err, gc.ErrorMatches, test.err)
341
c.Check(got, gc.Equals, test.want)
345
func (s *bootstrapSuite) assertCanLogInAsAdmin(c *gc.C, modelTag names.ModelTag, password string) {
346
info := &mongo.MongoInfo{
348
Addrs: []string{s.mgoInst.Addr()},
349
CACert: testing.CACert,
351
Tag: nil, // admin user
354
st, err := state.Open(modelTag, info, mongo.DefaultDialOpts(), environs.NewStatePolicy())
355
c.Assert(err, jc.ErrorIsNil)
357
_, err = st.Machine("0")
358
c.Assert(err, jc.ErrorIsNil)