github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/uniter/runner/util_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package runner_test 5 6 import ( 7 "fmt" 8 "os" 9 "path/filepath" 10 "runtime" 11 "strings" 12 "time" 13 14 "github.com/juju/clock/testclock" 15 jc "github.com/juju/testing/checkers" 16 "github.com/juju/utils" 17 "github.com/juju/utils/fs" 18 gc "gopkg.in/check.v1" 19 "gopkg.in/juju/names.v2" 20 21 "github.com/juju/juju/api" 22 "github.com/juju/juju/api/uniter" 23 "github.com/juju/juju/core/instance" 24 "github.com/juju/juju/juju/testing" 25 "github.com/juju/juju/network" 26 "github.com/juju/juju/state" 27 "github.com/juju/juju/storage" 28 "github.com/juju/juju/testcharms" 29 "github.com/juju/juju/worker/uniter/runner" 30 "github.com/juju/juju/worker/uniter/runner/context" 31 runnertesting "github.com/juju/juju/worker/uniter/runner/testing" 32 ) 33 34 var apiAddrs = []string{"a1:123", "a2:123"} 35 36 type ContextSuite struct { 37 testing.JujuConnSuite 38 39 paths runnertesting.RealPaths 40 factory runner.Factory 41 contextFactory context.ContextFactory 42 membership map[int][]string 43 44 st api.Connection 45 model *state.Model 46 application *state.Application 47 machine *state.Machine 48 unit *state.Unit 49 uniter *uniter.State 50 apiUnit *uniter.Unit 51 storage *runnertesting.StorageContextAccessor 52 53 apiRelunits map[int]*uniter.RelationUnit 54 relch *state.Charm 55 relunits map[int]*state.RelationUnit 56 } 57 58 func (s *ContextSuite) SetUpTest(c *gc.C) { 59 s.JujuConnSuite.SetUpTest(c) 60 61 s.machine = nil 62 63 ch := s.AddTestingCharm(c, "wordpress") 64 s.application = s.AddTestingApplication(c, "u", ch) 65 s.unit = s.AddUnit(c, s.application) 66 67 storageData0 := names.NewStorageTag("data/0") 68 s.storage = &runnertesting.StorageContextAccessor{ 69 map[names.StorageTag]*runnertesting.ContextStorage{ 70 storageData0: { 71 storageData0, 72 storage.StorageKindBlock, 73 "/dev/sdb", 74 }, 75 }, 76 } 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 s.model, err = s.State.Model() 88 c.Assert(err, jc.ErrorIsNil) 89 90 s.paths = runnertesting.NewRealPaths(c) 91 s.membership = map[int][]string{} 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(ch.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 s.contextFactory, err = context.NewContextFactory(context.FactoryConfig{ 105 State: s.uniter, 106 UnitTag: s.unit.Tag().(names.UnitTag), 107 Tracker: runnertesting.FakeTracker{}, 108 GetRelationInfos: s.getRelationInfos, 109 Storage: s.storage, 110 Paths: s.paths, 111 Clock: testclock.NewClock(time.Time{}), 112 }) 113 c.Assert(err, jc.ErrorIsNil) 114 115 factory, err := runner.NewFactory( 116 s.uniter, 117 s.paths, 118 s.contextFactory, 119 ) 120 c.Assert(err, jc.ErrorIsNil) 121 s.factory = factory 122 } 123 124 func (s *ContextSuite) AddContextRelation(c *gc.C, name string) { 125 s.AddTestingApplication(c, name, s.relch) 126 eps, err := s.State.InferEndpoints("u", name) 127 c.Assert(err, jc.ErrorIsNil) 128 rel, err := s.State.AddRelation(eps...) 129 c.Assert(err, jc.ErrorIsNil) 130 ru, err := rel.Unit(s.unit) 131 c.Assert(err, jc.ErrorIsNil) 132 err = ru.EnterScope(map[string]interface{}{"relation-name": name}) 133 c.Assert(err, jc.ErrorIsNil) 134 s.relunits[rel.Id()] = ru 135 apiRel, err := s.uniter.Relation(rel.Tag().(names.RelationTag)) 136 c.Assert(err, jc.ErrorIsNil) 137 apiRelUnit, err := apiRel.Unit(s.apiUnit) 138 c.Assert(err, jc.ErrorIsNil) 139 s.apiRelunits[rel.Id()] = apiRelUnit 140 } 141 142 func (s *ContextSuite) AddUnit(c *gc.C, svc *state.Application) *state.Unit { 143 unit, err := svc.AddUnit(state.AddUnitParams{}) 144 c.Assert(err, jc.ErrorIsNil) 145 if s.machine != nil { 146 err = unit.AssignToMachine(s.machine) 147 c.Assert(err, jc.ErrorIsNil) 148 return unit 149 } 150 151 err = s.State.AssignUnit(unit, state.AssignCleanEmpty) 152 c.Assert(err, jc.ErrorIsNil) 153 machineId, err := unit.AssignedMachineId() 154 c.Assert(err, jc.ErrorIsNil) 155 s.machine, err = s.State.Machine(machineId) 156 c.Assert(err, jc.ErrorIsNil) 157 zone := "a-zone" 158 hwc := instance.HardwareCharacteristics{ 159 AvailabilityZone: &zone, 160 } 161 err = s.machine.SetProvisioned("i-exist", "", "fake_nonce", &hwc) 162 c.Assert(err, jc.ErrorIsNil) 163 164 name := strings.Replace(unit.Name(), "/", "-", 1) 165 privateAddr := network.NewScopedAddress(name+".testing.invalid", network.ScopeCloudLocal) 166 err = s.machine.SetProviderAddresses(privateAddr) 167 c.Assert(err, jc.ErrorIsNil) 168 return unit 169 } 170 171 func (s *ContextSuite) SetCharm(c *gc.C, name string) { 172 err := os.RemoveAll(s.paths.GetCharmDir()) 173 c.Assert(err, jc.ErrorIsNil) 174 err = fs.Copy(testcharms.Repo.CharmDirPath(name), s.paths.GetCharmDir()) 175 c.Assert(err, jc.ErrorIsNil) 176 } 177 178 func (s *ContextSuite) getRelationInfos() map[int]*context.RelationInfo { 179 info := map[int]*context.RelationInfo{} 180 for relId, relUnit := range s.apiRelunits { 181 info[relId] = &context.RelationInfo{ 182 RelationUnit: relUnit, 183 MemberNames: s.membership[relId], 184 } 185 } 186 return info 187 } 188 189 // hookSpec supports makeCharm. 190 type hookSpec struct { 191 // dir is the directory to create the hook in. 192 dir string 193 // name is the name of the hook. 194 name string 195 // perm is the file permissions of the hook. 196 perm os.FileMode 197 // code is the exit status of the hook. 198 code int 199 // stdout holds a string to print to stdout 200 stdout string 201 // stderr holds a string to print to stderr 202 stderr string 203 // background holds a string to print in the background after 0.2s. 204 background string 205 } 206 207 // makeCharm constructs a fake charm dir containing a single named hook 208 // with permissions perm and exit code code. If output is non-empty, 209 // the charm will write it to stdout and stderr, with each one prefixed 210 // by name of the stream. 211 func makeCharm(c *gc.C, spec hookSpec, charmDir string) { 212 dir := charmDir 213 if spec.dir != "" { 214 dir = filepath.Join(dir, spec.dir) 215 err := os.Mkdir(dir, 0755) 216 c.Assert(err, jc.ErrorIsNil) 217 } 218 c.Logf("openfile perm %v", spec.perm) 219 hook, err := os.OpenFile( 220 filepath.Join(dir, spec.name), os.O_CREATE|os.O_WRONLY, spec.perm, 221 ) 222 c.Assert(err, jc.ErrorIsNil) 223 defer func() { 224 c.Assert(hook.Close(), gc.IsNil) 225 }() 226 227 printf := func(f string, a ...interface{}) { 228 _, err := fmt.Fprintf(hook, f+"\n", a...) 229 c.Assert(err, jc.ErrorIsNil) 230 } 231 if runtime.GOOS != "windows" { 232 printf("#!/bin/bash") 233 } 234 printf(echoPidScript) 235 if spec.stdout != "" { 236 printf("echo %s", spec.stdout) 237 } 238 if spec.stderr != "" { 239 printf("echo %s >&2", spec.stderr) 240 } 241 if spec.background != "" { 242 // Print something fairly quickly, then sleep for 243 // quite a long time - if the hook execution is 244 // blocking because of the background process, 245 // the hook execution will take much longer than 246 // expected. 247 printf("(sleep 0.2; echo %s; sleep 10) &", spec.background) 248 } 249 printf("exit %d", spec.code) 250 }