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
gc "gopkg.in/check.v1"
17
"github.com/juju/juju/agent"
18
"github.com/juju/juju/apiserver/params"
19
"github.com/juju/juju/constraints"
20
"github.com/juju/juju/environs"
21
"github.com/juju/juju/environs/config"
22
"github.com/juju/juju/instance"
23
"github.com/juju/juju/mongo"
24
"github.com/juju/juju/network"
25
"github.com/juju/juju/provider/dummy"
26
"github.com/juju/juju/state"
27
"github.com/juju/juju/state/multiwatcher"
28
"github.com/juju/juju/testing"
29
"github.com/juju/juju/version"
32
type bootstrapSuite struct {
34
mgoInst gitjujutesting.MgoInstance
37
var _ = gc.Suite(&bootstrapSuite{})
39
func (s *bootstrapSuite) SetUpTest(c *gc.C) {
40
s.BaseSuite.SetUpTest(c)
41
// Don't use MgoSuite, because we need to ensure
42
// we have a fresh mongo for each test case.
43
s.mgoInst.EnableAuth = true
44
err := s.mgoInst.Start(testing.Certs)
45
c.Assert(err, jc.ErrorIsNil)
48
func (s *bootstrapSuite) TearDownTest(c *gc.C) {
50
s.BaseSuite.TearDownTest(c)
53
func (s *bootstrapSuite) TestInitializeStateNonLocal(c *gc.C) {
54
s.testInitializeState(c, false)
57
func (s *bootstrapSuite) TestInitializeStateLocal(c *gc.C) {
58
s.testInitializeState(c, true)
61
func (s *bootstrapSuite) testInitializeState(c *gc.C, fakeLocalEnv bool) {
64
lxcFakeNetConfig := filepath.Join(c.MkDir(), "lxc-net")
69
LXC_BRIDGE="foobar" # detected
71
LXC_BRIDGE="ignored"`[1:])
72
err := ioutil.WriteFile(lxcFakeNetConfig, netConf, 0644)
73
c.Assert(err, jc.ErrorIsNil)
74
s.PatchValue(&network.InterfaceByNameAddrs, func(name string) ([]net.Addr, error) {
75
c.Assert(name, gc.Equals, "foobar")
77
&net.IPAddr{IP: net.IPv4(10, 0, 3, 1)},
78
&net.IPAddr{IP: net.IPv4(10, 0, 3, 4)},
81
s.PatchValue(&network.LXCNetDefaultConfig, lxcFakeNetConfig)
82
s.PatchValue(agent.IsLocalEnv, func(*config.Config) bool {
83
c.Logf("fakeLocalEnv=%v", fakeLocalEnv)
87
pwHash := utils.UserPasswordHash(testing.DefaultMongoPassword, utils.CompatSalt)
88
configParams := agent.AgentConfigParams{
90
Tag: names.NewMachineTag("0"),
91
UpgradedToVersion: version.Current.Number,
92
StateAddresses: []string{s.mgoInst.Addr()},
93
CACert: testing.CACert,
95
Environment: testing.EnvironmentTag,
97
servingInfo := params.StateServingInfo{
98
Cert: testing.ServerCert,
99
PrivateKey: testing.ServerKey,
100
CAPrivateKey: testing.CAKey,
102
StatePort: s.mgoInst.Port(),
103
SystemIdentity: "def456",
106
cfg, err := agent.NewStateMachineConfig(configParams, servingInfo)
107
c.Assert(err, jc.ErrorIsNil)
109
_, available := cfg.StateServingInfo()
110
c.Assert(available, jc.IsTrue)
111
expectConstraints := constraints.MustParse("mem=1024M")
112
expectHW := instance.MustParseHardware("mem=2048M")
113
initialAddrs := network.NewAddresses(
116
"10.0.3.1", // lxc bridge address filtered (when fakeLocalEnv=false).
117
"10.0.3.4", // lxc bridge address filtered (-"-).
118
"10.0.3.3", // not a lxc bridge address
120
mcfg := agent.BootstrapMachineConfig{
121
Addresses: initialAddrs,
122
Constraints: expectConstraints,
123
Jobs: []multiwatcher.MachineJob{multiwatcher.JobManageEnviron},
124
InstanceId: "i-bootstrap",
125
Characteristics: expectHW,
126
SharedSecret: "abc123",
128
filteredAddrs := network.NewAddresses(
134
// For local environments - no filtering.
135
filteredAddrs = append([]network.Address{}, initialAddrs...)
137
envAttrs := dummy.SampleConfig().Delete("admin-secret").Merge(testing.Attrs{
138
"agent-version": version.Current.Number.String(),
139
"state-id": "1", // needed so policy can Open config
141
envCfg, err := config.New(config.NoDefaults, envAttrs)
142
c.Assert(err, jc.ErrorIsNil)
144
adminUser := names.NewLocalUserTag("agent-admin")
145
st, m, err := agent.InitializeState(adminUser, cfg, envCfg, mcfg, mongo.DefaultDialOpts(), environs.NewStatePolicy())
146
c.Assert(err, jc.ErrorIsNil)
150
c.Assert(err, jc.ErrorIsNil)
152
// Check that the environment has been set up.
153
env, err := st.Environment()
154
c.Assert(err, jc.ErrorIsNil)
155
uuid, ok := envCfg.UUID()
156
c.Assert(ok, jc.IsTrue)
157
c.Assert(env.UUID(), gc.Equals, uuid)
159
// Check that initial admin user has been set up correctly.
160
envTag := env.Tag().(names.EnvironTag)
161
s.assertCanLogInAsAdmin(c, envTag, pwHash)
162
user, err := st.User(env.Owner())
163
c.Assert(err, jc.ErrorIsNil)
164
c.Assert(user.PasswordValid(testing.DefaultMongoPassword), jc.IsTrue)
166
// Check that environment configuration has been added.
167
newEnvCfg, err := st.EnvironConfig()
168
c.Assert(err, jc.ErrorIsNil)
169
c.Assert(newEnvCfg.AllAttrs(), gc.DeepEquals, envCfg.AllAttrs())
171
// Check that the bootstrap machine looks correct.
172
c.Assert(m.Id(), gc.Equals, "0")
173
c.Assert(m.Jobs(), gc.DeepEquals, []state.MachineJob{state.JobManageEnviron})
174
c.Assert(m.Series(), gc.Equals, version.Current.Series)
175
c.Assert(m.CheckProvisioned(agent.BootstrapNonce), jc.IsTrue)
176
c.Assert(m.Addresses(), jc.DeepEquals, filteredAddrs)
177
gotConstraints, err := m.Constraints()
178
c.Assert(err, jc.ErrorIsNil)
179
c.Assert(gotConstraints, gc.DeepEquals, expectConstraints)
180
c.Assert(err, jc.ErrorIsNil)
181
gotHW, err := m.HardwareCharacteristics()
182
c.Assert(err, jc.ErrorIsNil)
183
c.Assert(*gotHW, gc.DeepEquals, expectHW)
185
// Check that the API host ports are initialised correctly.
186
apiHostPorts, err := st.APIHostPorts()
187
c.Assert(err, jc.ErrorIsNil)
188
c.Assert(apiHostPorts, jc.DeepEquals, [][]network.HostPort{
189
network.AddressesWithPort(filteredAddrs, 1234),
192
// Check that the state serving info is initialised correctly.
193
stateServingInfo, err := st.StateServingInfo()
194
c.Assert(err, jc.ErrorIsNil)
195
c.Assert(stateServingInfo, jc.DeepEquals, state.StateServingInfo{
197
StatePort: s.mgoInst.Port(),
198
Cert: testing.ServerCert,
199
PrivateKey: testing.ServerKey,
200
CAPrivateKey: testing.CAKey,
201
SharedSecret: "abc123",
202
SystemIdentity: "def456",
205
// Check that the machine agent's config has been written
206
// and that we can use it to connect to the state.
207
machine0 := names.NewMachineTag("0")
208
newCfg, err := agent.ReadConfig(agent.ConfigPath(dataDir, machine0))
209
c.Assert(err, jc.ErrorIsNil)
210
c.Assert(newCfg.Tag(), gc.Equals, machine0)
211
c.Assert(agent.Password(newCfg), gc.Not(gc.Equals), pwHash)
212
c.Assert(agent.Password(newCfg), gc.Not(gc.Equals), testing.DefaultMongoPassword)
213
info, ok := cfg.MongoInfo()
214
c.Assert(ok, jc.IsTrue)
215
st1, err := state.Open(newCfg.Environment(), info, mongo.DefaultDialOpts(), environs.NewStatePolicy())
216
c.Assert(err, jc.ErrorIsNil)
220
func (s *bootstrapSuite) TestInitializeStateWithStateServingInfoNotAvailable(c *gc.C) {
221
configParams := agent.AgentConfigParams{
223
Tag: names.NewMachineTag("0"),
224
UpgradedToVersion: version.Current.Number,
225
StateAddresses: []string{s.mgoInst.Addr()},
226
CACert: testing.CACert,
228
Environment: testing.EnvironmentTag,
230
cfg, err := agent.NewAgentConfig(configParams)
231
c.Assert(err, jc.ErrorIsNil)
233
_, available := cfg.StateServingInfo()
234
c.Assert(available, jc.IsFalse)
236
adminUser := names.NewLocalUserTag("agent-admin")
237
_, _, err = agent.InitializeState(adminUser, cfg, nil, agent.BootstrapMachineConfig{}, mongo.DefaultDialOpts(), environs.NewStatePolicy())
238
// InitializeState will fail attempting to get the api port information
239
c.Assert(err, gc.ErrorMatches, "state serving information not available")
242
func (s *bootstrapSuite) TestInitializeStateFailsSecondTime(c *gc.C) {
245
pwHash := utils.UserPasswordHash(testing.DefaultMongoPassword, utils.CompatSalt)
246
configParams := agent.AgentConfigParams{
248
Tag: names.NewMachineTag("0"),
249
UpgradedToVersion: version.Current.Number,
250
StateAddresses: []string{s.mgoInst.Addr()},
251
CACert: testing.CACert,
253
Environment: testing.EnvironmentTag,
255
cfg, err := agent.NewAgentConfig(configParams)
256
c.Assert(err, jc.ErrorIsNil)
257
cfg.SetStateServingInfo(params.StateServingInfo{
259
StatePort: s.mgoInst.Port(),
263
SystemIdentity: "qux",
265
expectConstraints := constraints.MustParse("mem=1024M")
266
expectHW := instance.MustParseHardware("mem=2048M")
267
mcfg := agent.BootstrapMachineConfig{
268
Constraints: expectConstraints,
269
Jobs: []multiwatcher.MachineJob{multiwatcher.JobManageEnviron},
270
InstanceId: "i-bootstrap",
271
Characteristics: expectHW,
273
envAttrs := dummy.SampleConfig().Delete("admin-secret").Merge(testing.Attrs{
274
"agent-version": version.Current.Number.String(),
275
"state-id": "1", // needed so policy can Open config
277
envCfg, err := config.New(config.NoDefaults, envAttrs)
278
c.Assert(err, jc.ErrorIsNil)
280
adminUser := names.NewLocalUserTag("agent-admin")
281
st, _, err := agent.InitializeState(adminUser, cfg, envCfg, mcfg, mongo.DefaultDialOpts(), environs.NewStatePolicy())
282
c.Assert(err, jc.ErrorIsNil)
285
st, _, err = agent.InitializeState(adminUser, cfg, envCfg, mcfg, mongo.DefaultDialOpts(), environs.NewStatePolicy())
289
c.Assert(err, gc.ErrorMatches, "failed to initialize mongo admin user: cannot set admin password: not authorized .*")
292
func (s *bootstrapSuite) TestMachineJobFromParams(c *gc.C) {
293
var tests = []struct {
294
name multiwatcher.MachineJob
295
want state.MachineJob
298
name: multiwatcher.JobHostUnits,
299
want: state.JobHostUnits,
301
name: multiwatcher.JobManageEnviron,
302
want: state.JobManageEnviron,
304
name: multiwatcher.JobManageNetworking,
305
want: state.JobManageNetworking,
307
name: multiwatcher.JobManageStateDeprecated,
308
want: state.JobManageStateDeprecated,
312
err: `invalid machine job "invalid"`,
314
for _, test := range tests {
315
got, err := agent.MachineJobFromParams(test.name)
317
c.Check(err, gc.ErrorMatches, test.err)
319
c.Check(got, gc.Equals, test.want)
323
func (s *bootstrapSuite) assertCanLogInAsAdmin(c *gc.C, environTag names.EnvironTag, password string) {
324
info := &mongo.MongoInfo{
326
Addrs: []string{s.mgoInst.Addr()},
327
CACert: testing.CACert,
329
Tag: nil, // admin user
332
st, err := state.Open(environTag, info, mongo.DefaultDialOpts(), environs.NewStatePolicy())
333
c.Assert(err, jc.ErrorIsNil)
335
_, err = st.Machine("0")
336
c.Assert(err, jc.ErrorIsNil)