github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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/errors" 8 "gopkg.in/juju/names.v2" 9 10 "github.com/juju/juju/apiserver/common" 11 "github.com/juju/juju/apiserver/facade" 12 "github.com/juju/juju/apiserver/params" 13 "github.com/juju/juju/permission" 14 "github.com/juju/juju/state" 15 ) 16 17 func init() { 18 common.RegisterStandardFacade("Action", 2, NewActionAPI) 19 } 20 21 // ActionAPI implements the client API for interacting with Actions 22 type ActionAPI struct { 23 state *state.State 24 resources facade.Resources 25 authorizer facade.Authorizer 26 check *common.BlockChecker 27 } 28 29 // NewActionAPI returns an initialized ActionAPI 30 func NewActionAPI(st *state.State, resources facade.Resources, authorizer facade.Authorizer) (*ActionAPI, error) { 31 if !authorizer.AuthClient() { 32 return nil, common.ErrPerm 33 } 34 35 return &ActionAPI{ 36 state: st, 37 resources: resources, 38 authorizer: authorizer, 39 check: common.NewBlockChecker(st), 40 }, nil 41 } 42 43 func (a *ActionAPI) checkCanRead() error { 44 canRead, err := a.authorizer.HasPermission(permission.ReadAccess, a.state.ModelTag()) 45 if err != nil { 46 return errors.Trace(err) 47 } 48 if !canRead { 49 return common.ErrPerm 50 } 51 return nil 52 } 53 54 func (a *ActionAPI) checkCanWrite() error { 55 canWrite, err := a.authorizer.HasPermission(permission.WriteAccess, a.state.ModelTag()) 56 if err != nil { 57 return errors.Trace(err) 58 } 59 if !canWrite { 60 return common.ErrPerm 61 } 62 return nil 63 } 64 65 func (a *ActionAPI) checkCanAdmin() error { 66 canAdmin, err := a.authorizer.HasPermission(permission.AdminAccess, a.state.ModelTag()) 67 if err != nil { 68 return errors.Trace(err) 69 } 70 if !canAdmin { 71 return common.ErrPerm 72 } 73 return nil 74 } 75 76 // Actions takes a list of ActionTags, and returns the full Action for 77 // each ID. 78 func (a *ActionAPI) Actions(arg params.Entities) (params.ActionResults, error) { 79 if err := a.checkCanRead(); err != nil { 80 return params.ActionResults{}, errors.Trace(err) 81 } 82 83 response := params.ActionResults{Results: make([]params.ActionResult, len(arg.Entities))} 84 for i, entity := range arg.Entities { 85 currentResult := &response.Results[i] 86 tag, err := names.ParseTag(entity.Tag) 87 if err != nil { 88 currentResult.Error = common.ServerError(common.ErrBadId) 89 continue 90 } 91 actionTag, ok := tag.(names.ActionTag) 92 if !ok { 93 currentResult.Error = common.ServerError(common.ErrBadId) 94 continue 95 } 96 action, err := a.state.ActionByTag(actionTag) 97 if err != nil { 98 currentResult.Error = common.ServerError(common.ErrBadId) 99 continue 100 } 101 receiverTag, err := names.ActionReceiverTag(action.Receiver()) 102 if err != nil { 103 currentResult.Error = common.ServerError(err) 104 continue 105 } 106 response.Results[i] = common.MakeActionResult(receiverTag, action) 107 } 108 return response, nil 109 } 110 111 // FindActionTagsByPrefix takes a list of string prefixes and finds 112 // corresponding ActionTags that match that prefix. 113 func (a *ActionAPI) FindActionTagsByPrefix(arg params.FindTags) (params.FindTagsResults, error) { 114 if err := a.checkCanRead(); err != nil { 115 return params.FindTagsResults{}, errors.Trace(err) 116 } 117 118 response := params.FindTagsResults{Matches: make(map[string][]params.Entity)} 119 for _, prefix := range arg.Prefixes { 120 found := a.state.FindActionTagsByPrefix(prefix) 121 matches := make([]params.Entity, len(found)) 122 for i, tag := range found { 123 matches[i] = params.Entity{Tag: tag.String()} 124 } 125 response.Matches[prefix] = matches 126 } 127 return response, nil 128 } 129 130 func (a *ActionAPI) FindActionsByNames(arg params.FindActionsByNames) (params.ActionsByNames, error) { 131 if err := a.checkCanWrite(); err != nil { 132 return params.ActionsByNames{}, errors.Trace(err) 133 } 134 135 response := params.ActionsByNames{Actions: make([]params.ActionsByName, len(arg.ActionNames))} 136 for i, name := range arg.ActionNames { 137 currentResult := &response.Actions[i] 138 currentResult.Name = name 139 140 actions, err := a.state.FindActionsByName(name) 141 if err != nil { 142 currentResult.Error = common.ServerError(err) 143 continue 144 } 145 for _, action := range actions { 146 recvTag, err := names.ActionReceiverTag(action.Receiver()) 147 if err != nil { 148 currentResult.Actions = append(currentResult.Actions, params.ActionResult{Error: common.ServerError(err)}) 149 continue 150 } 151 currentAction := common.MakeActionResult(recvTag, action) 152 currentResult.Actions = append(currentResult.Actions, currentAction) 153 } 154 } 155 return response, nil 156 } 157 158 // Enqueue takes a list of Actions and queues them up to be executed by 159 // the designated ActionReceiver, returning the params.Action for each 160 // enqueued Action, or an error if there was a problem enqueueing the 161 // Action. 162 func (a *ActionAPI) Enqueue(arg params.Actions) (params.ActionResults, error) { 163 if err := a.checkCanWrite(); err != nil { 164 return params.ActionResults{}, errors.Trace(err) 165 } 166 167 if err := a.check.ChangeAllowed(); err != nil { 168 return params.ActionResults{}, errors.Trace(err) 169 } 170 171 tagToActionReceiver := common.TagToActionReceiverFn(a.state.FindEntity) 172 response := params.ActionResults{Results: make([]params.ActionResult, len(arg.Actions))} 173 for i, action := range arg.Actions { 174 currentResult := &response.Results[i] 175 receiver, err := tagToActionReceiver(action.Receiver) 176 if err != nil { 177 currentResult.Error = common.ServerError(err) 178 continue 179 } 180 enqueued, err := receiver.AddAction(action.Name, action.Parameters) 181 if err != nil { 182 currentResult.Error = common.ServerError(err) 183 continue 184 } 185 186 response.Results[i] = common.MakeActionResult(receiver.Tag(), enqueued) 187 } 188 return response, nil 189 } 190 191 // ListAll takes a list of Entities representing ActionReceivers and 192 // returns all of the Actions that have been enqueued or run by each of 193 // those Entities. 194 func (a *ActionAPI) ListAll(arg params.Entities) (params.ActionsByReceivers, error) { 195 if err := a.checkCanRead(); err != nil { 196 return params.ActionsByReceivers{}, errors.Trace(err) 197 } 198 199 return a.internalList(arg, combine(pendingActions, runningActions, completedActions)) 200 } 201 202 // ListPending takes a list of Entities representing ActionReceivers 203 // and returns all of the Actions that are enqueued for each of those 204 // Entities. 205 func (a *ActionAPI) ListPending(arg params.Entities) (params.ActionsByReceivers, error) { 206 if err := a.checkCanRead(); err != nil { 207 return params.ActionsByReceivers{}, errors.Trace(err) 208 } 209 210 return a.internalList(arg, pendingActions) 211 } 212 213 // ListRunning takes a list of Entities representing ActionReceivers and 214 // returns all of the Actions that have are running on each of those 215 // Entities. 216 func (a *ActionAPI) ListRunning(arg params.Entities) (params.ActionsByReceivers, error) { 217 if err := a.checkCanRead(); err != nil { 218 return params.ActionsByReceivers{}, errors.Trace(err) 219 } 220 221 return a.internalList(arg, runningActions) 222 } 223 224 // ListCompleted takes a list of Entities representing ActionReceivers 225 // and returns all of the Actions that have been run on each of those 226 // Entities. 227 func (a *ActionAPI) ListCompleted(arg params.Entities) (params.ActionsByReceivers, error) { 228 if err := a.checkCanRead(); err != nil { 229 return params.ActionsByReceivers{}, errors.Trace(err) 230 } 231 232 return a.internalList(arg, completedActions) 233 } 234 235 // Cancel attempts to cancel enqueued Actions from running. 236 func (a *ActionAPI) Cancel(arg params.Entities) (params.ActionResults, error) { 237 if err := a.checkCanWrite(); err != nil { 238 return params.ActionResults{}, errors.Trace(err) 239 } 240 241 if err := a.check.ChangeAllowed(); err != nil { 242 return params.ActionResults{}, errors.Trace(err) 243 } 244 245 response := params.ActionResults{Results: make([]params.ActionResult, len(arg.Entities))} 246 for i, entity := range arg.Entities { 247 currentResult := &response.Results[i] 248 tag, err := names.ParseTag(entity.Tag) 249 if err != nil { 250 currentResult.Error = common.ServerError(common.ErrBadId) 251 continue 252 } 253 actionTag, ok := tag.(names.ActionTag) 254 if !ok { 255 currentResult.Error = common.ServerError(common.ErrBadId) 256 continue 257 } 258 action, err := a.state.ActionByTag(actionTag) 259 if err != nil { 260 currentResult.Error = common.ServerError(err) 261 continue 262 } 263 result, err := action.Finish(state.ActionResults{Status: state.ActionCancelled, Message: "action cancelled via the API"}) 264 if err != nil { 265 currentResult.Error = common.ServerError(err) 266 continue 267 } 268 receiverTag, err := names.ActionReceiverTag(result.Receiver()) 269 if err != nil { 270 currentResult.Error = common.ServerError(err) 271 continue 272 } 273 274 response.Results[i] = common.MakeActionResult(receiverTag, result) 275 } 276 return response, nil 277 } 278 279 // ApplicationsCharmsActions returns a slice of charm Actions for a slice of 280 // services. 281 func (a *ActionAPI) ApplicationsCharmsActions(args params.Entities) (params.ApplicationsCharmActionsResults, error) { 282 result := params.ApplicationsCharmActionsResults{Results: make([]params.ApplicationCharmActionsResult, len(args.Entities))} 283 if err := a.checkCanWrite(); err != nil { 284 return result, errors.Trace(err) 285 } 286 287 for i, entity := range args.Entities { 288 currentResult := &result.Results[i] 289 svcTag, err := names.ParseApplicationTag(entity.Tag) 290 if err != nil { 291 currentResult.Error = common.ServerError(common.ErrBadId) 292 continue 293 } 294 currentResult.ApplicationTag = svcTag.String() 295 svc, err := a.state.Application(svcTag.Id()) 296 if err != nil { 297 currentResult.Error = common.ServerError(err) 298 continue 299 } 300 ch, _, err := svc.Charm() 301 if err != nil { 302 currentResult.Error = common.ServerError(err) 303 continue 304 } 305 if actions := ch.Actions(); actions != nil { 306 charmActions := make(map[string]params.ActionSpec) 307 for key, value := range actions.ActionSpecs { 308 charmActions[key] = params.ActionSpec{ 309 Description: value.Description, 310 Params: value.Params, 311 } 312 } 313 currentResult.Actions = charmActions 314 } 315 } 316 return result, nil 317 } 318 319 // internalList takes a list of Entities representing ActionReceivers 320 // and returns all of the Actions the extractorFn can get out of the 321 // ActionReceiver. 322 func (a *ActionAPI) internalList(arg params.Entities, fn extractorFn) (params.ActionsByReceivers, error) { 323 tagToActionReceiver := common.TagToActionReceiverFn(a.state.FindEntity) 324 response := params.ActionsByReceivers{Actions: make([]params.ActionsByReceiver, len(arg.Entities))} 325 for i, entity := range arg.Entities { 326 currentResult := &response.Actions[i] 327 receiver, err := tagToActionReceiver(entity.Tag) 328 if err != nil { 329 currentResult.Error = common.ServerError(common.ErrBadId) 330 continue 331 } 332 currentResult.Receiver = receiver.Tag().String() 333 334 results, err := fn(receiver) 335 if err != nil { 336 currentResult.Error = common.ServerError(err) 337 continue 338 } 339 currentResult.Actions = results 340 } 341 return response, nil 342 } 343 344 // extractorFn is the generic signature for functions that extract 345 // state.Actions from an ActionReceiver, and return them as a slice of 346 // params.ActionResult. 347 type extractorFn func(state.ActionReceiver) ([]params.ActionResult, error) 348 349 // combine takes multiple extractorFn's and combines them into one 350 // function. 351 func combine(funcs ...extractorFn) extractorFn { 352 return func(ar state.ActionReceiver) ([]params.ActionResult, error) { 353 result := []params.ActionResult{} 354 for _, fn := range funcs { 355 items, err := fn(ar) 356 if err != nil { 357 return result, errors.Trace(err) 358 } 359 result = append(result, items...) 360 } 361 return result, nil 362 } 363 } 364 365 // pendingActions iterates through the Actions() enqueued for an 366 // ActionReceiver, and converts them to a slice of params.ActionResult. 367 func pendingActions(ar state.ActionReceiver) ([]params.ActionResult, error) { 368 return common.ConvertActions(ar, ar.PendingActions) 369 } 370 371 // runningActions iterates through the Actions() running on an 372 // ActionReceiver, and converts them to a slice of params.ActionResult. 373 func runningActions(ar state.ActionReceiver) ([]params.ActionResult, error) { 374 return common.ConvertActions(ar, ar.RunningActions) 375 } 376 377 // completedActions iterates through the Actions() that have run to 378 // completion for an ActionReceiver, and converts them to a slice of 379 // params.ActionResult. 380 func completedActions(ar state.ActionReceiver) ([]params.ActionResult, error) { 381 return common.ConvertActions(ar, ar.CompletedActions) 382 }