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