github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/worker/uniter/runner/context/util_test.go (about) 1 // Copyright 2012-2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package context_test 5 6 import ( 7 "path/filepath" 8 "strings" 9 "time" 10 11 "github.com/juju/names" 12 jujutesting "github.com/juju/testing" 13 jc "github.com/juju/testing/checkers" 14 "github.com/juju/utils" 15 "github.com/juju/utils/proxy" 16 gc "gopkg.in/check.v1" 17 "gopkg.in/juju/charm.v6-unstable" 18 19 "github.com/juju/juju/api" 20 "github.com/juju/juju/api/block" 21 "github.com/juju/juju/api/uniter" 22 "github.com/juju/juju/instance" 23 "github.com/juju/juju/juju/testing" 24 "github.com/juju/juju/network" 25 "github.com/juju/juju/state" 26 "github.com/juju/juju/state/multiwatcher" 27 "github.com/juju/juju/storage" 28 coretesting "github.com/juju/juju/testing" 29 "github.com/juju/juju/worker/uniter/runner/context" 30 "github.com/juju/juju/worker/uniter/runner/jujuc" 31 runnertesting "github.com/juju/juju/worker/uniter/runner/testing" 32 ) 33 34 var noProxies = proxy.Settings{} 35 var apiAddrs = []string{"a1:123", "a2:123"} 36 var expectedApiAddrs = strings.Join(apiAddrs, " ") 37 38 // HookContextSuite contains shared setup for various other test suites. Test 39 // methods should not be added to this type, because they'll get run repeatedly. 40 type HookContextSuite struct { 41 testing.JujuConnSuite 42 service *state.Service 43 unit *state.Unit 44 machine *state.Machine 45 relch *state.Charm 46 relunits map[int]*state.RelationUnit 47 storage *runnertesting.StorageContextAccessor 48 clock *coretesting.Clock 49 50 st api.Connection 51 uniter *uniter.State 52 apiUnit *uniter.Unit 53 meteredApiUnit *uniter.Unit 54 meteredCharm *state.Charm 55 apiRelunits map[int]*uniter.RelationUnit 56 BlockHelper 57 } 58 59 func (s *HookContextSuite) SetUpTest(c *gc.C) { 60 var err error 61 s.JujuConnSuite.SetUpTest(c) 62 s.BlockHelper = NewBlockHelper(s.APIState) 63 c.Assert(s.BlockHelper, gc.NotNil) 64 s.AddCleanup(func(*gc.C) { s.BlockHelper.Close() }) 65 66 // reset 67 s.machine = nil 68 69 sch := s.AddTestingCharm(c, "wordpress") 70 s.service = s.AddTestingService(c, "u", sch) 71 s.unit = s.AddUnit(c, s.service) 72 73 s.meteredCharm = s.AddTestingCharm(c, "metered") 74 meteredService := s.AddTestingService(c, "m", s.meteredCharm) 75 meteredUnit := s.addUnit(c, meteredService) 76 err = meteredUnit.SetCharmURL(s.meteredCharm.URL()) 77 c.Assert(err, jc.ErrorIsNil) 78 79 password, err := utils.RandomPassword() 80 err = s.unit.SetPassword(password) 81 c.Assert(err, jc.ErrorIsNil) 82 s.st = s.OpenAPIAs(c, s.unit.Tag(), password) 83 s.uniter, err = s.st.Uniter() 84 c.Assert(err, jc.ErrorIsNil) 85 c.Assert(s.uniter, gc.NotNil) 86 s.apiUnit, err = s.uniter.Unit(s.unit.Tag().(names.UnitTag)) 87 c.Assert(err, jc.ErrorIsNil) 88 89 err = meteredUnit.SetPassword(password) 90 c.Assert(err, jc.ErrorIsNil) 91 meteredState := s.OpenAPIAs(c, meteredUnit.Tag(), password) 92 meteredUniter, err := meteredState.Uniter() 93 s.meteredApiUnit, err = meteredUniter.Unit(meteredUnit.Tag().(names.UnitTag)) 94 c.Assert(err, jc.ErrorIsNil) 95 96 // Note: The unit must always have a charm URL set, because this 97 // happens as part of the installation process (that happens 98 // before the initial install hook). 99 err = s.unit.SetCharmURL(sch.URL()) 100 c.Assert(err, jc.ErrorIsNil) 101 s.relch = s.AddTestingCharm(c, "mysql") 102 s.relunits = map[int]*state.RelationUnit{} 103 s.apiRelunits = map[int]*uniter.RelationUnit{} 104 s.AddContextRelation(c, "db0") 105 s.AddContextRelation(c, "db1") 106 107 storageData0 := names.NewStorageTag("data/0") 108 s.storage = &runnertesting.StorageContextAccessor{ 109 map[names.StorageTag]*runnertesting.ContextStorage{ 110 storageData0: &runnertesting.ContextStorage{ 111 storageData0, 112 storage.StorageKindBlock, 113 "/dev/sdb", 114 }, 115 }, 116 } 117 118 s.clock = coretesting.NewClock(time.Time{}) 119 } 120 121 func (s *HookContextSuite) GetContext( 122 c *gc.C, relId int, remoteName string, 123 ) jujuc.Context { 124 uuid, err := utils.NewUUID() 125 c.Assert(err, jc.ErrorIsNil) 126 return s.getHookContext( 127 c, uuid.String(), relId, remoteName, noProxies, 128 ) 129 } 130 131 func (s *HookContextSuite) addUnit(c *gc.C, svc *state.Service) *state.Unit { 132 unit, err := svc.AddUnit() 133 c.Assert(err, jc.ErrorIsNil) 134 if s.machine != nil { 135 err = unit.AssignToMachine(s.machine) 136 c.Assert(err, jc.ErrorIsNil) 137 return unit 138 } 139 140 err = s.State.AssignUnit(unit, state.AssignCleanEmpty) 141 c.Assert(err, jc.ErrorIsNil) 142 machineId, err := unit.AssignedMachineId() 143 c.Assert(err, jc.ErrorIsNil) 144 s.machine, err = s.State.Machine(machineId) 145 c.Assert(err, jc.ErrorIsNil) 146 zone := "a-zone" 147 hwc := instance.HardwareCharacteristics{ 148 AvailabilityZone: &zone, 149 } 150 err = s.machine.SetProvisioned("i-exist", "fake_nonce", &hwc) 151 c.Assert(err, jc.ErrorIsNil) 152 return unit 153 } 154 155 func (s *HookContextSuite) AddUnit(c *gc.C, svc *state.Service) *state.Unit { 156 unit := s.addUnit(c, svc) 157 name := strings.Replace(unit.Name(), "/", "-", 1) 158 privateAddr := network.NewScopedAddress(name+".testing.invalid", network.ScopeCloudLocal) 159 err := s.machine.SetProviderAddresses(privateAddr) 160 c.Assert(err, jc.ErrorIsNil) 161 return unit 162 } 163 164 func (s *HookContextSuite) AddContextRelation(c *gc.C, name string) { 165 s.AddTestingService(c, name, s.relch) 166 eps, err := s.State.InferEndpoints("u", name) 167 c.Assert(err, jc.ErrorIsNil) 168 rel, err := s.State.AddRelation(eps...) 169 c.Assert(err, jc.ErrorIsNil) 170 ru, err := rel.Unit(s.unit) 171 c.Assert(err, jc.ErrorIsNil) 172 err = ru.EnterScope(map[string]interface{}{"relation-name": name}) 173 c.Assert(err, jc.ErrorIsNil) 174 s.relunits[rel.Id()] = ru 175 apiRel, err := s.uniter.Relation(rel.Tag().(names.RelationTag)) 176 c.Assert(err, jc.ErrorIsNil) 177 apiRelUnit, err := apiRel.Unit(s.apiUnit) 178 c.Assert(err, jc.ErrorIsNil) 179 s.apiRelunits[rel.Id()] = apiRelUnit 180 } 181 182 func (s *HookContextSuite) getHookContext(c *gc.C, uuid string, relid int, 183 remote string, proxies proxy.Settings) *context.HookContext { 184 if relid != -1 { 185 _, found := s.apiRelunits[relid] 186 c.Assert(found, jc.IsTrue) 187 } 188 facade, err := s.st.Uniter() 189 c.Assert(err, jc.ErrorIsNil) 190 191 relctxs := map[int]*context.ContextRelation{} 192 for relId, relUnit := range s.apiRelunits { 193 cache := context.NewRelationCache(relUnit.ReadSettings, nil) 194 relctxs[relId] = context.NewContextRelation(relUnit, cache) 195 } 196 197 env, err := s.State.Model() 198 c.Assert(err, jc.ErrorIsNil) 199 200 context, err := context.NewHookContext(s.apiUnit, facade, "TestCtx", uuid, 201 env.Name(), relid, remote, relctxs, apiAddrs, 202 proxies, false, nil, nil, s.machine.Tag().(names.MachineTag), 203 runnertesting.NewRealPaths(c), s.clock) 204 c.Assert(err, jc.ErrorIsNil) 205 return context 206 } 207 208 func (s *HookContextSuite) getMeteredHookContext(c *gc.C, uuid string, relid int, 209 remote string, proxies proxy.Settings, canAddMetrics bool, metrics *charm.Metrics, paths runnertesting.RealPaths) *context.HookContext { 210 if relid != -1 { 211 _, found := s.apiRelunits[relid] 212 c.Assert(found, jc.IsTrue) 213 } 214 facade, err := s.st.Uniter() 215 c.Assert(err, jc.ErrorIsNil) 216 217 relctxs := map[int]*context.ContextRelation{} 218 for relId, relUnit := range s.apiRelunits { 219 cache := context.NewRelationCache(relUnit.ReadSettings, nil) 220 relctxs[relId] = context.NewContextRelation(relUnit, cache) 221 } 222 223 context, err := context.NewHookContext(s.meteredApiUnit, facade, "TestCtx", uuid, 224 "test-model-name", relid, remote, relctxs, apiAddrs, 225 proxies, canAddMetrics, metrics, nil, s.machine.Tag().(names.MachineTag), 226 paths, s.clock) 227 c.Assert(err, jc.ErrorIsNil) 228 return context 229 } 230 231 func (s *HookContextSuite) metricsDefinition(name string) *charm.Metrics { 232 return &charm.Metrics{Metrics: map[string]charm.Metric{name: {Type: charm.MetricTypeGauge, Description: "generated metric"}}} 233 } 234 235 func (s *HookContextSuite) AssertCoreContext(c *gc.C, ctx *context.HookContext) { 236 c.Assert(ctx.UnitName(), gc.Equals, "u/0") 237 c.Assert(context.ContextMachineTag(ctx), jc.DeepEquals, names.NewMachineTag("0")) 238 239 expect, expectErr := s.unit.PrivateAddress() 240 actual, actualErr := ctx.PrivateAddress() 241 c.Assert(actual, gc.Equals, expect.Value) 242 c.Assert(actualErr, jc.DeepEquals, expectErr) 243 244 expect, expectErr = s.unit.PublicAddress() 245 actual, actualErr = ctx.PublicAddress() 246 c.Assert(actual, gc.Equals, expect.Value) 247 c.Assert(actualErr, jc.DeepEquals, expectErr) 248 249 env, err := s.State.Model() 250 c.Assert(err, jc.ErrorIsNil) 251 name, uuid := context.ContextEnvInfo(ctx) 252 c.Assert(name, gc.Equals, env.Name()) 253 c.Assert(uuid, gc.Equals, env.UUID()) 254 255 ids, err := ctx.RelationIds() 256 c.Assert(err, jc.ErrorIsNil) 257 c.Assert(ids, gc.HasLen, 2) 258 259 r, err := ctx.Relation(0) 260 c.Assert(err, jc.ErrorIsNil) 261 c.Assert(r.Name(), gc.Equals, "db") 262 c.Assert(r.FakeId(), gc.Equals, "db:0") 263 264 r, err = ctx.Relation(1) 265 c.Assert(err, jc.ErrorIsNil) 266 c.Assert(r.Name(), gc.Equals, "db") 267 c.Assert(r.FakeId(), gc.Equals, "db:1") 268 269 az, err := ctx.AvailabilityZone() 270 c.Assert(err, jc.ErrorIsNil) 271 c.Assert(az, gc.Equals, "a-zone") 272 } 273 274 func (s *HookContextSuite) AssertNotActionContext(c *gc.C, ctx *context.HookContext) { 275 actionData, err := ctx.ActionData() 276 c.Assert(actionData, gc.IsNil) 277 c.Assert(err, gc.ErrorMatches, "not running an action") 278 } 279 280 func (s *HookContextSuite) AssertActionContext(c *gc.C, ctx *context.HookContext) { 281 actionData, err := ctx.ActionData() 282 c.Assert(actionData, gc.NotNil) 283 c.Assert(err, jc.ErrorIsNil) 284 } 285 286 func (s *HookContextSuite) AssertNotStorageContext(c *gc.C, ctx *context.HookContext) { 287 storageAttachment, err := ctx.HookStorage() 288 c.Assert(storageAttachment, gc.IsNil) 289 c.Assert(err, gc.ErrorMatches, ".*") 290 } 291 292 func (s *HookContextSuite) AssertStorageContext(c *gc.C, ctx *context.HookContext, id string, attachment storage.StorageAttachmentInfo) { 293 fromCache, err := ctx.HookStorage() 294 c.Assert(err, jc.ErrorIsNil) 295 c.Assert(fromCache, gc.NotNil) 296 c.Assert(fromCache.Tag().Id(), gc.Equals, id) 297 c.Assert(fromCache.Kind(), gc.Equals, attachment.Kind) 298 c.Assert(fromCache.Location(), gc.Equals, attachment.Location) 299 } 300 301 func (s *HookContextSuite) AssertRelationContext(c *gc.C, ctx *context.HookContext, relId int, remoteUnit string) *context.ContextRelation { 302 actualRemoteUnit, _ := ctx.RemoteUnitName() 303 c.Assert(actualRemoteUnit, gc.Equals, remoteUnit) 304 rel, err := ctx.HookRelation() 305 c.Assert(err, jc.ErrorIsNil) 306 c.Assert(rel.Id(), gc.Equals, relId) 307 return rel.(*context.ContextRelation) 308 } 309 310 func (s *HookContextSuite) AssertNotRelationContext(c *gc.C, ctx *context.HookContext) { 311 rel, err := ctx.HookRelation() 312 c.Assert(rel, gc.IsNil) 313 c.Assert(err, gc.ErrorMatches, ".*") 314 } 315 316 type BlockHelper struct { 317 blockClient *block.Client 318 } 319 320 // NewBlockHelper creates a block switch used in testing 321 // to manage desired juju blocks. 322 func NewBlockHelper(st api.Connection) BlockHelper { 323 return BlockHelper{ 324 blockClient: block.NewClient(st), 325 } 326 } 327 328 // on switches on desired block and 329 // asserts that no errors were encountered. 330 func (s *BlockHelper) on(c *gc.C, blockType multiwatcher.BlockType, msg string) { 331 c.Assert(s.blockClient.SwitchBlockOn(string(blockType), msg), gc.IsNil) 332 } 333 334 // BlockAllChanges switches changes block on. 335 // This prevents all changes to juju environment. 336 func (s *BlockHelper) BlockAllChanges(c *gc.C, msg string) { 337 s.on(c, multiwatcher.BlockChange, msg) 338 } 339 340 // BlockRemoveObject switches remove block on. 341 // This prevents any object/entity removal on juju environment 342 func (s *BlockHelper) BlockRemoveObject(c *gc.C, msg string) { 343 s.on(c, multiwatcher.BlockRemove, msg) 344 } 345 346 // BlockDestroyModel switches destroy block on. 347 // This prevents juju environment destruction. 348 func (s *BlockHelper) BlockDestroyModel(c *gc.C, msg string) { 349 s.on(c, multiwatcher.BlockDestroy, msg) 350 } 351 352 func (s *BlockHelper) Close() { 353 s.blockClient.Close() 354 } 355 356 // StubMetricsRecorder implements the MetricsRecorder interface. 357 type StubMetricsRecorder struct { 358 *jujutesting.Stub 359 } 360 361 // AddMetric implements the MetricsRecorder interface. 362 func (s StubMetricsRecorder) AddMetric(key, value string, created time.Time) error { 363 s.AddCall("AddMetric", key, value, created) 364 return nil 365 } 366 367 func (mr *StubMetricsRecorder) IsDeclaredMetric(key string) bool { 368 mr.MethodCall(mr, "IsDeclaredMetric", key) 369 return true 370 } 371 372 // Close implements the MetricsRecorder interface. 373 func (s StubMetricsRecorder) Close() error { 374 s.AddCall("Close") 375 return nil 376 } 377 378 // MockEnvPaths implements Paths for tests that don't need to actually touch 379 // the filesystem. 380 type MockEnvPaths struct{} 381 382 func (MockEnvPaths) GetToolsDir() string { 383 return "path-to-tools" 384 } 385 386 func (MockEnvPaths) GetCharmDir() string { 387 return "path-to-charm" 388 } 389 390 func (MockEnvPaths) GetJujucSocket() string { 391 return "path-to-jujuc.socket" 392 } 393 394 func (MockEnvPaths) GetMetricsSpoolDir() string { 395 return "path-to-metrics-spool-dir" 396 } 397 398 func (MockEnvPaths) ComponentDir(name string) string { 399 return filepath.Join("path-to-base-dir", name) 400 }