github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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 jujutesting "github.com/juju/testing" 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/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 service *state.Application 46 machine *state.Machine 47 unit *state.Unit 48 uniter *uniter.State 49 apiUnit *uniter.Unit 50 storage *runnertesting.StorageContextAccessor 51 52 apiRelunits map[int]*uniter.RelationUnit 53 relch *state.Charm 54 relunits map[int]*state.RelationUnit 55 } 56 57 func (s *ContextSuite) SetUpTest(c *gc.C) { 58 s.JujuConnSuite.SetUpTest(c) 59 60 s.machine = nil 61 62 ch := s.AddTestingCharm(c, "wordpress") 63 s.service = s.AddTestingService(c, "u", ch) 64 s.unit = s.AddUnit(c, s.service) 65 66 storageData0 := names.NewStorageTag("data/0") 67 s.storage = &runnertesting.StorageContextAccessor{ 68 map[names.StorageTag]*runnertesting.ContextStorage{ 69 storageData0: &runnertesting.ContextStorage{ 70 storageData0, 71 storage.StorageKindBlock, 72 "/dev/sdb", 73 }, 74 }, 75 } 76 77 password, err := utils.RandomPassword() 78 err = s.unit.SetPassword(password) 79 c.Assert(err, jc.ErrorIsNil) 80 s.st = s.OpenAPIAs(c, s.unit.Tag(), password) 81 s.uniter, err = s.st.Uniter() 82 c.Assert(err, jc.ErrorIsNil) 83 c.Assert(s.uniter, gc.NotNil) 84 s.apiUnit, err = s.uniter.Unit(s.unit.Tag().(names.UnitTag)) 85 c.Assert(err, jc.ErrorIsNil) 86 87 s.paths = runnertesting.NewRealPaths(c) 88 s.membership = map[int][]string{} 89 90 // Note: The unit must always have a charm URL set, because this 91 // happens as part of the installation process (that happens 92 // before the initial install hook). 93 err = s.unit.SetCharmURL(ch.URL()) 94 c.Assert(err, jc.ErrorIsNil) 95 s.relch = s.AddTestingCharm(c, "mysql") 96 s.relunits = map[int]*state.RelationUnit{} 97 s.apiRelunits = map[int]*uniter.RelationUnit{} 98 s.AddContextRelation(c, "db0") 99 s.AddContextRelation(c, "db1") 100 101 s.contextFactory, err = context.NewContextFactory( 102 s.uniter, 103 s.unit.Tag().(names.UnitTag), 104 runnertesting.FakeTracker{}, 105 s.getRelationInfos, 106 s.storage, 107 s.paths, 108 jujutesting.NewClock(time.Time{}), 109 ) 110 c.Assert(err, jc.ErrorIsNil) 111 112 factory, err := runner.NewFactory( 113 s.uniter, 114 s.paths, 115 s.contextFactory, 116 ) 117 c.Assert(err, jc.ErrorIsNil) 118 s.factory = factory 119 } 120 121 func (s *ContextSuite) AddContextRelation(c *gc.C, name string) { 122 s.AddTestingService(c, name, s.relch) 123 eps, err := s.State.InferEndpoints("u", name) 124 c.Assert(err, jc.ErrorIsNil) 125 rel, err := s.State.AddRelation(eps...) 126 c.Assert(err, jc.ErrorIsNil) 127 ru, err := rel.Unit(s.unit) 128 c.Assert(err, jc.ErrorIsNil) 129 err = ru.EnterScope(map[string]interface{}{"relation-name": name}) 130 c.Assert(err, jc.ErrorIsNil) 131 s.relunits[rel.Id()] = ru 132 apiRel, err := s.uniter.Relation(rel.Tag().(names.RelationTag)) 133 c.Assert(err, jc.ErrorIsNil) 134 apiRelUnit, err := apiRel.Unit(s.apiUnit) 135 c.Assert(err, jc.ErrorIsNil) 136 s.apiRelunits[rel.Id()] = apiRelUnit 137 } 138 139 func (s *ContextSuite) AddUnit(c *gc.C, svc *state.Application) *state.Unit { 140 unit, err := svc.AddUnit() 141 c.Assert(err, jc.ErrorIsNil) 142 if s.machine != nil { 143 err = unit.AssignToMachine(s.machine) 144 c.Assert(err, jc.ErrorIsNil) 145 return unit 146 } 147 148 err = s.State.AssignUnit(unit, state.AssignCleanEmpty) 149 c.Assert(err, jc.ErrorIsNil) 150 machineId, err := unit.AssignedMachineId() 151 c.Assert(err, jc.ErrorIsNil) 152 s.machine, err = s.State.Machine(machineId) 153 c.Assert(err, jc.ErrorIsNil) 154 zone := "a-zone" 155 hwc := instance.HardwareCharacteristics{ 156 AvailabilityZone: &zone, 157 } 158 err = s.machine.SetProvisioned("i-exist", "fake_nonce", &hwc) 159 c.Assert(err, jc.ErrorIsNil) 160 161 name := strings.Replace(unit.Name(), "/", "-", 1) 162 privateAddr := network.NewScopedAddress(name+".testing.invalid", network.ScopeCloudLocal) 163 err = s.machine.SetProviderAddresses(privateAddr) 164 c.Assert(err, jc.ErrorIsNil) 165 return unit 166 } 167 168 func (s *ContextSuite) SetCharm(c *gc.C, name string) { 169 err := os.RemoveAll(s.paths.GetCharmDir()) 170 c.Assert(err, jc.ErrorIsNil) 171 err = fs.Copy(testcharms.Repo.CharmDirPath(name), s.paths.GetCharmDir()) 172 c.Assert(err, jc.ErrorIsNil) 173 } 174 175 func (s *ContextSuite) getRelationInfos() map[int]*context.RelationInfo { 176 info := map[int]*context.RelationInfo{} 177 for relId, relUnit := range s.apiRelunits { 178 info[relId] = &context.RelationInfo{ 179 RelationUnit: relUnit, 180 MemberNames: s.membership[relId], 181 } 182 } 183 return info 184 } 185 186 // hookSpec supports makeCharm. 187 type hookSpec struct { 188 // dir is the directory to create the hook in. 189 dir string 190 // name is the name of the hook. 191 name string 192 // perm is the file permissions of the hook. 193 perm os.FileMode 194 // code is the exit status of the hook. 195 code int 196 // stdout holds a string to print to stdout 197 stdout string 198 // stderr holds a string to print to stderr 199 stderr string 200 // background holds a string to print in the background after 0.2s. 201 background string 202 } 203 204 // makeCharm constructs a fake charm dir containing a single named hook 205 // with permissions perm and exit code code. If output is non-empty, 206 // the charm will write it to stdout and stderr, with each one prefixed 207 // by name of the stream. 208 func makeCharm(c *gc.C, spec hookSpec, charmDir string) { 209 dir := charmDir 210 if spec.dir != "" { 211 dir = filepath.Join(dir, spec.dir) 212 err := os.Mkdir(dir, 0755) 213 c.Assert(err, jc.ErrorIsNil) 214 } 215 c.Logf("openfile perm %v", spec.perm) 216 hook, err := os.OpenFile( 217 filepath.Join(dir, spec.name), os.O_CREATE|os.O_WRONLY, spec.perm, 218 ) 219 c.Assert(err, jc.ErrorIsNil) 220 defer func() { 221 c.Assert(hook.Close(), gc.IsNil) 222 }() 223 224 printf := func(f string, a ...interface{}) { 225 _, err := fmt.Fprintf(hook, f+"\n", a...) 226 c.Assert(err, jc.ErrorIsNil) 227 } 228 if runtime.GOOS != "windows" { 229 printf("#!/bin/bash") 230 } 231 printf(echoPidScript) 232 if spec.stdout != "" { 233 printf("echo %s", spec.stdout) 234 } 235 if spec.stderr != "" { 236 printf("echo %s >&2", spec.stderr) 237 } 238 if spec.background != "" { 239 // Print something fairly quickly, then sleep for 240 // quite a long time - if the hook execution is 241 // blocking because of the background process, 242 // the hook execution will take much longer than 243 // expected. 244 printf("(sleep 0.2; echo %s; sleep 10) &", spec.background) 245 } 246 printf("exit %d", spec.code) 247 }