diff options
| author | Samuele Pedroni <pedronis@lucediurna.net> | 2016-02-22 17:57:11 +0100 |
|---|---|---|
| committer | Samuele Pedroni <pedronis@lucediurna.net> | 2016-02-22 17:57:11 +0100 |
| commit | f72b94a0b813506eaefad53581b9a988c4034cc9 (patch) | |
| tree | 293cd6b6d2ec9a7268edd2d2fb6699ebdb3ffe7f | |
| parent | aff9cc8c26be728bd0bda8e7e5e9537449907e07 (diff) | |
| parent | 050e0cd9399a3b7ab94e993e2b35df5db46b3357 (diff) | |
Merge pull request #494 from pedronis/asserts-snap-declaration
asserts: introduce snap-declaration
| -rw-r--r-- | asserts/asserts.go | 22 | ||||
| -rw-r--r-- | asserts/snap_asserts.go | 69 | ||||
| -rw-r--r-- | asserts/snap_asserts_test.go | 79 |
3 files changed, 156 insertions, 14 deletions
diff --git a/asserts/asserts.go b/asserts/asserts.go index f82d809051..fd86598e4a 100644 --- a/asserts/asserts.go +++ b/asserts/asserts.go @@ -45,21 +45,23 @@ type AssertionType struct { // Understood assertion types. var ( AccountKeyType = &AssertionType{"account-key", []string{"account-id", "public-key-id"}, assembleAccountKey} - // ModelType XXX: is series actually part of the primary key? - ModelType = &AssertionType{"model", []string{"brand-id", "model", "series"}, assembleModel} - DeviceType = &AssertionType{"device", []string{"brand-id", "model", "serial"}, assembleDevice} - SnapBuildType = &AssertionType{"snap-build", []string{"snap-id", "snap-digest"}, assembleSnapBuild} - SnapRevisionType = &AssertionType{"snap-revision", []string{"snap-id", "snap-digest"}, assembleSnapRevision} + // XXX: is series actually part of the primary key? + ModelType = &AssertionType{"model", []string{"brand-id", "model", "series"}, assembleModel} + DeviceType = &AssertionType{"device", []string{"brand-id", "model", "serial"}, assembleDevice} + SnapDeclarationType = &AssertionType{"snap-declaration", []string{"series", "snap-id"}, assembleSnapDeclaration} + SnapBuildType = &AssertionType{"snap-build", []string{"snap-id", "snap-digest"}, assembleSnapBuild} + SnapRevisionType = &AssertionType{"snap-revision", []string{"snap-id", "snap-digest"}, assembleSnapRevision} // ... ) var typeRegistry = map[string]*AssertionType{ - AccountKeyType.Name: AccountKeyType, - ModelType.Name: ModelType, - DeviceType.Name: DeviceType, - SnapBuildType.Name: SnapBuildType, - SnapRevisionType.Name: SnapRevisionType, + AccountKeyType.Name: AccountKeyType, + ModelType.Name: ModelType, + DeviceType.Name: DeviceType, + SnapDeclarationType.Name: SnapDeclarationType, + SnapBuildType.Name: SnapBuildType, + SnapRevisionType.Name: SnapRevisionType, } // Type returns the AssertionType with name or nil diff --git a/asserts/snap_asserts.go b/asserts/snap_asserts.go index a8fbf1453b..6e09383ffa 100644 --- a/asserts/snap_asserts.go +++ b/asserts/snap_asserts.go @@ -23,6 +23,75 @@ import ( "time" ) +// SnapDeclaration holds a snap-declaration assertion, declaring a +// snap binding its identifying snap-id to a name, asserting its +// publisher and its other properties. +type SnapDeclaration struct { + assertionBase + gates []string + timestamp time.Time +} + +// Series returns the series for which the snap is being declared. +func (snapdcl *SnapDeclaration) Series() string { + return snapdcl.Header("series") +} + +// SnapID returns the snap id of the declared snap. +func (snapdcl *SnapDeclaration) SnapID() string { + return snapdcl.Header("snap-id") +} + +// SnapName returns the declared snap name. +func (snapdcl *SnapDeclaration) SnapName() string { + return snapdcl.Header("snap-name") +} + +// PublisherID returns the identifier of the publisher of the declared snap. +func (snapdcl *SnapDeclaration) PublisherID() string { + return snapdcl.Header("publisher-id") +} + +// Gates returns the list of snap-ids gated by this snap. +func (snapdcl *SnapDeclaration) Gates() []string { + return snapdcl.gates +} + +// Timestamp returns the time when the snap-declaration was issued. +func (snapdcl *SnapDeclaration) Timestamp() time.Time { + return snapdcl.timestamp +} + +// XXX: consistency check is signed by canonical + +func assembleSnapDeclaration(assert assertionBase) (Assertion, error) { + _, err := checkMandatory(assert.headers, "snap-name") + if err != nil { + return nil, err + } + + _, err = checkMandatory(assert.headers, "publisher-id") + if err != nil { + return nil, err + } + + gates, err := checkCommaSepList(assert.headers, "gates") + if err != nil { + return nil, err + } + + timestamp, err := checkRFC3339Date(assert.headers, "timestamp") + if err != nil { + return nil, err + } + + return &SnapDeclaration{ + assertionBase: assert, + gates: gates, + timestamp: timestamp, + }, nil +} + // SnapBuild holds a snap-build assertion, asserting the properties of a snap // at the time it was built by the developer. type SnapBuild struct { diff --git a/asserts/snap_asserts_test.go b/asserts/snap_asserts_test.go index 34d28f145e..5fe1b11add 100644 --- a/asserts/snap_asserts_test.go +++ b/asserts/snap_asserts_test.go @@ -29,16 +29,87 @@ import ( "github.com/ubuntu-core/snappy/asserts" ) -type snapBuildSuite struct { +var ( + _ = Suite(&snapDeclSuite{}) + _ = Suite(&snapBuildSuite{}) + _ = Suite(&snapRevSuite{}) +) + +type snapDeclSuite struct { ts time.Time tsLine string } -var ( - _ = Suite(&snapBuildSuite{}) - _ = Suite(&snapRevSuite{}) +func (sds *snapDeclSuite) SetUpSuite(c *C) { + sds.ts = time.Now().Truncate(time.Second).UTC() + sds.tsLine = "timestamp: " + sds.ts.Format(time.RFC3339) + "\n" +} + +func (sds *snapDeclSuite) TestDecodeOK(c *C) { + encoded := "type: snap-declaration\n" + + "authority-id: canonical\n" + + "series: 16\n" + + "snap-id: snap-id-1\n" + + "snap-name: first\n" + + "publisher-id: dev-id1\n" + + "gates: snap-id-3,snap-id-4\n" + + sds.tsLine + + "body-length: 0" + + "\n\n" + + "openpgp c2ln" + a, err := asserts.Decode([]byte(encoded)) + c.Assert(err, IsNil) + c.Check(a.Type(), Equals, asserts.SnapDeclarationType) + snapDecl := a.(*asserts.SnapDeclaration) + c.Check(snapDecl.AuthorityID(), Equals, "canonical") + c.Check(snapDecl.Timestamp(), Equals, sds.ts) + c.Check(snapDecl.Series(), Equals, "16") + c.Check(snapDecl.SnapID(), Equals, "snap-id-1") + c.Check(snapDecl.SnapName(), Equals, "first") + c.Check(snapDecl.PublisherID(), Equals, "dev-id1") + c.Check(snapDecl.Gates(), DeepEquals, []string{"snap-id-3", "snap-id-4"}) +} + +const ( + snapDeclErrPrefix = "assertion snap-declaration: " ) +func (sds *snapDeclSuite) TestDecodeInvalid(c *C) { + encoded := "type: snap-declaration\n" + + "authority-id: canonical\n" + + "series: 16\n" + + "snap-id: snap-id-1\n" + + "snap-name: first\n" + + "publisher-id: dev-id1\n" + + "gates: snap-id-3,snap-id-4\n" + + sds.tsLine + + "body-length: 0" + + "\n\n" + + "openpgp c2ln" + + invalidTests := []struct{ original, invalid, expectedErr string }{ + {"series: 16\n", "", `"series" header is mandatory`}, + {"snap-id: snap-id-1\n", "", `"snap-id" header is mandatory`}, + {"snap-name: first\n", "", `"snap-name" header is mandatory`}, + {"publisher-id: dev-id1\n", "", `"publisher-id" header is mandatory`}, + {"gates: snap-id-3,snap-id-4\n", "", `\"gates\" header is mandatory`}, + {"gates: snap-id-3,snap-id-4\n", "gates: foo,\n", `empty entry in comma separated "gates" header: "foo,"`}, + {sds.tsLine, "timestamp: 12:30\n", `"timestamp" header is not a RFC3339 date: .*`}, + } + + for _, test := range invalidTests { + invalid := strings.Replace(encoded, test.original, test.invalid, 1) + _, err := asserts.Decode([]byte(invalid)) + c.Check(err, ErrorMatches, snapDeclErrPrefix+test.expectedErr) + } + +} + +type snapBuildSuite struct { + ts time.Time + tsLine string +} + func (sbs *snapBuildSuite) SetUpSuite(c *C) { sbs.ts = time.Now().Truncate(time.Second).UTC() sbs.tsLine = "timestamp: " + sbs.ts.Format(time.RFC3339) + "\n" |
