github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/state/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 gc "launchpad.net/gocheck" 12 13 "launchpad.net/juju-core/constraints" 14 "launchpad.net/juju-core/environs" 15 "launchpad.net/juju-core/environs/config" 16 "launchpad.net/juju-core/errors" 17 "launchpad.net/juju-core/instance" 18 "launchpad.net/juju-core/juju/testing" 19 "launchpad.net/juju-core/state" 20 "launchpad.net/juju-core/state/api" 21 "launchpad.net/juju-core/state/api/params" 22 coretesting "launchpad.net/juju-core/testing" 23 jc "launchpad.net/juju-core/testing/checkers" 24 ) 25 26 func TestAll(t *stdtesting.T) { 27 coretesting.MgoTestPackage(t) 28 } 29 30 type baseSuite struct { 31 testing.JujuConnSuite 32 } 33 34 var _ = gc.Suite(&baseSuite{}) 35 36 func chanReadEmpty(c *gc.C, ch <-chan struct{}, what string) bool { 37 select { 38 case _, ok := <-ch: 39 return ok 40 case <-time.After(10 * time.Second): 41 c.Fatalf("timed out reading from %s", what) 42 } 43 panic("unreachable") 44 } 45 46 func chanReadStrings(c *gc.C, ch <-chan []string, what string) ([]string, bool) { 47 select { 48 case changes, ok := <-ch: 49 return changes, ok 50 case <-time.After(10 * time.Second): 51 c.Fatalf("timed out reading from %s", what) 52 } 53 panic("unreachable") 54 } 55 56 func chanReadConfig(c *gc.C, ch <-chan *config.Config, what string) (*config.Config, bool) { 57 select { 58 case envConfig, ok := <-ch: 59 return envConfig, ok 60 case <-time.After(10 * time.Second): 61 c.Fatalf("timed out reading from %s", what) 62 } 63 panic("unreachable") 64 } 65 66 func removeServiceAndUnits(c *gc.C, service *state.Service) { 67 // Destroy all units for the service. 68 units, err := service.AllUnits() 69 c.Assert(err, gc.IsNil) 70 for _, unit := range units { 71 err = unit.EnsureDead() 72 c.Assert(err, gc.IsNil) 73 err = unit.Remove() 74 c.Assert(err, gc.IsNil) 75 } 76 err = service.Destroy() 77 c.Assert(err, gc.IsNil) 78 79 err = service.Refresh() 80 c.Assert(err, jc.Satisfies, errors.IsNotFoundError) 81 } 82 83 // apiAuthenticator represents a simple authenticator object with only the 84 // SetPassword and Tag methods. This will fit types from both the state 85 // and api packages, as those in the api package do not have PasswordValid(). 86 type apiAuthenticator interface { 87 state.Entity 88 SetPassword(string) error 89 } 90 91 func setDefaultPassword(c *gc.C, e apiAuthenticator) { 92 err := e.SetPassword(defaultPassword(e)) 93 c.Assert(err, gc.IsNil) 94 } 95 96 func defaultPassword(e apiAuthenticator) string { 97 return e.Tag() + " password-1234567890" 98 } 99 100 type setStatuser interface { 101 SetStatus(status params.Status, info string, data params.StatusData) error 102 } 103 104 func setDefaultStatus(c *gc.C, entity setStatuser) { 105 err := entity.SetStatus(params.StatusStarted, "", nil) 106 c.Assert(err, gc.IsNil) 107 } 108 109 func (s *baseSuite) tryOpenState(c *gc.C, e apiAuthenticator, password string) error { 110 stateInfo := s.StateInfo(c) 111 stateInfo.Tag = e.Tag() 112 stateInfo.Password = password 113 st, err := state.Open(stateInfo, state.DialOpts{ 114 Timeout: 25 * time.Millisecond, 115 }, environs.NewStatePolicy()) 116 if err == nil { 117 st.Close() 118 } 119 return err 120 } 121 122 // openAs connects to the API state as the given entity 123 // with the default password for that entity. 124 func (s *baseSuite) openAs(c *gc.C, tag string) *api.State { 125 _, info, err := s.APIConn.Environ.StateInfo() 126 c.Assert(err, gc.IsNil) 127 info.Tag = tag 128 // Must match defaultPassword() 129 info.Password = fmt.Sprintf("%s password-1234567890", tag) 130 // Set this always, so that the login attempts as a machine will 131 // not fail with ErrNotProvisioned; it's not used otherwise. 132 info.Nonce = "fake_nonce" 133 c.Logf("opening state; entity %q; password %q", info.Tag, info.Password) 134 st, err := api.Open(info, api.DialOpts{}) 135 c.Assert(err, gc.IsNil) 136 c.Assert(st, gc.NotNil) 137 return st 138 } 139 140 // scenarioStatus describes the expected state 141 // of the juju environment set up by setUpScenario. 142 // 143 // NOTE: AgentState: "down", AgentStateInfo: "(started)" here is due 144 // to the scenario not calling SetAgentAlive on the respective entities, 145 // but this behavior is already tested in cmd/juju/status_test.go and 146 // also tested live and it works. 147 var scenarioStatus = &api.Status{ 148 EnvironmentName: "dummyenv", 149 Machines: map[string]api.MachineStatus{ 150 "0": { 151 Id: "0", 152 InstanceId: instance.Id("i-machine-0"), 153 AgentState: "down", 154 AgentStateInfo: "(started)", 155 Series: "quantal", 156 Containers: map[string]api.MachineStatus{}, 157 }, 158 "1": { 159 Id: "1", 160 InstanceId: instance.Id("i-machine-1"), 161 AgentState: "down", 162 AgentStateInfo: "(started)", 163 Series: "quantal", 164 Containers: map[string]api.MachineStatus{}, 165 }, 166 "2": { 167 Id: "2", 168 InstanceId: instance.Id("i-machine-2"), 169 AgentState: "down", 170 AgentStateInfo: "(started)", 171 Series: "quantal", 172 Containers: map[string]api.MachineStatus{}, 173 }, 174 }, 175 Services: map[string]api.ServiceStatus{ 176 "logging": api.ServiceStatus{ 177 Charm: "local:quantal/logging-1", 178 Relations: map[string][]string{ 179 "logging-directory": []string{"wordpress"}, 180 }, 181 SubordinateTo: []string{"wordpress"}, 182 }, 183 "mysql": api.ServiceStatus{ 184 Charm: "local:quantal/mysql-1", 185 Relations: map[string][]string{}, 186 SubordinateTo: []string{}, 187 Units: map[string]api.UnitStatus{}, 188 }, 189 "wordpress": api.ServiceStatus{ 190 Charm: "local:quantal/wordpress-3", 191 Relations: map[string][]string{ 192 "logging-dir": []string{"logging"}, 193 }, 194 SubordinateTo: []string{}, 195 Units: map[string]api.UnitStatus{ 196 "wordpress/0": api.UnitStatus{ 197 AgentState: "pending", 198 Machine: "1", 199 Subordinates: map[string]api.UnitStatus{ 200 "logging/0": api.UnitStatus{ 201 AgentState: "pending", 202 }, 203 }, 204 }, 205 "wordpress/1": api.UnitStatus{ 206 AgentState: "pending", 207 Machine: "2", 208 Subordinates: map[string]api.UnitStatus{ 209 "logging/1": api.UnitStatus{ 210 AgentState: "pending", 211 }, 212 }, 213 }, 214 }, 215 }, 216 }, 217 } 218 219 // setUpScenario makes an environment scenario suitable for 220 // testing most kinds of access scenario. It returns 221 // a list of all the entities in the scenario. 222 // 223 // When the scenario is initialized, we have: 224 // user-admin 225 // user-other 226 // machine-0 227 // instance-id="i-machine-0" 228 // nonce="fake_nonce" 229 // jobs=manage-environ 230 // status=started, info="" 231 // machine-1 232 // instance-id="i-machine-1" 233 // nonce="fake_nonce" 234 // jobs=host-units 235 // status=started, info="" 236 // constraints=mem=1G 237 // machine-2 238 // instance-id="i-machine-2" 239 // nonce="fake_nonce" 240 // jobs=host-units 241 // status=started, info="" 242 // service-wordpress 243 // service-logging 244 // unit-wordpress-0 245 // deployer-name=machine-1 246 // unit-logging-0 247 // deployer-name=unit-wordpress-0 248 // unit-wordpress-1 249 // deployer-name=machine-2 250 // unit-logging-1 251 // deployer-name=unit-wordpress-1 252 // 253 // The passwords for all returned entities are 254 // set to the entity name with a " password" suffix. 255 // 256 // Note that there is nothing special about machine-0 257 // here - it's the environment manager in this scenario 258 // just because machine 0 has traditionally been the 259 // environment manager (bootstrap machine), so is 260 // hopefully easier to remember as such. 261 func (s *baseSuite) setUpScenario(c *gc.C) (entities []string) { 262 add := func(e state.Entity) { 263 entities = append(entities, e.Tag()) 264 } 265 u, err := s.State.User("admin") 266 c.Assert(err, gc.IsNil) 267 setDefaultPassword(c, u) 268 add(u) 269 270 u, err = s.State.AddUser("other", "") 271 c.Assert(err, gc.IsNil) 272 setDefaultPassword(c, u) 273 add(u) 274 275 m, err := s.State.AddMachine("quantal", state.JobManageEnviron) 276 c.Assert(err, gc.IsNil) 277 c.Assert(m.Tag(), gc.Equals, "machine-0") 278 err = m.SetProvisioned(instance.Id("i-"+m.Tag()), "fake_nonce", nil) 279 c.Assert(err, gc.IsNil) 280 setDefaultPassword(c, m) 281 setDefaultStatus(c, m) 282 add(m) 283 s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 284 wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 285 s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) 286 eps, err := s.State.InferEndpoints([]string{"logging", "wordpress"}) 287 c.Assert(err, gc.IsNil) 288 rel, err := s.State.AddRelation(eps...) 289 c.Assert(err, gc.IsNil) 290 291 for i := 0; i < 2; i++ { 292 wu, err := wordpress.AddUnit() 293 c.Assert(err, gc.IsNil) 294 c.Assert(wu.Tag(), gc.Equals, fmt.Sprintf("unit-wordpress-%d", i)) 295 setDefaultPassword(c, wu) 296 add(wu) 297 298 m, err := s.State.AddMachine("quantal", state.JobHostUnits) 299 c.Assert(err, gc.IsNil) 300 c.Assert(m.Tag(), gc.Equals, fmt.Sprintf("machine-%d", i+1)) 301 if i == 1 { 302 err = m.SetConstraints(constraints.MustParse("mem=1G")) 303 c.Assert(err, gc.IsNil) 304 } 305 err = m.SetProvisioned(instance.Id("i-"+m.Tag()), "fake_nonce", nil) 306 c.Assert(err, gc.IsNil) 307 setDefaultPassword(c, m) 308 setDefaultStatus(c, m) 309 add(m) 310 311 err = wu.AssignToMachine(m) 312 c.Assert(err, gc.IsNil) 313 314 deployer, ok := wu.DeployerTag() 315 c.Assert(ok, gc.Equals, true) 316 c.Assert(deployer, gc.Equals, fmt.Sprintf("machine-%d", i+1)) 317 318 wru, err := rel.Unit(wu) 319 c.Assert(err, gc.IsNil) 320 321 // Create the subordinate unit as a side-effect of entering 322 // scope in the principal's relation-unit. 323 err = wru.EnterScope(nil) 324 c.Assert(err, gc.IsNil) 325 326 lu, err := s.State.Unit(fmt.Sprintf("logging/%d", i)) 327 c.Assert(err, gc.IsNil) 328 c.Assert(lu.IsPrincipal(), gc.Equals, false) 329 deployer, ok = lu.DeployerTag() 330 c.Assert(ok, gc.Equals, true) 331 c.Assert(deployer, gc.Equals, fmt.Sprintf("unit-wordpress-%d", i)) 332 setDefaultPassword(c, lu) 333 add(lu) 334 } 335 return 336 }