github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/apiserver/action/action.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package action 5 6 import ( 7 "github.com/juju/loggo" 8 "github.com/juju/names" 9 10 "github.com/juju/juju/apiserver/common" 11 "github.com/juju/juju/apiserver/params" 12 "github.com/juju/juju/state" 13 ) 14 15 var logger = loggo.GetLogger("juju.apiserver.action") 16 17 func init() { 18 common.RegisterStandardFacade("Action", 0, NewActionAPI) 19 } 20 21 // ActionAPI implements the client API for interacting with Actions 22 type ActionAPI struct { 23 state *state.State 24 resources *common.Resources 25 authorizer common.Authorizer 26 } 27 28 // NewActionAPI returns an initialized ActionAPI 29 func NewActionAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*ActionAPI, error) { 30 if !authorizer.AuthClient() { 31 return nil, common.ErrPerm 32 } 33 34 return &ActionAPI{ 35 state: st, 36 resources: resources, 37 authorizer: authorizer, 38 }, nil 39 } 40 41 // Actions takes a list of ActionTags, and returns the full Action for 42 // each ID. 43 func (a *ActionAPI) Actions(arg params.Entities) (params.ActionResults, error) { 44 response := params.ActionResults{Results: make([]params.ActionResult, len(arg.Entities))} 45 for i, entity := range arg.Entities { 46 currentResult := &response.Results[i] 47 tag, err := names.ParseTag(entity.Tag) 48 if err != nil { 49 currentResult.Error = common.ServerError(common.ErrBadId) 50 continue 51 } 52 actionTag, ok := tag.(names.ActionTag) 53 if !ok { 54 currentResult.Error = common.ServerError(common.ErrBadId) 55 continue 56 } 57 action, err := a.state.ActionByTag(actionTag) 58 if err != nil { 59 currentResult.Error = common.ServerError(common.ErrBadId) 60 continue 61 } 62 receiverTag, err := names.ActionReceiverTag(action.Receiver()) 63 if err != nil { 64 currentResult.Error = common.ServerError(err) 65 continue 66 } 67 response.Results[i] = makeActionResult(receiverTag, action) 68 } 69 return response, nil 70 } 71 72 // FindActionTagsByPrefix takes a list of string prefixes and finds 73 // corresponding ActionTags that match that prefix. 74 func (a *ActionAPI) FindActionTagsByPrefix(arg params.FindTags) (params.FindTagsResults, error) { 75 response := params.FindTagsResults{Matches: make(map[string][]params.Entity)} 76 for _, prefix := range arg.Prefixes { 77 found := a.state.FindActionTagsByPrefix(prefix) 78 matches := make([]params.Entity, len(found)) 79 for i, tag := range found { 80 matches[i] = params.Entity{Tag: tag.String()} 81 } 82 response.Matches[prefix] = matches 83 } 84 return response, nil 85 } 86 87 // Enqueue takes a list of Actions and queues them up to be executed by 88 // the designated ActionReceiver, returning the params.Action for each 89 // enqueued Action, or an error if there was a problem enqueueing the 90 // Action. 91 func (a *ActionAPI) Enqueue(arg params.Actions) (params.ActionResults, error) { 92 response := params.ActionResults{Results: make([]params.ActionResult, len(arg.Actions))} 93 for i, action := range arg.Actions { 94 currentResult := &response.Results[i] 95 receiver, err := tagToActionReceiver(a.state, action.Receiver) 96 if err != nil { 97 currentResult.Error = common.ServerError(err) 98 continue 99 } 100 enqueued, err := receiver.AddAction(action.Name, action.Parameters) 101 if err != nil { 102 currentResult.Error = common.ServerError(err) 103 continue 104 } 105 106 response.Results[i] = makeActionResult(receiver.Tag(), enqueued) 107 } 108 return response, nil 109 } 110 111 // ListAll takes a list of Entities representing ActionReceivers and 112 // returns all of the Actions that have been enqueued or run by each of 113 // those Entities. 114 func (a *ActionAPI) ListAll(arg params.Entities) (params.ActionsByReceivers, error) { 115 return a.internalList(arg, combine(pendingActions, runningActions, completedActions)) 116 } 117 118 // ListPending takes a list of Entities representing ActionReceivers 119 // and returns all of the Actions that are enqueued for each of those 120 // Entities. 121 func (a *ActionAPI) ListPending(arg params.Entities) (params.ActionsByReceivers, error) { 122 return a.internalList(arg, pendingActions) 123 } 124 125 // ListRunning takes a list of Entities representing ActionReceivers and 126 // returns all of the Actions that have are running on each of those 127 // Entities. 128 func (a *ActionAPI) ListRunning(arg params.Entities) (params.ActionsByReceivers, error) { 129 return a.internalList(arg, runningActions) 130 } 131 132 // ListCompleted takes a list of Entities representing ActionReceivers 133 // and returns all of the Actions that have been run on each of those 134 // Entities. 135 func (a *ActionAPI) ListCompleted(arg params.Entities) (params.ActionsByReceivers, error) { 136 return a.internalList(arg, completedActions) 137 } 138 139 // Cancel attempts to cancel enqueued Actions from running. 140 func (a *ActionAPI) Cancel(arg params.Entities) (params.ActionResults, error) { 141 response := params.ActionResults{Results: make([]params.ActionResult, len(arg.Entities))} 142 for i, entity := range arg.Entities { 143 currentResult := &response.Results[i] 144 tag, err := names.ParseTag(entity.Tag) 145 if err != nil { 146 currentResult.Error = common.ServerError(common.ErrBadId) 147 continue 148 } 149 actionTag, ok := tag.(names.ActionTag) 150 if !ok { 151 currentResult.Error = common.ServerError(common.ErrBadId) 152 continue 153 } 154 action, err := a.state.ActionByTag(actionTag) 155 if err != nil { 156 currentResult.Error = common.ServerError(err) 157 continue 158 } 159 result, err := action.Finish(state.ActionResults{Status: state.ActionCancelled, Message: "action cancelled via the API"}) 160 if err != nil { 161 currentResult.Error = common.ServerError(err) 162 continue 163 } 164 receiverTag, err := names.ActionReceiverTag(result.Receiver()) 165 if err != nil { 166 currentResult.Error = common.ServerError(err) 167 continue 168 } 169 170 response.Results[i] = makeActionResult(receiverTag, result) 171 } 172 return response, nil 173 } 174 175 // ServicesCharmActions returns a slice of charm Actions for a slice of 176 // services. 177 func (a *ActionAPI) ServicesCharmActions(args params.Entities) (params.ServicesCharmActionsResults, error) { 178 result := params.ServicesCharmActionsResults{Results: make([]params.ServiceCharmActionsResult, len(args.Entities))} 179 for i, entity := range args.Entities { 180 currentResult := &result.Results[i] 181 svcTag, err := names.ParseServiceTag(entity.Tag) 182 if err != nil { 183 currentResult.Error = common.ServerError(common.ErrBadId) 184 continue 185 } 186 currentResult.ServiceTag = svcTag.String() 187 svc, err := a.state.Service(svcTag.Id()) 188 if err != nil { 189 currentResult.Error = common.ServerError(err) 190 continue 191 } 192 ch, _, err := svc.Charm() 193 if err != nil { 194 currentResult.Error = common.ServerError(err) 195 continue 196 } 197 currentResult.Actions = ch.Actions() 198 } 199 return result, nil 200 } 201 202 // internalList takes a list of Entities representing ActionReceivers 203 // and returns all of the Actions the extractorFn can get out of the 204 // ActionReceiver. 205 func (a *ActionAPI) internalList(arg params.Entities, fn extractorFn) (params.ActionsByReceivers, error) { 206 response := params.ActionsByReceivers{Actions: make([]params.ActionsByReceiver, len(arg.Entities))} 207 for i, entity := range arg.Entities { 208 currentResult := &response.Actions[i] 209 receiver, err := tagToActionReceiver(a.state, entity.Tag) 210 if err != nil { 211 currentResult.Error = common.ServerError(common.ErrBadId) 212 continue 213 } 214 currentResult.Receiver = receiver.Tag().String() 215 216 results, err := fn(receiver) 217 if err != nil { 218 currentResult.Error = common.ServerError(err) 219 continue 220 } 221 currentResult.Actions = results 222 } 223 return response, nil 224 } 225 226 // tagToActionReceiver takes a tag string and tries to convert it to an 227 // ActionReceiver. 228 func tagToActionReceiver(st *state.State, tag string) (state.ActionReceiver, error) { 229 receiverTag, err := names.ParseTag(tag) 230 if err != nil { 231 return nil, common.ErrBadId 232 } 233 entity, err := st.FindEntity(receiverTag) 234 if err != nil { 235 return nil, common.ErrBadId 236 } 237 receiver, ok := entity.(state.ActionReceiver) 238 if !ok { 239 return nil, common.ErrBadId 240 } 241 return receiver, nil 242 } 243 244 // extractorFn is the generic signature for functions that extract 245 // state.Actions from an ActionReceiver, and return them as a slice of 246 // params.ActionResult. 247 type extractorFn func(state.ActionReceiver) ([]params.ActionResult, error) 248 249 // combine takes multiple extractorFn's and combines them into one 250 // function. 251 func combine(funcs ...extractorFn) extractorFn { 252 return func(ar state.ActionReceiver) ([]params.ActionResult, error) { 253 result := []params.ActionResult{} 254 for _, fn := range funcs { 255 items, err := fn(ar) 256 if err != nil { 257 return result, err 258 } 259 result = append(result, items...) 260 } 261 return result, nil 262 } 263 } 264 265 // pendingActions iterates through the Actions() enqueued for an 266 // ActionReceiver, and converts them to a slice of params.ActionResult. 267 func pendingActions(ar state.ActionReceiver) ([]params.ActionResult, error) { 268 return convertActions(ar, ar.PendingActions) 269 } 270 271 // runningActions iterates through the Actions() running on an 272 // ActionReceiver, and converts them to a slice of params.ActionResult. 273 func runningActions(ar state.ActionReceiver) ([]params.ActionResult, error) { 274 return convertActions(ar, ar.RunningActions) 275 } 276 277 // completedActions iterates through the Actions() that have run to 278 // completion for an ActionReceiver, and converts them to a slice of 279 // params.ActionResult. 280 func completedActions(ar state.ActionReceiver) ([]params.ActionResult, error) { 281 return convertActions(ar, ar.CompletedActions) 282 } 283 284 // getActionsFn declares the function type that returns a slice of 285 // *state.Action and error, used to curry specific list functions. 286 type getActionsFn func() ([]*state.Action, error) 287 288 // convertActions takes a generic getActionsFn to obtain a slice 289 // of *state.Action and then converts them to the API slice of 290 // params.ActionResult. 291 func convertActions(ar state.ActionReceiver, fn getActionsFn) ([]params.ActionResult, error) { 292 items := []params.ActionResult{} 293 actions, err := fn() 294 if err != nil { 295 return items, err 296 } 297 for _, action := range actions { 298 if action == nil { 299 continue 300 } 301 items = append(items, makeActionResult(ar.Tag(), action)) 302 } 303 return items, nil 304 } 305 306 // makeActionResult does the actual type conversion from *state.Action 307 // to params.ActionResult. 308 func makeActionResult(actionReceiverTag names.Tag, action *state.Action) params.ActionResult { 309 output, message := action.Results() 310 return params.ActionResult{ 311 Action: ¶ms.Action{ 312 Receiver: actionReceiverTag.String(), 313 Tag: action.ActionTag().String(), 314 Name: action.Name(), 315 Parameters: action.Parameters(), 316 }, 317 Status: string(action.Status()), 318 Message: message, 319 Output: output, 320 Enqueued: action.Enqueued(), 321 Started: action.Started(), 322 Completed: action.Completed(), 323 } 324 }