github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/client/action/run.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package action
     5  
     6  import (
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/juju/collections/set"
    11  	"github.com/juju/errors"
    12  	"gopkg.in/juju/names.v2"
    13  
    14  	"github.com/juju/juju/apiserver/common"
    15  	"github.com/juju/juju/apiserver/params"
    16  	"github.com/juju/juju/core/actions"
    17  	"github.com/juju/juju/state"
    18  )
    19  
    20  // getAllUnitNames returns a sequence of valid Unit objects from state. If any
    21  // of the application names or unit names are not found, an error is returned.
    22  func getAllUnitNames(st *state.State, units, services []string) (result []names.Tag, err error) {
    23  	var leaders map[string]string
    24  	getLeader := func(appName string) (string, error) {
    25  		if leaders == nil {
    26  			var err error
    27  			leaders, err = st.ApplicationLeaders()
    28  			if err != nil {
    29  				return "", err
    30  			}
    31  		}
    32  		if leader, ok := leaders[appName]; ok {
    33  			return leader, nil
    34  		}
    35  		return "", errors.Errorf("could not determine leader for %q", appName)
    36  	}
    37  
    38  	// Replace units matching $app/leader with the appropriate unit for
    39  	// the leader.
    40  	unitsSet := set.NewStrings()
    41  	for _, unit := range units {
    42  		if !strings.HasSuffix(unit, "leader") {
    43  			unitsSet.Add(unit)
    44  			continue
    45  		}
    46  
    47  		app := strings.Split(unit, "/")[0]
    48  		leaderUnit, err := getLeader(app)
    49  		if err != nil {
    50  			return nil, common.ServerError(err)
    51  		}
    52  
    53  		unitsSet.Add(leaderUnit)
    54  	}
    55  
    56  	for _, name := range services {
    57  		service, err := st.Application(name)
    58  		if err != nil {
    59  			return nil, err
    60  		}
    61  		units, err := service.AllUnits()
    62  		if err != nil {
    63  			return nil, err
    64  		}
    65  		for _, unit := range units {
    66  			unitsSet.Add(unit.Name())
    67  		}
    68  	}
    69  	for _, unitName := range unitsSet.SortedValues() {
    70  		if !names.IsValidUnit(unitName) {
    71  			return nil, errors.Errorf("invalid unit name %q", unitName)
    72  		}
    73  		result = append(result, names.NewUnitTag(unitName))
    74  	}
    75  	return result, nil
    76  }
    77  
    78  // Run the commands specified on the machines identified through the
    79  // list of machines, units and services.
    80  func (a *ActionAPI) Run(run params.RunParams) (results params.ActionResults, err error) {
    81  	if err := a.checkCanAdmin(); err != nil {
    82  		return results, err
    83  	}
    84  	if err := a.check.ChangeAllowed(); err != nil {
    85  		return results, errors.Trace(err)
    86  	}
    87  
    88  	units, err := getAllUnitNames(a.state, run.Units, run.Applications)
    89  	if err != nil {
    90  		return results, errors.Trace(err)
    91  	}
    92  
    93  	machines := make([]names.Tag, len(run.Machines))
    94  	for i, machineId := range run.Machines {
    95  		if !names.IsValidMachine(machineId) {
    96  			return results, errors.Errorf("invalid machine id %q", machineId)
    97  		}
    98  		machines[i] = names.NewMachineTag(machineId)
    99  	}
   100  
   101  	actionParams := a.createActionsParams(append(units, machines...), run.Commands, run.Timeout)
   102  
   103  	return queueActions(a, actionParams)
   104  }
   105  
   106  // RunOnAllMachines attempts to run the specified command on all the machines.
   107  func (a *ActionAPI) RunOnAllMachines(run params.RunParams) (results params.ActionResults, err error) {
   108  	if err := a.checkCanAdmin(); err != nil {
   109  		return results, err
   110  	}
   111  
   112  	if err := a.check.ChangeAllowed(); err != nil {
   113  		return results, errors.Trace(err)
   114  	}
   115  
   116  	machines, err := a.state.AllMachines()
   117  	if err != nil {
   118  		return results, err
   119  	}
   120  	machineTags := make([]names.Tag, len(machines))
   121  	for i, machine := range machines {
   122  		machineTags[i] = machine.Tag()
   123  	}
   124  
   125  	actionParams := a.createActionsParams(machineTags, run.Commands, run.Timeout)
   126  
   127  	return queueActions(a, actionParams)
   128  }
   129  
   130  func (a *ActionAPI) createActionsParams(actionReceiverTags []names.Tag, quotedCommands string, timeout time.Duration) params.Actions {
   131  
   132  	apiActionParams := params.Actions{Actions: []params.Action{}}
   133  
   134  	actionParams := map[string]interface{}{}
   135  	actionParams["command"] = quotedCommands
   136  	actionParams["timeout"] = timeout.Nanoseconds()
   137  
   138  	for _, tag := range actionReceiverTags {
   139  		apiActionParams.Actions = append(apiActionParams.Actions, params.Action{
   140  			Receiver:   tag.String(),
   141  			Name:       actions.JujuRunActionName,
   142  			Parameters: actionParams,
   143  		})
   144  	}
   145  
   146  	return apiActionParams
   147  }
   148  
   149  var queueActions = func(a *ActionAPI, args params.Actions) (results params.ActionResults, err error) {
   150  	return a.Enqueue(args)
   151  }