~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/apiserver/common/action.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2016 Canonical Ltd.
 
2
// Copyright 2016 Cloudbase Solutions
 
3
// Licensed under the AGPLv3, see LICENCE file for details.
 
4
 
 
5
package common
 
6
 
 
7
import (
 
8
        "github.com/juju/errors"
 
9
        "gopkg.in/juju/names.v2"
 
10
 
 
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"
 
15
)
 
16
 
 
17
// ParamsActionExecutionResultsToStateActionResults does exactly what
 
18
// the name implies.
 
19
func ParamsActionExecutionResultsToStateActionResults(arg params.ActionExecutionResult) (state.ActionResults, error) {
 
20
        var status state.ActionStatus
 
21
        switch arg.Status {
 
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
 
30
        default:
 
31
                return state.ActionResults{}, errors.Errorf("unrecognized action status '%s'", arg.Status)
 
32
        }
 
33
        return state.ActionResults{
 
34
                Status:  status,
 
35
                Results: arg.Results,
 
36
                Message: arg.Message,
 
37
        }, nil
 
38
}
 
39
 
 
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)
 
45
                if err != nil {
 
46
                        return nil, ErrBadId
 
47
                }
 
48
                entity, err := findEntity(receiverTag)
 
49
                if err != nil {
 
50
                        return nil, ErrBadId
 
51
                }
 
52
                receiver, ok := entity.(state.ActionReceiver)
 
53
                if !ok {
 
54
                        return nil, ErrBadId
 
55
                }
 
56
                return receiver, nil
 
57
        }
 
58
}
 
59
 
 
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)
 
65
                if err != nil {
 
66
                        return nil, errors.Trace(err)
 
67
                }
 
68
                action, err := getActionByTag(actionTag)
 
69
                if err != nil {
 
70
                        return nil, errors.Trace(err)
 
71
                }
 
72
                receiverTag, err := names.ActionReceiverTag(action.Receiver())
 
73
                if err != nil {
 
74
                        return nil, errors.Trace(err)
 
75
                }
 
76
                if !canAccess(receiverTag) {
 
77
                        return nil, ErrPerm
 
78
                }
 
79
                return action, nil
 
80
        }
 
81
}
 
82
 
 
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))}
 
88
 
 
89
        for i, arg := range args.Entities {
 
90
                action, err := actionFn(arg.Tag)
 
91
                if err != nil {
 
92
                        results.Results[i].Error = ServerError(err)
 
93
                        continue
 
94
                }
 
95
 
 
96
                _, err = action.Begin()
 
97
                if err != nil {
 
98
                        results.Results[i].Error = ServerError(err)
 
99
                        continue
 
100
                }
 
101
        }
 
102
 
 
103
        return results
 
104
}
 
105
 
 
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))}
 
111
 
 
112
        for i, arg := range args.Results {
 
113
                action, err := actionFn(arg.ActionTag)
 
114
                if err != nil {
 
115
                        results.Results[i].Error = ServerError(err)
 
116
                        continue
 
117
                }
 
118
                actionResults, err := ParamsActionExecutionResultsToStateActionResults(arg)
 
119
                if err != nil {
 
120
                        results.Results[i].Error = ServerError(err)
 
121
                        continue
 
122
                }
 
123
 
 
124
                _, err = action.Finish(actionResults)
 
125
                if err != nil {
 
126
                        results.Results[i].Error = ServerError(err)
 
127
                        continue
 
128
                }
 
129
        }
 
130
 
 
131
        return results
 
132
}
 
133
 
 
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)),
 
141
        }
 
142
 
 
143
        for i, arg := range args.Entities {
 
144
                action, err := actionFn(arg.Tag)
 
145
                if err != nil {
 
146
                        results.Results[i].Error = ServerError(err)
 
147
                        continue
 
148
                }
 
149
                if action.Status() != state.ActionPending {
 
150
                        results.Results[i].Error = ServerError(ErrActionNotAvailable)
 
151
                        continue
 
152
                }
 
153
                results.Results[i].Action = &params.Action{
 
154
                        Name:       action.Name(),
 
155
                        Parameters: action.Parameters(),
 
156
                }
 
157
        }
 
158
 
 
159
        return results
 
160
}
 
161
 
 
162
// WatchOneActionReceiverNotifications to create a watcher for one receiver.
 
163
// It needs a tagToActionReceiver function and a registerFunc to register
 
164
// resources.
 
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())
 
170
                if err != nil {
 
171
                        return nothing, err
 
172
                }
 
173
                watch := receiver.WatchActionNotifications()
 
174
 
 
175
                if changes, ok := <-watch.Changes(); ok {
 
176
                        return params.StringsWatchResult{
 
177
                                StringsWatcherId: registerFunc(watch),
 
178
                                Changes:          changes,
 
179
                        }, nil
 
180
                }
 
181
                return nothing, watcher.EnsureErr(watch)
 
182
        }
 
183
}
 
184
 
 
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)),
 
192
        }
 
193
 
 
194
        for i, entity := range args.Entities {
 
195
                tag, err := names.ActionReceiverFromTag(entity.Tag)
 
196
                if err != nil {
 
197
                        result.Results[i].Error = ServerError(err)
 
198
                        continue
 
199
                }
 
200
                err = ErrPerm
 
201
                if canAccess(tag) {
 
202
                        result.Results[i], err = watchOne(tag)
 
203
                }
 
204
                result.Results[i].Error = ServerError(err)
 
205
        }
 
206
 
 
207
        return result
 
208
}
 
209
 
 
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)
 
213
 
 
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{}
 
219
        actions, err := fn()
 
220
        if err != nil {
 
221
                return items, err
 
222
        }
 
223
        for _, action := range actions {
 
224
                if action == nil {
 
225
                        continue
 
226
                }
 
227
                items = append(items, MakeActionResult(ar.Tag(), action))
 
228
        }
 
229
        return items, nil
 
230
}
 
231
 
 
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: &params.Action{
 
238
                        Receiver:   actionReceiverTag.String(),
 
239
                        Tag:        action.ActionTag().String(),
 
240
                        Name:       action.Name(),
 
241
                        Parameters: action.Parameters(),
 
242
                },
 
243
                Status:    string(action.Status()),
 
244
                Message:   message,
 
245
                Output:    output,
 
246
                Enqueued:  action.Enqueued(),
 
247
                Started:   action.Started(),
 
248
                Completed: action.Completed(),
 
249
        }
 
250
}