github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/uniter/runner/runner_test.go (about) 1 // Copyright 2012-2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package runner_test 5 6 import ( 7 "fmt" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "strings" 12 "time" 13 14 "github.com/juju/errors" 15 envtesting "github.com/juju/testing" 16 jc "github.com/juju/testing/checkers" 17 "github.com/juju/utils" 18 gc "gopkg.in/check.v1" 19 20 "github.com/juju/juju/worker/uniter/runner" 21 ) 22 23 type RunCommandSuite struct { 24 HookContextSuite 25 } 26 27 var _ = gc.Suite(&RunCommandSuite{}) 28 29 func (s *RunCommandSuite) getHookContext(c *gc.C) *runner.HookContext { 30 uuid, err := utils.NewUUID() 31 c.Assert(err, jc.ErrorIsNil) 32 return s.HookContextSuite.getHookContext(c, uuid.String(), -1, "", noProxies) 33 } 34 35 func (s *RunCommandSuite) TestRunCommandsEnvStdOutAndErrAndRC(c *gc.C) { 36 ctx := s.getHookContext(c) 37 paths := NewRealPaths(c) 38 runner := runner.NewRunner(ctx, paths) 39 40 commands := ` 41 echo $JUJU_CHARM_DIR 42 echo this is standard err >&2 43 exit 42 44 ` 45 result, err := runner.RunCommands(commands) 46 c.Assert(err, jc.ErrorIsNil) 47 48 c.Assert(result.Code, gc.Equals, 42) 49 c.Assert(string(result.Stdout), gc.Equals, paths.charm+"\n") 50 c.Assert(string(result.Stderr), gc.Equals, "this is standard err\n") 51 c.Assert(ctx.GetProcess(), gc.NotNil) 52 } 53 54 type RunHookSuite struct { 55 HookContextSuite 56 } 57 58 var _ = gc.Suite(&RunHookSuite{}) 59 60 // LineBufferSize matches the constant used when creating 61 // the bufio line reader. 62 const lineBufferSize = 4096 63 64 var runHookTests = []struct { 65 summary string 66 relid int 67 remote string 68 spec hookSpec 69 err string 70 }{ 71 { 72 summary: "missing hook is not an error", 73 relid: -1, 74 }, { 75 summary: "report failure to execute hook", 76 relid: -1, 77 spec: hookSpec{perm: 0600}, 78 err: `exec: .*something-happened": permission denied`, 79 }, { 80 summary: "report error indicated by hook's exit status", 81 relid: -1, 82 spec: hookSpec{ 83 perm: 0700, 84 code: 99, 85 }, 86 err: "exit status 99", 87 }, { 88 summary: "output logging", 89 relid: -1, 90 spec: hookSpec{ 91 perm: 0700, 92 stdout: "stdout", 93 stderr: "stderr", 94 }, 95 }, { 96 summary: "output logging with background process", 97 relid: -1, 98 spec: hookSpec{ 99 perm: 0700, 100 stdout: "stdout", 101 background: "not printed", 102 }, 103 }, { 104 summary: "long line split", 105 relid: -1, 106 spec: hookSpec{ 107 perm: 0700, 108 stdout: strings.Repeat("a", lineBufferSize+10), 109 }, 110 }, 111 } 112 113 func (s *RunHookSuite) TestRunHook(c *gc.C) { 114 uuid, err := utils.NewUUID() 115 c.Assert(err, jc.ErrorIsNil) 116 for i, t := range runHookTests { 117 c.Logf("\ntest %d: %s; perm %v", i, t.summary, t.spec.perm) 118 ctx := s.getHookContext(c, uuid.String(), t.relid, t.remote, noProxies) 119 paths := NewRealPaths(c) 120 rnr := runner.NewRunner(ctx, paths) 121 var hookExists bool 122 if t.spec.perm != 0 { 123 spec := t.spec 124 spec.dir = "hooks" 125 spec.name = "something-happened" 126 c.Logf("makeCharm %#v", spec) 127 makeCharm(c, spec, paths.charm) 128 hookExists = true 129 } 130 t0 := time.Now() 131 err := rnr.RunHook("something-happened") 132 if t.err == "" && hookExists { 133 c.Assert(err, jc.ErrorIsNil) 134 } else if !hookExists { 135 c.Assert(runner.IsMissingHookError(err), jc.IsTrue) 136 } else { 137 c.Assert(err, gc.ErrorMatches, t.err) 138 } 139 if t.spec.background != "" && time.Now().Sub(t0) > 5*time.Second { 140 c.Errorf("background process holding up hook execution") 141 } 142 } 143 } 144 145 type MockContext struct { 146 runner.Context 147 actionData *runner.ActionData 148 expectPid int 149 flushBadge string 150 flushFailure error 151 flushResult error 152 } 153 154 func (ctx *MockContext) UnitName() string { 155 return "some-unit/999" 156 } 157 158 func (ctx *MockContext) HookVars(paths runner.Paths) []string { 159 return []string{"VAR=value"} 160 } 161 162 func (ctx *MockContext) ActionData() (*runner.ActionData, error) { 163 if ctx.actionData == nil { 164 return nil, errors.New("blam") 165 } 166 return ctx.actionData, nil 167 } 168 169 func (ctx *MockContext) SetProcess(process *os.Process) { 170 ctx.expectPid = process.Pid 171 } 172 173 func (ctx *MockContext) FlushContext(badge string, failure error) error { 174 ctx.flushBadge = badge 175 ctx.flushFailure = failure 176 return ctx.flushResult 177 } 178 179 type RunMockContextSuite struct { 180 envtesting.IsolationSuite 181 paths RealPaths 182 } 183 184 var _ = gc.Suite(&RunMockContextSuite{}) 185 186 func (s *RunMockContextSuite) SetUpTest(c *gc.C) { 187 s.IsolationSuite.SetUpTest(c) 188 s.paths = NewRealPaths(c) 189 } 190 191 func (s *RunMockContextSuite) assertRecordedPid(c *gc.C, expectPid int) { 192 path := filepath.Join(s.paths.charm, "pid") 193 content, err := ioutil.ReadFile(path) 194 c.Assert(err, jc.ErrorIsNil) 195 expectContent := fmt.Sprintf("%d\n", expectPid) 196 c.Assert(string(content), gc.Equals, expectContent) 197 } 198 199 func (s *RunMockContextSuite) TestRunHookFlushSuccess(c *gc.C) { 200 expectErr := errors.New("pew pew pew") 201 ctx := &MockContext{ 202 flushResult: expectErr, 203 } 204 makeCharm(c, hookSpec{ 205 dir: "hooks", 206 name: "something-happened", 207 perm: 0700, 208 }, s.paths.charm) 209 actualErr := runner.NewRunner(ctx, s.paths).RunHook("something-happened") 210 c.Assert(actualErr, gc.Equals, expectErr) 211 c.Assert(ctx.flushBadge, gc.Equals, "something-happened") 212 c.Assert(ctx.flushFailure, gc.IsNil) 213 s.assertRecordedPid(c, ctx.expectPid) 214 } 215 216 func (s *RunMockContextSuite) TestRunHookFlushFailure(c *gc.C) { 217 expectErr := errors.New("pew pew pew") 218 ctx := &MockContext{ 219 flushResult: expectErr, 220 } 221 makeCharm(c, hookSpec{ 222 dir: "hooks", 223 name: "something-happened", 224 perm: 0700, 225 code: 123, 226 }, s.paths.charm) 227 actualErr := runner.NewRunner(ctx, s.paths).RunHook("something-happened") 228 c.Assert(actualErr, gc.Equals, expectErr) 229 c.Assert(ctx.flushBadge, gc.Equals, "something-happened") 230 c.Assert(ctx.flushFailure, gc.ErrorMatches, "exit status 123") 231 s.assertRecordedPid(c, ctx.expectPid) 232 } 233 234 func (s *RunMockContextSuite) TestRunActionFlushSuccess(c *gc.C) { 235 expectErr := errors.New("pew pew pew") 236 ctx := &MockContext{ 237 flushResult: expectErr, 238 actionData: &runner.ActionData{}, 239 } 240 makeCharm(c, hookSpec{ 241 dir: "actions", 242 name: "do-something", 243 perm: 0700, 244 }, s.paths.charm) 245 actualErr := runner.NewRunner(ctx, s.paths).RunAction("do-something") 246 c.Assert(actualErr, gc.Equals, expectErr) 247 c.Assert(ctx.flushBadge, gc.Equals, "do-something") 248 c.Assert(ctx.flushFailure, gc.IsNil) 249 s.assertRecordedPid(c, ctx.expectPid) 250 } 251 252 func (s *RunMockContextSuite) TestRunActionFlushFailure(c *gc.C) { 253 expectErr := errors.New("pew pew pew") 254 ctx := &MockContext{ 255 flushResult: expectErr, 256 actionData: &runner.ActionData{}, 257 } 258 makeCharm(c, hookSpec{ 259 dir: "actions", 260 name: "do-something", 261 perm: 0700, 262 code: 123, 263 }, s.paths.charm) 264 actualErr := runner.NewRunner(ctx, s.paths).RunAction("do-something") 265 c.Assert(actualErr, gc.Equals, expectErr) 266 c.Assert(ctx.flushBadge, gc.Equals, "do-something") 267 c.Assert(ctx.flushFailure, gc.ErrorMatches, "exit status 123") 268 s.assertRecordedPid(c, ctx.expectPid) 269 } 270 271 func (s *RunMockContextSuite) TestRunCommandsFlushSuccess(c *gc.C) { 272 expectErr := errors.New("pew pew pew") 273 ctx := &MockContext{ 274 flushResult: expectErr, 275 } 276 _, actualErr := runner.NewRunner(ctx, s.paths).RunCommands("echo $$ > pid") 277 c.Assert(actualErr, gc.Equals, expectErr) 278 c.Assert(ctx.flushBadge, gc.Equals, "run commands") 279 c.Assert(ctx.flushFailure, gc.IsNil) 280 s.assertRecordedPid(c, ctx.expectPid) 281 } 282 283 func (s *RunMockContextSuite) TestRunCommandsFlushFailure(c *gc.C) { 284 expectErr := errors.New("pew pew pew") 285 ctx := &MockContext{ 286 flushResult: expectErr, 287 } 288 _, actualErr := runner.NewRunner(ctx, s.paths).RunCommands("echo $$ > pid; exit 123") 289 c.Assert(actualErr, gc.Equals, expectErr) 290 c.Assert(ctx.flushBadge, gc.Equals, "run commands") 291 c.Assert(ctx.flushFailure, gc.IsNil) // exit code in _ result, as tested elsewhere 292 s.assertRecordedPid(c, ctx.expectPid) 293 }