github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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/clock/testclock" 12 "github.com/juju/proxy" 13 jc "github.com/juju/testing/checkers" 14 "github.com/juju/utils" 15 gc "gopkg.in/check.v1" 16 "gopkg.in/juju/charm.v6" 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/core/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 application *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 *testclock.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.application = s.AddTestingApplication(c, "u", sch) 70 s.unit = s.AddUnit(c, s.application) 71 72 s.meteredCharm = s.AddTestingCharm(c, "metered") 73 meteredApplication := s.AddTestingApplication(c, "m", s.meteredCharm) 74 meteredUnit := s.addUnit(c, meteredApplication) 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: { 110 storageData0, 111 storage.StorageKindBlock, 112 "/dev/sdb", 113 }, 114 }, 115 } 116 117 s.clock = testclock.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(c, uuid.String(), relId, remoteName) 126 } 127 128 func (s *HookContextSuite) addUnit(c *gc.C, svc *state.Application) *state.Unit { 129 unit, err := svc.AddUnit(state.AddUnitParams{}) 130 c.Assert(err, jc.ErrorIsNil) 131 if s.machine != nil { 132 err = unit.AssignToMachine(s.machine) 133 c.Assert(err, jc.ErrorIsNil) 134 return unit 135 } 136 137 err = s.State.AssignUnit(unit, state.AssignCleanEmpty) 138 c.Assert(err, jc.ErrorIsNil) 139 machineId, err := unit.AssignedMachineId() 140 c.Assert(err, jc.ErrorIsNil) 141 s.machine, err = s.State.Machine(machineId) 142 c.Assert(err, jc.ErrorIsNil) 143 zone := "a-zone" 144 hwc := instance.HardwareCharacteristics{ 145 AvailabilityZone: &zone, 146 } 147 err = s.machine.SetProvisioned("i-exist", "", "fake_nonce", &hwc) 148 c.Assert(err, jc.ErrorIsNil) 149 return unit 150 } 151 152 func (s *HookContextSuite) AddUnit(c *gc.C, svc *state.Application) *state.Unit { 153 unit := s.addUnit(c, svc) 154 name := strings.Replace(unit.Name(), "/", "-", 1) 155 privateAddr := network.NewScopedAddress(name+".testing.invalid", network.ScopeCloudLocal) 156 err := s.machine.SetProviderAddresses(privateAddr) 157 c.Assert(err, jc.ErrorIsNil) 158 return unit 159 } 160 161 func (s *HookContextSuite) AddContextRelation(c *gc.C, name string) { 162 s.AddTestingApplication(c, name, s.relch) 163 eps, err := s.State.InferEndpoints("u", name) 164 c.Assert(err, jc.ErrorIsNil) 165 rel, err := s.State.AddRelation(eps...) 166 c.Assert(err, jc.ErrorIsNil) 167 ru, err := rel.Unit(s.unit) 168 c.Assert(err, jc.ErrorIsNil) 169 err = ru.EnterScope(map[string]interface{}{"relation-name": name}) 170 c.Assert(err, jc.ErrorIsNil) 171 s.relunits[rel.Id()] = ru 172 apiRel, err := s.uniter.Relation(rel.Tag().(names.RelationTag)) 173 c.Assert(err, jc.ErrorIsNil) 174 apiRelUnit, err := apiRel.Unit(s.apiUnit) 175 c.Assert(err, jc.ErrorIsNil) 176 s.apiRelunits[rel.Id()] = apiRelUnit 177 } 178 179 func (s *HookContextSuite) getHookContext(c *gc.C, uuid string, relid int, remote string) *context.HookContext { 180 if relid != -1 { 181 _, found := s.apiRelunits[relid] 182 c.Assert(found, jc.IsTrue) 183 } 184 facade, err := s.st.Uniter() 185 c.Assert(err, jc.ErrorIsNil) 186 187 relctxs := map[int]*context.ContextRelation{} 188 for relId, relUnit := range s.apiRelunits { 189 cache := context.NewRelationCache(relUnit.ReadSettings, nil) 190 relctxs[relId] = context.NewContextRelation(relUnit, cache) 191 } 192 193 env, err := s.State.Model() 194 c.Assert(err, jc.ErrorIsNil) 195 196 context, err := context.NewHookContext(s.apiUnit, facade, "TestCtx", uuid, 197 env.Name(), relid, remote, relctxs, apiAddrs, 198 noProxies, noProxies, false, nil, nil, s.machine.Tag().(names.MachineTag), 199 runnertesting.NewRealPaths(c), s.clock) 200 c.Assert(err, jc.ErrorIsNil) 201 return context 202 } 203 204 func (s *HookContextSuite) getMeteredHookContext(c *gc.C, uuid string, relid int, 205 remote string, canAddMetrics bool, metrics *charm.Metrics, paths runnertesting.RealPaths) *context.HookContext { 206 if relid != -1 { 207 _, found := s.apiRelunits[relid] 208 c.Assert(found, jc.IsTrue) 209 } 210 facade, err := s.st.Uniter() 211 c.Assert(err, jc.ErrorIsNil) 212 213 relctxs := map[int]*context.ContextRelation{} 214 for relId, relUnit := range s.apiRelunits { 215 cache := context.NewRelationCache(relUnit.ReadSettings, nil) 216 relctxs[relId] = context.NewContextRelation(relUnit, cache) 217 } 218 219 context, err := context.NewHookContext(s.meteredAPIUnit, facade, "TestCtx", uuid, 220 "test-model-name", relid, remote, relctxs, apiAddrs, 221 noProxies, noProxies, canAddMetrics, metrics, nil, s.machine.Tag().(names.MachineTag), 222 paths, s.clock) 223 c.Assert(err, jc.ErrorIsNil) 224 return context 225 } 226 227 func (s *HookContextSuite) metricsDefinition(name string) *charm.Metrics { 228 return &charm.Metrics{Metrics: map[string]charm.Metric{name: {Type: charm.MetricTypeGauge, Description: "generated metric"}}} 229 } 230 231 func (s *HookContextSuite) AssertCoreContext(c *gc.C, ctx *context.HookContext) { 232 c.Assert(ctx.UnitName(), gc.Equals, "u/0") 233 c.Assert(context.ContextMachineTag(ctx), jc.DeepEquals, names.NewMachineTag("0")) 234 235 expect, expectErr := s.unit.PrivateAddress() 236 actual, actualErr := ctx.PrivateAddress() 237 c.Assert(actual, gc.Equals, expect.Value) 238 c.Assert(actualErr, jc.DeepEquals, expectErr) 239 240 expect, expectErr = s.unit.PublicAddress() 241 actual, actualErr = ctx.PublicAddress() 242 c.Assert(actual, gc.Equals, expect.Value) 243 c.Assert(actualErr, jc.DeepEquals, expectErr) 244 245 env, err := s.State.Model() 246 c.Assert(err, jc.ErrorIsNil) 247 name, uuid := context.ContextEnvInfo(ctx) 248 c.Assert(name, gc.Equals, env.Name()) 249 c.Assert(uuid, gc.Equals, env.UUID()) 250 251 ids, err := ctx.RelationIds() 252 c.Assert(err, jc.ErrorIsNil) 253 c.Assert(ids, gc.HasLen, 2) 254 255 r, err := ctx.Relation(0) 256 c.Assert(err, jc.ErrorIsNil) 257 c.Assert(r.Name(), gc.Equals, "db") 258 c.Assert(r.FakeId(), gc.Equals, "db:0") 259 260 r, err = ctx.Relation(1) 261 c.Assert(err, jc.ErrorIsNil) 262 c.Assert(r.Name(), gc.Equals, "db") 263 c.Assert(r.FakeId(), gc.Equals, "db:1") 264 265 az, err := ctx.AvailabilityZone() 266 c.Assert(err, jc.ErrorIsNil) 267 c.Assert(az, gc.Equals, "a-zone") 268 } 269 270 func (s *HookContextSuite) AssertNotActionContext(c *gc.C, ctx *context.HookContext) { 271 actionData, err := ctx.ActionData() 272 c.Assert(actionData, gc.IsNil) 273 c.Assert(err, gc.ErrorMatches, "not running an action") 274 } 275 276 func (s *HookContextSuite) AssertActionContext(c *gc.C, ctx *context.HookContext) { 277 actionData, err := ctx.ActionData() 278 c.Assert(actionData, gc.NotNil) 279 c.Assert(err, jc.ErrorIsNil) 280 } 281 282 func (s *HookContextSuite) AssertNotStorageContext(c *gc.C, ctx *context.HookContext) { 283 storageAttachment, err := ctx.HookStorage() 284 c.Assert(storageAttachment, gc.IsNil) 285 c.Assert(err, gc.ErrorMatches, ".*") 286 } 287 288 func (s *HookContextSuite) AssertStorageContext(c *gc.C, ctx *context.HookContext, id string, attachment storage.StorageAttachmentInfo) { 289 fromCache, err := ctx.HookStorage() 290 c.Assert(err, jc.ErrorIsNil) 291 c.Assert(fromCache, gc.NotNil) 292 c.Assert(fromCache.Tag().Id(), gc.Equals, id) 293 c.Assert(fromCache.Kind(), gc.Equals, attachment.Kind) 294 c.Assert(fromCache.Location(), gc.Equals, attachment.Location) 295 } 296 297 func (s *HookContextSuite) AssertRelationContext(c *gc.C, ctx *context.HookContext, relId int, remoteUnit string) *context.ContextRelation { 298 actualRemoteUnit, _ := ctx.RemoteUnitName() 299 c.Assert(actualRemoteUnit, gc.Equals, remoteUnit) 300 rel, err := ctx.HookRelation() 301 c.Assert(err, jc.ErrorIsNil) 302 c.Assert(rel.Id(), gc.Equals, relId) 303 return rel.(*context.ContextRelation) 304 } 305 306 func (s *HookContextSuite) AssertNotRelationContext(c *gc.C, ctx *context.HookContext) { 307 rel, err := ctx.HookRelation() 308 c.Assert(rel, gc.IsNil) 309 c.Assert(err, gc.ErrorMatches, ".*") 310 } 311 312 type BlockHelper struct { 313 blockClient *block.Client 314 } 315 316 // NewBlockHelper creates a block switch used in testing 317 // to manage desired juju blocks. 318 func NewBlockHelper(st api.Connection) BlockHelper { 319 return BlockHelper{ 320 blockClient: block.NewClient(st), 321 } 322 } 323 324 // on switches on desired block and 325 // asserts that no errors were encountered. 326 func (s *BlockHelper) on(c *gc.C, blockType multiwatcher.BlockType, msg string) { 327 c.Assert(s.blockClient.SwitchBlockOn(string(blockType), msg), gc.IsNil) 328 } 329 330 // BlockAllChanges switches changes block on. 331 // This prevents all changes to juju environment. 332 func (s *BlockHelper) BlockAllChanges(c *gc.C, msg string) { 333 s.on(c, multiwatcher.BlockChange, msg) 334 } 335 336 // BlockRemoveObject switches remove block on. 337 // This prevents any object/entity removal on juju environment 338 func (s *BlockHelper) BlockRemoveObject(c *gc.C, msg string) { 339 s.on(c, multiwatcher.BlockRemove, msg) 340 } 341 342 // BlockDestroyModel switches destroy block on. 343 // This prevents juju environment destruction. 344 func (s *BlockHelper) BlockDestroyModel(c *gc.C, msg string) { 345 s.on(c, multiwatcher.BlockDestroy, msg) 346 } 347 348 func (s *BlockHelper) Close() { 349 s.blockClient.Close() 350 } 351 352 // MockEnvPaths implements Paths for tests that don't need to actually touch 353 // the filesystem. 354 type MockEnvPaths struct{} 355 356 func (MockEnvPaths) GetToolsDir() string { 357 return "path-to-tools" 358 } 359 360 func (MockEnvPaths) GetCharmDir() string { 361 return "path-to-charm" 362 } 363 364 func (MockEnvPaths) GetJujucSocket() string { 365 return "path-to-jujuc.socket" 366 } 367 368 func (MockEnvPaths) GetMetricsSpoolDir() string { 369 return "path-to-metrics-spool-dir" 370 } 371 372 func (MockEnvPaths) ComponentDir(name string) string { 373 return filepath.Join("path-to-base-dir", name) 374 }