summaryrefslogtreecommitdiff
diff options
authorMichael Vogt <mvo@ubuntu.com>2016-11-23 08:22:24 +0100
committerMichael Vogt <mvo@ubuntu.com>2016-11-23 08:22:24 +0100
commit5163798cf7b1875f9fb5f2ac1070339f01b82509 (patch)
tree6cef8c9ee9a2d8d6316623783d4c80c42aa476a3
parent2946c5a3f9c197dd7187e92d0c43c6f199d1070f (diff)
parent9c65dd43be8359fccf3f3724e50a1859f96a962c (diff)
Merge remote-tracking branch 'upstream/master' into feature/gofmt-s-testfeature/gofmt-s-test
-rw-r--r--asserts/account_key.go4
-rw-r--r--asserts/account_key_test.go3
-rw-r--r--asserts/asserts_test.go3
-rw-r--r--asserts/database_test.go9
-rw-r--r--asserts/device_asserts.go16
-rw-r--r--asserts/device_asserts_test.go20
-rw-r--r--asserts/membackstore.go2
-rw-r--r--asserts/snap_asserts.go10
-rw-r--r--asserts/snap_asserts_test.go1
-rw-r--r--cmd/snap/cmd_delete_key_test.go3
-rw-r--r--cmd/snap/cmd_keys_test.go4
-rw-r--r--daemon/api.go31
-rw-r--r--daemon/api_test.go15
-rw-r--r--daemon/snap.go6
-rw-r--r--i18n/xgettext-go/main.go2
-rw-r--r--i18n/xgettext-go/main_test.go28
-rw-r--r--interfaces/apparmor/backend_test.go1
-rw-r--r--interfaces/backend.go41
-rw-r--r--interfaces/builtin/all.go2
-rw-r--r--interfaces/builtin/all_test.go2
-rw-r--r--interfaces/builtin/basedeclaration.go10
-rw-r--r--interfaces/builtin/basedeclaration_test.go42
-rw-r--r--interfaces/builtin/browser_support.go3
-rw-r--r--interfaces/builtin/gpio.go2
-rw-r--r--interfaces/builtin/modem_manager.go2
-rw-r--r--interfaces/builtin/network_manager.go2
-rw-r--r--interfaces/builtin/raw_usb.go (renamed from interfaces/builtin/usb_raw.go)8
-rw-r--r--interfaces/builtin/raw_usb_test.go (renamed from interfaces/builtin/usb_raw_test.go)36
-rw-r--r--interfaces/dbus/backend_test.go1
-rw-r--r--interfaces/repo_test.go4
-rw-r--r--interfaces/seccomp/backend_test.go1
-rw-r--r--interfaces/systemd/backend_test.go26
-rw-r--r--interfaces/udev/backend_test.go3
-rw-r--r--osutil/exitcode_test.go1
-rw-r--r--overlord/assertstate/assertmgr.go8
-rw-r--r--overlord/auth/auth.go2
-rw-r--r--overlord/auth/auth_test.go1
-rw-r--r--overlord/devicestate/devicemgr_test.go1
-rw-r--r--overlord/devicestate/firstboot_test.go1
-rw-r--r--overlord/ifacestate/handlers.go6
-rw-r--r--overlord/managers_test.go2
-rw-r--r--overlord/patch/patch1.go10
-rw-r--r--overlord/patch/patch5.go6
-rw-r--r--overlord/patch/patch_test.go2
-rw-r--r--overlord/snapstate/snapmgr.go3
-rw-r--r--overlord/snapstate/snapmgr_test.go11
-rw-r--r--overlord/state/state_test.go2
-rw-r--r--overlord/state/task.go2
-rw-r--r--partition/bootloader_test.go1
-rw-r--r--progress/progress.go3
-rw-r--r--snap/implicit.go2
-rw-r--r--snap/info_test.go1
-rw-r--r--snap/snaptest/build_test.go3
-rw-r--r--snap/validate_test.go30
-rw-r--r--store/auth.go3
-rw-r--r--store/store.go506
-rw-r--r--store/store_test.go2
-rw-r--r--systemd/escape.go2
-rw-r--r--tests/main/ubuntu-core-custom-device-reg-extras/manip_seed.py21
-rwxr-xr-xtests/main/ubuntu-core-custom-device-reg-extras/prepare-device5
-rw-r--r--tests/main/ubuntu-core-custom-device-reg-extras/task.yaml81
-rwxr-xr-xtests/main/ubuntu-core-custom-device-reg/prepare-device2
-rw-r--r--tests/main/ubuntu-core-custom-device-reg/task.yaml7
63 files changed, 632 insertions, 438 deletions
diff --git a/asserts/account_key.go b/asserts/account_key.go
index 3e01b4ad12..a6320ff700 100644
--- a/asserts/account_key.go
+++ b/asserts/account_key.go
@@ -138,7 +138,7 @@ var _ consistencyChecker = (*AccountKey)(nil)
// Prerequisites returns references to this account-key's prerequisite assertions.
func (ak *AccountKey) Prerequisites() []*Ref {
return []*Ref{
- &Ref{Type: AccountType, PrimaryKey: []string{ak.AccountID()}},
+ {Type: AccountType, PrimaryKey: []string{ak.AccountID()}},
}
}
@@ -245,7 +245,7 @@ var (
// Prerequisites returns references to this account-key-request's prerequisite assertions.
func (akr *AccountKeyRequest) Prerequisites() []*Ref {
return []*Ref{
- &Ref{Type: AccountType, PrimaryKey: []string{akr.AccountID()}},
+ {Type: AccountType, PrimaryKey: []string{akr.AccountID()}},
}
}
diff --git a/asserts/account_key_test.go b/asserts/account_key_test.go
index 37887ce183..d1fd153b9e 100644
--- a/asserts/account_key_test.go
+++ b/asserts/account_key_test.go
@@ -394,6 +394,7 @@ func (aks *accountKeySuite) TestAccountKeyCheckSameAccountAndDifferentName(c *C)
newPubKey, err := db.PublicKey(newPrivKey.PublicKey().ID())
c.Assert(err, IsNil)
newPubKeyEncoded, err := asserts.EncodePublicKey(newPubKey)
+ c.Assert(err, IsNil)
headers["name"] = "another"
headers["public-key-sha3-384"] = newPubKey.ID()
@@ -432,6 +433,7 @@ func (aks *accountKeySuite) TestAccountKeyCheckSameNameAndDifferentAccount(c *C)
newPubKey, err := db.PublicKey(newPrivKey.PublicKey().ID())
c.Assert(err, IsNil)
newPubKeyEncoded, err := asserts.EncodePublicKey(newPubKey)
+ c.Assert(err, IsNil)
acct2 := assertstest.NewAccount(db, "acc-id2", map[string]interface{}{
"authority-id": "canonical",
@@ -475,6 +477,7 @@ func (aks *accountKeySuite) TestAccountKeyCheckNameClash(c *C) {
newPubKey, err := db.PublicKey(newPrivKey.PublicKey().ID())
c.Assert(err, IsNil)
newPubKeyEncoded, err := asserts.EncodePublicKey(newPubKey)
+ c.Assert(err, IsNil)
headers["public-key-sha3-384"] = newPubKey.ID()
headers["revision"] = "1"
diff --git a/asserts/asserts_test.go b/asserts/asserts_test.go
index 00f9af8e23..a6b3314b21 100644
--- a/asserts/asserts_test.go
+++ b/asserts/asserts_test.go
@@ -353,6 +353,7 @@ func (as *assertsSuite) TestDecoderHappyWithSeparatorsVariations(c *C) {
checkContent(c, a, streamData)
a, err = decoder.Decode()
+ c.Check(a, IsNil)
c.Check(err, Equals, io.EOF, Commentf("stream: %q", streamData))
}
}
@@ -379,6 +380,7 @@ func (as *assertsSuite) TestDecoderHappyWithTrailerDoubleNewlines(c *C) {
checkContent(c, a, streamData)
a, err = decoder.Decode()
+ c.Check(a, IsNil)
c.Check(err, Equals, io.EOF, Commentf("stream: %q", streamData))
}
}
@@ -578,6 +580,7 @@ func (as *assertsSuite) TestSignFormatAndRevision(c *C) {
}
a, err := asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, nil, testPrivKey1)
+ c.Assert(err, IsNil)
c.Check(a.Revision(), Equals, 11)
c.Check(a.Format(), Equals, 1)
diff --git a/asserts/database_test.go b/asserts/database_test.go
index b3af17fee7..e2c30436ba 100644
--- a/asserts/database_test.go
+++ b/asserts/database_test.go
@@ -94,6 +94,7 @@ func (opens *openSuite) TestOpenDatabaseTrustedWrongType(c *C) {
"primary-key": "0",
}
a, err := asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, nil, testPrivKey0)
+ c.Assert(err, IsNil)
cfg := &asserts.DatabaseConfig{
Trusted: []asserts.Assertion{a},
@@ -559,12 +560,6 @@ func (safs *signAddFindSuite) TestAddUnsupportedFormat(c *C) {
"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" +
"\n\n" +
"AXNpZw=="
- headers := map[string]interface{}{
- "authority-id": "canonical",
- "primary-key": "a",
- "format": "77",
- "payload": "unsupported",
- }
aUnsupp, err := asserts.Decode([]byte(unsupported))
c.Assert(err, IsNil)
c.Assert(aUnsupp.SupportedFormat(), Equals, false)
@@ -575,7 +570,7 @@ func (safs *signAddFindSuite) TestAddUnsupportedFormat(c *C) {
c.Check(err, ErrorMatches, `proposed "test-only" assertion has format 77 but 1 is latest supported`)
c.Check(asserts.IsUnaccceptedUpdate(err), Equals, false)
- headers = map[string]interface{}{
+ headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "a",
"format": "1",
diff --git a/asserts/device_asserts.go b/asserts/device_asserts.go
index 2e427fab0c..83a3099bef 100644
--- a/asserts/device_asserts.go
+++ b/asserts/device_asserts.go
@@ -45,6 +45,16 @@ func (mod *Model) Model() string {
return mod.HeaderString("model")
}
+// DisplayName returns the human-friendly name of the model or
+// falls back to Model if this was not set.
+func (mod *Model) DisplayName() string {
+ display := mod.HeaderString("display-name")
+ if display == "" {
+ return mod.Model()
+ }
+ return display
+}
+
// Series returns the series of the core software the model uses.
func (mod *Model) Series() string {
return mod.HeaderString("series")
@@ -168,6 +178,12 @@ func assembleModel(assert assertionBase) (Assertion, error) {
return nil, err
}
+ // display-name is optional but must be a string
+ _, err = checkOptionalString(assert.headers, "display-name")
+ if err != nil {
+ return nil, err
+ }
+
reqSnaps, err := checkStringList(assert.headers, "required-snaps")
if err != nil {
return nil, err
diff --git a/asserts/device_asserts_test.go b/asserts/device_asserts_test.go
index b0540101ad..b468683848 100644
--- a/asserts/device_asserts_test.go
+++ b/asserts/device_asserts_test.go
@@ -53,6 +53,7 @@ const modelExample = "type: model\n" +
"series: 16\n" +
"brand-id: brand-id1\n" +
"model: baz-3000\n" +
+ "display-name: Baz 3000\n" +
"architecture: amd64\n" +
"gadget: brand-gadget\n" +
"kernel: baz-linux\n" +
@@ -76,6 +77,7 @@ func (mods *modelSuite) TestDecodeOK(c *C) {
c.Check(model.Series(), Equals, "16")
c.Check(model.BrandID(), Equals, "brand-id1")
c.Check(model.Model(), Equals, "baz-3000")
+ c.Check(model.DisplayName(), Equals, "Baz 3000")
c.Check(model.Architecture(), Equals, "amd64")
c.Check(model.Gadget(), Equals, "brand-gadget")
c.Check(model.Kernel(), Equals, "baz-linux")
@@ -99,6 +101,23 @@ func (mods *modelSuite) TestDecodeStoreIsOptional(c *C) {
c.Check(model.Store(), Equals, "")
}
+func (mods *modelSuite) TestDecodeDisplayNameIsOptional(c *C) {
+ withTimestamp := strings.Replace(modelExample, "TSLINE", mods.tsLine, 1)
+ encoded := strings.Replace(withTimestamp, "display-name: Baz 3000\n", "display-name: \n", 1)
+ a, err := asserts.Decode([]byte(encoded))
+ c.Assert(err, IsNil)
+ model := a.(*asserts.Model)
+ // optional but we fallback to Model
+ c.Check(model.DisplayName(), Equals, "baz-3000")
+
+ encoded = strings.Replace(withTimestamp, "display-name: Baz 3000\n", "", 1)
+ a, err = asserts.Decode([]byte(encoded))
+ c.Assert(err, IsNil)
+ model = a.(*asserts.Model)
+ // optional but we fallback to Model
+ c.Check(model.DisplayName(), Equals, "baz-3000")
+}
+
func (mods *modelSuite) TestDecodeRequiredSnapsAreOptional(c *C) {
withTimestamp := strings.Replace(modelExample, "TSLINE", mods.tsLine, 1)
encoded := strings.Replace(withTimestamp, reqSnaps, "", 1)
@@ -142,6 +161,7 @@ func (mods *modelSuite) TestDecodeInvalid(c *C) {
{"model: baz-3000\n", "model: baz/3000\n", `"model" primary key header cannot contain '/'`},
// lift this restriction at a later point
{"model: baz-3000\n", "model: BAZ-3000\n", `"model" header cannot contain uppercase letters`},
+ {"display-name: Baz 3000\n", "display-name:\n - xyz\n", `"display-name" header must be a string`},
{"architecture: amd64\n", "", `"architecture" header is mandatory`},
{"architecture: amd64\n", "architecture: \n", `"architecture" header should not be empty`},
{"gadget: brand-gadget\n", "", `"gadget" header is mandatory`},
diff --git a/asserts/membackstore.go b/asserts/membackstore.go
index 214359b34c..fe744fefd9 100644
--- a/asserts/membackstore.go
+++ b/asserts/membackstore.go
@@ -116,7 +116,7 @@ func (br memBSBranch) search(hint []string, found func(Assertion), maxFormat int
func (leaf memBSLeaf) search(hint []string, found func(Assertion), maxFormat int) {
hint0 := hint[0]
if hint0 == "" {
- for key, _ := range leaf {
+ for key := range leaf {
cand := leaf.cur(key, maxFormat)
if cand != nil {
found(cand)
diff --git a/asserts/snap_asserts.go b/asserts/snap_asserts.go
index 65a09c020d..84666a58de 100644
--- a/asserts/snap_asserts.go
+++ b/asserts/snap_asserts.go
@@ -106,7 +106,7 @@ var _ consistencyChecker = (*SnapDeclaration)(nil)
// Prerequisites returns references to this snap-declaration's prerequisite assertions.
func (snapdcl *SnapDeclaration) Prerequisites() []*Ref {
return []*Ref{
- &Ref{Type: AccountType, PrimaryKey: []string{snapdcl.PublisherID()}},
+ {Type: AccountType, PrimaryKey: []string{snapdcl.PublisherID()}},
}
}
@@ -332,8 +332,8 @@ var _ consistencyChecker = (*SnapRevision)(nil)
func (snaprev *SnapRevision) Prerequisites() []*Ref {
return []*Ref{
// XXX: mediate getting current series through some context object? this gets the job done for now
- &Ref{Type: SnapDeclarationType, PrimaryKey: []string{release.Series, snaprev.SnapID()}},
- &Ref{Type: AccountType, PrimaryKey: []string{snaprev.DeveloperID()}},
+ {Type: SnapDeclarationType, PrimaryKey: []string{release.Series, snaprev.SnapID()}},
+ {Type: AccountType, PrimaryKey: []string{snaprev.DeveloperID()}},
}
}
@@ -457,8 +457,8 @@ var _ consistencyChecker = (*Validation)(nil)
// Prerequisites returns references to this validation's prerequisite assertions.
func (validation *Validation) Prerequisites() []*Ref {
return []*Ref{
- &Ref{Type: SnapDeclarationType, PrimaryKey: []string{validation.Series(), validation.SnapID()}},
- &Ref{Type: SnapDeclarationType, PrimaryKey: []string{validation.Series(), validation.ApprovedSnapID()}},
+ {Type: SnapDeclarationType, PrimaryKey: []string{validation.Series(), validation.SnapID()}},
+ {Type: SnapDeclarationType, PrimaryKey: []string{validation.Series(), validation.ApprovedSnapID()}},
}
}
diff --git a/asserts/snap_asserts_test.go b/asserts/snap_asserts_test.go
index ec5c791d48..684cbe1934 100644
--- a/asserts/snap_asserts_test.go
+++ b/asserts/snap_asserts_test.go
@@ -1080,6 +1080,7 @@ AXNpZw==`
baseDecl := a.(*asserts.BaseDeclaration)
c.Check(baseDecl.Series(), Equals, "16")
ts, err := time.Parse(time.RFC3339, "2016-09-29T19:50:49Z")
+ c.Assert(err, IsNil)
c.Check(baseDecl.Timestamp().Equal(ts), Equals, true)
c.Check(baseDecl.PlugRule("interfaceX"), IsNil)
diff --git a/cmd/snap/cmd_delete_key_test.go b/cmd/snap/cmd_delete_key_test.go
index 2b8b18490a..65b83dc3c4 100644
--- a/cmd/snap/cmd_delete_key_test.go
+++ b/cmd/snap/cmd_delete_key_test.go
@@ -50,8 +50,9 @@ func (s *SnapKeysSuite) TestDeleteKey(c *C) {
c.Check(s.Stdout(), Equals, "")
c.Check(s.Stderr(), Equals, "")
_, err = snap.Parser().ParseArgs([]string{"keys", "--json"})
+ c.Assert(err, IsNil)
expectedResponse := []snap.Key{
- snap.Key{
+ {
Name: "default",
Sha3_384: "g4Pks54W_US4pZuxhgG_RHNAf_UeZBBuZyGRLLmMj1Do3GkE_r_5A5BFjx24ZwVJ",
},
diff --git a/cmd/snap/cmd_keys_test.go b/cmd/snap/cmd_keys_test.go
index af20d05bf3..eec332e5ff 100644
--- a/cmd/snap/cmd_keys_test.go
+++ b/cmd/snap/cmd_keys_test.go
@@ -101,11 +101,11 @@ func (s *SnapKeysSuite) TestKeysJSON(c *C) {
c.Assert(err, IsNil)
c.Assert(rest, DeepEquals, []string{})
expectedResponse := []snap.Key{
- snap.Key{
+ {
Name: "default",
Sha3_384: "g4Pks54W_US4pZuxhgG_RHNAf_UeZBBuZyGRLLmMj1Do3GkE_r_5A5BFjx24ZwVJ",
},
- snap.Key{
+ {
Name: "another",
Sha3_384: "DVQf1U4mIsuzlQqAebjjTPYtYJ-GEhJy0REuj3zvpQYTZ7EJj7adBxIXLJ7Vmk3L",
},
diff --git a/daemon/api.go b/daemon/api.go
index 3c62f96b70..c8a36af764 100644
--- a/daemon/api.go
+++ b/daemon/api.go
@@ -768,6 +768,16 @@ func modeFlags(devMode, jailMode bool) (snapstate.Flags, error) {
}
+// quotedNames formats a slice of snap names (or other strings) to a
+// quoted list of comma separated names, e.g. `"snap1", "snap2"`
+func quotedNames(names []string) string {
+ quoted := make([]string, len(names))
+ for i, name := range names {
+ quoted[i] = strconv.Quote(name)
+ }
+ return strings.Join(quoted, ", ")
+}
+
func snapUpdateMany(inst *snapInstruction, st *state.State) (msg string, updated []string, tasksets []*state.TaskSet, err error) {
// we need refreshed snap-declarations to enforce refresh-control as best as we can, this also ensures that snap-declarations and their prerequisite assertions are updated regularly
if err := assertstateRefreshSnapDeclarations(st, inst.userID); err != nil {
@@ -786,12 +796,9 @@ func snapUpdateMany(inst *snapInstruction, st *state.State) (msg string, updated
case 1:
msg = fmt.Sprintf(i18n.G("Refresh snap %q"), inst.Snaps[0])
default:
- quoted := make([]string, len(inst.Snaps))
- for i, name := range inst.Snaps {
- quoted[i] = strconv.Quote(name)
- }
+ quoted := quotedNames(inst.Snaps)
// TRANSLATORS: the %s is a comma-separated list of quoted snap names
- msg = fmt.Sprintf(i18n.G("Refresh snaps %s"), strings.Join(quoted, ", "))
+ msg = fmt.Sprintf(i18n.G("Refresh snaps %s"), quoted)
}
return msg, updated, tasksets, nil
@@ -809,12 +816,9 @@ func snapInstallMany(inst *snapInstruction, st *state.State) (msg string, instal
case 1:
msg = fmt.Sprintf(i18n.G("Install snap %q"), inst.Snaps[0])
default:
- quoted := make([]string, len(inst.Snaps))
- for i, name := range inst.Snaps {
- quoted[i] = strconv.Quote(name)
- }
+ quoted := quotedNames(inst.Snaps)
// TRANSLATORS: the %s is a comma-separated list of quoted snap names
- msg = fmt.Sprintf(i18n.G("Install snaps %s"), strings.Join(quoted, ", "))
+ msg = fmt.Sprintf(i18n.G("Install snaps %s"), quoted)
}
return msg, installed, tasksets, nil
@@ -884,12 +888,9 @@ func snapRemoveMany(inst *snapInstruction, st *state.State) (msg string, removed
case 1:
msg = fmt.Sprintf(i18n.G("Remove snap %q"), inst.Snaps[0])
default:
- quoted := make([]string, len(inst.Snaps))
- for i, name := range inst.Snaps {
- quoted[i] = strconv.Quote(name)
- }
+ quoted := quotedNames(inst.Snaps)
// TRANSLATORS: the %s is a comma-separated list of quoted snap names
- msg = fmt.Sprintf(i18n.G("Remove snaps %s"), strings.Join(quoted, ", "))
+ msg = fmt.Sprintf(i18n.G("Remove snaps %s"), quoted)
}
return msg, removed, tasksets, nil
diff --git a/daemon/api_test.go b/daemon/api_test.go
index 26d26e6bf8..5cb89df147 100644
--- a/daemon/api_test.go
+++ b/daemon/api_test.go
@@ -312,6 +312,19 @@ func (s *apiSuite) TestSnapInfoOneIntegration(c *check.C) {
c.Check(rsp.Result, check.DeepEquals, expected.Result)
}
+func (s *apiSuite) TestQuotedNames(c *check.C) {
+ for _, t := range []struct {
+ in []string
+ out string
+ }{
+ {[]string{}, ""},
+ {[]string{"snap1"}, `"snap1"`},
+ {[]string{"snap1", "snap2"}, `"snap1", "snap2"`},
+ } {
+ c.Check(quotedNames(t.in), check.Equals, t.out)
+ }
+}
+
func (s *apiSuite) TestSnapInfoWithAuth(c *check.C) {
state := snapCmd.d.overlord.State()
state.Lock()
@@ -4305,6 +4318,7 @@ func (s *postCreateUserSuite) TestPostCreateUserFromAssertion(c *check.C) {
st := s.d.overlord.State()
st.Lock()
users, err := auth.Users(st)
+ c.Assert(err, check.IsNil)
st.Unlock()
c.Check(users, check.HasLen, 1)
}
@@ -4359,6 +4373,7 @@ func (s *postCreateUserSuite) TestPostCreateUserFromAssertionAllKnown(c *check.C
st := s.d.overlord.State()
st.Lock()
users, err := auth.Users(st)
+ c.Assert(err, check.IsNil)
st.Unlock()
c.Check(users, check.HasLen, 2)
}
diff --git a/daemon/snap.go b/daemon/snap.go
index b1c84b49b5..082e0da4fa 100644
--- a/daemon/snap.go
+++ b/daemon/snap.go
@@ -93,8 +93,8 @@ func allLocalSnapInfos(st *state.State) ([]aboutSnap, error) {
about := make([]aboutSnap, 0, len(snapStates))
var firstErr error
- for _, snapState := range snapStates {
- info, err := snapState.CurrentInfo()
+ for _, snapst := range snapStates {
+ info, err := snapst.CurrentInfo()
if err != nil {
// XXX: aggregate instead?
if firstErr == nil {
@@ -102,7 +102,7 @@ func allLocalSnapInfos(st *state.State) ([]aboutSnap, error) {
}
continue
}
- about = append(about, aboutSnap{info, snapState})
+ about = append(about, aboutSnap{info, snapst})
}
return about, firstErr
diff --git a/i18n/xgettext-go/main.go b/i18n/xgettext-go/main.go
index ce36cee466..b7cc234bfd 100644
--- a/i18n/xgettext-go/main.go
+++ b/i18n/xgettext-go/main.go
@@ -84,7 +84,7 @@ func constructValue(val interface{}) string {
left = left[0 : len(left)-1]
right := constructValue(val.(*ast.BinaryExpr).Y)
// strip left " (or `)
- right = right[1:len(right)]
+ right = right[1:]
return left + right
default:
panic(fmt.Sprintf("unknown type: %v", val))
diff --git a/i18n/xgettext-go/main_test.go b/i18n/xgettext-go/main_test.go
index 7ac1611b65..02183daf64 100644
--- a/i18n/xgettext-go/main_test.go
+++ b/i18n/xgettext-go/main_test.go
@@ -91,7 +91,7 @@ func main() {
c.Assert(err, IsNil)
c.Assert(msgIDs, DeepEquals, map[string][]msgID{
- "foo": []msgID{
+ "foo": {
{
comment: "#. TRANSLATORS: foo comment\n",
fname: fname,
@@ -116,7 +116,7 @@ func main() {
c.Assert(err, IsNil)
c.Assert(msgIDs, DeepEquals, map[string][]msgID{
- "foo": []msgID{
+ "foo": {
{
comment: "#. TRANSLATORS: foo comment\n",
fname: fname,
@@ -152,7 +152,7 @@ msgstr "Project-Id-Version: snappy\n"
func (s *xgettextTestSuite) TestWriteOutputSimple(c *C) {
msgIDs = map[string][]msgID{
- "foo": []msgID{
+ "foo": {
{
fname: "fname",
line: 2,
@@ -175,7 +175,7 @@ msgstr ""
func (s *xgettextTestSuite) TestWriteOutputMultiple(c *C) {
msgIDs = map[string][]msgID{
- "foo": []msgID{
+ "foo": {
{
fname: "fname",
line: 2,
@@ -204,7 +204,7 @@ msgstr ""
func (s *xgettextTestSuite) TestWriteOutputNoComment(c *C) {
msgIDs = map[string][]msgID{
- "foo": []msgID{
+ "foo": {
{
fname: "fname",
line: 2,
@@ -225,7 +225,7 @@ msgstr ""
func (s *xgettextTestSuite) TestWriteOutputNoLocation(c *C) {
msgIDs = map[string][]msgID{
- "foo": []msgID{
+ "foo": {
{
fname: "fname",
line: 2,
@@ -247,7 +247,7 @@ msgstr ""
func (s *xgettextTestSuite) TestWriteOutputFormatHint(c *C) {
msgIDs = map[string][]msgID{
- "foo": []msgID{
+ "foo": {
{
fname: "fname",
line: 2,
@@ -271,7 +271,7 @@ msgstr ""
func (s *xgettextTestSuite) TestWriteOutputPlural(c *C) {
msgIDs = map[string][]msgID{
- "foo": []msgID{
+ "foo": {
{
msgidPlural: "plural",
fname: "fname",
@@ -296,13 +296,13 @@ msgstr[1] ""
func (s *xgettextTestSuite) TestWriteOutputSorted(c *C) {
msgIDs = map[string][]msgID{
- "aaa": []msgID{
+ "aaa": {
{
fname: "fname",
line: 2,
},
},
- "zzz": []msgID{
+ "zzz": {
{
fname: "fname",
line: 2,
@@ -403,7 +403,7 @@ func main() {
c.Assert(err, IsNil)
c.Assert(msgIDs, DeepEquals, map[string][]msgID{
- "foo\\nbar\\nbaz": []msgID{
+ "foo\\nbar\\nbaz": {
{
comment: "#. TRANSLATORS: foo comment\n",
fname: fname,
@@ -438,7 +438,7 @@ msgstr ""
func (s *xgettextTestSuite) TestWriteOutputMultilines(c *C) {
msgIDs = map[string][]msgID{
- "foo\\nbar\\nbaz": []msgID{
+ "foo\\nbar\\nbaz": {
{
fname: "fname",
line: 2,
@@ -462,13 +462,13 @@ msgstr ""
func (s *xgettextTestSuite) TestWriteOutputTidy(c *C) {
msgIDs = map[string][]msgID{
- "foo\\nbar\\nbaz": []msgID{
+ "foo\\nbar\\nbaz": {
{
fname: "fname",
line: 2,
},
},
- "zzz\\n": []msgID{
+ "zzz\\n": {
{
fname: "fname",
line: 4,
diff --git a/interfaces/apparmor/backend_test.go b/interfaces/apparmor/backend_test.go
index ada77890d3..77589f667a 100644
--- a/interfaces/apparmor/backend_test.go
+++ b/interfaces/apparmor/backend_test.go
@@ -364,6 +364,7 @@ func (s *backendSuite) TestCombineSnippets(c *C) {
c.Assert(err, IsNil)
c.Check(string(data), Equals, scenario.content)
stat, err := os.Stat(profile)
+ c.Assert(err, IsNil)
c.Check(stat.Mode(), Equals, os.FileMode(0644))
s.RemoveSnap(c, snapInfo)
}
diff --git a/interfaces/backend.go b/interfaces/backend.go
index fc9becba1c..0875c7dcbc 100644
--- a/interfaces/backend.go
+++ b/interfaces/backend.go
@@ -23,6 +23,47 @@ import (
"github.com/snapcore/snapd/snap"
)
+// ConfinementOptions describe confinement configuration.
+//
+// The confinement system controls the initial layout of the mount namespace as
+// well as the set of actions a process is allowed to perform. Confinement is
+// initially defined by the ConfinementType declared by the snap. It can be
+// either "strict", "devmode" or "classic".
+//
+// The "strict" type uses mount layout that puts the core snap as the root
+// filesystem and provides strong isolation from the system and from other
+// snaps. Violations cause permission errors or mandatory process termination.
+//
+// The "devmode" type uses the same mount layout as "strict" but switches
+// confinement to non-enforcing mode whenever possible. Violations that would
+// result in permission error or process termination are instead permitted. A
+// diagnostic message is logged when this occurs.
+//
+// The "classic" type uses mount layout that is identical to the runtime of the
+// classic system snapd runs in, in other words there is no "chroot". Most of
+// the confinement is lifted, specifically there's no seccomp filter being
+// applied and apparmor is using complain mode by default.
+//
+// The three types defined above map to some combinations of the three flags
+// defined below.
+//
+// The DevMode flag attempts to switch all confinement facilities into
+// non-enforcing mode even if the snap requested otherwise.
+//
+// The JailMode flag attempts to switch all confinement facilities into
+// enforcing mode even if the snap requested otherwise.
+//
+// The Classic flag switches the layout of the mount namespace so that there's
+// no "chroot" to the core snap.
+type ConfinementOptions struct {
+ // DevMode flag switches confinement to non-enforcing mode.
+ DevMode bool
+ // JailMode flag switches confinement to enforcing mode.
+ JailMode bool
+ // Classic flag switches the core snap "chroot" off.
+ Classic bool
+}
+
// SecurityBackend abstracts interactions between the interface system and the
// needs of a particular security system.
type SecurityBackend interface {
diff --git a/interfaces/builtin/all.go b/interfaces/builtin/all.go
index c7a370bf5a..4563064fe4 100644
--- a/interfaces/builtin/all.go
+++ b/interfaces/builtin/all.go
@@ -71,6 +71,7 @@ var allInterfaces = []interfaces.Interface{
NewOpenglInterface(),
NewOpticalDriveInterface(),
NewProcessControlInterface(),
+ NewRawUsbInterface(),
NewRemovableMediaInterface(),
NewScreenInhibitControlInterface(),
NewShutdownInterface(),
@@ -83,7 +84,6 @@ var allInterfaces = []interfaces.Interface{
NewTpmInterface(),
NewUPowerObserveInterface(),
NewUnity7Interface(),
- NewUsbRawInterface(),
NewX11Interface(),
}
diff --git a/interfaces/builtin/all_test.go b/interfaces/builtin/all_test.go
index 381ef405ea..4411ec5c7d 100644
--- a/interfaces/builtin/all_test.go
+++ b/interfaces/builtin/all_test.go
@@ -70,6 +70,7 @@ func (s *AllSuite) TestInterfaces(c *C) {
c.Check(all, DeepContains, builtin.NewOpenglInterface())
c.Check(all, DeepContains, builtin.NewOpticalDriveInterface())
c.Check(all, DeepContains, builtin.NewProcessControlInterface())
+ c.Check(all, DeepContains, builtin.NewRawUsbInterface())
c.Check(all, DeepContains, builtin.NewRemovableMediaInterface())
c.Check(all, DeepContains, builtin.NewScreenInhibitControlInterface())
c.Check(all, DeepContains, builtin.NewSnapdControlInterface())
@@ -81,6 +82,5 @@ func (s *AllSuite) TestInterfaces(c *C) {
c.Check(all, DeepContains, builtin.NewTpmInterface())
c.Check(all, DeepContains, builtin.NewUPowerObserveInterface())
c.Check(all, DeepContains, builtin.NewUnity7Interface())
- c.Check(all, DeepContains, builtin.NewUsbRawInterface())
c.Check(all, DeepContains, builtin.NewX11Interface())
}
diff --git a/interfaces/builtin/basedeclaration.go b/interfaces/builtin/basedeclaration.go
index e8baaa22e2..02abc20cf0 100644
--- a/interfaces/builtin/basedeclaration.go
+++ b/interfaces/builtin/basedeclaration.go
@@ -397,6 +397,11 @@ slots:
- core
deny-connection:
on-classic: false
+ raw-usb:
+ allow-installation:
+ slot-snap-type:
+ - core
+ deny-auto-connection: true
removable-media:
allow-installation:
slot-snap-type:
@@ -466,11 +471,6 @@ slots:
allow-installation:
slot-snap-type:
- core
- usb-raw:
- allow-installation:
- slot-snap-type:
- - core
- deny-auto-connection: true
x11:
allow-installation:
slot-snap-type:
diff --git a/interfaces/builtin/basedeclaration_test.go b/interfaces/builtin/basedeclaration_test.go
index cb1e07ae94..675cfc71c0 100644
--- a/interfaces/builtin/basedeclaration_test.go
+++ b/interfaces/builtin/basedeclaration_test.go
@@ -345,27 +345,27 @@ var (
slotInstallation = map[string][]string{
// other
- "bluez": []string{"app"},
- "bool-file": []string{"core", "gadget"},
- "browser-support": []string{"core"},
- "content": []string{"app", "gadget"},
- "docker-support": []string{"core"},
- "fwupd": []string{"app"},
- "gpio": []string{"core", "gadget"},
- "hidraw": []string{"core", "gadget"},
- "i2c": []string{"core", "gadget"},
- "location-control": []string{"app"},
- "location-observe": []string{"app"},
- "lxd-support": []string{"core"},
- "mir": []string{"app"},
- "modem-manager": []string{"app", "core"},
- "mpris": []string{"app"},
- "network-manager": []string{"app", "core"},
- "ofono": []string{"app", "core"},
- "ppp": []string{"core"},
- "pulseaudio": []string{"app", "core"},
- "serial-port": []string{"core", "gadget"},
- "udisks2": []string{"app"},
+ "bluez": {"app"},
+ "bool-file": {"core", "gadget"},
+ "browser-support": {"core"},
+ "content": {"app", "gadget"},
+ "docker-support": {"core"},
+ "fwupd": {"app"},
+ "gpio": {"core", "gadget"},
+ "hidraw": {"core", "gadget"},
+ "i2c": {"core", "gadget"},
+ "location-control": {"app"},
+ "location-observe": {"app"},
+ "lxd-support": {"core"},
+ "mir": {"app"},
+ "modem-manager": {"app", "core"},
+ "mpris": {"app"},
+ "network-manager": {"app", "core"},
+ "ofono": {"app", "core"},
+ "ppp": {"core"},
+ "pulseaudio": {"app", "core"},
+ "serial-port": {"core", "gadget"},
+ "udisks2": {"app"},
// snowflakes
"docker": nil,
"lxd": nil,
diff --git a/interfaces/builtin/browser_support.go b/interfaces/builtin/browser_support.go
index 55a01294f1..e5daa92b82 100644
--- a/interfaces/builtin/browser_support.go
+++ b/interfaces/builtin/browser_support.go
@@ -169,6 +169,9 @@ capability sys_chroot,
owner @{PROC}/@{pid}/setgroups rw,
owner @{PROC}/@{pid}/uid_map rw,
owner @{PROC}/@{pid}/gid_map rw,
+
+# Webkit uses a particular SHM names # LP: 1578217
+owner /{dev,run}/shm/WK2SharedMemory.* rw,
`
const browserSupportConnectedPlugSecComp = `
diff --git a/interfaces/builtin/gpio.go b/interfaces/builtin/gpio.go
index fae814393a..e158384f5d 100644
--- a/interfaces/builtin/gpio.go
+++ b/interfaces/builtin/gpio.go
@@ -119,7 +119,7 @@ func (iface *GpioInterface) ConnectedSlotRichSnippet(plug *interfaces.Plug, slot
serviceName := interfaces.InterfaceServiceName(slot.Snap.Name(), fmt.Sprintf("gpio-%d", gpioNum))
snippet := &systemd.Snippet{
Services: map[string]systemd.Service{
- serviceName: systemd.Service{
+ serviceName: {
Type: "oneshot",
RemainAfterExit: true,
ExecStart: fmt.Sprintf("/bin/sh -c 'test -e /sys/class/gpio/gpio%d || echo %d > /sys/class/gpio/export'", gpioNum, gpioNum),
diff --git a/interfaces/builtin/modem_manager.go b/interfaces/builtin/modem_manager.go
index 2883395645..b2ecae97a5 100644
--- a/interfaces/builtin/modem_manager.go
+++ b/interfaces/builtin/modem_manager.go
@@ -1160,7 +1160,7 @@ func (iface *ModemManagerInterface) ConnectedPlugSnippet(plug *interfaces.Plug,
return nil, nil
case interfaces.SecurityAppArmor:
old := []byte("###SLOT_SECURITY_TAGS###")
- new := []byte("")
+ var new []byte
if release.OnClassic {
// If we're running on classic ModemManager will be part
// of the OS snap and will run unconfined.
diff --git a/interfaces/builtin/network_manager.go b/interfaces/builtin/network_manager.go
index b02cb7b5d6..b39060edc6 100644
--- a/interfaces/builtin/network_manager.go
+++ b/interfaces/builtin/network_manager.go
@@ -410,7 +410,7 @@ func (iface *NetworkManagerInterface) ConnectedPlugSnippet(plug *interfaces.Plug
return nil, nil
case interfaces.SecurityAppArmor:
old := []byte("###SLOT_SECURITY_TAGS###")
- new := []byte("")
+ var new []byte
if release.OnClassic {
// If we're running on classic NetworkManager will be part
// of the OS snap and will run unconfined.
diff --git a/interfaces/builtin/usb_raw.go b/interfaces/builtin/raw_usb.go
index 1e10bff44b..485bce9314 100644
--- a/interfaces/builtin/usb_raw.go
+++ b/interfaces/builtin/raw_usb.go
@@ -23,7 +23,7 @@ import (
"github.com/snapcore/snapd/interfaces"
)
-const usbrawConnectedPlugAppArmor = `
+const rawusbConnectedPlugAppArmor = `
# Description: Allow raw access to all connected USB devices.
# Reserved because this gives privileged access to the system.
# Usage: reserved
@@ -40,10 +40,10 @@ const usbrawConnectedPlugAppArmor = `
`
// Transitional interface which allows access to all usb devices.
-func NewUsbRawInterface() interfaces.Interface {
+func NewRawUsbInterface() interfaces.Interface {
return &commonInterface{
- name: "usb-raw",
- connectedPlugAppArmor: usbrawConnectedPlugAppArmor,
+ name: "raw-usb",
+ connectedPlugAppArmor: rawusbConnectedPlugAppArmor,
reservedForOS: true,
}
}
diff --git a/interfaces/builtin/usb_raw_test.go b/interfaces/builtin/raw_usb_test.go
index 03dbbb12c5..1a6f925c08 100644
--- a/interfaces/builtin/usb_raw_test.go
+++ b/interfaces/builtin/raw_usb_test.go
@@ -28,58 +28,58 @@ import (
"github.com/snapcore/snapd/testutil"
)
-type UsbRawSuite struct {
+type RawUsbSuite struct {
iface interfaces.Interface
slot *interfaces.Slot
plug *interfaces.Plug
}
-var _ = Suite(&UsbRawSuite{
- iface: builtin.NewUsbRawInterface(),
+var _ = Suite(&RawUsbSuite{
+ iface: builtin.NewRawUsbInterface(),
slot: &interfaces.Slot{
SlotInfo: &snap.SlotInfo{
Snap: &snap.Info{SuggestedName: "core", Type: snap.TypeOS},
- Name: "usb-raw",
- Interface: "usb-raw",
+ Name: "raw-usb",
+ Interface: "raw-usb",
},
},
plug: &interfaces.Plug{
PlugInfo: &snap.PlugInfo{
Snap: &snap.Info{SuggestedName: "other"},
- Name: "usb-raw",
- Interface: "usb-raw",
+ Name: "raw-usb",
+ Interface: "raw-usb",
},
},
})
-func (s *UsbRawSuite) TestName(c *C) {
- c.Assert(s.iface.Name(), Equals, "usb-raw")
+func (s *RawUsbSuite) TestName(c *C) {
+ c.Assert(s.iface.Name(), Equals, "raw-usb")
}
-func (s *UsbRawSuite) TestSanitizeSlot(c *C) {
+func (s *RawUsbSuite) TestSanitizeSlot(c *C) {
err := s.iface.SanitizeSlot(s.slot)
c.Assert(err, IsNil)
err = s.iface.SanitizeSlot(&interfaces.Slot{SlotInfo: &snap.SlotInfo{
Snap: &snap.Info{SuggestedName: "some-snap"},
- Name: "usb-raw",
- Interface: "usb-raw",
+ Name: "raw-usb",
+ Interface: "raw-usb",
}})
- c.Assert(err, ErrorMatches, "usb-raw slots are reserved for the operating system snap")
+ c.Assert(err, ErrorMatches, "raw-usb slots are reserved for the operating system snap")
}
-func (s *UsbRawSuite) TestSanitizePlug(c *C) {
+func (s *RawUsbSuite) TestSanitizePlug(c *C) {
err := s.iface.SanitizePlug(s.plug)
c.Assert(err, IsNil)
}
-func (s *UsbRawSuite) TestSanitizeIncorrectInterface(c *C) {
+func (s *RawUsbSuite) TestSanitizeIncorrectInterface(c *C) {
c.Assert(func() { s.iface.SanitizeSlot(&interfaces.Slot{SlotInfo: &snap.SlotInfo{Interface: "other"}}) },
- PanicMatches, `slot is not of interface "usb-raw"`)
+ PanicMatches, `slot is not of interface "raw-usb"`)
c.Assert(func() { s.iface.SanitizePlug(&interfaces.Plug{PlugInfo: &snap.PlugInfo{Interface: "other"}}) },
- PanicMatches, `plug is not of interface "usb-raw"`)
+ PanicMatches, `plug is not of interface "raw-usb"`)
}
-func (s *UsbRawSuite) TestUsedSecuritySystems(c *C) {
+func (s *RawUsbSuite) TestUsedSecuritySystems(c *C) {
// connected plugs have a non-nil security snippet for apparmor
snippet, err := s.iface.ConnectedPlugSnippet(s.plug, s.slot, interfaces.SecurityAppArmor)
c.Assert(err, IsNil)
diff --git a/interfaces/dbus/backend_test.go b/interfaces/dbus/backend_test.go
index cf563fae73..9946092f12 100644
--- a/interfaces/dbus/backend_test.go
+++ b/interfaces/dbus/backend_test.go
@@ -212,6 +212,7 @@ func (s *backendSuite) TestCombineSnippetsWithActualSnippets(c *C) {
c.Assert(err, IsNil)
c.Check(string(data), Equals, "<?xml>\n<policy>...</policy>\n</xml>")
stat, err := os.Stat(profile)
+ c.Assert(err, IsNil)
c.Check(stat.Mode(), Equals, os.FileMode(0644))
s.RemoveSnap(c, snapInfo)
}
diff --git a/interfaces/repo_test.go b/interfaces/repo_test.go
index ba3b48cf63..02a20df8db 100644
--- a/interfaces/repo_test.go
+++ b/interfaces/repo_test.go
@@ -595,6 +595,7 @@ slots:
// ResolveConnect detects lack of candidates
func (s *RepositorySuite) TestResolveConnectNoImplicitCandidates(c *C) {
err := s.testRepo.AddInterface(&TestInterface{InterfaceName: "other-interface"})
+ c.Assert(err, IsNil)
coreSnap := snaptest.MockInfo(c, `
name: core
type: os
@@ -1183,11 +1184,13 @@ func (s *AddRemoveSuite) TestAddSnapComplexErrorHandling(c *C) {
SanitizePlugCallback: func(plug *Plug) error { return fmt.Errorf("plug is invalid") },
SanitizeSlotCallback: func(slot *Slot) error { return fmt.Errorf("slot is invalid") },
})
+ c.Assert(err, IsNil)
err = s.repo.AddInterface(&TestInterface{
InterfaceName: "invalid-slot-iface",
SanitizePlugCallback: func(plug *Plug) error { return fmt.Errorf("plug is invalid") },
SanitizeSlotCallback: func(slot *Slot) error { return fmt.Errorf("slot is invalid") },
})
+ c.Assert(err, IsNil)
snapInfo := snaptest.MockInfo(c, `
name: complex
plugs:
@@ -1385,6 +1388,7 @@ func contentAutoConnect(plug *Plug, slot *Slot) bool {
func makeContentConnectionTestSnaps(c *C, plugContentToken, slotContentToken string) (*Repository, *snap.Info, *snap.Info) {
repo := NewRepository()
err := repo.AddInterface(&TestInterface{InterfaceName: "content", AutoConnectCallback: contentAutoConnect})
+ c.Assert(err, IsNil)
plugSnap := snaptest.MockInfo(c, fmt.Sprintf(`
name: content-plug-snap
diff --git a/interfaces/seccomp/backend_test.go b/interfaces/seccomp/backend_test.go
index 6d6eed1092..d817028cc9 100644
--- a/interfaces/seccomp/backend_test.go
+++ b/interfaces/seccomp/backend_test.go
@@ -208,6 +208,7 @@ func (s *backendSuite) TestCombineSnippets(c *C) {
c.Assert(err, IsNil)
c.Check(string(data), Equals, scenario.content)
stat, err := os.Stat(profile)
+ c.Assert(err, IsNil)
c.Check(stat.Mode(), Equals, os.FileMode(0644))
s.RemoveSnap(c, snapInfo)
}
diff --git a/interfaces/systemd/backend_test.go b/interfaces/systemd/backend_test.go
index 219aeb96f8..f62a97281d 100644
--- a/interfaces/systemd/backend_test.go
+++ b/interfaces/systemd/backend_test.go
@@ -60,7 +60,7 @@ func (s *backendSuite) TestName(c *C) {
func (s *backendSuite) TestUnmarshalRawSnippetMap(c *C) {
rawSnippetMap := map[string][][]byte{
- "security-tag": [][]byte{
+ "security-tag": {
[]byte(`{"services": {"foo.service": {"exec-start": "/bin/true"}}}`),
[]byte(`{"services": {"bar.service": {"exec-start": "/bin/false"}}}`),
},
@@ -68,13 +68,13 @@ func (s *backendSuite) TestUnmarshalRawSnippetMap(c *C) {
richSnippetMap, err := systemd.UnmarshalRawSnippetMap(rawSnippetMap)
c.Assert(err, IsNil)
c.Assert(richSnippetMap, DeepEquals, map[string][]*systemd.Snippet{
- "security-tag": []*systemd.Snippet{
- &systemd.Snippet{
+ "security-tag": {
+ {
Services: map[string]systemd.Service{
"foo.service": {ExecStart: "/bin/true"},
},
},
- &systemd.Snippet{
+ {
Services: map[string]systemd.Service{
"bar.service": {ExecStart: "/bin/false"},
},
@@ -85,15 +85,15 @@ func (s *backendSuite) TestUnmarshalRawSnippetMap(c *C) {
func (s *backendSuite) TestMergeSnippetMapOK(c *C) {
snippetMap := map[string][]*systemd.Snippet{
- "security-tag": []*systemd.Snippet{
- &systemd.Snippet{
+ "security-tag": {
+ {
Services: map[string]systemd.Service{
"foo.service": {ExecStart: "/bin/true"},
},
},
},
- "another-tag": []*systemd.Snippet{
- &systemd.Snippet{
+ "another-tag": {
+ {
Services: map[string]systemd.Service{
"bar.service": {ExecStart: "/bin/false"},
},
@@ -112,15 +112,15 @@ func (s *backendSuite) TestMergeSnippetMapOK(c *C) {
func (s *backendSuite) TestMergeSnippetMapClashing(c *C) {
snippetMap := map[string][]*systemd.Snippet{
- "security-tag": []*systemd.Snippet{
- &systemd.Snippet{
+ "security-tag": {
+ {
Services: map[string]systemd.Service{
"foo.service": {ExecStart: "/bin/true"},
},
},
},
- "another-tag": []*systemd.Snippet{
- &systemd.Snippet{
+ "another-tag": {
+ {
Services: map[string]systemd.Service{
"foo.service": {ExecStart: "/bin/evil"},
},
@@ -141,7 +141,7 @@ func (s *backendSuite) TestRenderSnippet(c *C) {
content, err := systemd.RenderSnippet(snippet)
c.Assert(err, IsNil)
c.Assert(content, DeepEquals, map[string]*osutil.FileState{
- "foo.service": &osutil.FileState{
+ "foo.service": {
Content: []byte("[Service]\nExecStart=/bin/true\n\n[Install]\nWantedBy=multi-user.target\n"),
Mode: 0644,
},
diff --git a/interfaces/udev/backend_test.go b/interfaces/udev/backend_test.go
index 1825c65501..71b458030a 100644
--- a/interfaces/udev/backend_test.go
+++ b/interfaces/udev/backend_test.go
@@ -267,6 +267,7 @@ func (s *backendSuite) TestCombineSnippetsWithActualSnippets(c *C) {
c.Assert(err, IsNil)
c.Check(string(data), Equals, "# This file is automatically generated.\ndummy\n")
stat, err := os.Stat(fname)
+ c.Assert(err, IsNil)
c.Check(stat.Mode(), Equals, os.FileMode(0644))
s.RemoveSnap(c, snapInfo)
}
@@ -284,6 +285,7 @@ func (s *backendSuite) TestCombineSnippetsWithActualSnippetsWhenPlugNoApps(c *C)
c.Assert(err, IsNil)
c.Check(string(data), Equals, "# This file is automatically generated.\ndummy\n")
stat, err := os.Stat(fname)
+ c.Assert(err, IsNil)
c.Check(stat.Mode(), Equals, os.FileMode(0644))
s.RemoveSnap(c, snapInfo)
}
@@ -301,6 +303,7 @@ func (s *backendSuite) TestCombineSnippetsWithActualSnippetsWhenSlotNoApps(c *C)
c.Assert(err, IsNil)
c.Check(string(data), Equals, "# This file is automatically generated.\ndummy\n")
stat, err := os.Stat(fname)
+ c.Assert(err, IsNil)
c.Check(stat.Mode(), Equals, os.FileMode(0644))
s.RemoveSnap(c, snapInfo)
}
diff --git a/osutil/exitcode_test.go b/osutil/exitcode_test.go
index 01a4d80d6b..296a624633 100644
--- a/osutil/exitcode_test.go
+++ b/osutil/exitcode_test.go
@@ -45,6 +45,7 @@ func (ts *ExitCodeTestSuite) TestExitCode(c *C) {
cmd = exec.Command("sh", "-c", "exit 7")
err = cmd.Run()
e, err = ExitCode(err)
+ c.Assert(err, IsNil)
c.Assert(e, Equals, 7)
// ensure that non exec.ExitError values give a error
diff --git a/overlord/assertstate/assertmgr.go b/overlord/assertstate/assertmgr.go
index ebc242699e..fdfaa5bcf2 100644
--- a/overlord/assertstate/assertmgr.go
+++ b/overlord/assertstate/assertmgr.go
@@ -341,8 +341,8 @@ func RefreshSnapDeclarations(s *state.State, userID int) error {
return nil
}
fetching := func(f asserts.Fetcher) error {
- for _, snapState := range snapStates {
- info, err := snapState.CurrentInfo()
+ for _, snapst := range snapStates {
+ info, err := snapst.CurrentInfo()
if err != nil {
return err
}
@@ -385,8 +385,8 @@ func ValidateRefreshes(s *state.State, snapInfos []*snap.Info, userID int) (vali
if err != nil {
return nil, err
}
- for snapName, snapState := range snapStates {
- info, err := snapState.CurrentInfo()
+ for snapName, snapst := range snapStates {
+ info, err := snapst.CurrentInfo()
if err != nil {
return nil, err
}
diff --git a/overlord/auth/auth.go b/overlord/auth/auth.go
index 8c910a667c..b1ff031975 100644
--- a/overlord/auth/auth.go
+++ b/overlord/auth/auth.go
@@ -192,7 +192,7 @@ func Users(st *state.State) ([]*UserState, error) {
}
users := make([]*UserState, len(authStateData.Users))
- for i, _ := range authStateData.Users {
+ for i := range authStateData.Users {
users[i] = &authStateData.Users[i]
}
return users, nil
diff --git a/overlord/auth/auth_test.go b/overlord/auth/auth_test.go
index 299d3a3d8c..a3bdc4fbb9 100644
--- a/overlord/auth/auth_test.go
+++ b/overlord/auth/auth_test.go
@@ -120,6 +120,7 @@ func (as *authSuite) TestNewUser(c *C) {
func (as *authSuite) TestNewUserSortsDischarges(c *C) {
as.state.Lock()
user, err := auth.NewUser(as.state, "", "email@test.com", "macaroon", []string{"discharge2", "discharge1"})
+ c.Assert(err, IsNil)
as.state.Unlock()
expected := []string{"discharge1", "discharge2"}
diff --git a/overlord/devicestate/devicemgr_test.go b/overlord/devicestate/devicemgr_test.go
index ea0d212342..97843adfbb 100644
--- a/overlord/devicestate/devicemgr_test.go
+++ b/overlord/devicestate/devicemgr_test.go
@@ -733,6 +733,7 @@ func (s *deviceMgrSuite) TestDeviceAssertionsDeviceSessionRequest(c *C) {
s.state.Lock()
devKey, _ := assertstest.GenerateKey(1024)
encDevKey, err := asserts.EncodePublicKey(devKey.PublicKey())
+ c.Check(err, IsNil)
seriala, err := s.storeSigning.Sign(asserts.SerialType, map[string]interface{}{
"brand-id": "canonical",
"model": "pc",
diff --git a/overlord/devicestate/firstboot_test.go b/overlord/devicestate/firstboot_test.go
index 777c112ca4..336bdb122d 100644
--- a/overlord/devicestate/firstboot_test.go
+++ b/overlord/devicestate/firstboot_test.go
@@ -356,6 +356,7 @@ snaps:
defer st.Unlock()
tsAll, err := devicestate.PopulateStateFromSeedImpl(st)
+ c.Assert(err, IsNil)
chg := st.NewChange("run-it", "run the populate from seed changes")
for _, ts := range tsAll {
chg.AddAll(ts)
diff --git a/overlord/ifacestate/handlers.go b/overlord/ifacestate/handlers.go
index f90a8e9fb3..c65c1316ec 100644
--- a/overlord/ifacestate/handlers.go
+++ b/overlord/ifacestate/handlers.go
@@ -228,13 +228,13 @@ func (m *InterfaceManager) doDiscardConns(task *state.Task, _ *tomb.Tomb) error
snapName := snapSetup.Name()
- var snapState snapstate.SnapState
- err = snapstate.Get(st, snapName, &snapState)
+ var snapst snapstate.SnapState
+ err = snapstate.Get(st, snapName, &snapst)
if err != nil && err != state.ErrNoState {
return err
}
- if err == nil && len(snapState.Sequence) != 0 {
+ if err == nil && len(snapst.Sequence) != 0 {
return fmt.Errorf("cannot discard connections for snap %q while it is present", snapName)
}
conns, err := getConns(st)
diff --git a/overlord/managers_test.go b/overlord/managers_test.go
index 79eaeba729..a239794dfd 100644
--- a/overlord/managers_test.go
+++ b/overlord/managers_test.go
@@ -702,6 +702,8 @@ version: @VERSION@
ms.serveSnap(snapPath, revno)
updated, tss, err := snapstate.UpdateMany(st, []string{"foo"}, 0)
+ c.Check(updated, IsNil)
+ c.Check(tss, IsNil)
// no validation we, get an error
c.Check(err, ErrorMatches, `cannot refresh "foo" to revision 50: no validation by "bar"`)
diff --git a/overlord/patch/patch1.go b/overlord/patch/patch1.go
index 3c9f17eb5b..c254bb0926 100644
--- a/overlord/patch/patch1.go
+++ b/overlord/patch/patch1.go
@@ -103,19 +103,19 @@ func patch1(s *state.State) error {
return err
}
- for snapName, snapState := range stateMap {
- seq := snapState.Sequence
+ for snapName, snapst := range stateMap {
+ seq := snapst.Sequence
if len(seq) == 0 {
continue
}
- snapState.Current = seq[len(seq)-1].Revision
- typ, err := patch1ReadType(snapName, snapState.Current)
+ snapst.Current = seq[len(seq)-1].Revision
+ typ, err := patch1ReadType(snapName, snapst.Current)
if err != nil {
logger.Noticef("Recording type for snap %q: cannot retrieve info, assuming it's a app: %v", snapName, err)
} else {
logger.Noticef("Recording type for snap %q: setting to %q", snapName, typ)
}
- snapState.SnapType = string(typ)
+ snapst.SnapType = string(typ)
}
s.Set("snaps", stateMap)
diff --git a/overlord/patch/patch5.go b/overlord/patch/patch5.go
index 92f7e5dc0d..cf4df82505 100644
--- a/overlord/patch/patch5.go
+++ b/overlord/patch/patch5.go
@@ -46,12 +46,12 @@ func patch5(st *state.State) error {
return err
}
- for snapName, snapState := range snapStates {
- if !snapState.Active {
+ for snapName, snapst := range snapStates {
+ if !snapst.Active {
continue
}
- info, err := snapState.CurrentInfo()
+ info, err := snapst.CurrentInfo()
if err != nil {
return err
}
diff --git a/overlord/patch/patch_test.go b/overlord/patch/patch_test.go
index 784686a35f..df8a4f562e 100644
--- a/overlord/patch/patch_test.go
+++ b/overlord/patch/patch_test.go
@@ -179,7 +179,7 @@ func (s *patchSuite) TestError(c *C) {
func (s *patchSuite) TestSanity(c *C) {
patches := patch.PatchesForTest()
levels := make([]int, 0, len(patches))
- for l, _ := range patches {
+ for l := range patches {
levels = append(levels, l)
}
sort.Ints(levels)
diff --git a/overlord/snapstate/snapmgr.go b/overlord/snapstate/snapmgr.go
index fbe1af17a1..25874bd8d7 100644
--- a/overlord/snapstate/snapmgr.go
+++ b/overlord/snapstate/snapmgr.go
@@ -416,10 +416,11 @@ func (m *SnapManager) doDownloadSnap(t *state.Task, _ *tomb.Tomb) error {
targetFn := snapsup.MountFile()
if snapsup.DownloadInfo == nil {
+ var storeInfo *snap.Info
// COMPATIBILITY - this task was created from an older version
// of snapd that did not store the DownloadInfo in the state
// yet.
- storeInfo, err := theStore.Snap(snapsup.Name(), snapsup.Channel, snapsup.DevModeAllowed(), snapsup.Revision(), user)
+ storeInfo, err = theStore.Snap(snapsup.Name(), snapsup.Channel, snapsup.DevModeAllowed(), snapsup.Revision(), user)
if err != nil {
return err
}
diff --git a/overlord/snapstate/snapmgr_test.go b/overlord/snapstate/snapmgr_test.go
index d4a25ca6f8..701f7782bd 100644
--- a/overlord/snapstate/snapmgr_test.go
+++ b/overlord/snapstate/snapmgr_test.go
@@ -3343,15 +3343,10 @@ func (s *snapmgrQuerySuite) TestAll(c *C) {
snapStates, err := snapstate.All(st)
c.Assert(err, IsNil)
+ c.Assert(snapStates, HasLen, 1)
- c.Check(snapStates, HasLen, 1)
-
- var snapst *snapstate.SnapState
-
- for name, sst := range snapStates {
- c.Assert(name, Equals, "name1")
- snapst = sst
- }
+ snapst := snapStates["name1"]
+ c.Assert(snapst, NotNil)
c.Check(snapst.Active, Equals, true)
c.Check(snapst.CurrentSideInfo(), NotNil)
diff --git a/overlord/state/state_test.go b/overlord/state/state_test.go
index 26cf21e660..bbfacdbdd1 100644
--- a/overlord/state/state_test.go
+++ b/overlord/state/state_test.go
@@ -327,6 +327,7 @@ func (ss *stateSuite) TestNewChangeAndCheckpoint(c *C) {
var v int
err = chg0.Get("a", &v)
+ c.Check(err, IsNil)
c.Check(v, Equals, 1)
c.Check(chg0.Status(), Equals, state.ErrorStatus)
@@ -434,6 +435,7 @@ func (ss *stateSuite) TestNewTaskAndCheckpoint(c *C) {
var v int
err = task0_1.Get("a", &v)
+ c.Check(err, IsNil)
c.Check(v, Equals, 1)
c.Check(task0_1.Status(), Equals, state.DoneStatus)
diff --git a/overlord/state/task.go b/overlord/state/task.go
index 995157c190..0aecdd4e41 100644
--- a/overlord/state/task.go
+++ b/overlord/state/task.go
@@ -279,7 +279,7 @@ func (t *Task) AtTime() time.Time {
const (
// Messages logged in tasks are guaranteed to use the time formatted
// per RFC3339 plus the following strings as a prefix, so these may
- // be handled programatically and parsed or stripped for presentation.
+ // be handled programmatically and parsed or stripped for presentation.
LogInfo = "INFO"
LogError = "ERROR"
)
diff --git a/partition/bootloader_test.go b/partition/bootloader_test.go
index 806d6b3b17..01fdf3a192 100644
--- a/partition/bootloader_test.go
+++ b/partition/bootloader_test.go
@@ -149,6 +149,7 @@ func (s *PartitionTestSuite) TestInstallBootloaderConfig(c *C) {
} {
mockGadgetDir := c.MkDir()
err := ioutil.WriteFile(filepath.Join(mockGadgetDir, t.gadgetFile), nil, 0644)
+ c.Assert(err, IsNil)
err = InstallBootConfig(mockGadgetDir)
c.Assert(err, IsNil)
fn := filepath.Join(dirs.GlobalRootDir, t.systemFile)
diff --git a/progress/progress.go b/progress/progress.go
index e734f027d5..334924b6e3 100644
--- a/progress/progress.go
+++ b/progress/progress.go
@@ -100,8 +100,7 @@ func NewTextProgress() *TextProgress {
// Start starts showing progress
func (t *TextProgress) Start(label string, total float64) {
- t.pbar = pb.New64(0)
- t.pbar.Total = int64(total)
+ t.pbar = pb.New64(int64(total))
t.pbar.ShowSpeed = true
t.pbar.Units = pb.U_BYTES
t.pbar.Prefix(label)
diff --git a/snap/implicit.go b/snap/implicit.go
index 6bb08b8564..af9e87da67 100644
--- a/snap/implicit.go
+++ b/snap/implicit.go
@@ -50,6 +50,7 @@ var implicitSlots = []string{
"opengl",
"ppp",
"process-control",
+ "raw-usb",
"removable-media",
"shutdown",
"snapd-control",
@@ -59,7 +60,6 @@ var implicitSlots = []string{
"timeserver-control",
"timezone-control",
"tpm",
- "usb-raw",
}
var implicitClassicSlots = []string{
diff --git a/snap/info_test.go b/snap/info_test.go
index 9817f827bf..e94494a2df 100644
--- a/snap/info_test.go
+++ b/snap/info_test.go
@@ -142,6 +142,7 @@ func makeTestSnap(c *C, yaml string) string {
snapSource := filepath.Join(tmp, "snapsrc")
err := os.MkdirAll(filepath.Join(snapSource, "meta"), 0755)
+ c.Assert(err, IsNil)
// our regular snap.yaml
err = ioutil.WriteFile(filepath.Join(snapSource, "meta", "snap.yaml"), []byte(yaml), 0644)
diff --git a/snap/snaptest/build_test.go b/snap/snaptest/build_test.go
index 52883be108..5ba305cd9f 100644
--- a/snap/snaptest/build_test.go
+++ b/snap/snaptest/build_test.go
@@ -91,6 +91,7 @@ printf "hello world"
err = ioutil.WriteFile(someFile, []byte(""), 0666)
c.Assert(err, IsNil)
err = os.Chmod(someFile, 0666)
+ c.Assert(err, IsNil)
// an example symlink
err = os.Symlink("bin/hello-world", filepath.Join(tempdir, "symlink"))
@@ -172,6 +173,7 @@ func (s *BuildTestSuite) TestCopyExcludesWholeDirs(c *C) {
c.Assert(ioutil.WriteFile(filepath.Join(sourceDir, ".bzr", "foo"), []byte("hi"), 0755), IsNil)
c.Assert(snaptest.CopyToBuildDir(sourceDir, target), IsNil)
out, _ := exec.Command("find", sourceDir).Output()
+ c.Check(string(out), Not(Equals), "")
cmd := exec.Command("diff", "-qr", sourceDir, target)
cmd.Env = append(cmd.Env, "LANG=C")
out, err := cmd.Output()
@@ -260,6 +262,7 @@ integration:
outputDir := filepath.Join(c.MkDir(), "output")
snapOutput := filepath.Join(outputDir, "hello_1.0.1_multi.snap")
resultSnap, err := snaptest.BuildSquashfsSnap(sourceDir, outputDir)
+ c.Assert(err, IsNil)
// check that there is result
_, err = os.Stat(resultSnap)
diff --git a/snap/validate_test.go b/snap/validate_test.go
index 3f3d8a18b2..4ff503268c 100644
--- a/snap/validate_test.go
+++ b/snap/validate_test.go
@@ -83,27 +83,27 @@ func (s *ValidateSuite) TestValidateEpoch(c *C) {
func (s *ValidateSuite) TestValidateHook(c *C) {
validHooks := []*HookInfo{
- &HookInfo{Name: "a"},
- &HookInfo{Name: "aaa"},
- &HookInfo{Name: "a-a"},
- &HookInfo{Name: "aa-a"},
- &HookInfo{Name: "a-aa"},
- &HookInfo{Name: "a-b-c"},
+ {Name: "a"},
+ {Name: "aaa"},
+ {Name: "a-a"},
+ {Name: "aa-a"},
+ {Name: "a-aa"},
+ {Name: "a-b-c"},
}
for _, hook := range validHooks {
err := ValidateHook(hook)
c.Assert(err, IsNil)
}
invalidHooks := []*HookInfo{
- &HookInfo{Name: ""},
- &HookInfo{Name: "a a"},
- &HookInfo{Name: "a--a"},
- &HookInfo{Name: "-a"},
- &HookInfo{Name: "a-"},
- &HookInfo{Name: "0"},
- &HookInfo{Name: "123"},
- &HookInfo{Name: "123abc"},
- &HookInfo{Name: "日本語"},
+ {Name: ""},
+ {Name: "a a"},
+ {Name: "a--a"},
+ {Name: "-a"},
+ {Name: "a-"},
+ {Name: "0"},
+ {Name: "123"},
+ {Name: "123abc"},
+ {Name: "日本語"},
}
for _, hook := range invalidHooks {
err := ValidateHook(hook)
diff --git a/store/auth.go b/store/auth.go
index b99a3c80a6..e6746583f2 100644
--- a/store/auth.go
+++ b/store/auth.go
@@ -86,6 +86,9 @@ func requestStoreMacaroon() (string, error) {
"permissions": []string{"package_access", "package_purchase"},
}
macaroonJSONData, err := json.Marshal(data)
+ if err != nil {
+ return "", fmt.Errorf(errorPrefix+"%v", err)
+ }
req, err := http.NewRequest("POST", MyAppsMacaroonACLAPI, bytes.NewReader(macaroonJSONData))
if err != nil {
diff --git a/store/store.go b/store/store.go
index a1bc528a16..2e0c2d19e9 100644
--- a/store/store.go
+++ b/store/store.go
@@ -576,6 +576,28 @@ type requestOptions struct {
Data []byte
}
+// retryRequest uses defaultRetryStrategy to call doRequest with retry
+func (s *Store) retryRequest(client *http.Client, reqOptions *requestOptions, user *auth.UserState) (resp *http.Response, err error) {
+ for attempt := retry.Start(defaultRetryStrategy, nil); attempt.Next(); {
+ resp, err = s.doRequest(client, reqOptions, user)
+ if err != nil {
+ if shouldRetryError(attempt, err) {
+ continue
+ }
+ break
+ }
+
+ if shouldRetryHttpResponse(attempt, resp) {
+ resp.Body.Close()
+ continue
+ }
+
+ break
+ }
+
+ return resp, err
+}
+
// doRequest does an authenticated request to the store handling a potential macaroon refresh required if needed
func (s *Store) doRequest(client *http.Client, reqOptions *requestOptions, user *auth.UserState) (*http.Response, error) {
req, err := s.newRequest(reqOptions, user)
@@ -823,58 +845,46 @@ func (s *Store) Snap(name, channel string, devmode bool, revision snap.Revision,
u.RawQuery = query.Encode()
- for attempt := retry.Start(defaultRetryStrategy, nil); attempt.Next(); {
- reqOptions := &requestOptions{
- Method: "GET",
- URL: u,
- Accept: halJsonContentType,
- }
-
- resp, err := s.doRequest(s.client, reqOptions, user)
- if err != nil {
- if shouldRetryError(attempt, err) {
- continue
- }
- return nil, err
- }
+ reqOptions := &requestOptions{
+ Method: "GET",
+ URL: u,
+ Accept: halJsonContentType,
+ }
- if shouldRetryHttpResponse(attempt, resp) {
- resp.Body.Close()
- continue
- }
+ resp, err := s.retryRequest(s.client, reqOptions, user)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
- defer resp.Body.Close()
-
- // check statusCode
- switch resp.StatusCode {
- case http.StatusOK:
- // OK
- case http.StatusNotFound:
- return nil, ErrSnapNotFound
- default:
- msg := fmt.Sprintf("get details for snap %q in channel %q", name, channel)
- return nil, respToError(resp, msg)
- }
+ // check statusCode
+ switch resp.StatusCode {
+ case http.StatusOK:
+ // OK
+ case http.StatusNotFound:
+ return nil, ErrSnapNotFound
+ default:
+ msg := fmt.Sprintf("get details for snap %q in channel %q", name, channel)
+ return nil, respToError(resp, msg)
+ }
- // and decode json
- var remote snapDetails
- dec := json.NewDecoder(resp.Body)
- if err := dec.Decode(&remote); err != nil {
- return nil, err
- }
+ // and decode json
+ var remote snapDetails
+ dec := json.NewDecoder(resp.Body)
+ if err := dec.Decode(&remote); err != nil {
+ return nil, err
+ }
- info := infoFromRemote(remote)
+ info := infoFromRemote(remote)
- err = s.decorateOrders([]*snap.Info{info}, channel, user)
- if err != nil {
- logger.Noticef("cannot get user orders: %v", err)
- }
+ err = s.decorateOrders([]*snap.Info{info}, channel, user)
+ if err != nil {
+ logger.Noticef("cannot get user orders: %v", err)
+ }
- s.extractSuggestedCurrency(resp)
+ s.extractSuggestedCurrency(resp)
- return info, nil
- }
- panic("unreachable")
+ return info, nil
}
// A Search is what you do in order to Find something
@@ -931,56 +941,46 @@ func (s *Store) Find(search *Search, user *auth.UserState) ([]*snap.Info, error)
q.Set("confinement", "strict")
u.RawQuery = q.Encode()
- for attempt := retry.Start(defaultRetryStrategy, nil); attempt.Next(); {
- reqOptions := &requestOptions{
- Method: "GET",
- URL: &u,
- Accept: halJsonContentType,
- }
- resp, err := s.doRequest(s.client, reqOptions, user)
- if err != nil {
- if shouldRetryError(attempt, err) {
- continue
- }
- return nil, err
- }
+ reqOptions := &requestOptions{
+ Method: "GET",
+ URL: &u,
+ Accept: halJsonContentType,
+ }
+ resp, err := s.retryRequest(s.client, reqOptions, user)
+ if err != nil {
+ return nil, err
+ }
- if shouldRetryHttpResponse(attempt, resp) {
- resp.Body.Close()
- continue
- }
- defer resp.Body.Close()
+ defer resp.Body.Close()
- if resp.StatusCode != 200 {
- return nil, respToError(resp, "search")
- }
+ if resp.StatusCode != 200 {
+ return nil, respToError(resp, "search")
+ }
- if ct := resp.Header.Get("Content-Type"); ct != halJsonContentType {
- return nil, fmt.Errorf("received an unexpected content type (%q) when trying to search via %q", ct, resp.Request.URL)
- }
+ if ct := resp.Header.Get("Content-Type"); ct != halJsonContentType {
+ return nil, fmt.Errorf("received an unexpected content type (%q) when trying to search via %q", ct, resp.Request.URL)
+ }
- var searchData searchResults
+ var searchData searchResults
- dec := json.NewDecoder(resp.Body)
- if err := dec.Decode(&searchData); err != nil {
- return nil, fmt.Errorf("cannot decode reply (got %v) when trying to search via %q", err, resp.Request.URL)
- }
+ dec := json.NewDecoder(resp.Body)
+ if err := dec.Decode(&searchData); err != nil {
+ return nil, fmt.Errorf("cannot decode reply (got %v) when trying to search via %q", err, resp.Request.URL)
+ }
- snaps := make([]*snap.Info, len(searchData.Payload.Packages))
- for i, pkg := range searchData.Payload.Packages {
- snaps[i] = infoFromRemote(pkg)
- }
+ snaps := make([]*snap.Info, len(searchData.Payload.Packages))
+ for i, pkg := range searchData.Payload.Packages {
+ snaps[i] = infoFromRemote(pkg)
+ }
- err = s.decorateOrders(snaps, "", user)
- if err != nil {
- logger.Noticef("cannot get user orders: %v", err)
- }
+ err = s.decorateOrders(snaps, "", user)
+ if err != nil {
+ logger.Noticef("cannot get user orders: %v", err)
+ }
- s.extractSuggestedCurrency(resp)
+ s.extractSuggestedCurrency(resp)
- return snaps, nil
- }
- panic("unreachable")
+ return snaps, nil
}
// RefreshCandidate contains information for the store about the currently
@@ -1055,68 +1055,58 @@ func (s *Store) ListRefresh(installed []*RefreshCandidate, user *auth.UserState)
return nil, err
}
- for attempt := retry.Start(defaultRetryStrategy, nil); attempt.Next(); {
- reqOptions := &requestOptions{
- Method: "POST",
- URL: s.bulkURI,
- Accept: halJsonContentType,
- ContentType: jsonContentType,
- Data: jsonData,
- }
+ reqOptions := &requestOptions{
+ Method: "POST",
+ URL: s.bulkURI,
+ Accept: halJsonContentType,
+ ContentType: jsonContentType,
+ Data: jsonData,
+ }
- if useDeltas() {
- logger.Debugf("Deltas enabled. Adding header X-Ubuntu-Delta-Formats: %v", s.deltaFormat)
- reqOptions.ExtraHeaders = map[string]string{
- "X-Ubuntu-Delta-Formats": s.deltaFormat,
- }
+ if useDeltas() {
+ logger.Debugf("Deltas enabled. Adding header X-Ubuntu-Delta-Formats: %v", s.deltaFormat)
+ reqOptions.ExtraHeaders = map[string]string{
+ "X-Ubuntu-Delta-Formats": s.deltaFormat,
}
+ }
- resp, err := s.doRequest(s.client, reqOptions, user)
- if err != nil {
- if shouldRetryError(attempt, err) {
- continue
- }
- return nil, err
- }
+ resp, err := s.retryRequest(s.client, reqOptions, user)
+ if err != nil {
+ return nil, err
+ }
- if shouldRetryHttpResponse(attempt, resp) {
- resp.Body.Close()
- continue
- }
- defer resp.Body.Close()
+ defer resp.Body.Close()
- if resp.StatusCode != http.StatusOK {
- return nil, respToError(resp, "query the store for updates")
- }
+ if resp.StatusCode != http.StatusOK {
+ return nil, respToError(resp, "query the store for updates")
+ }
- var updateData searchResults
- dec := json.NewDecoder(resp.Body)
- if err := dec.Decode(&updateData); err != nil {
- return nil, err
- }
+ var updateData searchResults
+ dec := json.NewDecoder(resp.Body)
+ if err := dec.Decode(&updateData); err != nil {
+ return nil, err
+ }
- res := make([]*snap.Info, 0, len(updateData.Payload.Packages))
- for _, rsnap := range updateData.Payload.Packages {
- rrev := snap.R(rsnap.Revision)
- cand := candidateMap[rsnap.SnapID]
+ res := make([]*snap.Info, 0, len(updateData.Payload.Packages))
+ for _, rsnap := range updateData.Payload.Packages {
+ rrev := snap.R(rsnap.Revision)
+ cand := candidateMap[rsnap.SnapID]
- // the store also gives us identical revisions, filter those
- // out, we are not interested
- if rrev == cand.Revision {
- continue
- }
- // do not upgade to a version we rolledback back from
- if findRev(rrev, cand.Block) {
- continue
- }
- res = append(res, infoFromRemote(rsnap))
+ // the store also gives us identical revisions, filter those
+ // out, we are not interested
+ if rrev == cand.Revision {
+ continue
+ }
+ // do not upgade to a version we rolledback back from
+ if findRev(rrev, cand.Block) {
+ continue
}
+ res = append(res, infoFromRemote(rsnap))
+ }
- s.extractSuggestedCurrency(resp)
+ s.extractSuggestedCurrency(resp)
- return res, nil
- }
- panic("unreachable")
+ return res, nil
}
func findRev(needle snap.Revision, haystack []snap.Revision) bool {
@@ -1369,47 +1359,37 @@ func (s *Store) Assertion(assertType *asserts.AssertionType, primaryKey []string
v.Set("max-format", strconv.Itoa(assertType.MaxSupportedFormat()))
u.RawQuery = v.Encode()
- for attempt := retry.Start(defaultRetryStrategy, nil); attempt.Next(); {
- reqOptions := &requestOptions{
- Method: "GET",
- URL: u,
- Accept: asserts.MediaType,
- }
- resp, err := s.doRequest(s.client, reqOptions, user)
- if err != nil {
- if shouldRetryError(attempt, err) {
- continue
- }
- return nil, err
- }
+ reqOptions := &requestOptions{
+ Method: "GET",
+ URL: u,
+ Accept: asserts.MediaType,
+ }
+ resp, err := s.retryRequest(s.client, reqOptions, user)
+ if err != nil {
+ return nil, err
+ }
- if shouldRetryHttpResponse(attempt, resp) {
- resp.Body.Close()
- continue
- }
- defer resp.Body.Close()
-
- if resp.StatusCode != 200 {
- contentType := resp.Header.Get("Content-Type")
- if contentType == jsonContentType || contentType == "application/problem+json" {
- var svcErr assertionSvcError
- dec := json.NewDecoder(resp.Body)
- if err := dec.Decode(&svcErr); err != nil {
- return nil, fmt.Errorf("cannot decode assertion service error with HTTP status code %d: %v", resp.StatusCode, err)
- }
- if svcErr.Status == 404 {
- return nil, &AssertionNotFoundError{&asserts.Ref{Type: assertType, PrimaryKey: primaryKey}}
- }
- return nil, fmt.Errorf("assertion service error: [%s] %q", svcErr.Title, svcErr.Detail)
+ defer resp.Body.Close()
+
+ if resp.StatusCode != 200 {
+ contentType := resp.Header.Get("Content-Type")
+ if contentType == jsonContentType || contentType == "application/problem+json" {
+ var svcErr assertionSvcError
+ dec := json.NewDecoder(resp.Body)
+ if err := dec.Decode(&svcErr); err != nil {
+ return nil, fmt.Errorf("cannot decode assertion service error with HTTP status code %d: %v", resp.StatusCode, err)
+ }
+ if svcErr.Status == 404 {
+ return nil, &AssertionNotFoundError{&asserts.Ref{Type: assertType, PrimaryKey: primaryKey}}
}
- return nil, respToError(resp, "fetch assertion")
+ return nil, fmt.Errorf("assertion service error: [%s] %q", svcErr.Title, svcErr.Detail)
}
-
- // and decode assertion
- dec := asserts.NewDecoder(resp.Body)
- return dec.Decode()
+ return nil, respToError(resp, "fetch assertion")
}
- panic("unreachable")
+
+ // and decode assertion
+ dec := asserts.NewDecoder(resp.Body)
+ return dec.Decode()
}
// SuggestedCurrency retrieves the cached value for the store's suggested currency
@@ -1499,71 +1479,61 @@ func (s *Store) Buy(options *BuyOptions, user *auth.UserState) (*BuyResult, erro
return nil, err
}
- for attempt := retry.Start(defaultRetryStrategy, nil); attempt.Next(); {
- reqOptions := &requestOptions{
- Method: "POST",
- URL: s.ordersURI,
- Accept: jsonContentType,
- ContentType: jsonContentType,
- Data: jsonData,
- }
- resp, err := s.doRequest(s.client, reqOptions, user)
- if err != nil {
- if shouldRetryError(attempt, err) {
- continue
- }
+ reqOptions := &requestOptions{
+ Method: "POST",
+ URL: s.ordersURI,
+ Accept: jsonContentType,
+ ContentType: jsonContentType,
+ Data: jsonData,
+ }
+ resp, err := s.retryRequest(s.client, reqOptions, user)
+ if err != nil {
+ return nil, err
+ }
+
+ defer resp.Body.Close()
+
+ switch resp.StatusCode {
+ case http.StatusOK, http.StatusCreated:
+ // user already ordered or order successful
+ var orderDetails order
+ dec := json.NewDecoder(resp.Body)
+ if err := dec.Decode(&orderDetails); err != nil {
return nil, err
}
- if shouldRetryHttpResponse(attempt, resp) {
- resp.Body.Close()
- continue
+ if orderDetails.State == "Cancelled" {
+ return buyOptionError("payment cancelled")
}
- defer resp.Body.Close()
- switch resp.StatusCode {
- case http.StatusOK, http.StatusCreated:
- // user already ordered or order successful
- var orderDetails order
- dec := json.NewDecoder(resp.Body)
- if err := dec.Decode(&orderDetails); err != nil {
- return nil, err
- }
-
- if orderDetails.State == "Cancelled" {
- return buyOptionError("payment cancelled")
- }
-
- return &BuyResult{
- State: orderDetails.State,
- }, nil
- case http.StatusBadRequest:
- // Invalid price was specified, etc.
- var errorInfo storeErrors
- dec := json.NewDecoder(resp.Body)
- if err := dec.Decode(&errorInfo); err != nil {
- return nil, err
- }
- return buyOptionError(fmt.Sprintf("bad request: %v", errorInfo.Error()))
- case http.StatusNotFound:
- // Likely because snap ID doesn't exist.
- return buyOptionError("server says not found (snap got removed?)")
- case http.StatusPaymentRequired:
- // Payment failed for some reason.
- return nil, ErrPaymentDeclined
- case http.StatusUnauthorized:
- // TODO handle token expiry and refresh
- return nil, ErrInvalidCredentials
- default:
- var errorInfo storeErrors
- dec := json.NewDecoder(resp.Body)
- if err := dec.Decode(&errorInfo); err != nil {
- return nil, err
- }
- return nil, respToError(resp, fmt.Sprintf("buy snap: %v", errorInfo))
+ return &BuyResult{
+ State: orderDetails.State,
+ }, nil
+ case http.StatusBadRequest:
+ // Invalid price was specified, etc.
+ var errorInfo storeErrors
+ dec := json.NewDecoder(resp.Body)
+ if err := dec.Decode(&errorInfo); err != nil {
+ return nil, err
}
+ return buyOptionError(fmt.Sprintf("bad request: %v", errorInfo.Error()))
+ case http.StatusNotFound:
+ // Likely because snap ID doesn't exist.
+ return buyOptionError("server says not found (snap got removed?)")
+ case http.StatusPaymentRequired:
+ // Payment failed for some reason.
+ return nil, ErrPaymentDeclined
+ case http.StatusUnauthorized:
+ // TODO handle token expiry and refresh
+ return nil, ErrInvalidCredentials
+ default:
+ var errorInfo storeErrors
+ dec := json.NewDecoder(resp.Body)
+ if err := dec.Decode(&errorInfo); err != nil {
+ return nil, err
+ }
+ return nil, respToError(resp, fmt.Sprintf("buy snap: %v", errorInfo))
}
- panic("unreachable")
}
type storeCustomer struct {
@@ -1585,51 +1555,39 @@ func (s *Store) ReadyToBuy(user *auth.UserState) error {
Accept: jsonContentType,
}
- for attempt := retry.Start(defaultRetryStrategy, nil); attempt.Next(); {
- resp, err := s.doRequest(s.client, reqOptions, user)
- if err != nil {
- if shouldRetryError(attempt, err) {
- continue
- }
+ resp, err := s.retryRequest(s.client, reqOptions, user)
+ if err != nil {
+ return err
+ }
+
+ switch resp.StatusCode {
+ case http.StatusOK:
+ var customer storeCustomer
+ dec := json.NewDecoder(resp.Body)
+ if err := dec.Decode(&customer); err != nil {
return err
}
-
- if shouldRetryHttpResponse(attempt, resp) {
- resp.Body.Close()
- continue
+ if !customer.HasPaymentMethod {
+ return ErrNoPaymentMethods
}
-
- switch resp.StatusCode {
- case http.StatusOK:
- var customer storeCustomer
- dec := json.NewDecoder(resp.Body)
- if err := dec.Decode(&customer); err != nil {
- return err
- }
- if !customer.HasPaymentMethod {
- return ErrNoPaymentMethods
- }
- if !customer.LatestTOSAccepted {
- return ErrTOSNotAccepted
- }
- return nil
- case http.StatusNotFound:
- // Likely because user has no account registered on the pay server
- return fmt.Errorf("cannot get customer details: server says no account exists")
- case http.StatusUnauthorized:
- return ErrInvalidCredentials
- default:
- var errors storeErrors
- dec := json.NewDecoder(resp.Body)
- if err := dec.Decode(&errors); err != nil {
- return err
- }
- if len(errors.Errors) == 0 {
- return fmt.Errorf("cannot get customer details: unexpected HTTP code %d", resp.StatusCode)
- }
- return &errors
+ if !customer.LatestTOSAccepted {
+ return ErrTOSNotAccepted
}
+ return nil
+ case http.StatusNotFound:
+ // Likely because user has no account registered on the pay server
+ return fmt.Errorf("cannot get customer details: server says no account exists")
+ case http.StatusUnauthorized:
+ return ErrInvalidCredentials
+ default:
+ var errors storeErrors
+ dec := json.NewDecoder(resp.Body)
+ if err := dec.Decode(&errors); err != nil {
+ return err
+ }
+ if len(errors.Errors) == 0 {
+ return fmt.Errorf("cannot get customer details: unexpected HTTP code %d", resp.StatusCode)
+ }
+ return &errors
}
-
- return nil
}
diff --git a/store/store_test.go b/store/store_test.go
index 0809162e06..82402672b0 100644
--- a/store/store_test.go
+++ b/store/store_test.go
@@ -1464,6 +1464,7 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryRevision(c *C) {
defer mockPurchasesServer.Close()
ordersURI, err := url.Parse(mockPurchasesServer.URL + ordersPath)
+ c.Assert(err, IsNil)
detailsURI, err := url.Parse(mockServer.URL + "/details/")
c.Assert(err, IsNil)
cfg := DefaultConfig()
@@ -2673,6 +2674,7 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryAssertion500(c *C) {
repo := New(&cfg, nil)
_, err = repo.Assertion(asserts.SnapDeclarationType, []string{"16", "snapidfoo"}, nil)
+ c.Assert(err, ErrorMatches, `cannot fetch assertion: got unexpected HTTP status code 500 via .+`)
c.Assert(n, Equals, 6)
}
diff --git a/systemd/escape.go b/systemd/escape.go
index 615f646bad..b0fa7248c5 100644
--- a/systemd/escape.go
+++ b/systemd/escape.go
@@ -47,7 +47,7 @@ func EscapeUnitNamePath(in string) string {
// leading "." is special
if in[0] == '.' {
fmt.Fprintf(buf, `\x%x`, in[0])
- in = in[1:len(in)]
+ in = in[1:]
}
// replace all special chars
diff --git a/tests/main/ubuntu-core-custom-device-reg-extras/manip_seed.py b/tests/main/ubuntu-core-custom-device-reg-extras/manip_seed.py
new file mode 100644
index 0000000000..35251a91fd
--- /dev/null
+++ b/tests/main/ubuntu-core-custom-device-reg-extras/manip_seed.py
@@ -0,0 +1,21 @@
+import sys
+import yaml
+
+with open(sys.argv[1]) as f:
+ seed = yaml.load(f)
+
+i = 0
+snaps = seed['snaps']
+while i < len(snaps):
+ entry = snaps[i]
+ if entry['name'] == 'pc':
+ snaps[i] = {
+ "name": "pc",
+ "unasserted": True,
+ "file": "pc_x1.snap",
+ }
+ break
+ i += 1
+
+with open(sys.argv[1], 'w') as f:
+ yaml.dump(seed, stream=f, indent=2, default_flow_style=False)
diff --git a/tests/main/ubuntu-core-custom-device-reg-extras/prepare-device b/tests/main/ubuntu-core-custom-device-reg-extras/prepare-device
new file mode 100755
index 0000000000..bcee1572d5
--- /dev/null
+++ b/tests/main/ubuntu-core-custom-device-reg-extras/prepare-device
@@ -0,0 +1,5 @@
+#!/bin/sh
+snapctl set device-service.url=http://localhost:11029
+snapctl set device-service.headers='{"X-Use-Proposed": "yes"}'
+snapctl set registration.proposed-serial="Y1234"
+snapctl set registration.body='mac: "00:00:00:00:ff:00"'
diff --git a/tests/main/ubuntu-core-custom-device-reg-extras/task.yaml b/tests/main/ubuntu-core-custom-device-reg-extras/task.yaml
new file mode 100644
index 0000000000..fd20a25c06
--- /dev/null
+++ b/tests/main/ubuntu-core-custom-device-reg-extras/task.yaml
@@ -0,0 +1,81 @@
+summary: |
+ Test that device initialisation and registration can be customized
+ with the prepare-device gadget hook and this can set request headers,
+ a proposed serial and the body of the serial assertion
+systems: [ubuntu-core-16-64]
+prepare: |
+ if [ "$TRUST_TEST_KEYS" = "false" ]; then
+ echo "This test needs test keys to be trusted"
+ exit
+ fi
+ . $TESTSLIB/systemd.sh
+ systemctl stop snapd.service snapd.socket
+ rm -rf /var/lib/snapd/assertions/*
+ rm -rf /var/lib/snapd/device
+ rm -rf /var/lib/snapd/state.json
+ unsquashfs /var/lib/snapd/snaps/pc_*.snap
+ mkdir -p squashfs-root/meta/hooks
+ cp prepare-device squashfs-root/meta/hooks
+ mksquashfs squashfs-root pc_x1.snap -comp xz
+ rm -rf squashfs-root
+ cp pc_x1.snap /var/lib/snapd/seed/snaps/
+ mv /var/lib/snapd/seed/assertions/model model.bak
+ cp /var/lib/snapd/seed/seed.yaml seed.yaml.bak
+ python3 ./manip_seed.py /var/lib/snapd/seed/seed.yaml
+ cp $TESTSLIB/assertions/developer1.account /var/lib/snapd/seed/assertions
+ cp $TESTSLIB/assertions/developer1.account-key /var/lib/snapd/seed/assertions
+ cp $TESTSLIB/assertions/developer1-pc.model /var/lib/snapd/seed/assertions
+ cp $TESTSLIB/assertions/testrootorg-store.account-key /var/lib/snapd/seed/assertions
+ # start fake device svc
+ systemd_create_and_start_unit fakedevicesvc "$(which fakedevicesvc) localhost:11029"
+ # kick first boot again
+ systemctl start snapd.service snapd.socket
+restore: |
+ if [ "$TRUST_TEST_KEYS" = "false" ]; then
+ echo "This test needs test keys to be trusted"
+ exit
+ fi
+ . $TESTSLIB/systemd.sh
+ systemctl stop snapd.service snapd.socket
+ systemd_stop_and_destroy_unit fakedevicesvc
+ rm -rf /var/lib/snapd/assertions/*
+ rm -rf /var/lib/snapd/device
+ rm -rf /var/lib/snapd/state.json
+ if systemctl status snap-pc-x1.mount ; then
+ systemctl stop snap-pc-x1.mount
+ rm -f /etc/systemd/system/snap-pc-x1.mount
+ rm -f /etc/systemd/system/multi-user.target.wants/snap-pc-x1.mount
+ rm -f /var/lib/snapd/snaps/pc_x1.snap
+ systemctl daemon-reload
+ fi
+ rm /var/lib/snapd/seed/snaps/pc_x1.snap
+ cp seed.yaml.bak /var/lib/snapd/seed/seed.yaml
+ rm /var/lib/snapd/seed/assertions/developer1.account
+ rm /var/lib/snapd/seed/assertions/developer1.account-key
+ rm /var/lib/snapd/seed/assertions/developer1-pc.model
+ rm /var/lib/snapd/seed/assertions/testrootorg-store.account-key
+ cp model.bak /var/lib/snapd/seed/assertions/model
+ rm -f *.bak
+ # kick first boot again
+ systemctl start snapd.service snapd.socket
+ # wait for first boot to be done
+ while ! snap changes | grep -q "Done.*Initialize system state"; do sleep 1; done
+execute: |
+ if [ "$TRUST_TEST_KEYS" = "false" ]; then
+ echo "This test needs test keys to be trusted"
+ exit
+ fi
+ echo "Wait for first boot to be done"
+ while ! snap changes | grep -q "Done.*Initialize system state"; do sleep 1; done
+ echo "We have a model assertion"
+ snap known model|grep "model: my-model"
+
+ echo "Wait for device initialisation to be done"
+ while ! snap changes | grep -q "Done.*Initialize device"; do sleep 1; done
+
+ echo "Check we have a serial"
+ snap known serial|grep "authority-id: developer1"
+ snap known serial|grep "brand-id: developer1"
+ snap known serial|grep "model: my-model"
+ snap known serial|grep "serial: Y1234"
+ snap known serial|grep 'mac: "00:00:00:00:ff:00"'
diff --git a/tests/main/ubuntu-core-custom-device-reg/prepare-device b/tests/main/ubuntu-core-custom-device-reg/prepare-device
index fa24c090f8..ac974b4d96 100755
--- a/tests/main/ubuntu-core-custom-device-reg/prepare-device
+++ b/tests/main/ubuntu-core-custom-device-reg/prepare-device
@@ -1,2 +1,2 @@
#!/bin/sh
-snapctl set device-service.url=http://localhost:11029 \ No newline at end of file
+snapctl set device-service.url=http://localhost:11029
diff --git a/tests/main/ubuntu-core-custom-device-reg/task.yaml b/tests/main/ubuntu-core-custom-device-reg/task.yaml
index 925441d108..ead969a56c 100644
--- a/tests/main/ubuntu-core-custom-device-reg/task.yaml
+++ b/tests/main/ubuntu-core-custom-device-reg/task.yaml
@@ -40,6 +40,13 @@ restore: |
rm -rf /var/lib/snapd/assertions/*
rm -rf /var/lib/snapd/device
rm -rf /var/lib/snapd/state.json
+ if systemctl status snap-pc-x1.mount ; then
+ systemctl stop snap-pc-x1.mount
+ rm -f /etc/systemd/system/snap-pc-x1.mount
+ rm -f /etc/systemd/system/multi-user.target.wants/snap-pc-x1.mount
+ rm -f /var/lib/snapd/snaps/pc_x1.snap
+ systemctl daemon-reload
+ fi
rm /var/lib/snapd/seed/snaps/pc_x1.snap
cp seed.yaml.bak /var/lib/snapd/seed/seed.yaml
rm /var/lib/snapd/seed/assertions/developer1.account