github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/core/description/unit.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  	"github.com/juju/errors"
     8  	"github.com/juju/schema"
     9  	"gopkg.in/juju/names.v2"
    10  )
    11  
    12  type units struct {
    13  	Version int     `yaml:"version"`
    14  	Units_  []*unit `yaml:"units"`
    15  }
    16  
    17  type unit struct {
    18  	Name_ string `yaml:"name"`
    19  
    20  	Machine_ string `yaml:"machine"`
    21  
    22  	AgentStatus_        *status        `yaml:"agent-status"`
    23  	AgentStatusHistory_ StatusHistory_ `yaml:"agent-status-history"`
    24  
    25  	WorkloadStatus_        *status        `yaml:"workload-status"`
    26  	WorkloadStatusHistory_ StatusHistory_ `yaml:"workload-status-history"`
    27  
    28  	WorkloadVersion_        string         `yaml:"workload-version,omitempty"`
    29  	WorkloadVersionHistory_ StatusHistory_ `yaml:"workload-version-history"`
    30  
    31  	Principal_    string   `yaml:"principal,omitempty"`
    32  	Subordinates_ []string `yaml:"subordinates,omitempty"`
    33  
    34  	PasswordHash_ string      `yaml:"password-hash"`
    35  	Tools_        *agentTools `yaml:"tools"`
    36  
    37  	MeterStatusCode_ string `yaml:"meter-status-code,omitempty"`
    38  	MeterStatusInfo_ string `yaml:"meter-status-info,omitempty"`
    39  
    40  	Annotations_ `yaml:"annotations,omitempty"`
    41  
    42  	Constraints_ *constraints `yaml:"constraints,omitempty"`
    43  
    44  	Payloads_ payloads `yaml:"payloads"`
    45  }
    46  
    47  // UnitArgs is an argument struct used to add a Unit to a Application in the Model.
    48  type UnitArgs struct {
    49  	Tag          names.UnitTag
    50  	Machine      names.MachineTag
    51  	PasswordHash string
    52  	Principal    names.UnitTag
    53  	Subordinates []names.UnitTag
    54  
    55  	WorkloadVersion string
    56  	MeterStatusCode string
    57  	MeterStatusInfo string
    58  
    59  	// TODO: storage attachment count
    60  }
    61  
    62  func newUnit(args UnitArgs) *unit {
    63  	var subordinates []string
    64  	for _, s := range args.Subordinates {
    65  		subordinates = append(subordinates, s.Id())
    66  	}
    67  	u := &unit{
    68  		Name_:                   args.Tag.Id(),
    69  		Machine_:                args.Machine.Id(),
    70  		PasswordHash_:           args.PasswordHash,
    71  		Principal_:              args.Principal.Id(),
    72  		Subordinates_:           subordinates,
    73  		WorkloadVersion_:        args.WorkloadVersion,
    74  		MeterStatusCode_:        args.MeterStatusCode,
    75  		MeterStatusInfo_:        args.MeterStatusInfo,
    76  		WorkloadStatusHistory_:  newStatusHistory(),
    77  		WorkloadVersionHistory_: newStatusHistory(),
    78  		AgentStatusHistory_:     newStatusHistory(),
    79  	}
    80  	u.setPayloads(nil)
    81  	return u
    82  }
    83  
    84  // Tag implements Unit.
    85  func (u *unit) Tag() names.UnitTag {
    86  	return names.NewUnitTag(u.Name_)
    87  }
    88  
    89  // Name implements Unit.
    90  func (u *unit) Name() string {
    91  	return u.Name_
    92  }
    93  
    94  // Machine implements Unit.
    95  func (u *unit) Machine() names.MachineTag {
    96  	return names.NewMachineTag(u.Machine_)
    97  }
    98  
    99  // PasswordHash implements Unit.
   100  func (u *unit) PasswordHash() string {
   101  	return u.PasswordHash_
   102  }
   103  
   104  // Principal implements Unit.
   105  func (u *unit) Principal() names.UnitTag {
   106  	if u.Principal_ == "" {
   107  		return names.UnitTag{}
   108  	}
   109  	return names.NewUnitTag(u.Principal_)
   110  }
   111  
   112  // Subordinates implements Unit.
   113  func (u *unit) Subordinates() []names.UnitTag {
   114  	var subordinates []names.UnitTag
   115  	for _, s := range u.Subordinates_ {
   116  		subordinates = append(subordinates, names.NewUnitTag(s))
   117  	}
   118  	return subordinates
   119  }
   120  
   121  // MeterStatusCode implements Unit.
   122  func (u *unit) MeterStatusCode() string {
   123  	return u.MeterStatusCode_
   124  }
   125  
   126  // MeterStatusInfo implements Unit.
   127  func (u *unit) MeterStatusInfo() string {
   128  	return u.MeterStatusInfo_
   129  }
   130  
   131  // Tools implements Unit.
   132  func (u *unit) Tools() AgentTools {
   133  	// To avoid a typed nil, check before returning.
   134  	if u.Tools_ == nil {
   135  		return nil
   136  	}
   137  	return u.Tools_
   138  }
   139  
   140  // SetTools implements Unit.
   141  func (u *unit) SetTools(args AgentToolsArgs) {
   142  	u.Tools_ = newAgentTools(args)
   143  }
   144  
   145  // WorkloadVersion implements Unit.
   146  func (u *unit) WorkloadVersion() string {
   147  	return u.WorkloadVersion_
   148  }
   149  
   150  // WorkloadStatus implements Unit.
   151  func (u *unit) WorkloadStatus() Status {
   152  	// To avoid typed nils check nil here.
   153  	if u.WorkloadStatus_ == nil {
   154  		return nil
   155  	}
   156  	return u.WorkloadStatus_
   157  }
   158  
   159  // SetWorkloadStatus implements Unit.
   160  func (u *unit) SetWorkloadStatus(args StatusArgs) {
   161  	u.WorkloadStatus_ = newStatus(args)
   162  }
   163  
   164  // WorkloadStatusHistory implements Unit.
   165  func (u *unit) WorkloadStatusHistory() []Status {
   166  	return u.WorkloadStatusHistory_.StatusHistory()
   167  }
   168  
   169  // SetWorkloadStatusHistory implements Unit.
   170  func (u *unit) SetWorkloadStatusHistory(args []StatusArgs) {
   171  	u.WorkloadStatusHistory_.SetStatusHistory(args)
   172  }
   173  
   174  // WorkloadVersionHistory implements Unit.
   175  func (u *unit) WorkloadVersionHistory() []Status {
   176  	return u.WorkloadVersionHistory_.StatusHistory()
   177  }
   178  
   179  // SetWorkloadVersionHistory implements Unit.
   180  func (u *unit) SetWorkloadVersionHistory(args []StatusArgs) {
   181  	u.WorkloadVersionHistory_.SetStatusHistory(args)
   182  }
   183  
   184  // AgentStatus implements Unit.
   185  func (u *unit) AgentStatus() Status {
   186  	// To avoid typed nils check nil here.
   187  	if u.AgentStatus_ == nil {
   188  		return nil
   189  	}
   190  	return u.AgentStatus_
   191  }
   192  
   193  // SetAgentStatus implements Unit.
   194  func (u *unit) SetAgentStatus(args StatusArgs) {
   195  	u.AgentStatus_ = newStatus(args)
   196  }
   197  
   198  // AgentStatusHistory implements Unit.
   199  func (u *unit) AgentStatusHistory() []Status {
   200  	return u.AgentStatusHistory_.StatusHistory()
   201  }
   202  
   203  // SetAgentStatusHistory implements Unit.
   204  func (u *unit) SetAgentStatusHistory(args []StatusArgs) {
   205  	u.AgentStatusHistory_.SetStatusHistory(args)
   206  }
   207  
   208  // Constraints implements HasConstraints.
   209  func (u *unit) Constraints() Constraints {
   210  	if u.Constraints_ == nil {
   211  		return nil
   212  	}
   213  	return u.Constraints_
   214  }
   215  
   216  // SetConstraints implements HasConstraints.
   217  func (u *unit) SetConstraints(args ConstraintsArgs) {
   218  	u.Constraints_ = newConstraints(args)
   219  }
   220  
   221  // AddPayload implements Unit.
   222  func (u *unit) AddPayload(args PayloadArgs) Payload {
   223  	payload := newPayload(args)
   224  	u.Payloads_.Payloads_ = append(u.Payloads_.Payloads_, payload)
   225  	return payload
   226  }
   227  
   228  // Payloads implements Unit.
   229  func (u *unit) Payloads() []Payload {
   230  	var result []Payload
   231  	for _, payload := range u.Payloads_.Payloads_ {
   232  		result = append(result, payload)
   233  	}
   234  	return result
   235  }
   236  
   237  func (u *unit) setPayloads(payloadList []*payload) {
   238  	u.Payloads_ = payloads{
   239  		Version:   1,
   240  		Payloads_: payloadList,
   241  	}
   242  }
   243  
   244  // Validate impelements Unit.
   245  func (u *unit) Validate() error {
   246  	if u.Name_ == "" {
   247  		return errors.NotValidf("missing name")
   248  	}
   249  	if u.AgentStatus_ == nil {
   250  		return errors.NotValidf("unit %q missing agent status", u.Name_)
   251  	}
   252  	if u.WorkloadStatus_ == nil {
   253  		return errors.NotValidf("unit %q missing workload status", u.Name_)
   254  	}
   255  	if u.Tools_ == nil {
   256  		return errors.NotValidf("unit %q missing tools", u.Name_)
   257  	}
   258  	return nil
   259  }
   260  
   261  func importUnits(source map[string]interface{}) ([]*unit, error) {
   262  	checker := versionedChecker("units")
   263  	coerced, err := checker.Coerce(source, nil)
   264  	if err != nil {
   265  		return nil, errors.Annotatef(err, "units version schema check failed")
   266  	}
   267  	valid := coerced.(map[string]interface{})
   268  
   269  	version := int(valid["version"].(int64))
   270  	importFunc, ok := unitDeserializationFuncs[version]
   271  	if !ok {
   272  		return nil, errors.NotValidf("version %d", version)
   273  	}
   274  	sourceList := valid["units"].([]interface{})
   275  	return importUnitList(sourceList, importFunc)
   276  }
   277  
   278  func importUnitList(sourceList []interface{}, importFunc unitDeserializationFunc) ([]*unit, error) {
   279  	result := make([]*unit, 0, len(sourceList))
   280  	for i, value := range sourceList {
   281  		source, ok := value.(map[string]interface{})
   282  		if !ok {
   283  			return nil, errors.Errorf("unexpected value for unit %d, %T", i, value)
   284  		}
   285  		unit, err := importFunc(source)
   286  		if err != nil {
   287  			return nil, errors.Annotatef(err, "unit %d", i)
   288  		}
   289  		result = append(result, unit)
   290  	}
   291  	return result, nil
   292  }
   293  
   294  type unitDeserializationFunc func(map[string]interface{}) (*unit, error)
   295  
   296  var unitDeserializationFuncs = map[int]unitDeserializationFunc{
   297  	1: importUnitV1,
   298  }
   299  
   300  func importUnitV1(source map[string]interface{}) (*unit, error) {
   301  	fields := schema.Fields{
   302  		"name":    schema.String(),
   303  		"machine": schema.String(),
   304  
   305  		"agent-status":             schema.StringMap(schema.Any()),
   306  		"agent-status-history":     schema.StringMap(schema.Any()),
   307  		"workload-status":          schema.StringMap(schema.Any()),
   308  		"workload-status-history":  schema.StringMap(schema.Any()),
   309  		"workload-version":         schema.String(),
   310  		"workload-version-history": schema.StringMap(schema.Any()),
   311  
   312  		"principal":    schema.String(),
   313  		"subordinates": schema.List(schema.String()),
   314  
   315  		"password-hash": schema.String(),
   316  		"tools":         schema.StringMap(schema.Any()),
   317  
   318  		"meter-status-code": schema.String(),
   319  		"meter-status-info": schema.String(),
   320  
   321  		"payloads": schema.StringMap(schema.Any()),
   322  	}
   323  	defaults := schema.Defaults{
   324  		"principal":         "",
   325  		"subordinates":      schema.Omit,
   326  		"workload-version":  "",
   327  		"meter-status-code": "",
   328  		"meter-status-info": "",
   329  	}
   330  	addAnnotationSchema(fields, defaults)
   331  	addConstraintsSchema(fields, defaults)
   332  	checker := schema.FieldMap(fields, defaults)
   333  
   334  	coerced, err := checker.Coerce(source, nil)
   335  	if err != nil {
   336  		return nil, errors.Annotatef(err, "unit v1 schema check failed")
   337  	}
   338  	valid := coerced.(map[string]interface{})
   339  	// From here we know that the map returned from the schema coercion
   340  	// contains fields of the right type.
   341  
   342  	result := &unit{
   343  		Name_:                   valid["name"].(string),
   344  		Machine_:                valid["machine"].(string),
   345  		Principal_:              valid["principal"].(string),
   346  		PasswordHash_:           valid["password-hash"].(string),
   347  		WorkloadVersion_:        valid["workload-version"].(string),
   348  		MeterStatusCode_:        valid["meter-status-code"].(string),
   349  		MeterStatusInfo_:        valid["meter-status-info"].(string),
   350  		WorkloadStatusHistory_:  newStatusHistory(),
   351  		WorkloadVersionHistory_: newStatusHistory(),
   352  		AgentStatusHistory_:     newStatusHistory(),
   353  	}
   354  	result.importAnnotations(valid)
   355  
   356  	workloadStatusHistory := valid["workload-status-history"].(map[string]interface{})
   357  	if err := importStatusHistory(&result.WorkloadStatusHistory_, workloadStatusHistory); err != nil {
   358  		return nil, errors.Trace(err)
   359  	}
   360  	workloadVersionHistory := valid["workload-version-history"].(map[string]interface{})
   361  	if err := importStatusHistory(&result.WorkloadVersionHistory_, workloadVersionHistory); err != nil {
   362  		return nil, errors.Trace(err)
   363  	}
   364  	agentHistory := valid["agent-status-history"].(map[string]interface{})
   365  	if err := importStatusHistory(&result.AgentStatusHistory_, agentHistory); err != nil {
   366  		return nil, errors.Trace(err)
   367  	}
   368  
   369  	if constraintsMap, ok := valid["constraints"]; ok {
   370  		constraints, err := importConstraints(constraintsMap.(map[string]interface{}))
   371  		if err != nil {
   372  			return nil, errors.Trace(err)
   373  		}
   374  		result.Constraints_ = constraints
   375  	}
   376  
   377  	result.Subordinates_ = convertToStringSlice(valid["subordinates"])
   378  
   379  	// Tools and status are required, so we expect them to be there.
   380  	tools, err := importAgentTools(valid["tools"].(map[string]interface{}))
   381  	if err != nil {
   382  		return nil, errors.Trace(err)
   383  	}
   384  	result.Tools_ = tools
   385  
   386  	agentStatus, err := importStatus(valid["agent-status"].(map[string]interface{}))
   387  	if err != nil {
   388  		return nil, errors.Trace(err)
   389  	}
   390  	result.AgentStatus_ = agentStatus
   391  
   392  	workloadStatus, err := importStatus(valid["workload-status"].(map[string]interface{}))
   393  	if err != nil {
   394  		return nil, errors.Trace(err)
   395  	}
   396  	result.WorkloadStatus_ = workloadStatus
   397  
   398  	payloadMap := valid["payloads"].(map[string]interface{})
   399  	payloads, err := importPayloads(payloadMap)
   400  	if err != nil {
   401  		return nil, errors.Annotate(err, "payloads")
   402  	}
   403  	result.setPayloads(payloads)
   404  
   405  	return result, nil
   406  }