summaryrefslogtreecommitdiff
diff options
authorSamuele Pedroni <pedronis@lucediurna.net>2016-02-22 17:57:11 +0100
committerSamuele Pedroni <pedronis@lucediurna.net>2016-02-22 17:57:11 +0100
commitf72b94a0b813506eaefad53581b9a988c4034cc9 (patch)
tree293cd6b6d2ec9a7268edd2d2fb6699ebdb3ffe7f
parentaff9cc8c26be728bd0bda8e7e5e9537449907e07 (diff)
parent050e0cd9399a3b7ab94e993e2b35df5db46b3357 (diff)
Merge pull request #494 from pedronis/asserts-snap-declaration
asserts: introduce snap-declaration
-rw-r--r--asserts/asserts.go22
-rw-r--r--asserts/snap_asserts.go69
-rw-r--r--asserts/snap_asserts_test.go79
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"