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 }