github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/worker/machineactions/worker.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Copyright 2016 Cloudbase Solutions
     3  // Licensed under the AGPLv3, see LICENCE file for details.
     4  
     5  package machineactions
     6  
     7  import (
     8  	"github.com/juju/errors"
     9  	"github.com/juju/juju/api/machineactions"
    10  	"github.com/juju/juju/apiserver/params"
    11  	"github.com/juju/juju/watcher"
    12  	"github.com/juju/juju/worker"
    13  	"github.com/juju/loggo"
    14  	"gopkg.in/juju/names.v2"
    15  )
    16  
    17  var logger = loggo.GetLogger("juju.worker.machineactions")
    18  
    19  // Facade defines the capabilities required by the worker from the API.
    20  type Facade interface {
    21  	WatchActionNotifications(agent names.MachineTag) (watcher.StringsWatcher, error)
    22  	RunningActions(agent names.MachineTag) ([]params.ActionResult, error)
    23  
    24  	Action(names.ActionTag) (*machineactions.Action, error)
    25  	ActionBegin(names.ActionTag) error
    26  	ActionFinish(tag names.ActionTag, status string, results map[string]interface{}, message string) error
    27  }
    28  
    29  // WorkerConfig defines the worker's dependencies.
    30  type WorkerConfig struct {
    31  	Facade       Facade
    32  	MachineTag   names.MachineTag
    33  	HandleAction func(name string, params map[string]interface{}) (results map[string]interface{}, err error)
    34  }
    35  
    36  // Validate returns an error if the configuration is not complete.
    37  func (c WorkerConfig) Validate() error {
    38  	if c.Facade == nil {
    39  		return errors.NotValidf("nil Facade")
    40  	}
    41  	if c.MachineTag == (names.MachineTag{}) {
    42  		return errors.NotValidf("unspecified MachineTag")
    43  	}
    44  	if c.HandleAction == nil {
    45  		return errors.NotValidf("nil HandleAction")
    46  	}
    47  	return nil
    48  }
    49  
    50  // NewMachineActionsWorker returns a worker.Worker that watches for actions
    51  // enqueued on this machine and tries to execute them.
    52  func NewMachineActionsWorker(config WorkerConfig) (worker.Worker, error) {
    53  	if err := config.Validate(); err != nil {
    54  		return nil, errors.Trace(err)
    55  	}
    56  	swConfig := watcher.StringsConfig{
    57  		Handler: &handler{config},
    58  	}
    59  	return watcher.NewStringsWorker(swConfig)
    60  }
    61  
    62  // handler implements watcher.StringsHandler
    63  type handler struct {
    64  	config WorkerConfig
    65  }
    66  
    67  // SetUp is part of the watcher.StringsHandler interface.
    68  func (h *handler) SetUp() (watcher.StringsWatcher, error) {
    69  	actions, err := h.config.Facade.RunningActions(h.config.MachineTag)
    70  	if err != nil {
    71  		return nil, errors.Trace(err)
    72  	}
    73  	// We try to cancel any running action before starting up so actions don't linger around
    74  	// We *should* really have only one action coming up here if the execution is serial but
    75  	// this is best effort anyway.
    76  	for _, action := range actions {
    77  		tag, err := names.ParseActionTag(action.Action.Tag)
    78  		if err != nil {
    79  			logger.Infof("tried to cancel action %s but failed with error %v", action.Action.Tag, err)
    80  			continue
    81  		}
    82  		err = h.config.Facade.ActionFinish(tag, params.ActionFailed, nil, "action cancelled")
    83  		if err != nil {
    84  			logger.Infof("tried to cancel action %s but failed with error %v", action.Action.Tag, err)
    85  		}
    86  	}
    87  	return h.config.Facade.WatchActionNotifications(h.config.MachineTag)
    88  }
    89  
    90  // Handle is part of the watcher.StringsHandler interface.
    91  // It should give us any actions currently enqueued for this machine.
    92  // We try to execute every action before returning
    93  func (h *handler) Handle(_ <-chan struct{}, actionsSlice []string) error {
    94  	for _, actionId := range actionsSlice {
    95  		ok := names.IsValidAction(actionId)
    96  		if !ok {
    97  			return errors.Errorf("got invalid action id %s", actionId)
    98  		}
    99  
   100  		actionTag := names.NewActionTag(actionId)
   101  		action, err := h.config.Facade.Action(actionTag)
   102  		if err != nil {
   103  			return errors.Annotatef(err, "could not retrieve action %s", actionId)
   104  		}
   105  
   106  		err = h.config.Facade.ActionBegin(actionTag)
   107  		if err != nil {
   108  			return errors.Annotatef(err, "could not begin action %s", action.Name())
   109  		}
   110  
   111  		// We try to handle the action. The result returned from handling the action is
   112  		// sent through using ActionFinish. We only stop the loop if ActionFinish fails.
   113  		var finishErr error
   114  		results, err := h.config.HandleAction(action.Name(), action.Params())
   115  		if err != nil {
   116  			finishErr = h.config.Facade.ActionFinish(actionTag, params.ActionFailed, nil, err.Error())
   117  		} else {
   118  			finishErr = h.config.Facade.ActionFinish(actionTag, params.ActionCompleted, results, "")
   119  		}
   120  		if finishErr != nil {
   121  			return errors.Trace(finishErr)
   122  		}
   123  	}
   124  	return nil
   125  }
   126  
   127  // TearDown is part of the watcher.NotifyHandler interface.
   128  func (h *handler) TearDown() error {
   129  	// Nothing to cleanup, only state is the watcher
   130  	return nil
   131  }