github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/core/description/status.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 // StatusArgs is an argument struct used to set the agent, application, or 14 // workload status. 15 type StatusArgs struct { 16 Value string 17 Message string 18 Data map[string]interface{} 19 Updated time.Time 20 } 21 22 func newStatus(args StatusArgs) *status { 23 return &status{ 24 Version: 1, 25 StatusPoint_: StatusPoint_{ 26 Value_: args.Value, 27 Message_: args.Message, 28 Data_: args.Data, 29 Updated_: args.Updated.UTC(), 30 }, 31 } 32 } 33 34 func newStatusHistory() StatusHistory_ { 35 return StatusHistory_{ 36 Version: 1, 37 } 38 } 39 40 // StatusPoint_ implements Status, and represents the status 41 // of an entity at a point in time. Used in the serialization of 42 // both status and StatusHistory_. 43 type StatusPoint_ struct { 44 Value_ string `yaml:"value"` 45 Message_ string `yaml:"message,omitempty"` 46 Data_ map[string]interface{} `yaml:"data,omitempty"` 47 Updated_ time.Time `yaml:"updated"` 48 } 49 50 type status struct { 51 Version int `yaml:"version"` 52 StatusPoint_ `yaml:"status"` 53 } 54 55 type StatusHistory_ struct { 56 Version int `yaml:"version"` 57 History []*StatusPoint_ `yaml:"history"` 58 } 59 60 // Value implements Status. 61 func (a *StatusPoint_) Value() string { 62 return a.Value_ 63 } 64 65 // Message implements Status. 66 func (a *StatusPoint_) Message() string { 67 return a.Message_ 68 } 69 70 // Data implements Status. 71 func (a *StatusPoint_) Data() map[string]interface{} { 72 return a.Data_ 73 } 74 75 // Updated implements Status. 76 func (a *StatusPoint_) Updated() time.Time { 77 return a.Updated_ 78 } 79 80 func importStatus(source map[string]interface{}) (*status, error) { 81 checker := versionedEmbeddedChecker("status") 82 coerced, err := checker.Coerce(source, nil) 83 if err != nil { 84 return nil, errors.Annotate(err, "status version schema check failed") 85 } 86 valid := coerced.(map[string]interface{}) 87 88 version := int(valid["version"].(int64)) 89 importFunc, ok := statusDeserializationFuncs[version] 90 if !ok { 91 return nil, errors.NotValidf("version %d", version) 92 } 93 94 source = valid["status"].(map[string]interface{}) 95 point, err := importFunc(source) 96 if err != nil { 97 return nil, errors.Trace(err) 98 } 99 return &status{ 100 Version: 1, 101 StatusPoint_: point, 102 }, nil 103 } 104 105 func importStatusHistory(history *StatusHistory_, source map[string]interface{}) error { 106 checker := versionedChecker("history") 107 coerced, err := checker.Coerce(source, nil) 108 if err != nil { 109 return errors.Annotate(err, "status version schema check failed") 110 } 111 valid := coerced.(map[string]interface{}) 112 113 version := int(valid["version"].(int64)) 114 importFunc, ok := statusDeserializationFuncs[version] 115 if !ok { 116 return errors.NotValidf("version %d", version) 117 } 118 119 sourceList := valid["history"].([]interface{}) 120 points, err := importStatusList(sourceList, importFunc) 121 if err != nil { 122 return errors.Trace(err) 123 } 124 history.History = points 125 return nil 126 } 127 128 func importStatusList(sourceList []interface{}, importFunc statusDeserializationFunc) ([]*StatusPoint_, error) { 129 result := make([]*StatusPoint_, 0, len(sourceList)) 130 for i, value := range sourceList { 131 source, ok := value.(map[string]interface{}) 132 if !ok { 133 return nil, errors.Errorf("unexpected value for status %d, %T", i, value) 134 } 135 point, err := importFunc(source) 136 if err != nil { 137 return nil, errors.Annotatef(err, "status history %d", i) 138 } 139 result = append(result, &point) 140 } 141 return result, nil 142 } 143 144 type statusDeserializationFunc func(map[string]interface{}) (StatusPoint_, error) 145 146 var statusDeserializationFuncs = map[int]statusDeserializationFunc{ 147 1: importStatusV1, 148 } 149 150 func importStatusV1(source map[string]interface{}) (StatusPoint_, error) { 151 fields := schema.Fields{ 152 "value": schema.String(), 153 "message": schema.String(), 154 "data": schema.StringMap(schema.Any()), 155 "updated": schema.Time(), 156 } 157 // Some values don't have to be there. 158 defaults := schema.Defaults{ 159 "message": "", 160 "data": schema.Omit, 161 } 162 checker := schema.FieldMap(fields, defaults) 163 164 coerced, err := checker.Coerce(source, nil) 165 if err != nil { 166 return StatusPoint_{}, errors.Annotatef(err, "status v1 schema check failed") 167 } 168 valid := coerced.(map[string]interface{}) 169 // From here we know that the map returned from the schema coercion 170 // contains fields of the right type. 171 172 var data map[string]interface{} 173 if sourceData, set := valid["data"]; set { 174 data = sourceData.(map[string]interface{}) 175 } 176 return StatusPoint_{ 177 Value_: valid["value"].(string), 178 Message_: valid["message"].(string), 179 Data_: data, 180 Updated_: valid["updated"].(time.Time), 181 }, nil 182 } 183 184 // StatusHistory implements HasStatusHistory. 185 func (s *StatusHistory_) StatusHistory() []Status { 186 var result []Status 187 if count := len(s.History); count > 0 { 188 result = make([]Status, count) 189 for i, value := range s.History { 190 result[i] = value 191 } 192 } 193 return result 194 } 195 196 // SetStatusHistory implements HasStatusHistory. 197 func (s *StatusHistory_) SetStatusHistory(args []StatusArgs) { 198 points := make([]*StatusPoint_, len(args)) 199 for i, arg := range args { 200 points[i] = &StatusPoint_{ 201 Value_: arg.Value, 202 Message_: arg.Message, 203 Data_: arg.Data, 204 Updated_: arg.Updated.UTC(), 205 } 206 } 207 s.History = points 208 } 209 210 func addStatusHistorySchema(fields schema.Fields) { 211 fields["status-history"] = schema.StringMap(schema.Any()) 212 } 213 214 func (s *StatusHistory_) importStatusHistory(valid map[string]interface{}) error { 215 return importStatusHistory(s, valid["status-history"].(map[string]interface{})) 216 }