launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/juju/testing/conn.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package testing 5 6 import ( 7 "fmt" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 12 gc "launchpad.net/gocheck" 13 "launchpad.net/goyaml" 14 15 "launchpad.net/juju-core/agent" 16 "launchpad.net/juju-core/charm" 17 "launchpad.net/juju-core/constraints" 18 "launchpad.net/juju-core/environs" 19 "launchpad.net/juju-core/environs/bootstrap" 20 "launchpad.net/juju-core/environs/configstore" 21 envtesting "launchpad.net/juju-core/environs/testing" 22 "launchpad.net/juju-core/juju" 23 "launchpad.net/juju-core/juju/osenv" 24 "launchpad.net/juju-core/provider/dummy" 25 "launchpad.net/juju-core/state" 26 "launchpad.net/juju-core/state/api" 27 "launchpad.net/juju-core/testing" 28 "launchpad.net/juju-core/testing/testbase" 29 "launchpad.net/juju-core/utils" 30 "launchpad.net/juju-core/version" 31 ) 32 33 // JujuConnSuite provides a freshly bootstrapped juju.Conn 34 // for each test. It also includes testbase.LoggingSuite. 35 // 36 // It also sets up RootDir to point to a directory hierarchy 37 // mirroring the intended juju directory structure, including 38 // the following: 39 // RootDir/home/ubuntu/.juju/environments.yaml 40 // The dummy environments.yaml file, holding 41 // a default environment named "dummyenv" 42 // which uses the "dummy" environment type. 43 // RootDir/var/lib/juju 44 // An empty directory returned as DataDir - the 45 // root of the juju data storage space. 46 // $HOME is set to point to RootDir/home/ubuntu. 47 type JujuConnSuite struct { 48 // TODO: JujuConnSuite should not be concerned both with JUJU_HOME and with 49 // /var/lib/juju: the use cases are completely non-overlapping, and any tests that 50 // really do need both to exist ought to be embedding distinct fixtures for the 51 // distinct environments. 52 testbase.LoggingSuite 53 testing.MgoSuite 54 envtesting.ToolsFixture 55 Conn *juju.Conn 56 State *state.State 57 APIConn *juju.APIConn 58 APIState *api.State 59 ConfigStore configstore.Storage 60 BackingState *state.State // The State being used by the API server 61 RootDir string // The faked-up root directory. 62 oldHome string 63 oldJujuHome string 64 environ environs.Environ 65 } 66 67 const AdminSecret = "dummy-secret" 68 69 func (s *JujuConnSuite) SetUpSuite(c *gc.C) { 70 s.LoggingSuite.SetUpSuite(c) 71 s.MgoSuite.SetUpSuite(c) 72 } 73 74 func (s *JujuConnSuite) TearDownSuite(c *gc.C) { 75 s.MgoSuite.TearDownSuite(c) 76 s.LoggingSuite.TearDownSuite(c) 77 } 78 79 func (s *JujuConnSuite) SetUpTest(c *gc.C) { 80 s.LoggingSuite.SetUpTest(c) 81 s.MgoSuite.SetUpTest(c) 82 s.ToolsFixture.SetUpTest(c) 83 s.setUpConn(c) 84 } 85 86 func (s *JujuConnSuite) TearDownTest(c *gc.C) { 87 s.tearDownConn(c) 88 s.ToolsFixture.TearDownTest(c) 89 s.MgoSuite.TearDownTest(c) 90 s.LoggingSuite.TearDownTest(c) 91 } 92 93 // Reset returns environment state to that which existed at the start of 94 // the test. 95 func (s *JujuConnSuite) Reset(c *gc.C) { 96 s.tearDownConn(c) 97 s.setUpConn(c) 98 } 99 100 func (s *JujuConnSuite) StateInfo(c *gc.C) *state.Info { 101 info, _, err := s.Conn.Environ.StateInfo() 102 c.Assert(err, gc.IsNil) 103 info.Password = "dummy-secret" 104 return info 105 } 106 107 func (s *JujuConnSuite) APIInfo(c *gc.C) *api.Info { 108 _, apiInfo, err := s.APIConn.Environ.StateInfo() 109 c.Assert(err, gc.IsNil) 110 apiInfo.Tag = "user-admin" 111 apiInfo.Password = "dummy-secret" 112 return apiInfo 113 } 114 115 // openAPIAs opens the API and ensures that the *api.State returned will be 116 // closed during the test teardown by using a cleanup function. 117 func (s *JujuConnSuite) openAPIAs(c *gc.C, tag, password, nonce string) *api.State { 118 _, info, err := s.APIConn.Environ.StateInfo() 119 c.Assert(err, gc.IsNil) 120 info.Tag = tag 121 info.Password = password 122 info.Nonce = nonce 123 apiState, err := api.Open(info, api.DialOpts{}) 124 c.Assert(err, gc.IsNil) 125 c.Assert(apiState, gc.NotNil) 126 s.AddCleanup(func(c *gc.C) { 127 err := apiState.Close() 128 c.Check(err, gc.IsNil) 129 }) 130 return apiState 131 } 132 133 // OpenAPIAs opens the API using the given identity tag and password for 134 // authentication. The returned *api.State should not be closed by the caller 135 // as a cleanup function has been registered to do that. 136 func (s *JujuConnSuite) OpenAPIAs(c *gc.C, tag, password string) *api.State { 137 return s.openAPIAs(c, tag, password, "") 138 } 139 140 // OpenAPIAsMachine opens the API using the given machine tag, password and 141 // nonce for authentication. The returned *api.State should not be closed by 142 // the caller as a cleanup function has been registered to do that. 143 func (s *JujuConnSuite) OpenAPIAsMachine(c *gc.C, tag, password, nonce string) *api.State { 144 return s.openAPIAs(c, tag, password, nonce) 145 } 146 147 // OpenAPIAsNewMachine creates a new machine entry that lives in system state, 148 // and then uses that to open the API. The returned *api.State should not be 149 // closed by the caller as a cleanup function has been registered to do that. 150 // The machine will run the supplied jobs; if none are given, JobHostUnits is assumed. 151 func (s *JujuConnSuite) OpenAPIAsNewMachine(c *gc.C, jobs ...state.MachineJob) (*api.State, *state.Machine) { 152 if len(jobs) == 0 { 153 jobs = []state.MachineJob{state.JobHostUnits} 154 } 155 machine, err := s.State.AddMachine("quantal", jobs...) 156 c.Assert(err, gc.IsNil) 157 password, err := utils.RandomPassword() 158 c.Assert(err, gc.IsNil) 159 err = machine.SetPassword(password) 160 c.Assert(err, gc.IsNil) 161 err = machine.SetProvisioned("foo", "fake_nonce", nil) 162 c.Assert(err, gc.IsNil) 163 return s.openAPIAs(c, machine.Tag(), password, "fake_nonce"), machine 164 } 165 166 func (s *JujuConnSuite) setUpConn(c *gc.C) { 167 if s.RootDir != "" { 168 panic("JujuConnSuite.setUpConn without teardown") 169 } 170 s.RootDir = c.MkDir() 171 s.oldHome = osenv.Home() 172 home := filepath.Join(s.RootDir, "/home/ubuntu") 173 err := os.MkdirAll(home, 0777) 174 c.Assert(err, gc.IsNil) 175 osenv.SetHome(home) 176 s.oldJujuHome = osenv.SetJujuHome(filepath.Join(home, ".juju")) 177 err = os.Mkdir(osenv.JujuHome(), 0777) 178 c.Assert(err, gc.IsNil) 179 180 dataDir := filepath.Join(s.RootDir, "/var/lib/juju") 181 err = os.MkdirAll(dataDir, 0777) 182 c.Assert(err, gc.IsNil) 183 184 // TODO(rog) remove these files and add them only when 185 // the tests specifically need them (in cmd/juju for example) 186 s.writeSampleConfig(c, osenv.JujuHomePath("environments.yaml")) 187 188 err = ioutil.WriteFile(osenv.JujuHomePath("dummyenv-cert.pem"), []byte(testing.CACert), 0666) 189 c.Assert(err, gc.IsNil) 190 191 err = ioutil.WriteFile(osenv.JujuHomePath("dummyenv-private-key.pem"), []byte(testing.CAKey), 0600) 192 c.Assert(err, gc.IsNil) 193 194 store, err := configstore.Default() 195 c.Assert(err, gc.IsNil) 196 s.ConfigStore = store 197 198 environ, err := environs.PrepareFromName("dummyenv", s.ConfigStore) 199 c.Assert(err, gc.IsNil) 200 // sanity check we've got the correct environment. 201 c.Assert(environ.Name(), gc.Equals, "dummyenv") 202 203 envtesting.MustUploadFakeTools(environ.Storage()) 204 ctx := envtesting.NewBootstrapContext(testing.Context(c)) 205 c.Assert(bootstrap.Bootstrap(ctx, environ, constraints.Value{}), gc.IsNil) 206 207 s.BackingState = environ.(GetStater).GetStateInAPIServer() 208 209 conn, err := juju.NewConn(environ) 210 c.Assert(err, gc.IsNil) 211 s.Conn = conn 212 s.State = conn.State 213 214 apiConn, err := juju.NewAPIConn(environ, api.DialOpts{}) 215 c.Assert(err, gc.IsNil) 216 s.APIConn = apiConn 217 s.APIState = apiConn.State 218 s.environ = environ 219 } 220 221 func (s *JujuConnSuite) writeSampleConfig(c *gc.C, path string) { 222 attrs := dummy.SampleConfig().Merge(testing.Attrs{ 223 "admin-secret": AdminSecret, 224 "agent-version": version.Current.Number.String(), 225 }).Delete("name") 226 whole := map[string]interface{}{ 227 "environments": map[string]interface{}{ 228 "dummyenv": attrs, 229 }, 230 } 231 data, err := goyaml.Marshal(whole) 232 c.Assert(err, gc.IsNil) 233 s.WriteConfig(string(data)) 234 } 235 236 type GetStater interface { 237 GetStateInAPIServer() *state.State 238 } 239 240 func (s *JujuConnSuite) tearDownConn(c *gc.C) { 241 // Bootstrap will set the admin password, and render non-authorized use 242 // impossible. s.State may still hold the right password, so try to reset 243 // the password so that the MgoSuite soft-resetting works. If that fails, 244 // it will still work, but it will take a while since it has to kill the 245 // whole database and start over. 246 if err := s.State.SetAdminMongoPassword(""); err != nil { 247 c.Logf("cannot reset admin password: %v", err) 248 } 249 c.Assert(s.Conn.Close(), gc.IsNil) 250 c.Assert(s.APIConn.Close(), gc.IsNil) 251 dummy.Reset() 252 s.Conn = nil 253 s.State = nil 254 osenv.SetHome(s.oldHome) 255 osenv.SetJujuHome(s.oldJujuHome) 256 s.oldHome = "" 257 s.RootDir = "" 258 } 259 260 func (s *JujuConnSuite) DataDir() string { 261 if s.RootDir == "" { 262 panic("DataDir called out of test context") 263 } 264 return filepath.Join(s.RootDir, "/var/lib/juju") 265 } 266 267 // WriteConfig writes a juju config file to the "home" directory. 268 func (s *JujuConnSuite) WriteConfig(configData string) { 269 if s.RootDir == "" { 270 panic("SetUpTest has not been called; will not overwrite $JUJU_HOME/environments.yaml") 271 } 272 path := osenv.JujuHomePath("environments.yaml") 273 err := ioutil.WriteFile(path, []byte(configData), 0600) 274 if err != nil { 275 panic(err) 276 } 277 } 278 279 func (s *JujuConnSuite) AddTestingCharm(c *gc.C, name string) *state.Charm { 280 ch := testing.Charms.Dir(name) 281 ident := fmt.Sprintf("%s-%d", ch.Meta().Name, ch.Revision()) 282 curl := charm.MustParseURL("local:quantal/" + ident) 283 repo, err := charm.InferRepository(curl, testing.Charms.Path()) 284 c.Assert(err, gc.IsNil) 285 sch, err := s.Conn.PutCharm(curl, repo, false) 286 c.Assert(err, gc.IsNil) 287 return sch 288 } 289 290 func (s *JujuConnSuite) AddTestingService(c *gc.C, name string, ch *state.Charm) *state.Service { 291 c.Assert(s.State, gc.NotNil) 292 service, err := s.State.AddService(name, "user-admin", ch) 293 c.Assert(err, gc.IsNil) 294 return service 295 } 296 297 func (s *JujuConnSuite) AgentConfigForTag(c *gc.C, tag string) agent.Config { 298 password, err := utils.RandomPassword() 299 c.Assert(err, gc.IsNil) 300 config, err := agent.NewAgentConfig( 301 agent.AgentConfigParams{ 302 DataDir: s.DataDir(), 303 Tag: tag, 304 Password: password, 305 Nonce: "nonce", 306 StateAddresses: s.StateInfo(c).Addrs, 307 APIAddresses: s.APIInfo(c).Addrs, 308 CACert: []byte(testing.CACert), 309 }) 310 c.Assert(err, gc.IsNil) 311 return config 312 }