github.com/rothwerx/packer@v0.9.0/packer/hook.go (about)

     1  package packer
     2  
     3  import (
     4  	"sync"
     5  )
     6  
     7  // This is the hook that should be fired for provisioners to run.
     8  const HookProvision = "packer_provision"
     9  
    10  // A Hook is used to hook into an arbitrarily named location in a build,
    11  // allowing custom behavior to run at certain points along a build.
    12  //
    13  // Run is called when the hook is called, with the name of the hook and
    14  // arbitrary data associated with it. To know what format the data is in,
    15  // you must reference the documentation for the specific hook you're interested
    16  // in. In addition to that, the Hook is given access to a UI so that it can
    17  // output things to the user.
    18  //
    19  // Cancel is called when the hook needs to be cancelled. This will usually
    20  // be called when Run is still in progress so the mechanism that handles this
    21  // must be race-free. Cancel should attempt to cancel the hook in the
    22  // quickest, safest way possible.
    23  type Hook interface {
    24  	Run(string, Ui, Communicator, interface{}) error
    25  	Cancel()
    26  }
    27  
    28  // A Hook implementation that dispatches based on an internal mapping.
    29  type DispatchHook struct {
    30  	Mapping map[string][]Hook
    31  
    32  	l           sync.Mutex
    33  	cancelled   bool
    34  	runningHook Hook
    35  }
    36  
    37  // Runs the hook with the given name by dispatching it to the proper
    38  // hooks if a mapping exists. If a mapping doesn't exist, then nothing
    39  // happens.
    40  func (h *DispatchHook) Run(name string, ui Ui, comm Communicator, data interface{}) error {
    41  	h.l.Lock()
    42  	h.cancelled = false
    43  	h.l.Unlock()
    44  
    45  	// Make sure when we exit that we reset the running hook.
    46  	defer func() {
    47  		h.l.Lock()
    48  		defer h.l.Unlock()
    49  		h.runningHook = nil
    50  	}()
    51  
    52  	hooks, ok := h.Mapping[name]
    53  	if !ok {
    54  		// No hooks for that name. No problem.
    55  		return nil
    56  	}
    57  
    58  	for _, hook := range hooks {
    59  		h.l.Lock()
    60  		if h.cancelled {
    61  			h.l.Unlock()
    62  			return nil
    63  		}
    64  
    65  		h.runningHook = hook
    66  		h.l.Unlock()
    67  
    68  		if err := hook.Run(name, ui, comm, data); err != nil {
    69  			return err
    70  		}
    71  	}
    72  
    73  	return nil
    74  }
    75  
    76  // Cancels all the hooks that are currently in-flight, if any. This will
    77  // block until the hooks are all cancelled.
    78  func (h *DispatchHook) Cancel() {
    79  	h.l.Lock()
    80  	defer h.l.Unlock()
    81  
    82  	if h.runningHook != nil {
    83  		h.runningHook.Cancel()
    84  	}
    85  
    86  	h.cancelled = true
    87  }