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