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  }