launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/cmd/jujud/bootstrap_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "encoding/base64" 8 "io/ioutil" 9 "path/filepath" 10 11 gc "launchpad.net/gocheck" 12 "launchpad.net/goyaml" 13 14 "launchpad.net/juju-core/agent" 15 "launchpad.net/juju-core/constraints" 16 "launchpad.net/juju-core/environs/bootstrap" 17 "launchpad.net/juju-core/environs/jujutest" 18 "launchpad.net/juju-core/errors" 19 "launchpad.net/juju-core/instance" 20 "launchpad.net/juju-core/provider/dummy" 21 "launchpad.net/juju-core/state" 22 "launchpad.net/juju-core/testing" 23 jc "launchpad.net/juju-core/testing/checkers" 24 "launchpad.net/juju-core/testing/testbase" 25 "launchpad.net/juju-core/utils" 26 ) 27 28 // We don't want to use JujuConnSuite because it gives us 29 // an already-bootstrapped environment. 30 type BootstrapSuite struct { 31 testbase.LoggingSuite 32 testing.MgoSuite 33 dataDir string 34 providerStateURLFile string 35 } 36 37 var _ = gc.Suite(&BootstrapSuite{}) 38 39 var testRoundTripper = &jujutest.ProxyRoundTripper{} 40 41 func init() { 42 // Prepare mock http transport for provider-state output in tests. 43 testRoundTripper.RegisterForScheme("test") 44 } 45 46 func (s *BootstrapSuite) SetUpSuite(c *gc.C) { 47 s.LoggingSuite.SetUpSuite(c) 48 s.MgoSuite.SetUpSuite(c) 49 stateInfo := bootstrap.BootstrapState{ 50 StateInstances: []instance.Id{instance.Id("dummy.instance.id")}, 51 } 52 stateData, err := goyaml.Marshal(stateInfo) 53 c.Assert(err, gc.IsNil) 54 content := map[string]string{"/" + bootstrap.StateFile: string(stateData)} 55 testRoundTripper.Sub = jujutest.NewCannedRoundTripper(content, nil) 56 s.providerStateURLFile = filepath.Join(c.MkDir(), "provider-state-url") 57 providerStateURLFile = s.providerStateURLFile 58 } 59 60 func (s *BootstrapSuite) TearDownSuite(c *gc.C) { 61 s.MgoSuite.TearDownSuite(c) 62 s.LoggingSuite.TearDownSuite(c) 63 } 64 65 func (s *BootstrapSuite) SetUpTest(c *gc.C) { 66 s.LoggingSuite.SetUpTest(c) 67 s.MgoSuite.SetUpTest(c) 68 s.dataDir = c.MkDir() 69 } 70 71 func (s *BootstrapSuite) TearDownTest(c *gc.C) { 72 s.MgoSuite.TearDownTest(c) 73 s.LoggingSuite.TearDownTest(c) 74 } 75 76 var testPassword = "my-admin-secret" 77 78 func testPasswordHash() string { 79 return utils.UserPasswordHash(testPassword, utils.CompatSalt) 80 } 81 82 func (s *BootstrapSuite) initBootstrapCommand(c *gc.C, args ...string) (machineConf agent.Config, cmd *BootstrapCommand, err error) { 83 ioutil.WriteFile(s.providerStateURLFile, []byte("test://localhost/provider-state\n"), 0600) 84 // NOTE: the old test used an equivalent of the NewAgentConfig, but it 85 // really should be using NewStateMachineConfig. 86 params := agent.AgentConfigParams{ 87 DataDir: s.dataDir, 88 Tag: "bootstrap", 89 Password: testPasswordHash(), 90 Nonce: state.BootstrapNonce, 91 StateAddresses: []string{testing.MgoServer.Addr()}, 92 APIAddresses: []string{"0.1.2.3:1234"}, 93 CACert: []byte(testing.CACert), 94 } 95 bootConf, err := agent.NewAgentConfig(params) 96 c.Assert(err, gc.IsNil) 97 err = bootConf.Write() 98 c.Assert(err, gc.IsNil) 99 100 params.Tag = "machine-0" 101 machineConf, err = agent.NewAgentConfig(params) 102 c.Assert(err, gc.IsNil) 103 err = machineConf.Write() 104 c.Assert(err, gc.IsNil) 105 106 cmd = &BootstrapCommand{} 107 err = testing.InitCommand(cmd, append([]string{"--data-dir", s.dataDir}, args...)) 108 return machineConf, cmd, err 109 } 110 111 func (s *BootstrapSuite) TestInitializeEnvironment(c *gc.C) { 112 _, cmd, err := s.initBootstrapCommand(c, "--env-config", testConfig) 113 c.Assert(err, gc.IsNil) 114 err = cmd.Run(nil) 115 c.Assert(err, gc.IsNil) 116 117 st, err := state.Open(&state.Info{ 118 Addrs: []string{testing.MgoServer.Addr()}, 119 CACert: []byte(testing.CACert), 120 Password: testPasswordHash(), 121 }, state.DefaultDialOpts()) 122 c.Assert(err, gc.IsNil) 123 defer st.Close() 124 machines, err := st.AllMachines() 125 c.Assert(err, gc.IsNil) 126 c.Assert(machines, gc.HasLen, 1) 127 128 instid, err := machines[0].InstanceId() 129 c.Assert(err, gc.IsNil) 130 c.Assert(instid, gc.Equals, instance.Id("dummy.instance.id")) 131 132 cons, err := st.EnvironConstraints() 133 c.Assert(err, gc.IsNil) 134 c.Assert(&cons, jc.Satisfies, constraints.IsEmpty) 135 } 136 137 func (s *BootstrapSuite) TestSetConstraints(c *gc.C) { 138 tcons := constraints.Value{Mem: uint64p(2048), CpuCores: uint64p(2)} 139 _, cmd, err := s.initBootstrapCommand(c, "--env-config", testConfig, "--constraints", tcons.String()) 140 c.Assert(err, gc.IsNil) 141 err = cmd.Run(nil) 142 c.Assert(err, gc.IsNil) 143 144 st, err := state.Open(&state.Info{ 145 Addrs: []string{testing.MgoServer.Addr()}, 146 CACert: []byte(testing.CACert), 147 Password: testPasswordHash(), 148 }, state.DefaultDialOpts()) 149 c.Assert(err, gc.IsNil) 150 defer st.Close() 151 cons, err := st.EnvironConstraints() 152 c.Assert(err, gc.IsNil) 153 c.Assert(cons, gc.DeepEquals, tcons) 154 155 machines, err := st.AllMachines() 156 c.Assert(err, gc.IsNil) 157 c.Assert(machines, gc.HasLen, 1) 158 cons, err = machines[0].Constraints() 159 c.Assert(err, gc.IsNil) 160 c.Assert(cons, gc.DeepEquals, tcons) 161 } 162 163 func uint64p(v uint64) *uint64 { 164 return &v 165 } 166 167 func (s *BootstrapSuite) TestDefaultMachineJobs(c *gc.C) { 168 _, cmd, err := s.initBootstrapCommand(c, "--env-config", testConfig) 169 c.Assert(err, gc.IsNil) 170 err = cmd.Run(nil) 171 c.Assert(err, gc.IsNil) 172 173 st, err := state.Open(&state.Info{ 174 Addrs: []string{testing.MgoServer.Addr()}, 175 CACert: []byte(testing.CACert), 176 Password: testPasswordHash(), 177 }, state.DefaultDialOpts()) 178 c.Assert(err, gc.IsNil) 179 defer st.Close() 180 m, err := st.Machine("0") 181 c.Assert(err, gc.IsNil) 182 c.Assert(m.Jobs(), gc.DeepEquals, []state.MachineJob{ 183 state.JobManageEnviron, state.JobHostUnits, 184 }) 185 } 186 187 func (s *BootstrapSuite) TestConfiguredMachineJobs(c *gc.C) { 188 agentConf, cmd, err := s.initBootstrapCommand(c, "--env-config", testConfig) 189 c.Assert(err, gc.IsNil) 190 bootstrapJobs, err := agent.MarshalBootstrapJobs(state.JobManageEnviron) 191 c.Assert(err, gc.IsNil) 192 agentConf.SetValue(agent.BootstrapJobs, bootstrapJobs) 193 err = agentConf.Write() 194 c.Assert(err, gc.IsNil) 195 err = cmd.Run(nil) 196 c.Assert(err, gc.IsNil) 197 198 st, err := state.Open(&state.Info{ 199 Addrs: []string{testing.MgoServer.Addr()}, 200 CACert: []byte(testing.CACert), 201 Password: testPasswordHash(), 202 }, state.DefaultDialOpts()) 203 c.Assert(err, gc.IsNil) 204 defer st.Close() 205 m, err := st.Machine("0") 206 c.Assert(err, gc.IsNil) 207 c.Assert(m.Jobs(), gc.DeepEquals, []state.MachineJob{state.JobManageEnviron}) 208 } 209 210 func testOpenState(c *gc.C, info *state.Info, expectErrType error) { 211 st, err := state.Open(info, state.DefaultDialOpts()) 212 if st != nil { 213 st.Close() 214 } 215 if expectErrType != nil { 216 c.Assert(err, gc.FitsTypeOf, expectErrType) 217 } else { 218 c.Assert(err, gc.IsNil) 219 } 220 } 221 222 func (s *BootstrapSuite) TestInitialPassword(c *gc.C) { 223 machineConf, cmd, err := s.initBootstrapCommand(c, "--env-config", testConfig) 224 c.Assert(err, gc.IsNil) 225 226 err = cmd.Run(nil) 227 c.Assert(err, gc.IsNil) 228 229 // Check that we cannot now connect to the state without a 230 // password. 231 info := &state.Info{ 232 Addrs: []string{testing.MgoServer.Addr()}, 233 CACert: []byte(testing.CACert), 234 } 235 testOpenState(c, info, errors.Unauthorizedf("")) 236 237 // Check we can log in to mongo as admin. 238 info.Tag, info.Password = "", testPasswordHash() 239 st, err := state.Open(info, state.DefaultDialOpts()) 240 c.Assert(err, gc.IsNil) 241 // Reset password so the tests can continue to use the same server. 242 defer st.Close() 243 defer st.SetAdminMongoPassword("") 244 245 // Check that the admin user has been given an appropriate 246 // password 247 u, err := st.User("admin") 248 c.Assert(err, gc.IsNil) 249 c.Assert(u.PasswordValid(testPassword), gc.Equals, true) 250 251 // Check that the machine configuration has been given a new 252 // password and that we can connect to mongo as that machine 253 // and that the in-mongo password also verifies correctly. 254 machineConf1, err := agent.ReadConf(machineConf.DataDir(), "machine-0") 255 c.Assert(err, gc.IsNil) 256 257 st, err = machineConf1.OpenState() 258 c.Assert(err, gc.IsNil) 259 defer st.Close() 260 } 261 262 var base64ConfigTests = []struct { 263 input []string 264 err string 265 expected map[string]interface{} 266 }{ 267 { 268 // no value supplied 269 nil, 270 "--env-config option must be set", 271 nil, 272 }, { 273 // empty 274 []string{"--env-config", ""}, 275 "--env-config option must be set", 276 nil, 277 }, { 278 // wrong, should be base64 279 []string{"--env-config", "name: banana\n"}, 280 ".*illegal base64 data at input byte.*", 281 nil, 282 }, { 283 []string{"--env-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n"))}, 284 "", 285 map[string]interface{}{"name": "banana"}, 286 }, 287 } 288 289 func (s *BootstrapSuite) TestBase64Config(c *gc.C) { 290 for i, t := range base64ConfigTests { 291 c.Logf("test %d", i) 292 var args []string 293 args = append(args, t.input...) 294 _, cmd, err := s.initBootstrapCommand(c, args...) 295 if t.err == "" { 296 c.Assert(cmd, gc.NotNil) 297 c.Assert(err, gc.IsNil) 298 c.Assert(cmd.EnvConfig, gc.DeepEquals, t.expected) 299 } else { 300 c.Assert(err, gc.ErrorMatches, t.err) 301 } 302 } 303 } 304 305 type b64yaml map[string]interface{} 306 307 func (m b64yaml) encode() string { 308 data, err := goyaml.Marshal(m) 309 if err != nil { 310 panic(err) 311 } 312 return base64.StdEncoding.EncodeToString(data) 313 } 314 315 var testConfig = b64yaml( 316 dummy.SampleConfig().Merge( 317 testing.Attrs{ 318 "state-server": false, 319 "agent-version": "3.4.5", 320 }, 321 ).Delete("admin-secret", "ca-private-key")).encode()