github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/agent/agentbootstrap/bootstrap_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package agentbootstrap_test 5 6 import ( 7 "io/ioutil" 8 "net" 9 "path/filepath" 10 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" 17 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/mongo/mongotest" 27 "github.com/juju/juju/network" 28 "github.com/juju/juju/provider/dummy" 29 "github.com/juju/juju/state" 30 "github.com/juju/juju/state/multiwatcher" 31 "github.com/juju/juju/testing" 32 jujuversion "github.com/juju/juju/version" 33 ) 34 35 type bootstrapSuite struct { 36 testing.BaseSuite 37 mgoInst gitjujutesting.MgoInstance 38 } 39 40 var _ = gc.Suite(&bootstrapSuite{}) 41 42 func (s *bootstrapSuite) SetUpTest(c *gc.C) { 43 s.BaseSuite.SetUpTest(c) 44 // Don't use MgoSuite, because we need to ensure 45 // we have a fresh mongo for each test case. 46 s.mgoInst.EnableAuth = true 47 err := s.mgoInst.Start(testing.Certs) 48 c.Assert(err, jc.ErrorIsNil) 49 } 50 51 func (s *bootstrapSuite) TearDownTest(c *gc.C) { 52 s.mgoInst.Destroy() 53 s.BaseSuite.TearDownTest(c) 54 } 55 56 func (s *bootstrapSuite) TestInitializeState(c *gc.C) { 57 dataDir := c.MkDir() 58 59 lxcFakeNetConfig := filepath.Join(c.MkDir(), "lxc-net") 60 netConf := []byte(` 61 # comments ignored 62 LXC_BR= ignored 63 LXC_ADDR = "fooo" 64 LXC_BRIDGE="foobar" # detected 65 anything else ignored 66 LXC_BRIDGE="ignored"`[1:]) 67 err := ioutil.WriteFile(lxcFakeNetConfig, netConf, 0644) 68 c.Assert(err, jc.ErrorIsNil) 69 s.PatchValue(&network.InterfaceByNameAddrs, func(name string) ([]net.Addr, error) { 70 c.Assert(name, gc.Equals, "foobar") 71 return []net.Addr{ 72 &net.IPAddr{IP: net.IPv4(10, 0, 3, 1)}, 73 &net.IPAddr{IP: net.IPv4(10, 0, 3, 4)}, 74 }, nil 75 }) 76 s.PatchValue(&network.LXCNetDefaultConfig, lxcFakeNetConfig) 77 78 configParams := agent.AgentConfigParams{ 79 Paths: agent.Paths{DataDir: dataDir}, 80 Tag: names.NewMachineTag("0"), 81 UpgradedToVersion: jujuversion.Current, 82 StateAddresses: []string{s.mgoInst.Addr()}, 83 CACert: testing.CACert, 84 Password: testing.DefaultMongoPassword, 85 Model: testing.ModelTag, 86 } 87 servingInfo := params.StateServingInfo{ 88 Cert: testing.ServerCert, 89 PrivateKey: testing.ServerKey, 90 CAPrivateKey: testing.CAKey, 91 APIPort: 1234, 92 StatePort: s.mgoInst.Port(), 93 SystemIdentity: "def456", 94 } 95 96 cfg, err := agent.NewStateMachineConfig(configParams, servingInfo) 97 c.Assert(err, jc.ErrorIsNil) 98 99 _, available := cfg.StateServingInfo() 100 c.Assert(available, jc.IsTrue) 101 expectBootstrapConstraints := constraints.MustParse("mem=1024M") 102 expectModelConstraints := constraints.MustParse("mem=512M") 103 expectHW := instance.MustParseHardware("mem=2048M") 104 initialAddrs := network.NewAddresses( 105 "zeroonetwothree", 106 "0.1.2.3", 107 "10.0.3.1", // lxc bridge address filtered. 108 "10.0.3.4", // lxc bridge address filtered (-"-). 109 "10.0.3.3", // not a lxc bridge address 110 ) 111 mcfg := agentbootstrap.BootstrapMachineConfig{ 112 Addresses: initialAddrs, 113 BootstrapConstraints: expectBootstrapConstraints, 114 ModelConstraints: expectModelConstraints, 115 Jobs: []multiwatcher.MachineJob{multiwatcher.JobManageModel}, 116 InstanceId: "i-bootstrap", 117 Characteristics: expectHW, 118 SharedSecret: "abc123", 119 } 120 filteredAddrs := network.NewAddresses( 121 "zeroonetwothree", 122 "0.1.2.3", 123 "10.0.3.3", 124 ) 125 126 // Prepare bootstrap config, so we can use it in the state policy. 127 provider, err := environs.Provider("dummy") 128 c.Assert(err, jc.ErrorIsNil) 129 envAttrs := dummy.SampleConfig().Delete("admin-secret").Merge(testing.Attrs{ 130 "agent-version": jujuversion.Current.String(), 131 "not-for-hosted": "foo", 132 }) 133 envCfg, err := config.New(config.NoDefaults, envAttrs) 134 c.Assert(err, jc.ErrorIsNil) 135 envCfg, err = provider.BootstrapConfig(environs.BootstrapConfigParams{Config: envCfg}) 136 c.Assert(err, jc.ErrorIsNil) 137 defer dummy.Reset(c) 138 139 hostedModelUUID := utils.MustNewUUID().String() 140 hostedModelConfigAttrs := map[string]interface{}{ 141 "name": "hosted", 142 "uuid": hostedModelUUID, 143 } 144 145 adminUser := names.NewLocalUserTag("agent-admin") 146 st, m, err := agentbootstrap.InitializeState( 147 adminUser, cfg, envCfg, hostedModelConfigAttrs, mcfg, 148 mongotest.DialOpts(), environs.NewStatePolicy(), 149 ) 150 c.Assert(err, jc.ErrorIsNil) 151 defer st.Close() 152 153 err = cfg.Write() 154 c.Assert(err, jc.ErrorIsNil) 155 156 // Check that the environment has been set up. 157 env, err := st.Model() 158 c.Assert(err, jc.ErrorIsNil) 159 c.Assert(env.UUID(), gc.Equals, envCfg.UUID()) 160 161 // Check that initial admin user has been set up correctly. 162 modelTag := env.Tag().(names.ModelTag) 163 s.assertCanLogInAsAdmin(c, modelTag, testing.DefaultMongoPassword) 164 user, err := st.User(env.Owner()) 165 c.Assert(err, jc.ErrorIsNil) 166 c.Assert(user.PasswordValid(testing.DefaultMongoPassword), jc.IsTrue) 167 168 // Check that controller model configuration has been added, and 169 // model constraints set. 170 newEnvCfg, err := st.ModelConfig() 171 c.Assert(err, jc.ErrorIsNil) 172 c.Assert(newEnvCfg.AllAttrs(), gc.DeepEquals, envCfg.AllAttrs()) 173 gotModelConstraints, err := st.ModelConstraints() 174 c.Assert(err, jc.ErrorIsNil) 175 c.Assert(gotModelConstraints, gc.DeepEquals, expectModelConstraints) 176 177 // Check that the hosted model has been added, and model constraints 178 // set. 179 hostedModelSt, err := st.ForModel(names.NewModelTag(hostedModelUUID)) 180 c.Assert(err, jc.ErrorIsNil) 181 defer hostedModelSt.Close() 182 gotModelConstraints, err = hostedModelSt.ModelConstraints() 183 c.Assert(err, jc.ErrorIsNil) 184 c.Assert(gotModelConstraints, gc.DeepEquals, expectModelConstraints) 185 hostedModel, err := hostedModelSt.Model() 186 c.Assert(err, jc.ErrorIsNil) 187 c.Assert(hostedModel.Name(), gc.Equals, "hosted") 188 hostedCfg, err := hostedModelSt.ModelConfig() 189 c.Assert(err, jc.ErrorIsNil) 190 _, hasUnexpected := hostedCfg.AllAttrs()["not-for-hosted"] 191 c.Assert(hasUnexpected, jc.IsFalse) 192 193 // Check that the bootstrap machine looks correct. 194 c.Assert(m.Id(), gc.Equals, "0") 195 c.Assert(m.Jobs(), gc.DeepEquals, []state.MachineJob{state.JobManageModel}) 196 c.Assert(m.Series(), gc.Equals, series.HostSeries()) 197 c.Assert(m.CheckProvisioned(agent.BootstrapNonce), jc.IsTrue) 198 c.Assert(m.Addresses(), jc.DeepEquals, filteredAddrs) 199 gotBootstrapConstraints, err := m.Constraints() 200 c.Assert(err, jc.ErrorIsNil) 201 c.Assert(gotBootstrapConstraints, gc.DeepEquals, expectBootstrapConstraints) 202 c.Assert(err, jc.ErrorIsNil) 203 gotHW, err := m.HardwareCharacteristics() 204 c.Assert(err, jc.ErrorIsNil) 205 c.Assert(*gotHW, gc.DeepEquals, expectHW) 206 207 // Check that the API host ports are initialised correctly. 208 apiHostPorts, err := st.APIHostPorts() 209 c.Assert(err, jc.ErrorIsNil) 210 c.Assert(apiHostPorts, jc.DeepEquals, [][]network.HostPort{ 211 network.AddressesWithPort(filteredAddrs, 1234), 212 }) 213 214 // Check that the state serving info is initialised correctly. 215 stateServingInfo, err := st.StateServingInfo() 216 c.Assert(err, jc.ErrorIsNil) 217 c.Assert(stateServingInfo, jc.DeepEquals, state.StateServingInfo{ 218 APIPort: 1234, 219 StatePort: s.mgoInst.Port(), 220 Cert: testing.ServerCert, 221 PrivateKey: testing.ServerKey, 222 CAPrivateKey: testing.CAKey, 223 SharedSecret: "abc123", 224 SystemIdentity: "def456", 225 }) 226 227 // Check that the machine agent's config has been written 228 // and that we can use it to connect to the state. 229 machine0 := names.NewMachineTag("0") 230 newCfg, err := agent.ReadConfig(agent.ConfigPath(dataDir, machine0)) 231 c.Assert(err, jc.ErrorIsNil) 232 c.Assert(newCfg.Tag(), gc.Equals, machine0) 233 info, ok := cfg.MongoInfo() 234 c.Assert(ok, jc.IsTrue) 235 c.Assert(info.Password, gc.Not(gc.Equals), testing.DefaultMongoPassword) 236 st1, err := state.Open(newCfg.Model(), info, mongotest.DialOpts(), environs.NewStatePolicy()) 237 c.Assert(err, jc.ErrorIsNil) 238 defer st1.Close() 239 } 240 241 func (s *bootstrapSuite) TestInitializeStateWithStateServingInfoNotAvailable(c *gc.C) { 242 configParams := agent.AgentConfigParams{ 243 Paths: agent.Paths{DataDir: c.MkDir()}, 244 Tag: names.NewMachineTag("0"), 245 UpgradedToVersion: jujuversion.Current, 246 StateAddresses: []string{s.mgoInst.Addr()}, 247 CACert: testing.CACert, 248 Password: "fake", 249 Model: testing.ModelTag, 250 } 251 cfg, err := agent.NewAgentConfig(configParams) 252 c.Assert(err, jc.ErrorIsNil) 253 254 _, available := cfg.StateServingInfo() 255 c.Assert(available, jc.IsFalse) 256 257 adminUser := names.NewLocalUserTag("agent-admin") 258 _, _, err = agentbootstrap.InitializeState(adminUser, cfg, nil, nil, agentbootstrap.BootstrapMachineConfig{}, mongotest.DialOpts(), environs.NewStatePolicy()) 259 // InitializeState will fail attempting to get the api port information 260 c.Assert(err, gc.ErrorMatches, "state serving information not available") 261 } 262 263 func (s *bootstrapSuite) TestInitializeStateFailsSecondTime(c *gc.C) { 264 dataDir := c.MkDir() 265 266 configParams := agent.AgentConfigParams{ 267 Paths: agent.Paths{DataDir: dataDir}, 268 Tag: names.NewMachineTag("0"), 269 UpgradedToVersion: jujuversion.Current, 270 StateAddresses: []string{s.mgoInst.Addr()}, 271 CACert: testing.CACert, 272 Password: testing.DefaultMongoPassword, 273 Model: testing.ModelTag, 274 } 275 cfg, err := agent.NewAgentConfig(configParams) 276 c.Assert(err, jc.ErrorIsNil) 277 cfg.SetStateServingInfo(params.StateServingInfo{ 278 APIPort: 5555, 279 StatePort: s.mgoInst.Port(), 280 Cert: "foo", 281 PrivateKey: "bar", 282 SharedSecret: "baz", 283 SystemIdentity: "qux", 284 }) 285 expectHW := instance.MustParseHardware("mem=2048M") 286 mcfg := agentbootstrap.BootstrapMachineConfig{ 287 BootstrapConstraints: constraints.MustParse("mem=1024M"), 288 Jobs: []multiwatcher.MachineJob{multiwatcher.JobManageModel}, 289 InstanceId: "i-bootstrap", 290 Characteristics: expectHW, 291 } 292 envAttrs := dummy.SampleConfig().Delete("admin-secret").Merge(testing.Attrs{ 293 "agent-version": jujuversion.Current.String(), 294 }) 295 envCfg, err := config.New(config.NoDefaults, envAttrs) 296 c.Assert(err, jc.ErrorIsNil) 297 298 hostedModelConfigAttrs := map[string]interface{}{ 299 "name": "hosted", 300 "uuid": utils.MustNewUUID().String(), 301 } 302 303 adminUser := names.NewLocalUserTag("agent-admin") 304 st, _, err := agentbootstrap.InitializeState( 305 adminUser, cfg, envCfg, hostedModelConfigAttrs, mcfg, 306 mongotest.DialOpts(), state.Policy(nil), 307 ) 308 c.Assert(err, jc.ErrorIsNil) 309 st.Close() 310 311 st, _, err = agentbootstrap.InitializeState(adminUser, cfg, envCfg, nil, mcfg, mongotest.DialOpts(), environs.NewStatePolicy()) 312 if err == nil { 313 st.Close() 314 } 315 c.Assert(err, gc.ErrorMatches, "failed to initialize mongo admin user: cannot set admin password: not authorized .*") 316 } 317 318 func (s *bootstrapSuite) TestMachineJobFromParams(c *gc.C) { 319 var tests = []struct { 320 name multiwatcher.MachineJob 321 want state.MachineJob 322 err string 323 }{{ 324 name: multiwatcher.JobHostUnits, 325 want: state.JobHostUnits, 326 }, { 327 name: multiwatcher.JobManageModel, 328 want: state.JobManageModel, 329 }, { 330 name: multiwatcher.JobManageNetworking, 331 want: state.JobManageNetworking, 332 }, { 333 name: "invalid", 334 want: -1, 335 err: `invalid machine job "invalid"`, 336 }} 337 for _, test := range tests { 338 got, err := agentbootstrap.MachineJobFromParams(test.name) 339 if err != nil { 340 c.Check(err, gc.ErrorMatches, test.err) 341 } 342 c.Check(got, gc.Equals, test.want) 343 } 344 } 345 346 func (s *bootstrapSuite) assertCanLogInAsAdmin(c *gc.C, modelTag names.ModelTag, password string) { 347 info := &mongo.MongoInfo{ 348 Info: mongo.Info{ 349 Addrs: []string{s.mgoInst.Addr()}, 350 CACert: testing.CACert, 351 }, 352 Tag: nil, // admin user 353 Password: password, 354 } 355 st, err := state.Open(modelTag, info, mongotest.DialOpts(), environs.NewStatePolicy()) 356 c.Assert(err, jc.ErrorIsNil) 357 defer st.Close() 358 _, err = st.Machine("0") 359 c.Assert(err, jc.ErrorIsNil) 360 }