github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/apiserver/client/api_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package client_test 5 6 import ( 7 "fmt" 8 stdtesting "testing" 9 "time" 10 11 "github.com/juju/errors" 12 "github.com/juju/names" 13 jc "github.com/juju/testing/checkers" 14 gc "gopkg.in/check.v1" 15 16 "github.com/juju/juju/api" 17 "github.com/juju/juju/constraints" 18 "github.com/juju/juju/environs" 19 "github.com/juju/juju/environs/config" 20 "github.com/juju/juju/instance" 21 "github.com/juju/juju/juju/testing" 22 "github.com/juju/juju/mongo" 23 "github.com/juju/juju/state" 24 "github.com/juju/juju/state/multiwatcher" 25 coretesting "github.com/juju/juju/testing" 26 "github.com/juju/juju/testing/factory" 27 ) 28 29 func TestAll(t *stdtesting.T) { 30 coretesting.MgoTestPackage(t) 31 } 32 33 type baseSuite struct { 34 testing.JujuConnSuite 35 } 36 37 var _ = gc.Suite(&baseSuite{}) 38 39 func chanReadEmpty(c *gc.C, ch <-chan struct{}, what string) bool { 40 select { 41 case _, ok := <-ch: 42 return ok 43 case <-time.After(10 * time.Second): 44 c.Fatalf("timed out reading from %s", what) 45 } 46 panic("unreachable") 47 } 48 49 func chanReadStrings(c *gc.C, ch <-chan []string, what string) ([]string, bool) { 50 select { 51 case changes, ok := <-ch: 52 return changes, ok 53 case <-time.After(10 * time.Second): 54 c.Fatalf("timed out reading from %s", what) 55 } 56 panic("unreachable") 57 } 58 59 func chanReadConfig(c *gc.C, ch <-chan *config.Config, what string) (*config.Config, bool) { 60 select { 61 case envConfig, ok := <-ch: 62 return envConfig, ok 63 case <-time.After(10 * time.Second): 64 c.Fatalf("timed out reading from %s", what) 65 } 66 panic("unreachable") 67 } 68 69 func removeServiceAndUnits(c *gc.C, service *state.Service) { 70 // Destroy all units for the service. 71 units, err := service.AllUnits() 72 c.Assert(err, jc.ErrorIsNil) 73 for _, unit := range units { 74 err = unit.EnsureDead() 75 c.Assert(err, jc.ErrorIsNil) 76 err = unit.Remove() 77 c.Assert(err, jc.ErrorIsNil) 78 } 79 err = service.Destroy() 80 c.Assert(err, jc.ErrorIsNil) 81 82 err = service.Refresh() 83 c.Assert(err, jc.Satisfies, errors.IsNotFound) 84 } 85 86 // apiAuthenticator represents a simple authenticator object with only the 87 // SetPassword and Tag methods. This will fit types from both the state 88 // and api packages, as those in the api package do not have PasswordValid(). 89 type apiAuthenticator interface { 90 state.Entity 91 SetPassword(string) error 92 } 93 94 func setDefaultPassword(c *gc.C, e apiAuthenticator) { 95 err := e.SetPassword(defaultPassword(e)) 96 c.Assert(err, jc.ErrorIsNil) 97 } 98 99 func defaultPassword(e apiAuthenticator) string { 100 return e.Tag().String() + " password-1234567890" 101 } 102 103 type setStatuser interface { 104 SetStatus(status state.Status, info string, data map[string]interface{}) error 105 } 106 107 func setDefaultStatus(c *gc.C, entity setStatuser) { 108 err := entity.SetStatus(state.StatusStarted, "", nil) 109 c.Assert(err, jc.ErrorIsNil) 110 } 111 112 func (s *baseSuite) tryOpenState(c *gc.C, e apiAuthenticator, password string) error { 113 stateInfo := s.MongoInfo(c) 114 stateInfo.Tag = e.Tag() 115 stateInfo.Password = password 116 st, err := state.Open(stateInfo, mongo.DialOpts{ 117 Timeout: 25 * time.Millisecond, 118 }, environs.NewStatePolicy()) 119 if err == nil { 120 st.Close() 121 } 122 return err 123 } 124 125 // openAs connects to the API state as the given entity 126 // with the default password for that entity. 127 func (s *baseSuite) openAs(c *gc.C, tag names.Tag) *api.State { 128 info := s.APIInfo(c) 129 info.Tag = tag 130 // Must match defaultPassword() 131 info.Password = fmt.Sprintf("%s password-1234567890", tag) 132 // Set this always, so that the login attempts as a machine will 133 // not fail with ErrNotProvisioned; it's not used otherwise. 134 info.Nonce = "fake_nonce" 135 c.Logf("opening state; entity %q; password %q", info.Tag, info.Password) 136 st, err := api.Open(info, api.DialOpts{}) 137 c.Assert(err, jc.ErrorIsNil) 138 c.Assert(st, gc.NotNil) 139 return st 140 } 141 142 // scenarioStatus describes the expected state 143 // of the juju environment set up by setUpScenario. 144 // 145 // NOTE: AgentState: "down", AgentStateInfo: "(started)" here is due 146 // to the scenario not calling SetAgentPresence on the respective entities, 147 // but this behavior is already tested in cmd/juju/status_test.go and 148 // also tested live and it works. 149 var scenarioStatus = &api.Status{ 150 EnvironmentName: "dummyenv", 151 Machines: map[string]api.MachineStatus{ 152 "0": { 153 Id: "0", 154 InstanceId: instance.Id("i-machine-0"), 155 Agent: api.AgentStatus{ 156 Status: "started", 157 Data: make(map[string]interface{}), 158 }, 159 AgentState: "down", 160 AgentStateInfo: "(started)", 161 Series: "quantal", 162 Containers: map[string]api.MachineStatus{}, 163 Jobs: []multiwatcher.MachineJob{multiwatcher.JobManageEnviron}, 164 HasVote: false, 165 WantsVote: true, 166 }, 167 "1": { 168 Id: "1", 169 InstanceId: instance.Id("i-machine-1"), 170 Agent: api.AgentStatus{ 171 Status: "started", 172 Data: make(map[string]interface{}), 173 }, 174 AgentState: "down", 175 AgentStateInfo: "(started)", 176 Series: "quantal", 177 Containers: map[string]api.MachineStatus{}, 178 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 179 HasVote: false, 180 WantsVote: false, 181 }, 182 "2": { 183 Id: "2", 184 InstanceId: instance.Id("i-machine-2"), 185 Agent: api.AgentStatus{ 186 Status: "started", 187 Data: make(map[string]interface{}), 188 }, 189 AgentState: "down", 190 AgentStateInfo: "(started)", 191 Series: "quantal", 192 Containers: map[string]api.MachineStatus{}, 193 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 194 HasVote: false, 195 WantsVote: false, 196 }, 197 }, 198 Services: map[string]api.ServiceStatus{ 199 "logging": { 200 Charm: "local:quantal/logging-1", 201 Relations: map[string][]string{ 202 "logging-directory": {"wordpress"}, 203 }, 204 SubordinateTo: []string{"wordpress"}, 205 }, 206 "mysql": { 207 Charm: "local:quantal/mysql-1", 208 Relations: map[string][]string{}, 209 SubordinateTo: []string{}, 210 Units: map[string]api.UnitStatus{}, 211 }, 212 "wordpress": { 213 Charm: "local:quantal/wordpress-3", 214 Relations: map[string][]string{ 215 "logging-dir": {"logging"}, 216 }, 217 SubordinateTo: []string{}, 218 Units: map[string]api.UnitStatus{ 219 "wordpress/0": { 220 Agent: api.AgentStatus{ 221 Status: "error", 222 Info: "blam", 223 Data: map[string]interface{}{"relation-id": "0"}, 224 }, 225 AgentState: "down", 226 AgentStateInfo: "(error: blam)", 227 Machine: "1", 228 Subordinates: map[string]api.UnitStatus{ 229 "logging/0": { 230 Agent: api.AgentStatus{ 231 Status: "allocating", 232 Data: make(map[string]interface{}), 233 }, 234 AgentState: "allocating", 235 }, 236 }, 237 }, 238 "wordpress/1": { 239 Agent: api.AgentStatus{ 240 Status: "allocating", 241 Data: make(map[string]interface{}), 242 }, 243 AgentState: "allocating", 244 Machine: "2", 245 Subordinates: map[string]api.UnitStatus{ 246 "logging/1": { 247 Agent: api.AgentStatus{ 248 Status: "allocating", 249 Data: make(map[string]interface{}), 250 }, 251 AgentState: "allocating", 252 }, 253 }, 254 }, 255 }, 256 }, 257 }, 258 Relations: []api.RelationStatus{ 259 { 260 Id: 0, 261 Key: "logging:logging-directory wordpress:logging-dir", 262 Endpoints: []api.EndpointStatus{ 263 { 264 ServiceName: "logging", 265 Name: "logging-directory", 266 Role: "requirer", 267 Subordinate: true, 268 }, 269 { 270 ServiceName: "wordpress", 271 Name: "logging-dir", 272 Role: "provider", 273 Subordinate: false, 274 }, 275 }, 276 Interface: "logging", 277 Scope: "container", 278 }, 279 }, 280 Networks: map[string]api.NetworkStatus{}, 281 } 282 283 // setUpScenario makes an environment scenario suitable for 284 // testing most kinds of access scenario. It returns 285 // a list of all the entities in the scenario. 286 // 287 // When the scenario is initialized, we have: 288 // user-admin 289 // user-other 290 // machine-0 291 // instance-id="i-machine-0" 292 // nonce="fake_nonce" 293 // jobs=manage-environ 294 // status=started, info="" 295 // machine-1 296 // instance-id="i-machine-1" 297 // nonce="fake_nonce" 298 // jobs=host-units 299 // status=started, info="" 300 // constraints=mem=1G 301 // machine-2 302 // instance-id="i-machine-2" 303 // nonce="fake_nonce" 304 // jobs=host-units 305 // status=started, info="" 306 // service-wordpress 307 // service-logging 308 // unit-wordpress-0 309 // deployer-name=machine-1 310 // status=down with error and status data attached 311 // unit-logging-0 312 // deployer-name=unit-wordpress-0 313 // unit-wordpress-1 314 // deployer-name=machine-2 315 // unit-logging-1 316 // deployer-name=unit-wordpress-1 317 // 318 // The passwords for all returned entities are 319 // set to the entity name with a " password" suffix. 320 // 321 // Note that there is nothing special about machine-0 322 // here - it's the environment manager in this scenario 323 // just because machine 0 has traditionally been the 324 // environment manager (bootstrap machine), so is 325 // hopefully easier to remember as such. 326 func (s *baseSuite) setUpScenario(c *gc.C) (entities []names.Tag) { 327 add := func(e state.Entity) { 328 entities = append(entities, e.Tag()) 329 } 330 u, err := s.State.User(s.AdminUserTag(c)) 331 c.Assert(err, jc.ErrorIsNil) 332 setDefaultPassword(c, u) 333 add(u) 334 335 u = s.Factory.MakeUser(c, &factory.UserParams{Name: "other"}) 336 setDefaultPassword(c, u) 337 add(u) 338 339 m, err := s.State.AddMachine("quantal", state.JobManageEnviron) 340 c.Assert(err, jc.ErrorIsNil) 341 c.Assert(m.Tag(), gc.Equals, names.NewMachineTag("0")) 342 err = m.SetProvisioned(instance.Id("i-"+m.Tag().String()), "fake_nonce", nil) 343 c.Assert(err, jc.ErrorIsNil) 344 setDefaultPassword(c, m) 345 setDefaultStatus(c, m) 346 add(m) 347 s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 348 wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 349 s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) 350 eps, err := s.State.InferEndpoints("logging", "wordpress") 351 c.Assert(err, jc.ErrorIsNil) 352 rel, err := s.State.AddRelation(eps...) 353 c.Assert(err, jc.ErrorIsNil) 354 355 for i := 0; i < 2; i++ { 356 wu, err := wordpress.AddUnit() 357 c.Assert(err, jc.ErrorIsNil) 358 c.Assert(wu.Tag(), gc.Equals, names.NewUnitTag(fmt.Sprintf("wordpress/%d", i))) 359 setDefaultPassword(c, wu) 360 add(wu) 361 362 m, err := s.State.AddMachine("quantal", state.JobHostUnits) 363 c.Assert(err, jc.ErrorIsNil) 364 c.Assert(m.Tag(), gc.Equals, names.NewMachineTag(fmt.Sprintf("%d", i+1))) 365 if i == 1 { 366 err = m.SetConstraints(constraints.MustParse("mem=1G")) 367 c.Assert(err, jc.ErrorIsNil) 368 } 369 err = m.SetProvisioned(instance.Id("i-"+m.Tag().String()), "fake_nonce", nil) 370 c.Assert(err, jc.ErrorIsNil) 371 setDefaultPassword(c, m) 372 setDefaultStatus(c, m) 373 add(m) 374 375 err = wu.AssignToMachine(m) 376 c.Assert(err, jc.ErrorIsNil) 377 378 deployer, ok := wu.DeployerTag() 379 c.Assert(ok, jc.IsTrue) 380 c.Assert(deployer, gc.Equals, names.NewMachineTag(fmt.Sprintf("%d", i+1))) 381 382 wru, err := rel.Unit(wu) 383 c.Assert(err, jc.ErrorIsNil) 384 385 // Put wordpress/0 in error state (with extra status data set) 386 if i == 0 { 387 sd := map[string]interface{}{ 388 "relation-id": "0", 389 // these this should get filtered out 390 // (not in StatusData whitelist) 391 "remote-unit": "logging/0", 392 "foo": "bar", 393 } 394 wu.SetStatus(state.StatusError, "blam", sd) 395 } 396 397 // Create the subordinate unit as a side-effect of entering 398 // scope in the principal's relation-unit. 399 err = wru.EnterScope(nil) 400 c.Assert(err, jc.ErrorIsNil) 401 402 lu, err := s.State.Unit(fmt.Sprintf("logging/%d", i)) 403 c.Assert(err, jc.ErrorIsNil) 404 c.Assert(lu.IsPrincipal(), jc.IsFalse) 405 deployer, ok = lu.DeployerTag() 406 c.Assert(ok, jc.IsTrue) 407 c.Assert(deployer, gc.Equals, names.NewUnitTag(fmt.Sprintf("wordpress/%d", i))) 408 setDefaultPassword(c, lu) 409 add(lu) 410 } 411 return 412 }