diff --git a/model.go b/model.go index b57be80..9258a1d 100644 --- a/model.go +++ b/model.go @@ -957,7 +957,7 @@ func (m *model) AddSecret(args SecretArgs) Secret { func (m *model) setSecrets(secretList []*secret) { m.Secrets_ = secrets{ - Version: 1, + Version: 2, Secrets_: secretList, } } diff --git a/model_test.go b/model_test.go index 5927f44..c6c5133 100644 --- a/model_test.go +++ b/model_test.go @@ -145,7 +145,7 @@ func (s *ModelSerializationSuite) TestVersions(c *gc.C) { c.Assert(initial.Applications_.Version, gc.Equals, len(applicationDeserializationFuncs)) c.Assert(initial.Actions_.Version, gc.Equals, 4) c.Assert(initial.Operations_.Version, gc.Equals, 2) - c.Assert(initial.Secrets_.Version, gc.Equals, 1) + c.Assert(initial.Secrets_.Version, gc.Equals, 2) c.Assert(initial.Filesystems_.Version, gc.Equals, len(filesystemDeserializationFuncs)) c.Assert(initial.Relations_.Version, gc.Equals, len(relationFieldsFuncs)) c.Assert(initial.RemoteEntities_.Version, gc.Equals, len(remoteEntityFieldsFuncs)) @@ -1548,6 +1548,7 @@ func (s *ModelSerializationSuite) TestSecrets(c *gc.C) { c.Assert(secret.Label(), gc.Equals, secretArgs.Label) c.Assert(secret.RotatePolicy(), gc.Equals, secretArgs.RotatePolicy) c.Assert(secret.AutoPrune(), gc.Equals, secretArgs.AutoPrune) + c.Assert(secret.LatestRevisionChecksum(), gc.DeepEquals, secretArgs.LatestRevisionChecksum) owner, err := secret.Owner() c.Assert(err, jc.ErrorIsNil) c.Assert(owner.String(), gc.Equals, secretArgs.Owner.String()) diff --git a/secretremoteconsumers.go b/secretremoteconsumers.go index 3166b2a..34277b8 100644 --- a/secretremoteconsumers.go +++ b/secretremoteconsumers.go @@ -106,6 +106,11 @@ type secretRemoteConsumerDeserializationFunc func(map[interface{}]interface{}) ( var secretRemoteConsumerDeserializationFuncs = map[int]secretRemoteConsumerDeserializationFunc{ 1: importSecretRemoteConsumerV1, + 2: importSecretRemoteConsumerV2, +} + +func importSecretRemoteConsumerV2(source map[interface{}]interface{}) (*secretRemoteConsumer, error) { + return importSecretRemoteConsumerV1(source) } func importSecretRemoteConsumerV1(source map[interface{}]interface{}) (*secretRemoteConsumer, error) { diff --git a/secrets.go b/secrets.go index ecdf39c..178e7e1 100644 --- a/secrets.go +++ b/secrets.go @@ -33,6 +33,7 @@ type Secret interface { Revisions() []SecretRevision LatestRevision() int + LatestRevisionChecksum() string LatestExpireTime() *time.Time Validate() error @@ -61,6 +62,8 @@ type secret struct { NextRotateTime_ *time.Time `yaml:"next-rotate-time,omitempty"` + LatestRevisionChecksum_ string `yaml:"latest-revision-checksum"` + // These are updated when revisions are set // and are not exported. LatestRevision_ int `yaml:"-"` @@ -113,6 +116,11 @@ func (i *secret) LatestRevision() int { return i.LatestRevision_ } +// LatestRevisionChecksum implements Secret. +func (i *secret) LatestRevisionChecksum() string { + return i.LatestRevisionChecksum_ +} + // Id implements Secret. func (i *secret) Id() string { return i.ID_ @@ -233,21 +241,23 @@ type SecretArgs struct { Consumers []SecretConsumerArgs RemoteConsumers []SecretRemoteConsumerArgs - NextRotateTime *time.Time - AutoPrune bool + NextRotateTime *time.Time + LatestRevisionChecksum string + AutoPrune bool } func newSecret(args SecretArgs) *secret { secret := &secret{ - ID_: args.ID, - Version_: args.Version, - Description_: args.Description, - Label_: args.Label, - RotatePolicy_: args.RotatePolicy, - AutoPrune_: args.AutoPrune, - Created_: args.Created.UTC(), - Updated_: args.Updated.UTC(), - ACL_: newSecretAccess(args.ACL), + ID_: args.ID, + Version_: args.Version, + Description_: args.Description, + Label_: args.Label, + RotatePolicy_: args.RotatePolicy, + AutoPrune_: args.AutoPrune, + LatestRevisionChecksum_: args.LatestRevisionChecksum, + Created_: args.Created.UTC(), + Updated_: args.Updated.UTC(), + ACL_: newSecretAccess(args.ACL), } if args.NextRotateTime != nil { next := args.NextRotateTime.UTC() @@ -328,6 +338,7 @@ func importSecretList(sourceList []interface{}, version int) ([]*secret, error) var secretFieldsFuncs = map[int]fieldsFunc{ 1: secretV1Fields, + 2: secretV2Fields, } func secretV1Fields() (schema.Fields, schema.Defaults) { @@ -359,6 +370,13 @@ func secretV1Fields() (schema.Fields, schema.Defaults) { return fields, defaults } +func secretV2Fields() (schema.Fields, schema.Defaults) { + fields, defaults := secretV1Fields() + fields["latest-revision-checksum"] = schema.String() + defaults["latest-revision-checksum"] = schema.Omit + return fields, defaults +} + func importSecret(source map[string]interface{}, importVersion int, fieldFunc func() (schema.Fields, schema.Defaults)) (*secret, error) { fields, defaults := fieldFunc() checker := schema.FieldMap(fields, defaults) @@ -383,6 +401,12 @@ func importSecret(source map[string]interface{}, importVersion int, fieldFunc fu secret.RotatePolicy_ = policy } + if importVersion >= 2 { + if checksum, ok := valid["latest-revision-checksum"].(string); ok { + secret.LatestRevisionChecksum_ = checksum + } + } + // This should be in a v2 schema but it's already also in v1. if autoPrune, ok := valid["auto-prune"].(bool); ok { secret.AutoPrune_ = autoPrune @@ -491,6 +515,11 @@ type secretAccessDeserializationFunc func(map[interface{}]interface{}) (*secretA var secretAccessDeserializationFuncs = map[int]secretAccessDeserializationFunc{ 1: importSecretAccessV1, + 2: importSecretAccessV2, +} + +func importSecretAccessV2(source map[interface{}]interface{}) (*secretAccess, error) { + return importSecretAccessV1(source) } func importSecretAccessV1(source map[interface{}]interface{}) (*secretAccess, error) { @@ -609,6 +638,11 @@ type secretConsumerDeserializationFunc func(map[interface{}]interface{}) (*secre var secretConsumerDeserializationFuncs = map[int]secretConsumerDeserializationFunc{ 1: importSecretConsumerV1, + 2: importSecretConsumerV2, +} + +func importSecretConsumerV2(source map[interface{}]interface{}) (*secretConsumer, error) { + return importSecretConsumerV1(source) } func importSecretConsumerV1(source map[interface{}]interface{}) (*secretConsumer, error) { @@ -800,6 +834,11 @@ type secretRevisionDeserializationFunc func(map[interface{}]interface{}) (*secre var secretRevisionRangeDeserializationFuncs = map[int]secretRevisionDeserializationFunc{ 1: importSecretRevisionV1, + 2: importSecretRevisionV2, +} + +func importSecretRevisionV2(source map[interface{}]interface{}) (*secretRevision, error) { + return importSecretRevisionV1(source) } func importSecretRevisionV1(source map[interface{}]interface{}) (*secretRevision, error) { diff --git a/secrets_test.go b/secrets_test.go index 9416219..aadd1dc 100644 --- a/secrets_test.go +++ b/secrets_test.go @@ -37,20 +37,21 @@ func testSecretArgs() SecretArgs { updated := created.Add(time.Hour) nextRotate := created.Add(2 * time.Hour) return SecretArgs{ - ID: id, - Version: 1, - Description: "a secret", - Label: "secret label", - RotatePolicy: "hourly", - AutoPrune: true, - Owner: names.NewApplicationTag("postgresql"), - Created: created, - Updated: updated, - NextRotateTime: &nextRotate, - Revisions: testSecretRevisionsArgs(), - ACL: testSecretAccessArgs(), - Consumers: testSecretConsumerArgs(), - RemoteConsumers: testSecretRemoteConsumerArgs(), + ID: id, + Version: 1, + Description: "a secret", + Label: "secret label", + RotatePolicy: "hourly", + AutoPrune: true, + Owner: names.NewApplicationTag("postgresql"), + Created: created, + Updated: updated, + NextRotateTime: &nextRotate, + LatestRevisionChecksum: "checksum", + Revisions: testSecretRevisionsArgs(), + ACL: testSecretAccessArgs(), + Consumers: testSecretConsumerArgs(), + RemoteConsumers: testSecretRemoteConsumerArgs(), } } @@ -126,6 +127,7 @@ func (s *SecretsSerializationSuite) TestNewSecret(c *gc.C) { c.Check(secret.Updated(), jc.DeepEquals, args.Updated) c.Check(secret.NextRotateTime(), jc.DeepEquals, args.NextRotateTime) c.Check(secret.LatestRevision(), gc.Equals, 2) + c.Check(secret.LatestRevisionChecksum(), gc.Equals, "checksum") c.Check(secret.LatestExpireTime(), jc.DeepEquals, args.Revisions[1].ExpireTime) owner, err := secret.Owner() c.Check(err, jc.ErrorIsNil) @@ -248,7 +250,7 @@ func (s *SecretsSerializationSuite) exportImport(c *gc.C, secret_ *secret, versi func (s *SecretsSerializationSuite) TestParsingSerializedData(c *gc.C) { args := testSecretArgs() original := newSecret(args) - secret := s.exportImport(c, original, 1) + secret := s.exportImport(c, original, 2) c.Assert(secret, jc.DeepEquals, original) }