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