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