github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/state/migration_export_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state_test 5 6 import ( 7 "math/rand" 8 "time" 9 10 "github.com/juju/names" 11 jc "github.com/juju/testing/checkers" 12 "github.com/juju/version" 13 gc "gopkg.in/check.v1" 14 15 "github.com/juju/juju/constraints" 16 "github.com/juju/juju/core/description" 17 "github.com/juju/juju/state" 18 "github.com/juju/juju/status" 19 "github.com/juju/juju/testing/factory" 20 ) 21 22 // Constraints stores megabytes by default for memory and root disk. 23 const ( 24 gig uint64 = 1024 25 26 addedHistoryCount = 5 27 // 6 for the one initial + 5 added. 28 expectedHistoryCount = addedHistoryCount + 1 29 ) 30 31 var testAnnotations = map[string]string{ 32 "string": "value", 33 "another": "one", 34 } 35 36 type MigrationSuite struct { 37 ConnSuite 38 } 39 40 func (s *MigrationSuite) setLatestTools(c *gc.C, latestTools version.Number) { 41 dbModel, err := s.State.Model() 42 c.Assert(err, jc.ErrorIsNil) 43 err = dbModel.UpdateLatestToolsVersion(latestTools) 44 c.Assert(err, jc.ErrorIsNil) 45 } 46 47 func (s *MigrationSuite) setRandSequenceValue(c *gc.C, name string) int { 48 var value int 49 var err error 50 count := rand.Intn(5) + 1 51 for i := 0; i < count; i++ { 52 value, err = state.Sequence(s.State, name) 53 c.Assert(err, jc.ErrorIsNil) 54 } 55 // The value stored in the doc is one higher than what it returns. 56 return value + 1 57 } 58 59 func (s *MigrationSuite) primeStatusHistory(c *gc.C, entity statusSetter, statusVal status.Status, count int) { 60 primeStatusHistory(c, entity, statusVal, count, func(i int) map[string]interface{} { 61 return map[string]interface{}{"index": count - i} 62 }) 63 } 64 65 func (s *MigrationSuite) makeServiceWithLeader(c *gc.C, serviceName string, count int, leader int) { 66 c.Assert(leader < count, jc.IsTrue) 67 units := make([]*state.Unit, count) 68 service := s.Factory.MakeService(c, &factory.ServiceParams{ 69 Name: serviceName, 70 Charm: s.Factory.MakeCharm(c, &factory.CharmParams{ 71 Name: serviceName, 72 }), 73 }) 74 for i := 0; i < count; i++ { 75 units[i] = s.Factory.MakeUnit(c, &factory.UnitParams{ 76 Service: service, 77 }) 78 } 79 err := s.State.LeadershipClaimer().ClaimLeadership( 80 service.Name(), 81 units[leader].Name(), 82 time.Minute) 83 c.Assert(err, jc.ErrorIsNil) 84 } 85 86 type MigrationExportSuite struct { 87 MigrationSuite 88 } 89 90 var _ = gc.Suite(&MigrationExportSuite{}) 91 92 func (s *MigrationExportSuite) checkStatusHistory(c *gc.C, history []description.Status, statusVal status.Status) { 93 for i, st := range history { 94 c.Logf("status history #%d: %s", i, st.Updated()) 95 c.Check(st.Value(), gc.Equals, string(statusVal)) 96 c.Check(st.Message(), gc.Equals, "") 97 c.Check(st.Data(), jc.DeepEquals, map[string]interface{}{"index": i + 1}) 98 } 99 } 100 101 func (s *MigrationExportSuite) TestModelInfo(c *gc.C) { 102 stModel, err := s.State.Model() 103 c.Assert(err, jc.ErrorIsNil) 104 err = s.State.SetAnnotations(stModel, testAnnotations) 105 c.Assert(err, jc.ErrorIsNil) 106 latestTools := version.MustParse("2.0.1") 107 s.setLatestTools(c, latestTools) 108 err = s.State.SetModelConstraints(constraints.MustParse("arch=amd64 mem=8G")) 109 c.Assert(err, jc.ErrorIsNil) 110 machineSeq := s.setRandSequenceValue(c, "machine") 111 fooSeq := s.setRandSequenceValue(c, "service-foo") 112 s.State.SwitchBlockOn(state.ChangeBlock, "locked down") 113 114 model, err := s.State.Export() 115 c.Assert(err, jc.ErrorIsNil) 116 117 dbModel, err := s.State.Model() 118 c.Assert(err, jc.ErrorIsNil) 119 c.Assert(model.Tag(), gc.Equals, dbModel.ModelTag()) 120 c.Assert(model.Owner(), gc.Equals, dbModel.Owner()) 121 config, err := dbModel.Config() 122 c.Assert(err, jc.ErrorIsNil) 123 c.Assert(model.Config(), jc.DeepEquals, config.AllAttrs()) 124 c.Assert(model.LatestToolsVersion(), gc.Equals, latestTools) 125 c.Assert(model.Annotations(), jc.DeepEquals, testAnnotations) 126 constraints := model.Constraints() 127 c.Assert(constraints, gc.NotNil) 128 c.Assert(constraints.Architecture(), gc.Equals, "amd64") 129 c.Assert(constraints.Memory(), gc.Equals, 8*gig) 130 c.Assert(model.Sequences(), jc.DeepEquals, map[string]int{ 131 "machine": machineSeq, 132 "service-foo": fooSeq, 133 // blocks is added by the switch block on call above. 134 "block": 1, 135 }) 136 c.Assert(model.Blocks(), jc.DeepEquals, map[string]string{ 137 "all-changes": "locked down", 138 }) 139 } 140 141 func (s *MigrationExportSuite) TestModelUsers(c *gc.C) { 142 // Make sure we have some last connection times for the admin user, 143 // and create a few other users. 144 lastConnection := state.NowToTheSecond() 145 owner, err := s.State.ModelUser(s.Owner) 146 c.Assert(err, jc.ErrorIsNil) 147 err = state.UpdateModelUserLastConnection(owner, lastConnection) 148 c.Assert(err, jc.ErrorIsNil) 149 150 bobTag := names.NewUserTag("bob@external") 151 bob, err := s.State.AddModelUser(state.ModelUserSpec{ 152 User: bobTag, 153 CreatedBy: s.Owner, 154 Access: state.ModelReadAccess, 155 }) 156 c.Assert(err, jc.ErrorIsNil) 157 err = state.UpdateModelUserLastConnection(bob, lastConnection) 158 c.Assert(err, jc.ErrorIsNil) 159 160 model, err := s.State.Export() 161 c.Assert(err, jc.ErrorIsNil) 162 163 users := model.Users() 164 c.Assert(users, gc.HasLen, 2) 165 166 exportedBob := users[0] 167 // admin is "test-admin", and results are sorted 168 exportedAdmin := users[1] 169 170 c.Assert(exportedAdmin.Name(), gc.Equals, s.Owner) 171 c.Assert(exportedAdmin.DisplayName(), gc.Equals, owner.DisplayName()) 172 c.Assert(exportedAdmin.CreatedBy(), gc.Equals, s.Owner) 173 c.Assert(exportedAdmin.DateCreated(), gc.Equals, owner.DateCreated()) 174 c.Assert(exportedAdmin.LastConnection(), gc.Equals, lastConnection) 175 c.Assert(exportedAdmin.ReadOnly(), jc.IsFalse) 176 177 c.Assert(exportedBob.Name(), gc.Equals, bobTag) 178 c.Assert(exportedBob.DisplayName(), gc.Equals, "") 179 c.Assert(exportedBob.CreatedBy(), gc.Equals, s.Owner) 180 c.Assert(exportedBob.DateCreated(), gc.Equals, bob.DateCreated()) 181 c.Assert(exportedBob.LastConnection(), gc.Equals, lastConnection) 182 c.Assert(exportedBob.ReadOnly(), jc.IsTrue) 183 } 184 185 func (s *MigrationExportSuite) TestMachines(c *gc.C) { 186 // Add a machine with an LXC container. 187 machine1 := s.Factory.MakeMachine(c, &factory.MachineParams{ 188 Constraints: constraints.MustParse("arch=amd64 mem=8G"), 189 }) 190 nested := s.Factory.MakeMachineNested(c, machine1.Id(), nil) 191 err := s.State.SetAnnotations(machine1, testAnnotations) 192 c.Assert(err, jc.ErrorIsNil) 193 s.primeStatusHistory(c, machine1, status.StatusStarted, addedHistoryCount) 194 195 model, err := s.State.Export() 196 c.Assert(err, jc.ErrorIsNil) 197 198 machines := model.Machines() 199 c.Assert(machines, gc.HasLen, 1) 200 201 exported := machines[0] 202 c.Assert(exported.Tag(), gc.Equals, machine1.MachineTag()) 203 c.Assert(exported.Series(), gc.Equals, machine1.Series()) 204 c.Assert(exported.Annotations(), jc.DeepEquals, testAnnotations) 205 constraints := exported.Constraints() 206 c.Assert(constraints, gc.NotNil) 207 c.Assert(constraints.Architecture(), gc.Equals, "amd64") 208 c.Assert(constraints.Memory(), gc.Equals, 8*gig) 209 210 tools, err := machine1.AgentTools() 211 c.Assert(err, jc.ErrorIsNil) 212 exTools := exported.Tools() 213 c.Assert(exTools, gc.NotNil) 214 c.Assert(exTools.Version(), jc.DeepEquals, tools.Version) 215 216 history := exported.StatusHistory() 217 c.Assert(history, gc.HasLen, expectedHistoryCount) 218 s.checkStatusHistory(c, history[:addedHistoryCount], status.StatusStarted) 219 220 containers := exported.Containers() 221 c.Assert(containers, gc.HasLen, 1) 222 container := containers[0] 223 c.Assert(container.Tag(), gc.Equals, nested.MachineTag()) 224 } 225 226 func (s *MigrationExportSuite) TestServices(c *gc.C) { 227 service := s.Factory.MakeService(c, &factory.ServiceParams{ 228 Settings: map[string]interface{}{ 229 "foo": "bar", 230 }, 231 Constraints: constraints.MustParse("arch=amd64 mem=8G"), 232 }) 233 err := service.UpdateLeaderSettings(&goodToken{}, map[string]string{ 234 "leader": "true", 235 }) 236 c.Assert(err, jc.ErrorIsNil) 237 err = service.SetMetricCredentials([]byte("sekrit")) 238 c.Assert(err, jc.ErrorIsNil) 239 err = s.State.SetAnnotations(service, testAnnotations) 240 c.Assert(err, jc.ErrorIsNil) 241 s.primeStatusHistory(c, service, status.StatusActive, addedHistoryCount) 242 243 model, err := s.State.Export() 244 c.Assert(err, jc.ErrorIsNil) 245 246 services := model.Services() 247 c.Assert(services, gc.HasLen, 1) 248 249 exported := services[0] 250 c.Assert(exported.Name(), gc.Equals, service.Name()) 251 c.Assert(exported.Tag(), gc.Equals, service.ServiceTag()) 252 c.Assert(exported.Series(), gc.Equals, service.Series()) 253 c.Assert(exported.Annotations(), jc.DeepEquals, testAnnotations) 254 255 c.Assert(exported.Settings(), jc.DeepEquals, map[string]interface{}{ 256 "foo": "bar", 257 }) 258 c.Assert(exported.SettingsRefCount(), gc.Equals, 1) 259 c.Assert(exported.LeadershipSettings(), jc.DeepEquals, map[string]interface{}{ 260 "leader": "true", 261 }) 262 c.Assert(exported.MetricsCredentials(), jc.DeepEquals, []byte("sekrit")) 263 264 constraints := exported.Constraints() 265 c.Assert(constraints, gc.NotNil) 266 c.Assert(constraints.Architecture(), gc.Equals, "amd64") 267 c.Assert(constraints.Memory(), gc.Equals, 8*gig) 268 269 history := exported.StatusHistory() 270 c.Assert(history, gc.HasLen, expectedHistoryCount) 271 s.checkStatusHistory(c, history[:addedHistoryCount], status.StatusActive) 272 } 273 274 func (s *MigrationExportSuite) TestMultipleServices(c *gc.C) { 275 s.Factory.MakeService(c, &factory.ServiceParams{Name: "first"}) 276 s.Factory.MakeService(c, &factory.ServiceParams{Name: "second"}) 277 s.Factory.MakeService(c, &factory.ServiceParams{Name: "third"}) 278 279 model, err := s.State.Export() 280 c.Assert(err, jc.ErrorIsNil) 281 282 services := model.Services() 283 c.Assert(services, gc.HasLen, 3) 284 } 285 286 func (s *MigrationExportSuite) TestUnits(c *gc.C) { 287 unit := s.Factory.MakeUnit(c, &factory.UnitParams{ 288 Constraints: constraints.MustParse("arch=amd64 mem=8G"), 289 }) 290 err := unit.SetMeterStatus("GREEN", "some info") 291 c.Assert(err, jc.ErrorIsNil) 292 err = s.State.SetAnnotations(unit, testAnnotations) 293 c.Assert(err, jc.ErrorIsNil) 294 s.primeStatusHistory(c, unit, status.StatusActive, addedHistoryCount) 295 s.primeStatusHistory(c, unit.Agent(), status.StatusIdle, addedHistoryCount) 296 297 model, err := s.State.Export() 298 c.Assert(err, jc.ErrorIsNil) 299 300 services := model.Services() 301 c.Assert(services, gc.HasLen, 1) 302 303 service := services[0] 304 units := service.Units() 305 c.Assert(units, gc.HasLen, 1) 306 307 exported := units[0] 308 309 c.Assert(exported.Name(), gc.Equals, unit.Name()) 310 c.Assert(exported.Tag(), gc.Equals, unit.UnitTag()) 311 c.Assert(exported.Validate(), jc.ErrorIsNil) 312 c.Assert(exported.MeterStatusCode(), gc.Equals, "GREEN") 313 c.Assert(exported.MeterStatusInfo(), gc.Equals, "some info") 314 c.Assert(exported.Annotations(), jc.DeepEquals, testAnnotations) 315 constraints := exported.Constraints() 316 c.Assert(constraints, gc.NotNil) 317 c.Assert(constraints.Architecture(), gc.Equals, "amd64") 318 c.Assert(constraints.Memory(), gc.Equals, 8*gig) 319 320 workloadHistory := exported.WorkloadStatusHistory() 321 c.Assert(workloadHistory, gc.HasLen, expectedHistoryCount) 322 s.checkStatusHistory(c, workloadHistory[:addedHistoryCount], status.StatusActive) 323 324 agentHistory := exported.AgentStatusHistory() 325 c.Assert(agentHistory, gc.HasLen, expectedHistoryCount) 326 s.checkStatusHistory(c, agentHistory[:addedHistoryCount], status.StatusIdle) 327 } 328 329 func (s *MigrationExportSuite) TestServiceLeadership(c *gc.C) { 330 s.makeServiceWithLeader(c, "mysql", 2, 1) 331 s.makeServiceWithLeader(c, "wordpress", 4, 2) 332 333 model, err := s.State.Export() 334 c.Assert(err, jc.ErrorIsNil) 335 336 leaders := make(map[string]string) 337 for _, service := range model.Services() { 338 leaders[service.Name()] = service.Leader() 339 } 340 c.Assert(leaders, jc.DeepEquals, map[string]string{ 341 "mysql": "mysql/1", 342 "wordpress": "wordpress/2", 343 }) 344 } 345 346 func (s *MigrationExportSuite) TestUnitsOpenPorts(c *gc.C) { 347 unit := s.Factory.MakeUnit(c, nil) 348 err := unit.OpenPorts("tcp", 1234, 2345) 349 c.Assert(err, jc.ErrorIsNil) 350 351 model, err := s.State.Export() 352 c.Assert(err, jc.ErrorIsNil) 353 354 machines := model.Machines() 355 c.Assert(machines, gc.HasLen, 1) 356 357 ports := machines[0].OpenedPorts() 358 c.Assert(ports, gc.HasLen, 1) 359 360 port := ports[0] 361 c.Assert(port.SubnetID(), gc.Equals, "") 362 opened := port.OpenPorts() 363 c.Assert(opened, gc.HasLen, 1) 364 c.Assert(opened[0].UnitName(), gc.Equals, unit.Name()) 365 } 366 367 func (s *MigrationExportSuite) TestRelations(c *gc.C) { 368 // Need to remove owner from service. 369 ignored := s.Owner 370 wordpress := state.AddTestingService(c, s.State, "wordpress", state.AddTestingCharm(c, s.State, "wordpress"), ignored) 371 mysql := state.AddTestingService(c, s.State, "mysql", state.AddTestingCharm(c, s.State, "mysql"), ignored) 372 // InferEndpoints will always return provider, requirer 373 eps, err := s.State.InferEndpoints("mysql", "wordpress") 374 c.Assert(err, jc.ErrorIsNil) 375 rel, err := s.State.AddRelation(eps...) 376 msEp, wpEp := eps[0], eps[1] 377 c.Assert(err, jc.ErrorIsNil) 378 wordpress_0 := s.Factory.MakeUnit(c, &factory.UnitParams{Service: wordpress}) 379 mysql_0 := s.Factory.MakeUnit(c, &factory.UnitParams{Service: mysql}) 380 381 ru, err := rel.Unit(wordpress_0) 382 c.Assert(err, jc.ErrorIsNil) 383 wordpressSettings := map[string]interface{}{ 384 "name": "wordpress/0", 385 } 386 err = ru.EnterScope(wordpressSettings) 387 c.Assert(err, jc.ErrorIsNil) 388 389 ru, err = rel.Unit(mysql_0) 390 c.Assert(err, jc.ErrorIsNil) 391 mysqlSettings := map[string]interface{}{ 392 "name": "mysql/0", 393 } 394 err = ru.EnterScope(mysqlSettings) 395 c.Assert(err, jc.ErrorIsNil) 396 397 model, err := s.State.Export() 398 c.Assert(err, jc.ErrorIsNil) 399 400 rels := model.Relations() 401 c.Assert(rels, gc.HasLen, 1) 402 403 exRel := rels[0] 404 c.Assert(exRel.Id(), gc.Equals, rel.Id()) 405 c.Assert(exRel.Key(), gc.Equals, rel.String()) 406 407 exEps := exRel.Endpoints() 408 c.Assert(exEps, gc.HasLen, 2) 409 410 checkEndpoint := func( 411 exEndpoint description.Endpoint, 412 unitName string, 413 ep state.Endpoint, 414 settings map[string]interface{}, 415 ) { 416 c.Logf("%#v", exEndpoint) 417 c.Check(exEndpoint.ServiceName(), gc.Equals, ep.ServiceName) 418 c.Check(exEndpoint.Name(), gc.Equals, ep.Name) 419 c.Check(exEndpoint.UnitCount(), gc.Equals, 1) 420 c.Check(exEndpoint.Settings(unitName), jc.DeepEquals, settings) 421 c.Check(exEndpoint.Role(), gc.Equals, string(ep.Role)) 422 c.Check(exEndpoint.Interface(), gc.Equals, ep.Interface) 423 c.Check(exEndpoint.Optional(), gc.Equals, ep.Optional) 424 c.Check(exEndpoint.Limit(), gc.Equals, ep.Limit) 425 c.Check(exEndpoint.Scope(), gc.Equals, string(ep.Scope)) 426 } 427 checkEndpoint(exEps[0], mysql_0.Name(), msEp, mysqlSettings) 428 checkEndpoint(exEps[1], wordpress_0.Name(), wpEp, wordpressSettings) 429 } 430 431 type goodToken struct{} 432 433 // Check implements leadership.Token 434 func (*goodToken) Check(interface{}) error { 435 return nil 436 }