github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/core/description/relation.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  	"github.com/juju/utils/set"
    10  )
    11  
    12  type relations struct {
    13  	Version    int         `yaml:"version"`
    14  	Relations_ []*relation `yaml:"relations"`
    15  }
    16  
    17  type relation struct {
    18  	Id_        int        `yaml:"id"`
    19  	Key_       string     `yaml:"key"`
    20  	Endpoints_ *endpoints `yaml:"endpoints"`
    21  }
    22  
    23  // RelationArgs is an argument struct used to specify a relation.
    24  type RelationArgs struct {
    25  	Id  int
    26  	Key string
    27  }
    28  
    29  func newRelation(args RelationArgs) *relation {
    30  	relation := &relation{
    31  		Id_:  args.Id,
    32  		Key_: args.Key,
    33  	}
    34  	relation.setEndpoints(nil)
    35  	return relation
    36  }
    37  
    38  // Id implements Relation.
    39  func (r *relation) Id() int {
    40  	return r.Id_
    41  }
    42  
    43  // Key implements Relation.
    44  func (r *relation) Key() string {
    45  	return r.Key_
    46  }
    47  
    48  // Endpoints implements Relation.
    49  func (r *relation) Endpoints() []Endpoint {
    50  	result := make([]Endpoint, len(r.Endpoints_.Endpoints_))
    51  	for i, ep := range r.Endpoints_.Endpoints_ {
    52  		result[i] = ep
    53  	}
    54  	return result
    55  }
    56  
    57  // AddEndpoint implements Relation.
    58  func (r *relation) AddEndpoint(args EndpointArgs) Endpoint {
    59  	ep := newEndpoint(args)
    60  	r.Endpoints_.Endpoints_ = append(r.Endpoints_.Endpoints_, ep)
    61  	return ep
    62  }
    63  
    64  func (r *relation) setEndpoints(endpointList []*endpoint) {
    65  	r.Endpoints_ = &endpoints{
    66  		Version:    1,
    67  		Endpoints_: endpointList,
    68  	}
    69  }
    70  
    71  func importRelations(source map[string]interface{}) ([]*relation, error) {
    72  	checker := versionedChecker("relations")
    73  	coerced, err := checker.Coerce(source, nil)
    74  	if err != nil {
    75  		return nil, errors.Annotatef(err, "relations version schema check failed")
    76  	}
    77  	valid := coerced.(map[string]interface{})
    78  
    79  	version := int(valid["version"].(int64))
    80  	importFunc, ok := relationDeserializationFuncs[version]
    81  	if !ok {
    82  		return nil, errors.NotValidf("version %d", version)
    83  	}
    84  	relationList := valid["relations"].([]interface{})
    85  	return importRelationList(relationList, importFunc)
    86  }
    87  
    88  func importRelationList(sourceList []interface{}, importFunc relationDeserializationFunc) ([]*relation, error) {
    89  	result := make([]*relation, 0, len(sourceList))
    90  	for i, value := range sourceList {
    91  		source, ok := value.(map[string]interface{})
    92  		if !ok {
    93  			return nil, errors.Errorf("unexpected value for relation %d, %T", i, value)
    94  		}
    95  		relation, err := importFunc(source)
    96  		if err != nil {
    97  			return nil, errors.Annotatef(err, "relation %d", i)
    98  		}
    99  		result = append(result, relation)
   100  	}
   101  	return result, nil
   102  }
   103  
   104  type relationDeserializationFunc func(map[string]interface{}) (*relation, error)
   105  
   106  var relationDeserializationFuncs = map[int]relationDeserializationFunc{
   107  	1: importRelationV1,
   108  }
   109  
   110  func importRelationV1(source map[string]interface{}) (*relation, error) {
   111  	fields := schema.Fields{
   112  		"id":        schema.Int(),
   113  		"key":       schema.String(),
   114  		"endpoints": schema.StringMap(schema.Any()),
   115  	}
   116  
   117  	checker := schema.FieldMap(fields, nil) // no defaults
   118  
   119  	coerced, err := checker.Coerce(source, nil)
   120  	if err != nil {
   121  		return nil, errors.Annotatef(err, "relation v1 schema check failed")
   122  	}
   123  	valid := coerced.(map[string]interface{})
   124  	// From here we know that the map returned from the schema coercion
   125  	// contains fields of the right type.
   126  	result := &relation{
   127  		Id_:  int(valid["id"].(int64)),
   128  		Key_: valid["key"].(string),
   129  	}
   130  
   131  	endpoints, err := importEndpoints(valid["endpoints"].(map[string]interface{}))
   132  	if err != nil {
   133  		return nil, errors.Trace(err)
   134  	}
   135  	result.setEndpoints(endpoints)
   136  
   137  	return result, nil
   138  }
   139  
   140  type endpoints struct {
   141  	Version    int         `yaml:"version"`
   142  	Endpoints_ []*endpoint `yaml:"endpoints"`
   143  }
   144  
   145  type endpoint struct {
   146  	ApplicationName_ string `yaml:"application-name"`
   147  	Name_            string `yaml:"name"`
   148  	Role_            string `yaml:"role"`
   149  	Interface_       string `yaml:"interface"`
   150  	Optional_        bool   `yaml:"optional"`
   151  	Limit_           int    `yaml:"limit"`
   152  	Scope_           string `yaml:"scope"`
   153  
   154  	UnitSettings_ map[string]map[string]interface{} `yaml:"unit-settings"`
   155  }
   156  
   157  // EndpointArgs is an argument struct used to specify a relation.
   158  type EndpointArgs struct {
   159  	ApplicationName string
   160  	Name            string
   161  	Role            string
   162  	Interface       string
   163  	Optional        bool
   164  	Limit           int
   165  	Scope           string
   166  }
   167  
   168  func newEndpoint(args EndpointArgs) *endpoint {
   169  	return &endpoint{
   170  		ApplicationName_: args.ApplicationName,
   171  		Name_:            args.Name,
   172  		Role_:            args.Role,
   173  		Interface_:       args.Interface,
   174  		Optional_:        args.Optional,
   175  		Limit_:           args.Limit,
   176  		Scope_:           args.Scope,
   177  		UnitSettings_:    make(map[string]map[string]interface{}),
   178  	}
   179  }
   180  
   181  func (e *endpoint) unitNames() set.Strings {
   182  	result := set.NewStrings()
   183  	for key := range e.UnitSettings_ {
   184  		result.Add(key)
   185  	}
   186  	return result
   187  }
   188  
   189  // ApplicationName implements Endpoint.
   190  func (e *endpoint) ApplicationName() string {
   191  	return e.ApplicationName_
   192  }
   193  
   194  // Name implements Endpoint.
   195  func (e *endpoint) Name() string {
   196  	return e.Name_
   197  }
   198  
   199  // Role implements Endpoint.
   200  func (e *endpoint) Role() string {
   201  	return e.Role_
   202  }
   203  
   204  // Interface implements Endpoint.
   205  func (e *endpoint) Interface() string {
   206  	return e.Interface_
   207  }
   208  
   209  // Optional implements Endpoint.
   210  func (e *endpoint) Optional() bool {
   211  	return e.Optional_
   212  }
   213  
   214  // Limit implements Endpoint.
   215  func (e *endpoint) Limit() int {
   216  	return e.Limit_
   217  }
   218  
   219  // Scope implements Endpoint.
   220  func (e *endpoint) Scope() string {
   221  	return e.Scope_
   222  }
   223  
   224  // UnitCount implements Endpoint.
   225  func (e *endpoint) UnitCount() int {
   226  	return len(e.UnitSettings_)
   227  }
   228  
   229  // Settings implements Endpoint.
   230  func (e *endpoint) Settings(unitName string) map[string]interface{} {
   231  	return e.UnitSettings_[unitName]
   232  }
   233  
   234  // SetUnitSettings implements Endpoint.
   235  func (e *endpoint) SetUnitSettings(unitName string, settings map[string]interface{}) {
   236  	e.UnitSettings_[unitName] = settings
   237  }
   238  
   239  func importEndpoints(source map[string]interface{}) ([]*endpoint, error) {
   240  	checker := versionedChecker("endpoints")
   241  	coerced, err := checker.Coerce(source, nil)
   242  	if err != nil {
   243  		return nil, errors.Annotatef(err, "endpoints version schema check failed")
   244  	}
   245  	valid := coerced.(map[string]interface{})
   246  
   247  	version := int(valid["version"].(int64))
   248  	importFunc, ok := endpointDeserializationFuncs[version]
   249  	if !ok {
   250  		return nil, errors.NotValidf("version %d", version)
   251  	}
   252  	endpointList := valid["endpoints"].([]interface{})
   253  	return importEndpointList(endpointList, importFunc)
   254  }
   255  
   256  func importEndpointList(sourceList []interface{}, importFunc endpointDeserializationFunc) ([]*endpoint, error) {
   257  	result := make([]*endpoint, 0, len(sourceList))
   258  	for i, value := range sourceList {
   259  		source, ok := value.(map[string]interface{})
   260  		if !ok {
   261  			return nil, errors.Errorf("unexpected value for endpoint %d, %T", i, value)
   262  		}
   263  		application, err := importFunc(source)
   264  		if err != nil {
   265  			return nil, errors.Annotatef(err, "endpoint %d", i)
   266  		}
   267  		result = append(result, application)
   268  	}
   269  	return result, nil
   270  }
   271  
   272  type endpointDeserializationFunc func(map[string]interface{}) (*endpoint, error)
   273  
   274  var endpointDeserializationFuncs = map[int]endpointDeserializationFunc{
   275  	1: importEndpointV1,
   276  }
   277  
   278  func importEndpointV1(source map[string]interface{}) (*endpoint, error) {
   279  	fields := schema.Fields{
   280  		"application-name": schema.String(),
   281  		"name":             schema.String(),
   282  		"role":             schema.String(),
   283  		"interface":        schema.String(),
   284  		"optional":         schema.Bool(),
   285  		"limit":            schema.Int(),
   286  		"scope":            schema.String(),
   287  		"unit-settings":    schema.StringMap(schema.StringMap(schema.Any())),
   288  	}
   289  
   290  	checker := schema.FieldMap(fields, nil) // No defaults.
   291  
   292  	coerced, err := checker.Coerce(source, nil)
   293  	if err != nil {
   294  		return nil, errors.Annotatef(err, "endpoint v1 schema check failed")
   295  	}
   296  	valid := coerced.(map[string]interface{})
   297  	// From here we know that the map returned from the schema coercion
   298  	// contains fields of the right type.
   299  
   300  	result := &endpoint{
   301  		ApplicationName_: valid["application-name"].(string),
   302  		Name_:            valid["name"].(string),
   303  		Role_:            valid["role"].(string),
   304  		Interface_:       valid["interface"].(string),
   305  		Optional_:        valid["optional"].(bool),
   306  		Limit_:           int(valid["limit"].(int64)),
   307  		Scope_:           valid["scope"].(string),
   308  		UnitSettings_:    make(map[string]map[string]interface{}),
   309  	}
   310  
   311  	for unitname, settings := range valid["unit-settings"].(map[string]interface{}) {
   312  		result.UnitSettings_[unitname] = settings.(map[string]interface{})
   313  	}
   314  
   315  	return result, nil
   316  }