1
// Copyright 2016 Canonical Ltd.
2
// Copyright 2016 Cloudbase Solutions
3
// Licensed under the AGPLv3, see LICENCE file for details.
8
"github.com/juju/errors"
9
"gopkg.in/juju/names.v2"
11
"github.com/juju/juju/apiserver/facade"
12
"github.com/juju/juju/apiserver/params"
13
"github.com/juju/juju/state"
14
"github.com/juju/juju/state/watcher"
17
// ParamsActionExecutionResultsToStateActionResults does exactly what
19
func ParamsActionExecutionResultsToStateActionResults(arg params.ActionExecutionResult) (state.ActionResults, error) {
20
var status state.ActionStatus
22
case params.ActionCancelled:
23
status = state.ActionCancelled
24
case params.ActionCompleted:
25
status = state.ActionCompleted
26
case params.ActionFailed:
27
status = state.ActionFailed
28
case params.ActionPending:
29
status = state.ActionPending
31
return state.ActionResults{}, errors.Errorf("unrecognized action status '%s'", arg.Status)
33
return state.ActionResults{
40
// TagToActionReceiver takes a tag string and tries to convert it to an
41
// ActionReceiver. It needs a findEntity function passed in that can search for the tags in state.
42
func TagToActionReceiverFn(findEntity func(names.Tag) (state.Entity, error)) func(tag string) (state.ActionReceiver, error) {
43
return func(tag string) (state.ActionReceiver, error) {
44
receiverTag, err := names.ParseTag(tag)
48
entity, err := findEntity(receiverTag)
52
receiver, ok := entity.(state.ActionReceiver)
60
// AuthAndActionFromTagFn takes in an authorizer function and a function that can fetch action by tags from state
61
// and returns a function that can fetch an action from state by id and check the authorization.
62
func AuthAndActionFromTagFn(canAccess AuthFunc, getActionByTag func(names.ActionTag) (state.Action, error)) func(string) (state.Action, error) {
63
return func(tag string) (state.Action, error) {
64
actionTag, err := names.ParseActionTag(tag)
66
return nil, errors.Trace(err)
68
action, err := getActionByTag(actionTag)
70
return nil, errors.Trace(err)
72
receiverTag, err := names.ActionReceiverTag(action.Receiver())
74
return nil, errors.Trace(err)
76
if !canAccess(receiverTag) {
83
// BeginActions calls begin on every action passed in through args.
84
// It's a helper function currently used by the uniter and by machineactions
85
// It needs an actionFn that can fetch an action from state using it's id, that's usually created by AuthAndActionFromTagFn
86
func BeginActions(args params.Entities, actionFn func(string) (state.Action, error)) params.ErrorResults {
87
results := params.ErrorResults{Results: make([]params.ErrorResult, len(args.Entities))}
89
for i, arg := range args.Entities {
90
action, err := actionFn(arg.Tag)
92
results.Results[i].Error = ServerError(err)
96
_, err = action.Begin()
98
results.Results[i].Error = ServerError(err)
106
// FinishActions saves the result of a completed Action.
107
// It's a helper function currently used by the uniter and by machineactions
108
// It needs an actionFn that can fetch an action from state using it's id that's usually created by AuthAndActionFromTagFn
109
func FinishActions(args params.ActionExecutionResults, actionFn func(string) (state.Action, error)) params.ErrorResults {
110
results := params.ErrorResults{Results: make([]params.ErrorResult, len(args.Results))}
112
for i, arg := range args.Results {
113
action, err := actionFn(arg.ActionTag)
115
results.Results[i].Error = ServerError(err)
118
actionResults, err := ParamsActionExecutionResultsToStateActionResults(arg)
120
results.Results[i].Error = ServerError(err)
124
_, err = action.Finish(actionResults)
126
results.Results[i].Error = ServerError(err)
134
// Actions returns the Actions by Tags passed in and ensures that the receiver asking for
135
// them is the same one that has the action.
136
// It's a helper function currently used by the uniter and by machineactions.
137
// It needs an actionFn that can fetch an action from state using it's id that's usually created by AuthAndActionFromTagFn
138
func Actions(args params.Entities, actionFn func(string) (state.Action, error)) params.ActionResults {
139
results := params.ActionResults{
140
Results: make([]params.ActionResult, len(args.Entities)),
143
for i, arg := range args.Entities {
144
action, err := actionFn(arg.Tag)
146
results.Results[i].Error = ServerError(err)
149
if action.Status() != state.ActionPending {
150
results.Results[i].Error = ServerError(ErrActionNotAvailable)
153
results.Results[i].Action = ¶ms.Action{
155
Parameters: action.Parameters(),
162
// WatchOneActionReceiverNotifications to create a watcher for one receiver.
163
// It needs a tagToActionReceiver function and a registerFunc to register
165
// It's a helper function currently used by the uniter and by machineactions
166
func WatchOneActionReceiverNotifications(tagToActionReceiver func(tag string) (state.ActionReceiver, error), registerFunc func(r facade.Resource) string) func(names.Tag) (params.StringsWatchResult, error) {
167
return func(tag names.Tag) (params.StringsWatchResult, error) {
168
nothing := params.StringsWatchResult{}
169
receiver, err := tagToActionReceiver(tag.String())
173
watch := receiver.WatchActionNotifications()
175
if changes, ok := <-watch.Changes(); ok {
176
return params.StringsWatchResult{
177
StringsWatcherId: registerFunc(watch),
181
return nothing, watcher.EnsureErr(watch)
185
// WatchActionNotifications returns a StringsWatcher for observing incoming actions towards an actionreceiver.
186
// It's a helper function currently used by the uniter and by machineactions
187
// canAccess is passed in by the respective caller to provide authorization.
188
// watchOne is usually a function created by WatchOneActionReceiverNotifications
189
func WatchActionNotifications(args params.Entities, canAccess AuthFunc, watchOne func(names.Tag) (params.StringsWatchResult, error)) params.StringsWatchResults {
190
result := params.StringsWatchResults{
191
Results: make([]params.StringsWatchResult, len(args.Entities)),
194
for i, entity := range args.Entities {
195
tag, err := names.ActionReceiverFromTag(entity.Tag)
197
result.Results[i].Error = ServerError(err)
202
result.Results[i], err = watchOne(tag)
204
result.Results[i].Error = ServerError(err)
210
// GetActionsFn declares the function type that returns a slice of
211
// state.Action and error, used to curry specific list functions.
212
type GetActionsFn func() ([]state.Action, error)
214
// ConvertActions takes a generic getActionsFn to obtain a slice
215
// of state.Action and then converts them to the API slice of
216
// params.ActionResult.
217
func ConvertActions(ar state.ActionReceiver, fn GetActionsFn) ([]params.ActionResult, error) {
218
items := []params.ActionResult{}
223
for _, action := range actions {
227
items = append(items, MakeActionResult(ar.Tag(), action))
232
// MakeActionResult does the actual type conversion from state.Action
233
// to params.ActionResult.
234
func MakeActionResult(actionReceiverTag names.Tag, action state.Action) params.ActionResult {
235
output, message := action.Results()
236
return params.ActionResult{
237
Action: ¶ms.Action{
238
Receiver: actionReceiverTag.String(),
239
Tag: action.ActionTag().String(),
241
Parameters: action.Parameters(),
243
Status: string(action.Status()),
246
Enqueued: action.Enqueued(),
247
Started: action.Started(),
248
Completed: action.Completed(),