-
Notifications
You must be signed in to change notification settings - Fork 23
/
operation.go
232 lines (202 loc) · 6.09 KB
/
operation.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
// Copyright 2020 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package description
import (
"time"
"github.com/juju/errors"
"github.com/juju/schema"
)
// Operation represents an operation.
type Operation interface {
Id() string
Summary() string
Fail() string
Enqueued() time.Time
Started() time.Time
Completed() time.Time
Status() string
CompleteTaskCount() int
SpawnedTaskCount() int
}
type operations struct {
Version int `yaml:"version"`
Operations_ []*operation `yaml:"operations"`
}
type operation struct {
Id_ string `yaml:"id"`
Summary_ string `yaml:"summary"`
Enqueued_ time.Time `yaml:"enqueued"`
// Can't use omitempty with time.Time, it just doesn't work
// (nothing is serialised), so use a pointer in the struct.
Started_ *time.Time `yaml:"started,omitempty"`
Completed_ *time.Time `yaml:"completed,omitempty"`
Status_ string `yaml:"status"`
Fail_ string `yaml:"fail,omitempty"`
CompleteTaskCount_ int `yaml:"complete-task-count"`
SpawnedTaskCount_ int `yaml:"spawned-task-count"`
}
// Id implements Operation.
func (i *operation) Id() string {
return i.Id_
}
// Summary implements Operation.
func (i *operation) Summary() string {
return i.Summary_
}
// Fail implements Operation.
func (i *operation) Fail() string {
return i.Fail_
}
// Enqueued implements Operation.
func (i *operation) Enqueued() time.Time {
return i.Enqueued_
}
// Started implements Operation.
func (i *operation) Started() time.Time {
var zero time.Time
if i.Started_ == nil {
return zero
}
return *i.Started_
}
// Completed implements Operation.
func (i *operation) Completed() time.Time {
var zero time.Time
if i.Completed_ == nil {
return zero
}
return *i.Completed_
}
// Status implements Operation.
func (i *operation) Status() string {
return i.Status_
}
// CompleteTaskCount implements Operation.
func (i *operation) CompleteTaskCount() int {
return i.CompleteTaskCount_
}
// SpawnedTaskCount implements Operation.
func (i *operation) SpawnedTaskCount() int {
return i.SpawnedTaskCount_
}
// OperationArgs is an argument struct used to create a
// new internal operation type that supports the Operation interface.
type OperationArgs struct {
Id string
Summary string
Enqueued time.Time
Started time.Time
Completed time.Time
Status string
Fail string
CompleteTaskCount int
SpawnedTaskCount int
}
func newOperation(args OperationArgs) *operation {
operation := &operation{
Id_: args.Id,
Summary_: args.Summary,
Enqueued_: args.Enqueued,
Status_: args.Status,
Fail_: args.Fail,
CompleteTaskCount_: args.CompleteTaskCount,
SpawnedTaskCount_: args.SpawnedTaskCount,
}
if !args.Started.IsZero() {
value := args.Started
operation.Started_ = &value
}
if !args.Completed.IsZero() {
value := args.Completed
operation.Completed_ = &value
}
return operation
}
func importOperations(source map[string]interface{}) ([]*operation, error) {
checker := versionedChecker("operations")
coerced, err := checker.Coerce(source, nil)
if err != nil {
return nil, errors.Annotatef(err, "operations version schema check failed")
}
valid := coerced.(map[string]interface{})
version := int(valid["version"].(int64))
sourceList := valid["operations"].([]interface{})
return importOperationList(sourceList, version)
}
func importOperationList(sourceList []interface{}, version int) ([]*operation, error) {
getFields, ok := operationFieldsFuncs[version]
if !ok {
return nil, errors.NotValidf("version %d", version)
}
result := make([]*operation, 0, len(sourceList))
for i, value := range sourceList {
source, ok := value.(map[string]interface{})
if !ok {
return nil, errors.Errorf("unexpected value for operation %d, %T", i, value)
}
operation, err := importOperation(source, version, getFields)
if err != nil {
return nil, errors.Annotatef(err, "operation %d", i)
}
result = append(result, operation)
}
return result, nil
}
var operationFieldsFuncs = map[int]fieldsFunc{
1: operationV1Fields,
2: operationV2Fields,
3: operationV3Fields,
}
func operationV1Fields() (schema.Fields, schema.Defaults) {
fields := schema.Fields{
"id": schema.String(),
"summary": schema.String(),
"enqueued": schema.Time(),
"started": schema.Time(),
"completed": schema.Time(),
"status": schema.String(),
"complete-task-count": schema.Int(),
}
// Some values don't have to be there.
defaults := schema.Defaults{
"started": schema.Omit,
"completed": schema.Omit,
}
return fields, defaults
}
func operationV2Fields() (schema.Fields, schema.Defaults) {
fields, defaults := operationV1Fields()
fields["fail"] = schema.String()
defaults["fail"] = schema.Omit
return fields, defaults
}
func operationV3Fields() (schema.Fields, schema.Defaults) {
fields, defaults := operationV2Fields()
fields["spawned-task-count"] = schema.Int()
return fields, defaults
}
func importOperation(source map[string]interface{}, importVersion int, fieldFunc func() (schema.Fields, schema.Defaults)) (*operation, error) {
fields, defaults := fieldFunc()
checker := schema.FieldMap(fields, defaults)
coerced, err := checker.Coerce(source, nil)
if err != nil {
return nil, errors.Annotatef(err, "operation v%d schema check failed", importVersion)
}
valid := coerced.(map[string]interface{})
operation := &operation{
Id_: valid["id"].(string),
Summary_: valid["summary"].(string),
Status_: valid["status"].(string),
Enqueued_: valid["enqueued"].(time.Time).UTC(),
Started_: fieldToTimePtr(valid, "started"),
Completed_: fieldToTimePtr(valid, "completed"),
CompleteTaskCount_: int(valid["complete-task-count"].(int64)),
}
if importVersion >= 2 {
operation.Fail_, _ = valid["fail"].(string)
}
if importVersion >= 3 {
operation.SpawnedTaskCount_ = int(valid["spawned-task-count"].(int64))
}
return operation, nil
}