func (s *RunHookSuite) TestRunHook(c *gc.C) { uuid, err := utils.NewUUID() c.Assert(err, jc.ErrorIsNil) for i, t := range runHookTests { c.Logf("\ntest %d: %s; perm %v", i, t.summary, t.spec.perm) ctx := s.getHookContext(c, uuid.String(), t.relid, t.remote, noProxies) paths := NewRealPaths(c) rnr := runner.NewRunner(ctx, paths) var hookExists bool if t.spec.perm != 0 { spec := t.spec spec.dir = "hooks" spec.name = hookName c.Logf("makeCharm %#v", spec) makeCharm(c, spec, paths.charm) hookExists = true } t0 := time.Now() err := rnr.RunHook("something-happened") if t.err == "" && hookExists { c.Assert(err, jc.ErrorIsNil) } else if !hookExists { c.Assert(runner.IsMissingHookError(err), jc.IsTrue) } else { c.Assert(err, gc.ErrorMatches, t.err) } if t.spec.background != "" && time.Now().Sub(t0) > 5*time.Second { c.Errorf("background process holding up hook execution") } } }
// Execute runs the hook. // Execute is part of the Operation interface. func (rh *runHook) Execute(state State) (*State, error) { message := RunningHookMessage(rh.name) if err := rh.beforeHook(); err != nil { return nil, err } if err := rh.callbacks.SetExecutingStatus(message); err != nil { return nil, err } // The before hook may have updated unit status and we don't want that // to count so reset it here before running the hook. rh.runner.Context().ResetExecutionSetUnitStatus() ranHook := true step := Done err := rh.runner.RunHook(rh.name) cause := errors.Cause(err) switch { case runner.IsMissingHookError(cause): ranHook = false err = nil case cause == runner.ErrRequeueAndReboot: step = Queued fallthrough case cause == runner.ErrReboot: err = ErrNeedsReboot case err == nil: default: logger.Errorf("hook %q failed: %v", rh.name, err) rh.callbacks.NotifyHookFailed(rh.name, rh.runner.Context()) return nil, ErrHookFailed } if ranHook { logger.Infof("ran %q hook", rh.name) rh.callbacks.NotifyHookCompleted(rh.name, rh.runner.Context()) } else { logger.Infof("skipped %q hook (missing)", rh.name) } var hasRunStatusSet bool var afterHookErr error if hasRunStatusSet, afterHookErr = rh.afterHook(state); afterHookErr != nil { return nil, afterHookErr } return stateChange{ Kind: RunHook, Step: step, Hook: &rh.info, HasRunStatusSet: hasRunStatusSet, }.apply(state), err }