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