1
// Copyright 2012, 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
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/apiserver/params"
20
"github.com/juju/juju/constraints"
21
"github.com/juju/juju/environs"
22
"github.com/juju/juju/environs/config"
23
"github.com/juju/juju/instance"
24
"github.com/juju/juju/mongo"
25
"github.com/juju/juju/network"
26
"github.com/juju/juju/provider/dummy"
27
"github.com/juju/juju/state"
28
"github.com/juju/juju/state/multiwatcher"
29
"github.com/juju/juju/testing"
30
"github.com/juju/juju/version"
33
type bootstrapSuite struct {
35
mgoInst gitjujutesting.MgoInstance
38
var _ = gc.Suite(&bootstrapSuite{})
40
func (s *bootstrapSuite) SetUpTest(c *gc.C) {
41
s.BaseSuite.SetUpTest(c)
42
// Don't use MgoSuite, because we need to ensure
43
// we have a fresh mongo for each test case.
44
s.mgoInst.EnableAuth = true
45
err := s.mgoInst.Start(testing.Certs)
46
c.Assert(err, jc.ErrorIsNil)
49
func (s *bootstrapSuite) TearDownTest(c *gc.C) {
51
s.BaseSuite.TearDownTest(c)
54
func (s *bootstrapSuite) TestInitializeState(c *gc.C) {
57
lxcFakeNetConfig := filepath.Join(c.MkDir(), "lxc-net")
62
LXC_BRIDGE="foobar" # detected
64
LXC_BRIDGE="ignored"`[1:])
65
err := ioutil.WriteFile(lxcFakeNetConfig, netConf, 0644)
66
c.Assert(err, jc.ErrorIsNil)
67
s.PatchValue(&network.InterfaceByNameAddrs, func(name string) ([]net.Addr, error) {
68
c.Assert(name, gc.Equals, "foobar")
70
&net.IPAddr{IP: net.IPv4(10, 0, 3, 1)},
71
&net.IPAddr{IP: net.IPv4(10, 0, 3, 4)},
74
s.PatchValue(&network.LXCNetDefaultConfig, lxcFakeNetConfig)
76
pwHash := utils.UserPasswordHash(testing.DefaultMongoPassword, utils.CompatSalt)
77
configParams := agent.AgentConfigParams{
78
Paths: agent.Paths{DataDir: dataDir},
79
Tag: names.NewMachineTag("0"),
80
UpgradedToVersion: version.Current,
81
StateAddresses: []string{s.mgoInst.Addr()},
82
CACert: testing.CACert,
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 := agent.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(
124
envAttrs := dummy.SampleConfig().Delete("admin-secret").Merge(testing.Attrs{
125
"agent-version": version.Current.String(),
126
"state-id": "1", // needed so policy can Open config
128
envCfg, err := config.New(config.NoDefaults, envAttrs)
129
c.Assert(err, jc.ErrorIsNil)
131
adminUser := names.NewLocalUserTag("agent-admin")
132
st, m, err := agent.InitializeState(adminUser, cfg, envCfg, mcfg, mongo.DefaultDialOpts(), environs.NewStatePolicy())
133
c.Assert(err, jc.ErrorIsNil)
137
c.Assert(err, jc.ErrorIsNil)
139
// Check that the environment has been set up.
140
env, err := st.Model()
141
c.Assert(err, jc.ErrorIsNil)
142
uuid, ok := envCfg.UUID()
143
c.Assert(ok, jc.IsTrue)
144
c.Assert(env.UUID(), gc.Equals, uuid)
146
// Check that initial admin user has been set up correctly.
147
modelTag := env.Tag().(names.ModelTag)
148
s.assertCanLogInAsAdmin(c, modelTag, pwHash)
149
user, err := st.User(env.Owner())
150
c.Assert(err, jc.ErrorIsNil)
151
c.Assert(user.PasswordValid(testing.DefaultMongoPassword), jc.IsTrue)
153
// Check that model configuration has been added, and
154
// model constraints set.
155
newEnvCfg, err := st.ModelConfig()
156
c.Assert(err, jc.ErrorIsNil)
157
c.Assert(newEnvCfg.AllAttrs(), gc.DeepEquals, envCfg.AllAttrs())
158
gotModelConstraints, err := st.ModelConstraints()
159
c.Assert(err, jc.ErrorIsNil)
160
c.Assert(gotModelConstraints, gc.DeepEquals, expectModelConstraints)
162
// Check that the bootstrap machine looks correct.
163
c.Assert(m.Id(), gc.Equals, "0")
164
c.Assert(m.Jobs(), gc.DeepEquals, []state.MachineJob{state.JobManageModel})
165
c.Assert(m.Series(), gc.Equals, series.HostSeries())
166
c.Assert(m.CheckProvisioned(agent.BootstrapNonce), jc.IsTrue)
167
c.Assert(m.Addresses(), jc.DeepEquals, filteredAddrs)
168
gotBootstrapConstraints, err := m.Constraints()
169
c.Assert(err, jc.ErrorIsNil)
170
c.Assert(gotBootstrapConstraints, gc.DeepEquals, expectBootstrapConstraints)
171
c.Assert(err, jc.ErrorIsNil)
172
gotHW, err := m.HardwareCharacteristics()
173
c.Assert(err, jc.ErrorIsNil)
174
c.Assert(*gotHW, gc.DeepEquals, expectHW)
176
// Check that the API host ports are initialised correctly.
177
apiHostPorts, err := st.APIHostPorts()
178
c.Assert(err, jc.ErrorIsNil)
179
c.Assert(apiHostPorts, jc.DeepEquals, [][]network.HostPort{
180
network.AddressesWithPort(filteredAddrs, 1234),
183
// Check that the state serving info is initialised correctly.
184
stateServingInfo, err := st.StateServingInfo()
185
c.Assert(err, jc.ErrorIsNil)
186
c.Assert(stateServingInfo, jc.DeepEquals, state.StateServingInfo{
188
StatePort: s.mgoInst.Port(),
189
Cert: testing.ServerCert,
190
PrivateKey: testing.ServerKey,
191
CAPrivateKey: testing.CAKey,
192
SharedSecret: "abc123",
193
SystemIdentity: "def456",
196
// Check that the machine agent's config has been written
197
// and that we can use it to connect to the state.
198
machine0 := names.NewMachineTag("0")
199
newCfg, err := agent.ReadConfig(agent.ConfigPath(dataDir, machine0))
200
c.Assert(err, jc.ErrorIsNil)
201
c.Assert(newCfg.Tag(), gc.Equals, machine0)
202
c.Assert(agent.Password(newCfg), gc.Not(gc.Equals), pwHash)
203
c.Assert(agent.Password(newCfg), gc.Not(gc.Equals), testing.DefaultMongoPassword)
204
info, ok := cfg.MongoInfo()
205
c.Assert(ok, jc.IsTrue)
206
st1, err := state.Open(newCfg.Model(), info, mongo.DefaultDialOpts(), environs.NewStatePolicy())
207
c.Assert(err, jc.ErrorIsNil)
211
func (s *bootstrapSuite) TestInitializeStateWithStateServingInfoNotAvailable(c *gc.C) {
212
configParams := agent.AgentConfigParams{
213
Paths: agent.Paths{DataDir: c.MkDir()},
214
Tag: names.NewMachineTag("0"),
215
UpgradedToVersion: version.Current,
216
StateAddresses: []string{s.mgoInst.Addr()},
217
CACert: testing.CACert,
219
Model: testing.ModelTag,
221
cfg, err := agent.NewAgentConfig(configParams)
222
c.Assert(err, jc.ErrorIsNil)
224
_, available := cfg.StateServingInfo()
225
c.Assert(available, jc.IsFalse)
227
adminUser := names.NewLocalUserTag("agent-admin")
228
_, _, err = agent.InitializeState(adminUser, cfg, nil, agent.BootstrapMachineConfig{}, mongo.DefaultDialOpts(), environs.NewStatePolicy())
229
// InitializeState will fail attempting to get the api port information
230
c.Assert(err, gc.ErrorMatches, "state serving information not available")
233
func (s *bootstrapSuite) TestInitializeStateFailsSecondTime(c *gc.C) {
236
pwHash := utils.UserPasswordHash(testing.DefaultMongoPassword, utils.CompatSalt)
237
configParams := agent.AgentConfigParams{
238
Paths: agent.Paths{DataDir: dataDir},
239
Tag: names.NewMachineTag("0"),
240
UpgradedToVersion: version.Current,
241
StateAddresses: []string{s.mgoInst.Addr()},
242
CACert: testing.CACert,
244
Model: testing.ModelTag,
246
cfg, err := agent.NewAgentConfig(configParams)
247
c.Assert(err, jc.ErrorIsNil)
248
cfg.SetStateServingInfo(params.StateServingInfo{
250
StatePort: s.mgoInst.Port(),
254
SystemIdentity: "qux",
256
expectHW := instance.MustParseHardware("mem=2048M")
257
mcfg := agent.BootstrapMachineConfig{
258
BootstrapConstraints: constraints.MustParse("mem=1024M"),
259
Jobs: []multiwatcher.MachineJob{multiwatcher.JobManageModel},
260
InstanceId: "i-bootstrap",
261
Characteristics: expectHW,
263
envAttrs := dummy.SampleConfig().Delete("admin-secret").Merge(testing.Attrs{
264
"agent-version": version.Current.String(),
265
"state-id": "1", // needed so policy can Open config
267
envCfg, err := config.New(config.NoDefaults, envAttrs)
268
c.Assert(err, jc.ErrorIsNil)
270
adminUser := names.NewLocalUserTag("agent-admin")
271
st, _, err := agent.InitializeState(adminUser, cfg, envCfg, mcfg, mongo.DefaultDialOpts(), environs.NewStatePolicy())
272
c.Assert(err, jc.ErrorIsNil)
275
st, _, err = agent.InitializeState(adminUser, cfg, envCfg, mcfg, mongo.DefaultDialOpts(), environs.NewStatePolicy())
279
c.Assert(err, gc.ErrorMatches, "failed to initialize mongo admin user: cannot set admin password: not authorized .*")
282
func (s *bootstrapSuite) TestMachineJobFromParams(c *gc.C) {
283
var tests = []struct {
284
name multiwatcher.MachineJob
285
want state.MachineJob
288
name: multiwatcher.JobHostUnits,
289
want: state.JobHostUnits,
291
name: multiwatcher.JobManageModel,
292
want: state.JobManageModel,
294
name: multiwatcher.JobManageNetworking,
295
want: state.JobManageNetworking,
299
err: `invalid machine job "invalid"`,
301
for _, test := range tests {
302
got, err := agent.MachineJobFromParams(test.name)
304
c.Check(err, gc.ErrorMatches, test.err)
306
c.Check(got, gc.Equals, test.want)
310
func (s *bootstrapSuite) assertCanLogInAsAdmin(c *gc.C, modelTag names.ModelTag, password string) {
311
info := &mongo.MongoInfo{
313
Addrs: []string{s.mgoInst.Addr()},
314
CACert: testing.CACert,
316
Tag: nil, // admin user
319
st, err := state.Open(modelTag, info, mongo.DefaultDialOpts(), environs.NewStatePolicy())
320
c.Assert(err, jc.ErrorIsNil)
322
_, err = st.Machine("0")
323
c.Assert(err, jc.ErrorIsNil)