diff --git a/application.go b/application.go index 8f067f5..3086d7b 100644 --- a/application.go +++ b/application.go @@ -50,7 +50,7 @@ type Application interface { LeadershipSettings() map[string]interface{} MetricsCredentials() []byte - StorageConstraints() map[string]StorageConstraint + StorageDirectives() map[string]StorageDirective Resources() []Resource AddResource(ResourceArgs) Resource @@ -126,8 +126,8 @@ type application struct { Annotations_ `yaml:"annotations,omitempty"` - Constraints_ *constraints `yaml:"constraints,omitempty"` - StorageConstraints_ map[string]*storageconstraint `yaml:"storage-constraints,omitempty"` + Constraints_ *constraints `yaml:"constraints,omitempty"` + StorageDirectives_ map[string]*storageDirective `yaml:"storage-directives,omitempty"` // CAAS application fields. PasswordHash_ string `yaml:"password-hash,omitempty"` @@ -174,7 +174,7 @@ type ApplicationArgs struct { CharmConfig map[string]interface{} Leader string LeadershipSettings map[string]interface{} - StorageConstraints map[string]StorageConstraintArgs + StorageDirectives map[string]StorageDirectiveArgs MetricsCredentials []byte ProvisioningState *ProvisioningStateArgs } @@ -209,10 +209,10 @@ func newApplication(args ApplicationArgs) *application { } app.setUnits(nil) app.setResources(nil) - if len(args.StorageConstraints) > 0 { - app.StorageConstraints_ = make(map[string]*storageconstraint) - for key, value := range args.StorageConstraints { - app.StorageConstraints_[key] = newStorageConstraint(value) + if len(args.StorageDirectives) > 0 { + app.StorageDirectives_ = make(map[string]*storageDirective) + for key, value := range args.StorageDirectives { + app.StorageDirectives_[key] = newStorageDirective(value) } } if len(args.ExposedEndpoints) > 0 { @@ -337,10 +337,10 @@ func (a *application) LeadershipSettings() map[string]interface{} { return a.LeadershipSettings_ } -// StorageConstraints implements Application. -func (a *application) StorageConstraints() map[string]StorageConstraint { - result := make(map[string]StorageConstraint) - for key, value := range a.StorageConstraints_ { +// StorageDirectives implements Application. +func (a *application) StorageDirectives() map[string]StorageDirective { + result := make(map[string]StorageDirective) + for key, value := range a.StorageDirectives_ { result[key] = value } return result @@ -638,6 +638,7 @@ var applicationDeserializationFuncs = map[int]applicationDeserializationFunc{ 9: importApplicationV9, 10: importApplicationV10, 11: importApplicationV11, + 12: importApplicationV12, } func applicationV1Fields() (schema.Fields, schema.Defaults) { @@ -759,6 +760,15 @@ func applicationV11Fields() (schema.Fields, schema.Defaults) { return fields, defaults } +func applicationV12Fields() (schema.Fields, schema.Defaults) { + fields, defaults := applicationV11Fields() + delete(fields, "storage-constraints") + defaults["storage-constraints"] = schema.Omit + fields["storage-directives"] = schema.StringMap(schema.StringMap(schema.Any())) + defaults["storage-directives"] = schema.Omit + return fields, defaults +} + func importApplicationV1(source map[string]interface{}) (*application, error) { fields, defaults := applicationV1Fields() return importApplication(fields, defaults, 1, source) @@ -814,6 +824,11 @@ func importApplicationV11(source map[string]interface{}) (*application, error) { return importApplication(fields, defaults, 11, source) } +func importApplicationV12(source map[string]interface{}) (*application, error) { + fields, defaults := applicationV12Fields() + return importApplication(fields, defaults, 12, source) +} + func importApplication(fields schema.Fields, defaults schema.Defaults, importVersion int, source map[string]interface{}) (*application, error) { checker := schema.FieldMap(fields, defaults) @@ -945,12 +960,16 @@ func importApplication(fields schema.Fields, defaults schema.Defaults, importVer result.Constraints_ = constraints } - if constraintsMap, ok := valid["storage-constraints"]; ok { - constraints, err := importStorageConstraints(constraintsMap.(map[string]interface{})) + storageKey := "storage-directives" + if importVersion < 12 { + storageKey = "storage-constraints" + } + if constraintsMap, ok := valid[storageKey]; ok { + directives, err := importStorageDirectives(constraintsMap.(map[string]interface{})) if err != nil { return nil, errors.Trace(err) } - result.StorageConstraints_ = constraints + result.StorageDirectives_ = directives } if cloudServiceMap, ok := valid["cloud-service"]; ok { diff --git a/application_test.go b/application_test.go index 6a47cd6..208b9b2 100644 --- a/application_test.go +++ b/application_test.go @@ -259,7 +259,7 @@ func (s *ApplicationSerializationSuite) TestNewApplication(c *gc.C) { c.Assert(application.HasResources(), jc.IsTrue) c.Assert(application.DesiredScale(), gc.Equals, 2) c.Assert(application.CloudService(), gc.IsNil) - c.Assert(application.StorageConstraints(), gc.HasLen, 0) + c.Assert(application.StorageDirectives(), gc.HasLen, 0) c.Assert(application.MinUnits(), gc.Equals, 42) c.Assert(application.EndpointBindings(), jc.DeepEquals, args.EndpointBindings) c.Assert(application.ApplicationConfig(), jc.DeepEquals, args.ApplicationConfig) @@ -362,7 +362,7 @@ func (s *ApplicationSerializationSuite) exportImportVersion(c *gc.C, application } func (s *ApplicationSerializationSuite) exportImportLatest(c *gc.C, application_ *application) *application { - return s.exportImportVersion(c, application_, 11) + return s.exportImportVersion(c, application_, 12) } func (s *ApplicationSerializationSuite) TestV1ParsingReturnsLatest(c *gc.C) { @@ -502,9 +502,9 @@ func (s *ApplicationSerializationSuite) TestConstraints(c *gc.C) { c.Assert(application.Constraints(), jc.DeepEquals, newConstraints(args)) } -func (s *ApplicationSerializationSuite) TestStorageConstraints(c *gc.C) { +func (s *ApplicationSerializationSuite) TestStorageDirectives(c *gc.C) { args := minimalApplicationArgs(IAAS) - args.StorageConstraints = map[string]StorageConstraintArgs{ + args.StorageDirectives = map[string]StorageDirectiveArgs{ "first": {Pool: "first", Size: 1234, Count: 1}, "second": {Pool: "second", Size: 4321, Count: 7}, } @@ -512,15 +512,15 @@ func (s *ApplicationSerializationSuite) TestStorageConstraints(c *gc.C) { application := s.exportImportLatest(c, initial) - constraints := application.StorageConstraints() - c.Assert(constraints, gc.HasLen, 2) - first, found := constraints["first"] + directives := application.StorageDirectives() + c.Assert(directives, gc.HasLen, 2) + first, found := directives["first"] c.Assert(found, jc.IsTrue) c.Check(first.Pool(), gc.Equals, "first") c.Check(first.Size(), gc.Equals, uint64(1234)) c.Check(first.Count(), gc.Equals, uint64(1)) - second, found := constraints["second"] + second, found := directives["second"] c.Assert(found, jc.IsTrue) c.Check(second.Pool(), gc.Equals, "second") c.Check(second.Size(), gc.Equals, uint64(4321)) diff --git a/interfaces.go b/interfaces.go index 48b4bfc..5dd6e0f 100644 --- a/interfaces.go +++ b/interfaces.go @@ -182,9 +182,9 @@ type StoragePool interface { Attributes() map[string]interface{} } -// StorageConstraint represents the user-specified constraints for +// StorageDirective represents the user-specified storage directive for // provisioning storage instances for an application unit. -type StorageConstraint interface { +type StorageDirective interface { // Pool is the name of the storage pool from which to provision the // storage instances. Pool() string diff --git a/model.go b/model.go index a772f23..5729d19 100644 --- a/model.go +++ b/model.go @@ -473,7 +473,7 @@ func (m *model) AddApplication(args ApplicationArgs) Application { func (m *model) setApplications(applicationList []*application) { m.Applications_ = applications{ - Version: 11, + Version: 12, Applications_: applicationList, } } diff --git a/storageconstraint.go b/storageconstraint.go deleted file mode 100644 index 54f0adf..0000000 --- a/storageconstraint.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2016 Canonical Ltd. -// Licensed under the LGPLv3, see LICENCE file for details. - -package description - -import ( - "github.com/juju/errors" - "github.com/juju/schema" -) - -// StorageConstraintArgs is an argument struct used to create a new internal -// storageconstraint type that supports the StorageConstraint interface. -type StorageConstraintArgs struct { - Pool string - Size uint64 - Count uint64 -} - -func newStorageConstraint(args StorageConstraintArgs) *storageconstraint { - return &storageconstraint{ - Version: 1, - Pool_: args.Pool, - Size_: args.Size, - Count_: args.Count, - } -} - -type storageconstraint struct { - Version int `yaml:"version"` - - Pool_ string `yaml:"pool"` - Size_ uint64 `yaml:"size"` - Count_ uint64 `yaml:"count"` -} - -// Pool implements StorageConstraint. -func (s *storageconstraint) Pool() string { - return s.Pool_ -} - -// Size implements StorageConstraint. -func (s *storageconstraint) Size() uint64 { - return s.Size_ -} - -// Count implements StorageConstraint. -func (s *storageconstraint) Count() uint64 { - return s.Count_ -} - -func importStorageConstraints(sourceMap map[string]interface{}) (map[string]*storageconstraint, error) { - result := make(map[string]*storageconstraint) - for key, value := range sourceMap { - source, ok := value.(map[string]interface{}) - if !ok { - return nil, errors.Errorf("unexpected value for storageconstraint %q, %T", key, value) - } - constraint, err := importStorageConstraint(source) - if err != nil { - return nil, errors.Trace(err) - } - result[key] = constraint - } - return result, nil -} - -// importStorageConstraint constructs a new StorageConstraint from a map representing a serialised -// StorageConstraint instance. -func importStorageConstraint(source map[string]interface{}) (*storageconstraint, error) { - version, err := getVersion(source) - if err != nil { - return nil, errors.Annotate(err, "storageconstraint version schema check failed") - } - - importFunc, ok := storageconstraintDeserializationFuncs[version] - if !ok { - return nil, errors.NotValidf("version %d", version) - } - - return importFunc(source) -} - -type storageconstraintDeserializationFunc func(map[string]interface{}) (*storageconstraint, error) - -var storageconstraintDeserializationFuncs = map[int]storageconstraintDeserializationFunc{ - 1: importStorageConstraintV1, -} - -func importStorageConstraintV1(source map[string]interface{}) (*storageconstraint, error) { - fields := schema.Fields{ - "pool": schema.String(), - "size": schema.Uint(), - "count": schema.Uint(), - } - checker := schema.FieldMap(fields, nil) - - coerced, err := checker.Coerce(source, nil) - if err != nil { - return nil, errors.Annotatef(err, "storageconstraint v1 schema check failed") - } - valid := coerced.(map[string]interface{}) - // From here we know that the map returned from the schema coercion - // contains fields of the right type. - - return &storageconstraint{ - Version: 1, - Pool_: valid["pool"].(string), - Size_: valid["size"].(uint64), - Count_: valid["count"].(uint64), - }, nil -} diff --git a/storagedirective.go b/storagedirective.go new file mode 100644 index 0000000..3f5aec6 --- /dev/null +++ b/storagedirective.go @@ -0,0 +1,111 @@ +// Copyright 2016 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package description + +import ( + "github.com/juju/errors" + "github.com/juju/schema" +) + +// StorageDirectiveArgs is an argument struct used to create a new internal +// storageDirective type that supports the StorageDirective interface. +type StorageDirectiveArgs struct { + Pool string + Size uint64 + Count uint64 +} + +func newStorageDirective(args StorageDirectiveArgs) *storageDirective { + return &storageDirective{ + Version: 1, + Pool_: args.Pool, + Size_: args.Size, + Count_: args.Count, + } +} + +type storageDirective struct { + Version int `yaml:"version"` + + Pool_ string `yaml:"pool"` + Size_ uint64 `yaml:"size"` + Count_ uint64 `yaml:"count"` +} + +// Pool implements StorageDirective. +func (s *storageDirective) Pool() string { + return s.Pool_ +} + +// Size implements StorageDirective. +func (s *storageDirective) Size() uint64 { + return s.Size_ +} + +// Count implements StorageDirective. +func (s *storageDirective) Count() uint64 { + return s.Count_ +} + +func importStorageDirectives(sourceMap map[string]interface{}) (map[string]*storageDirective, error) { + result := make(map[string]*storageDirective) + for key, value := range sourceMap { + source, ok := value.(map[string]interface{}) + if !ok { + return nil, errors.Errorf("unexpected value for storage directive %q, %T", key, value) + } + directive, err := importStorageDirective(source) + if err != nil { + return nil, errors.Trace(err) + } + result[key] = directive + } + return result, nil +} + +// importStorageDirective constructs a new StorageDirective from a map representing a serialised +// StorageDirective instance. +func importStorageDirective(source map[string]interface{}) (*storageDirective, error) { + version, err := getVersion(source) + if err != nil { + return nil, errors.Annotate(err, "storage directive version schema check failed") + } + + importFunc, ok := storageconstraintDeserializationFuncs[version] + if !ok { + return nil, errors.NotValidf("version %d", version) + } + + return importFunc(source) +} + +type storageDirectiveDeserializationFunc func(map[string]interface{}) (*storageDirective, error) + +var storageconstraintDeserializationFuncs = map[int]storageDirectiveDeserializationFunc{ + 1: importStorageDirectiveV1, +} + +func importStorageDirectiveV1(source map[string]interface{}) (*storageDirective, error) { + fields := schema.Fields{ + "pool": schema.String(), + "size": schema.Uint(), + "count": schema.Uint(), + } + checker := schema.FieldMap(fields, nil) + + coerced, err := checker.Coerce(source, nil) + if err != nil { + return nil, errors.Annotatef(err, "storage directive v1 schema check failed") + } + valid := coerced.(map[string]interface{}) + // From here we know that the map returned from the schema coercion + // contains fields of the right type. + + return &storageDirective{ + Version: 1, + Pool_: valid["pool"].(string), + Size_: valid["size"].(uint64), + Count_: valid["count"].(uint64), + }, nil +} diff --git a/storageconstraint_test.go b/storagedirective_test.go similarity index 56% rename from storageconstraint_test.go rename to storagedirective_test.go index 9154c1c..43e343e 100644 --- a/storageconstraint_test.go +++ b/storagedirective_test.go @@ -9,17 +9,17 @@ import ( "gopkg.in/yaml.v2" ) -type StorageConstraintSerializationSuite struct { +type StorageDirectiveSerializationSuite struct { SerializationSuite } -var _ = gc.Suite(&StorageConstraintSerializationSuite{}) +var _ = gc.Suite(&StorageDirectiveSerializationSuite{}) -func (s *StorageConstraintSerializationSuite) SetUpTest(c *gc.C) { +func (s *StorageDirectiveSerializationSuite) SetUpTest(c *gc.C) { s.SerializationSuite.SetUpTest(c) - s.importName = "storageconstraint" + s.importName = "storage directive" s.importFunc = func(m map[string]interface{}) (interface{}, error) { - return importStorageConstraint(m) + return importStorageDirective(m) } s.testFields = func(m map[string]interface{}) { m["pool"] = "" @@ -28,22 +28,22 @@ func (s *StorageConstraintSerializationSuite) SetUpTest(c *gc.C) { } } -func (s *StorageConstraintSerializationSuite) TestMissingValue(c *gc.C) { +func (s *StorageDirectiveSerializationSuite) TestMissingValue(c *gc.C) { testMap := s.makeMap(1) delete(testMap, "pool") - _, err := importStorageConstraint(testMap) - c.Check(err.Error(), gc.Equals, "storageconstraint v1 schema check failed: pool: expected string, got nothing") + _, err := importStorageDirective(testMap) + c.Check(err.Error(), gc.Equals, "storage directive v1 schema check failed: pool: expected string, got nothing") } -func (*StorageConstraintSerializationSuite) TestParsing(c *gc.C) { - addr, err := importStorageConstraint(map[string]interface{}{ +func (*StorageDirectiveSerializationSuite) TestParsing(c *gc.C) { + addr, err := importStorageDirective(map[string]interface{}{ "version": 1, "pool": "olympic", "size": 50, "count": 2, }) c.Assert(err, jc.ErrorIsNil) - expected := &storageconstraint{ + expected := &storageDirective{ Version: 1, Pool_: "olympic", Size_: 50, @@ -52,8 +52,8 @@ func (*StorageConstraintSerializationSuite) TestParsing(c *gc.C) { c.Assert(addr, jc.DeepEquals, expected) } -func (*StorageConstraintSerializationSuite) TestParsingSerializedData(c *gc.C) { - initial := &storageconstraint{ +func (*StorageDirectiveSerializationSuite) TestParsingSerializedData(c *gc.C) { + initial := &storageDirective{ Version: 1, Pool_: "olympic", Size_: 50, @@ -67,8 +67,8 @@ func (*StorageConstraintSerializationSuite) TestParsingSerializedData(c *gc.C) { err = yaml.Unmarshal(bytes, &source) c.Assert(err, jc.ErrorIsNil) - storageconstraints, err := importStorageConstraint(source) + directives, err := importStorageDirective(source) c.Assert(err, jc.ErrorIsNil) - c.Assert(storageconstraints, jc.DeepEquals, initial) + c.Assert(directives, jc.DeepEquals, initial) }