github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/uniter/operation/runhook.go (about)

     1  // Copyright 2014-2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package operation
     5  
     6  import (
     7  	"fmt"
     8  	"time"
     9  
    10  	"github.com/juju/errors"
    11  	"gopkg.in/juju/charm.v4/hooks"
    12  
    13  	"github.com/juju/juju/worker/uniter/hook"
    14  	"github.com/juju/juju/worker/uniter/runner"
    15  )
    16  
    17  type runHook struct {
    18  	info hook.Info
    19  
    20  	callbacks     Callbacks
    21  	runnerFactory runner.Factory
    22  
    23  	name   string
    24  	runner runner.Runner
    25  }
    26  
    27  // String is part of the Operation interface.
    28  func (rh *runHook) String() string {
    29  	suffix := ""
    30  	if rh.info.Kind.IsRelation() {
    31  		if rh.info.RemoteUnit == "" {
    32  			suffix = fmt.Sprintf(" (%d)", rh.info.RelationId)
    33  		} else {
    34  			suffix = fmt.Sprintf(" (%d; %s)", rh.info.RelationId, rh.info.RemoteUnit)
    35  		}
    36  	}
    37  	return fmt.Sprintf("run %s%s hook", rh.info.Kind, suffix)
    38  }
    39  
    40  // Prepare ensures the hook can be executed.
    41  // Prepare is part of the Operation interface.
    42  func (rh *runHook) Prepare(state State) (*State, error) {
    43  	name, err := rh.callbacks.PrepareHook(rh.info)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  	rnr, err := rh.runnerFactory.NewHookRunner(rh.info)
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  	rh.name = name
    52  	rh.runner = rnr
    53  	return stateChange{
    54  		Kind: RunHook,
    55  		Step: Pending,
    56  		Hook: &rh.info,
    57  	}.apply(state), nil
    58  }
    59  
    60  // Execute runs the hook.
    61  // Execute is part of the Operation interface.
    62  func (rh *runHook) Execute(state State) (*State, error) {
    63  	message := fmt.Sprintf("running hook %s", rh.name)
    64  	unlock, err := rh.callbacks.AcquireExecutionLock(message)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  	defer unlock()
    69  
    70  	ranHook := true
    71  	step := Done
    72  
    73  	err = rh.runner.RunHook(rh.name)
    74  	cause := errors.Cause(err)
    75  	switch {
    76  	case runner.IsMissingHookError(cause):
    77  		ranHook = false
    78  		err = nil
    79  	case cause == runner.ErrRequeueAndReboot:
    80  		step = Queued
    81  		fallthrough
    82  	case cause == runner.ErrReboot:
    83  		err = ErrNeedsReboot
    84  	case err == nil:
    85  	default:
    86  		logger.Errorf("hook %q failed: %v", rh.name, err)
    87  		rh.callbacks.NotifyHookFailed(rh.name, rh.runner.Context())
    88  		return nil, ErrHookFailed
    89  	}
    90  
    91  	if ranHook {
    92  		logger.Infof("ran %q hook", rh.name)
    93  		rh.callbacks.NotifyHookCompleted(rh.name, rh.runner.Context())
    94  	} else {
    95  		logger.Infof("skipped %q hook (missing)", rh.name)
    96  	}
    97  	return stateChange{
    98  		Kind: RunHook,
    99  		Step: step,
   100  		Hook: &rh.info,
   101  	}.apply(state), err
   102  }
   103  
   104  // Commit updates relation state to include the fact of the hook's execution,
   105  // records the impact of start and collect-metrics hooks, and queues follow-up
   106  // config-changed hooks to directly follow install and upgrade-charm hooks.
   107  // Commit is part of the Operation interface.
   108  func (rh *runHook) Commit(state State) (*State, error) {
   109  	if err := rh.callbacks.CommitHook(rh.info); err != nil {
   110  		return nil, err
   111  	}
   112  
   113  	change := stateChange{
   114  		Kind: RunHook,
   115  		Step: Queued,
   116  	}
   117  	switch rh.info.Kind {
   118  	case hooks.Install, hooks.UpgradeCharm:
   119  		change.Hook = &hook.Info{Kind: hooks.ConfigChanged}
   120  	case hooks.ConfigChanged:
   121  		if !state.Started {
   122  			change.Hook = &hook.Info{Kind: hooks.Start}
   123  			break
   124  		}
   125  		fallthrough
   126  	default:
   127  		change = stateChange{
   128  			Kind: Continue,
   129  			Step: Pending,
   130  			Hook: &rh.info,
   131  		}
   132  	}
   133  
   134  	newState := change.apply(state)
   135  	switch rh.info.Kind {
   136  	case hooks.Start:
   137  		newState.Started = true
   138  	case hooks.CollectMetrics:
   139  		newState.CollectMetricsTime = time.Now().Unix()
   140  	}
   141  	return newState, nil
   142  }