github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/core/description/storage.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 storages struct {
    13  	Version   int        `yaml:"version"`
    14  	Storages_ []*storage `yaml:"storages"`
    15  }
    16  
    17  type storage struct {
    18  	ID_    string `yaml:"id"`
    19  	Kind_  string `yaml:"kind"`
    20  	Owner_ string `yaml:"owner"`
    21  	Name_  string `yaml:"name"`
    22  
    23  	Attachments_ []string `yaml:"attachments"`
    24  }
    25  
    26  // StorageArgs is an argument struct used to add a storage to the Model.
    27  type StorageArgs struct {
    28  	Tag         names.StorageTag
    29  	Kind        string
    30  	Owner       names.Tag
    31  	Name        string
    32  	Attachments []names.UnitTag
    33  }
    34  
    35  func newStorage(args StorageArgs) *storage {
    36  	s := &storage{
    37  		ID_:   args.Tag.Id(),
    38  		Kind_: args.Kind,
    39  		Name_: args.Name,
    40  	}
    41  	if args.Owner != nil {
    42  		s.Owner_ = args.Owner.String()
    43  	}
    44  	for _, unit := range args.Attachments {
    45  		s.Attachments_ = append(s.Attachments_, unit.Id())
    46  	}
    47  	return s
    48  }
    49  
    50  // Tag implements Storage.
    51  func (s *storage) Tag() names.StorageTag {
    52  	return names.NewStorageTag(s.ID_)
    53  }
    54  
    55  // Kind implements Storage.
    56  func (s *storage) Kind() string {
    57  	return s.Kind_
    58  }
    59  
    60  // Owner implements Storage.
    61  func (s *storage) Owner() (names.Tag, error) {
    62  	if s.Owner_ == "" {
    63  		return nil, nil
    64  	}
    65  	tag, err := names.ParseTag(s.Owner_)
    66  	if err != nil {
    67  		return nil, errors.Trace(err)
    68  	}
    69  	return tag, nil
    70  }
    71  
    72  // Name implements Storage.
    73  func (s *storage) Name() string {
    74  	return s.Name_
    75  }
    76  
    77  // Attachments implements Storage.
    78  func (s *storage) Attachments() []names.UnitTag {
    79  	var result []names.UnitTag
    80  	for _, unit := range s.Attachments_ {
    81  		result = append(result, names.NewUnitTag(unit))
    82  	}
    83  	return result
    84  }
    85  
    86  // Validate implements Storage.
    87  func (s *storage) Validate() error {
    88  	if s.ID_ == "" {
    89  		return errors.NotValidf("storage missing id")
    90  	}
    91  	if s.Owner_ == "" {
    92  		return errors.NotValidf("storage %q missing owner", s.ID_)
    93  	}
    94  	// Also check that the owner and attachments are valid.
    95  	if _, err := s.Owner(); err != nil {
    96  		return errors.Wrap(err, errors.NotValidf("storage %q invalid owner", s.ID_))
    97  	}
    98  	return nil
    99  }
   100  
   101  func importStorages(source map[string]interface{}) ([]*storage, error) {
   102  	checker := versionedChecker("storages")
   103  	coerced, err := checker.Coerce(source, nil)
   104  	if err != nil {
   105  		return nil, errors.Annotatef(err, "storages version schema check failed")
   106  	}
   107  	valid := coerced.(map[string]interface{})
   108  
   109  	version := int(valid["version"].(int64))
   110  	importFunc, ok := storageDeserializationFuncs[version]
   111  	if !ok {
   112  		return nil, errors.NotValidf("version %d", version)
   113  	}
   114  	sourceList := valid["storages"].([]interface{})
   115  	return importStorageList(sourceList, importFunc)
   116  }
   117  
   118  func importStorageList(sourceList []interface{}, importFunc storageDeserializationFunc) ([]*storage, error) {
   119  	result := make([]*storage, 0, len(sourceList))
   120  	for i, value := range sourceList {
   121  		source, ok := value.(map[string]interface{})
   122  		if !ok {
   123  			return nil, errors.Errorf("unexpected value for storage %d, %T", i, value)
   124  		}
   125  		storage, err := importFunc(source)
   126  		if err != nil {
   127  			return nil, errors.Annotatef(err, "storage %d", i)
   128  		}
   129  		result = append(result, storage)
   130  	}
   131  	return result, nil
   132  }
   133  
   134  type storageDeserializationFunc func(map[string]interface{}) (*storage, error)
   135  
   136  var storageDeserializationFuncs = map[int]storageDeserializationFunc{
   137  	1: importStorageV1,
   138  }
   139  
   140  func importStorageV1(source map[string]interface{}) (*storage, error) {
   141  	fields := schema.Fields{
   142  		"id":          schema.String(),
   143  		"kind":        schema.String(),
   144  		"owner":       schema.String(),
   145  		"name":        schema.String(),
   146  		"attachments": schema.List(schema.String()),
   147  	}
   148  
   149  	// Normally a list would have defaults, but in this case storage
   150  	// should always have at least one attachment.
   151  	checker := schema.FieldMap(fields, nil) // no defaults
   152  
   153  	coerced, err := checker.Coerce(source, nil)
   154  	if err != nil {
   155  		return nil, errors.Annotatef(err, "storage v1 schema check failed")
   156  	}
   157  	valid := coerced.(map[string]interface{})
   158  	// From here we know that the map returned from the schema coercion
   159  	// contains fields of the right type.
   160  	result := &storage{
   161  		ID_:          valid["id"].(string),
   162  		Kind_:        valid["kind"].(string),
   163  		Owner_:       valid["owner"].(string),
   164  		Name_:        valid["name"].(string),
   165  		Attachments_: convertToStringSlice(valid["attachments"]),
   166  	}
   167  
   168  	return result, nil
   169  }