github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/core/description/filesystem.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 filesystems struct {
    13  	Version      int           `yaml:"version"`
    14  	Filesystems_ []*filesystem `yaml:"filesystems"`
    15  }
    16  
    17  type filesystem struct {
    18  	ID_        string `yaml:"id"`
    19  	StorageID_ string `yaml:"storage-id,omitempty"`
    20  	VolumeID_  string `yaml:"volume-id,omitempty"`
    21  	Binding_   string `yaml:"binding,omitempty"`
    22  
    23  	Provisioned_  bool   `yaml:"provisioned"`
    24  	Size_         uint64 `yaml:"size"`
    25  	Pool_         string `yaml:"pool,omitempty"`
    26  	FilesystemID_ string `yaml:"filesystem-id,omitempty"`
    27  
    28  	Status_        *status `yaml:"status"`
    29  	StatusHistory_ `yaml:"status-history"`
    30  
    31  	Attachments_ filesystemAttachments `yaml:"attachments"`
    32  }
    33  
    34  type filesystemAttachments struct {
    35  	Version      int                     `yaml:"version"`
    36  	Attachments_ []*filesystemAttachment `yaml:"attachments"`
    37  }
    38  
    39  type filesystemAttachment struct {
    40  	MachineID_   string `yaml:"machine-id"`
    41  	Provisioned_ bool   `yaml:"provisioned"`
    42  	MountPoint_  string `yaml:"mount-point,omitempty"`
    43  	ReadOnly_    bool   `yaml:"read-only"`
    44  }
    45  
    46  // FilesystemArgs is an argument struct used to add a filesystem to the Model.
    47  type FilesystemArgs struct {
    48  	Tag          names.FilesystemTag
    49  	Storage      names.StorageTag
    50  	Volume       names.VolumeTag
    51  	Binding      names.Tag
    52  	Provisioned  bool
    53  	Size         uint64
    54  	Pool         string
    55  	FilesystemID string
    56  }
    57  
    58  func newFilesystem(args FilesystemArgs) *filesystem {
    59  	f := &filesystem{
    60  		ID_:            args.Tag.Id(),
    61  		StorageID_:     args.Storage.Id(),
    62  		VolumeID_:      args.Volume.Id(),
    63  		Provisioned_:   args.Provisioned,
    64  		Size_:          args.Size,
    65  		Pool_:          args.Pool,
    66  		FilesystemID_:  args.FilesystemID,
    67  		StatusHistory_: newStatusHistory(),
    68  	}
    69  	if args.Binding != nil {
    70  		f.Binding_ = args.Binding.String()
    71  	}
    72  	f.setAttachments(nil)
    73  	return f
    74  }
    75  
    76  // Tag implements Filesystem.
    77  func (f *filesystem) Tag() names.FilesystemTag {
    78  	return names.NewFilesystemTag(f.ID_)
    79  }
    80  
    81  // Volume implements Filesystem.
    82  func (f *filesystem) Volume() names.VolumeTag {
    83  	if f.VolumeID_ == "" {
    84  		return names.VolumeTag{}
    85  	}
    86  	return names.NewVolumeTag(f.VolumeID_)
    87  }
    88  
    89  // Storage implements Filesystem.
    90  func (f *filesystem) Storage() names.StorageTag {
    91  	if f.StorageID_ == "" {
    92  		return names.StorageTag{}
    93  	}
    94  	return names.NewStorageTag(f.StorageID_)
    95  }
    96  
    97  // Binding implements Filesystem.
    98  func (f *filesystem) Binding() (names.Tag, error) {
    99  	if f.Binding_ == "" {
   100  		return nil, nil
   101  	}
   102  	tag, err := names.ParseTag(f.Binding_)
   103  	if err != nil {
   104  		return nil, errors.Trace(err)
   105  	}
   106  	return tag, nil
   107  }
   108  
   109  // Provisioned implements Filesystem.
   110  func (f *filesystem) Provisioned() bool {
   111  	return f.Provisioned_
   112  }
   113  
   114  // Size implements Filesystem.
   115  func (f *filesystem) Size() uint64 {
   116  	return f.Size_
   117  }
   118  
   119  // Pool implements Filesystem.
   120  func (f *filesystem) Pool() string {
   121  	return f.Pool_
   122  }
   123  
   124  // FilesystemID implements Filesystem.
   125  func (f *filesystem) FilesystemID() string {
   126  	return f.FilesystemID_
   127  }
   128  
   129  // Status implements Filesystem.
   130  func (f *filesystem) Status() Status {
   131  	// To avoid typed nils check nil here.
   132  	if f.Status_ == nil {
   133  		return nil
   134  	}
   135  	return f.Status_
   136  }
   137  
   138  // SetStatus implements Filesystem.
   139  func (f *filesystem) SetStatus(args StatusArgs) {
   140  	f.Status_ = newStatus(args)
   141  }
   142  
   143  func (f *filesystem) setAttachments(attachments []*filesystemAttachment) {
   144  	f.Attachments_ = filesystemAttachments{
   145  		Version:      1,
   146  		Attachments_: attachments,
   147  	}
   148  }
   149  
   150  // Attachments implements Filesystem.
   151  func (f *filesystem) Attachments() []FilesystemAttachment {
   152  	var result []FilesystemAttachment
   153  	for _, attachment := range f.Attachments_.Attachments_ {
   154  		result = append(result, attachment)
   155  	}
   156  	return result
   157  }
   158  
   159  // AddAttachment implements Filesystem.
   160  func (f *filesystem) AddAttachment(args FilesystemAttachmentArgs) FilesystemAttachment {
   161  	a := newFilesystemAttachment(args)
   162  	f.Attachments_.Attachments_ = append(f.Attachments_.Attachments_, a)
   163  	return a
   164  }
   165  
   166  // Validate implements Filesystem.
   167  func (f *filesystem) Validate() error {
   168  	if f.ID_ == "" {
   169  		return errors.NotValidf("filesystem missing id")
   170  	}
   171  	if f.Size_ == 0 {
   172  		return errors.NotValidf("filesystem %q missing size", f.ID_)
   173  	}
   174  	if f.Status_ == nil {
   175  		return errors.NotValidf("filesystem %q missing status", f.ID_)
   176  	}
   177  	if _, err := f.Binding(); err != nil {
   178  		return errors.Wrap(err, errors.NotValidf("filesystem %q binding", f.ID_))
   179  	}
   180  	return nil
   181  }
   182  
   183  func importFilesystems(source map[string]interface{}) ([]*filesystem, error) {
   184  	checker := versionedChecker("filesystems")
   185  	coerced, err := checker.Coerce(source, nil)
   186  	if err != nil {
   187  		return nil, errors.Annotatef(err, "filesystems version schema check failed")
   188  	}
   189  	valid := coerced.(map[string]interface{})
   190  
   191  	version := int(valid["version"].(int64))
   192  	importFunc, ok := filesystemDeserializationFuncs[version]
   193  	if !ok {
   194  		return nil, errors.NotValidf("version %d", version)
   195  	}
   196  	sourceList := valid["filesystems"].([]interface{})
   197  	return importFilesystemList(sourceList, importFunc)
   198  }
   199  
   200  func importFilesystemList(sourceList []interface{}, importFunc filesystemDeserializationFunc) ([]*filesystem, error) {
   201  	result := make([]*filesystem, 0, len(sourceList))
   202  	for i, value := range sourceList {
   203  		source, ok := value.(map[string]interface{})
   204  		if !ok {
   205  			return nil, errors.Errorf("unexpected value for filesystem %d, %T", i, value)
   206  		}
   207  		filesystem, err := importFunc(source)
   208  		if err != nil {
   209  			return nil, errors.Annotatef(err, "filesystem %d", i)
   210  		}
   211  		result = append(result, filesystem)
   212  	}
   213  	return result, nil
   214  }
   215  
   216  type filesystemDeserializationFunc func(map[string]interface{}) (*filesystem, error)
   217  
   218  var filesystemDeserializationFuncs = map[int]filesystemDeserializationFunc{
   219  	1: importFilesystemV1,
   220  }
   221  
   222  func importFilesystemV1(source map[string]interface{}) (*filesystem, error) {
   223  	fields := schema.Fields{
   224  		"id":            schema.String(),
   225  		"storage-id":    schema.String(),
   226  		"volume-id":     schema.String(),
   227  		"binding":       schema.String(),
   228  		"provisioned":   schema.Bool(),
   229  		"size":          schema.ForceUint(),
   230  		"pool":          schema.String(),
   231  		"filesystem-id": schema.String(),
   232  		"status":        schema.StringMap(schema.Any()),
   233  		"attachments":   schema.StringMap(schema.Any()),
   234  	}
   235  
   236  	defaults := schema.Defaults{
   237  		"storage-id":    "",
   238  		"volume-id":     "",
   239  		"binding":       "",
   240  		"pool":          "",
   241  		"filesystem-id": "",
   242  		"attachments":   schema.Omit,
   243  	}
   244  	addStatusHistorySchema(fields)
   245  	checker := schema.FieldMap(fields, defaults)
   246  
   247  	coerced, err := checker.Coerce(source, nil)
   248  	if err != nil {
   249  		return nil, errors.Annotatef(err, "filesystem v1 schema check failed")
   250  	}
   251  	valid := coerced.(map[string]interface{})
   252  	// From here we know that the map returned from the schema coercion
   253  	// contains fields of the right type.
   254  	result := &filesystem{
   255  		ID_:            valid["id"].(string),
   256  		StorageID_:     valid["storage-id"].(string),
   257  		VolumeID_:      valid["volume-id"].(string),
   258  		Binding_:       valid["binding"].(string),
   259  		Provisioned_:   valid["provisioned"].(bool),
   260  		Size_:          valid["size"].(uint64),
   261  		Pool_:          valid["pool"].(string),
   262  		FilesystemID_:  valid["filesystem-id"].(string),
   263  		StatusHistory_: newStatusHistory(),
   264  	}
   265  	if err := result.importStatusHistory(valid); err != nil {
   266  		return nil, errors.Trace(err)
   267  	}
   268  
   269  	status, err := importStatus(valid["status"].(map[string]interface{}))
   270  	if err != nil {
   271  		return nil, errors.Trace(err)
   272  	}
   273  	result.Status_ = status
   274  
   275  	attachments, err := importFilesystemAttachments(valid["attachments"].(map[string]interface{}))
   276  	if err != nil {
   277  		return nil, errors.Trace(err)
   278  	}
   279  	result.setAttachments(attachments)
   280  
   281  	return result, nil
   282  }
   283  
   284  // FilesystemAttachmentArgs is an argument struct used to add information about the
   285  // cloud instance to a Filesystem.
   286  type FilesystemAttachmentArgs struct {
   287  	Machine     names.MachineTag
   288  	Provisioned bool
   289  	ReadOnly    bool
   290  	MountPoint  string
   291  }
   292  
   293  func newFilesystemAttachment(args FilesystemAttachmentArgs) *filesystemAttachment {
   294  	return &filesystemAttachment{
   295  		MachineID_:   args.Machine.Id(),
   296  		Provisioned_: args.Provisioned,
   297  		ReadOnly_:    args.ReadOnly,
   298  		MountPoint_:  args.MountPoint,
   299  	}
   300  }
   301  
   302  // Machine implements FilesystemAttachment
   303  func (a *filesystemAttachment) Machine() names.MachineTag {
   304  	return names.NewMachineTag(a.MachineID_)
   305  }
   306  
   307  // Provisioned implements FilesystemAttachment
   308  func (a *filesystemAttachment) Provisioned() bool {
   309  	return a.Provisioned_
   310  }
   311  
   312  // ReadOnly implements FilesystemAttachment
   313  func (a *filesystemAttachment) ReadOnly() bool {
   314  	return a.ReadOnly_
   315  }
   316  
   317  // MountPoint implements FilesystemAttachment
   318  func (a *filesystemAttachment) MountPoint() string {
   319  	return a.MountPoint_
   320  }
   321  
   322  func importFilesystemAttachments(source map[string]interface{}) ([]*filesystemAttachment, error) {
   323  	checker := versionedChecker("attachments")
   324  	coerced, err := checker.Coerce(source, nil)
   325  	if err != nil {
   326  		return nil, errors.Annotatef(err, "filesystem attachments version schema check failed")
   327  	}
   328  	valid := coerced.(map[string]interface{})
   329  
   330  	version := int(valid["version"].(int64))
   331  	importFunc, ok := filesystemAttachmentDeserializationFuncs[version]
   332  	if !ok {
   333  		return nil, errors.NotValidf("version %d", version)
   334  	}
   335  	sourceList := valid["attachments"].([]interface{})
   336  	return importFilesystemAttachmentList(sourceList, importFunc)
   337  }
   338  
   339  func importFilesystemAttachmentList(sourceList []interface{}, importFunc filesystemAttachmentDeserializationFunc) ([]*filesystemAttachment, error) {
   340  	result := make([]*filesystemAttachment, 0, len(sourceList))
   341  	for i, value := range sourceList {
   342  		source, ok := value.(map[string]interface{})
   343  		if !ok {
   344  			return nil, errors.Errorf("unexpected value for filesystemAttachment %d, %T", i, value)
   345  		}
   346  		filesystemAttachment, err := importFunc(source)
   347  		if err != nil {
   348  			return nil, errors.Annotatef(err, "filesystemAttachment %d", i)
   349  		}
   350  		result = append(result, filesystemAttachment)
   351  	}
   352  	return result, nil
   353  }
   354  
   355  type filesystemAttachmentDeserializationFunc func(map[string]interface{}) (*filesystemAttachment, error)
   356  
   357  var filesystemAttachmentDeserializationFuncs = map[int]filesystemAttachmentDeserializationFunc{
   358  	1: importFilesystemAttachmentV1,
   359  }
   360  
   361  func importFilesystemAttachmentV1(source map[string]interface{}) (*filesystemAttachment, error) {
   362  	fields := schema.Fields{
   363  		"machine-id":  schema.String(),
   364  		"provisioned": schema.Bool(),
   365  		"read-only":   schema.Bool(),
   366  		"mount-point": schema.String(),
   367  	}
   368  	defaults := schema.Defaults{
   369  		"mount-point": "",
   370  	}
   371  	checker := schema.FieldMap(fields, defaults)
   372  
   373  	coerced, err := checker.Coerce(source, nil)
   374  	if err != nil {
   375  		return nil, errors.Annotatef(err, "filesystemAttachment v1 schema check failed")
   376  	}
   377  	valid := coerced.(map[string]interface{})
   378  	// From here we know that the map returned from the schema coercion
   379  	// contains fields of the right type.
   380  
   381  	result := &filesystemAttachment{
   382  		MachineID_:   valid["machine-id"].(string),
   383  		Provisioned_: valid["provisioned"].(bool),
   384  		ReadOnly_:    valid["read-only"].(bool),
   385  		MountPoint_:  valid["mount-point"].(string),
   386  	}
   387  	return result, nil
   388  }