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