github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/core/description/action.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package description 5 6 import ( 7 "time" 8 9 "github.com/juju/errors" 10 "github.com/juju/schema" 11 ) 12 13 type actions struct { 14 Version int `yaml:"version"` 15 Actions_ []*action `yaml:"actions"` 16 } 17 18 type action struct { 19 Id_ string `yaml:"id"` 20 Receiver_ string `yaml:"receiver"` 21 Name_ string `yaml:"name"` 22 Parameters_ map[string]interface{} `yaml:"parameters"` 23 Enqueued_ time.Time `yaml:"enqueued"` 24 // Can't use omitempty with time.Time, it just doesn't work 25 // (nothing is serialised), so use a pointer in the struct. 26 Started_ *time.Time `yaml:"started,omitempty"` 27 Completed_ *time.Time `yaml:"completed,omitempty"` 28 Status_ string `yaml:"status"` 29 Message_ string `yaml:"message"` 30 Results_ map[string]interface{} `yaml:"results"` 31 } 32 33 // Id implements Action. 34 func (i *action) Id() string { 35 return i.Id_ 36 } 37 38 // Receiver implements Action. 39 func (i *action) Receiver() string { 40 return i.Receiver_ 41 } 42 43 // Name implements Action. 44 func (i *action) Name() string { 45 return i.Name_ 46 } 47 48 // Parameters implements Action. 49 func (i *action) Parameters() map[string]interface{} { 50 return i.Parameters_ 51 } 52 53 // Enqueued implements Action. 54 func (i *action) Enqueued() time.Time { 55 return i.Enqueued_ 56 } 57 58 // Started implements Action. 59 func (i *action) Started() time.Time { 60 var zero time.Time 61 if i.Started_ == nil { 62 return zero 63 } 64 return *i.Started_ 65 } 66 67 // Completed implements Action. 68 func (i *action) Completed() time.Time { 69 var zero time.Time 70 if i.Completed_ == nil { 71 return zero 72 } 73 return *i.Completed_ 74 } 75 76 // Status implements Action. 77 func (i *action) Status() string { 78 return i.Status_ 79 } 80 81 // Message implements Action. 82 func (i *action) Message() string { 83 return i.Message_ 84 } 85 86 // Results implements Action. 87 func (i *action) Results() map[string]interface{} { 88 return i.Results_ 89 } 90 91 // ActionArgs is an argument struct used to create a 92 // new internal action type that supports the Action interface. 93 type ActionArgs struct { 94 Id string 95 Receiver string 96 Name string 97 Parameters map[string]interface{} 98 Enqueued time.Time 99 Started time.Time 100 Completed time.Time 101 Status string 102 Message string 103 Results map[string]interface{} 104 } 105 106 func newAction(args ActionArgs) *action { 107 action := &action{ 108 Receiver_: args.Receiver, 109 Name_: args.Name, 110 Parameters_: args.Parameters, 111 Enqueued_: args.Enqueued, 112 Status_: args.Status, 113 Message_: args.Message, 114 Id_: args.Id, 115 Results_: args.Results, 116 } 117 if !args.Started.IsZero() { 118 value := args.Started 119 action.Started_ = &value 120 } 121 if !args.Completed.IsZero() { 122 value := args.Completed 123 action.Completed_ = &value 124 } 125 return action 126 } 127 128 func importActions(source map[string]interface{}) ([]*action, error) { 129 checker := versionedChecker("actions") 130 coerced, err := checker.Coerce(source, nil) 131 if err != nil { 132 return nil, errors.Annotatef(err, "actions version schema check failed") 133 } 134 valid := coerced.(map[string]interface{}) 135 136 version := int(valid["version"].(int64)) 137 importFunc, ok := actionDeserializationFuncs[version] 138 if !ok { 139 return nil, errors.NotValidf("version %d", version) 140 } 141 sourceList := valid["actions"].([]interface{}) 142 return importActionList(sourceList, importFunc) 143 } 144 145 func importActionList(sourceList []interface{}, importFunc actionDeserializationFunc) ([]*action, error) { 146 result := make([]*action, 0, len(sourceList)) 147 for i, value := range sourceList { 148 source, ok := value.(map[string]interface{}) 149 if !ok { 150 return nil, errors.Errorf("unexpected value for action %d, %T", i, value) 151 } 152 action, err := importFunc(source) 153 if err != nil { 154 return nil, errors.Annotatef(err, "action %d", i) 155 } 156 result = append(result, action) 157 } 158 return result, nil 159 } 160 161 type actionDeserializationFunc func(map[string]interface{}) (*action, error) 162 163 var actionDeserializationFuncs = map[int]actionDeserializationFunc{ 164 1: importActionV1, 165 } 166 167 func importActionV1(source map[string]interface{}) (*action, error) { 168 fields := schema.Fields{ 169 "receiver": schema.String(), 170 "name": schema.String(), 171 "parameters": schema.StringMap(schema.Any()), 172 "enqueued": schema.Time(), 173 "started": schema.Time(), 174 "completed": schema.Time(), 175 "status": schema.String(), 176 "message": schema.String(), 177 "results": schema.StringMap(schema.Any()), 178 "id": schema.String(), 179 } 180 // Some values don't have to be there. 181 defaults := schema.Defaults{ 182 "started": time.Time{}, 183 "completed": time.Time{}, 184 } 185 checker := schema.FieldMap(fields, defaults) 186 187 coerced, err := checker.Coerce(source, nil) 188 if err != nil { 189 return nil, errors.Annotatef(err, "action v1 schema check failed") 190 } 191 valid := coerced.(map[string]interface{}) 192 action := &action{ 193 Id_: valid["id"].(string), 194 Receiver_: valid["receiver"].(string), 195 Name_: valid["name"].(string), 196 Status_: valid["status"].(string), 197 Message_: valid["message"].(string), 198 Parameters_: valid["parameters"].(map[string]interface{}), 199 Enqueued_: valid["enqueued"].(time.Time).UTC(), 200 Results_: valid["results"].(map[string]interface{}), 201 } 202 203 started := valid["started"].(time.Time) 204 if !started.IsZero() { 205 started = started.UTC() 206 action.Started_ = &started 207 } 208 completed := valid["completed"].(time.Time) 209 if !started.IsZero() { 210 completed = completed.UTC() 211 action.Completed_ = &completed 212 } 213 return action, nil 214 }