diff options
| author | Michael Vogt <mvo@ubuntu.com> | 2016-09-12 09:16:57 +0200 |
|---|---|---|
| committer | Michael Vogt <mvo@ubuntu.com> | 2016-09-12 09:16:57 +0200 |
| commit | d64558d06e6c9a71255f9035f85e42f79ca761ed (patch) | |
| tree | dd81abacf356e5a633e8c0e6b59226c632b8abdb | |
| parent | 020d7f32b45421c9258e2da3b6a618606e4d02c6 (diff) | |
| parent | d011bda2d3ed62baa4a9765026f836f25790386d (diff) | |
Merge remote-tracking branch 'upstream/master' into feature/snap-download-assertionfeature/snap-download-assertion
46 files changed, 452 insertions, 226 deletions
diff --git a/cmd/snap/cmd_abort.go b/cmd/snap/cmd_abort.go index caab5f5913..5835df95b9 100644 --- a/cmd/snap/cmd_abort.go +++ b/cmd/snap/cmd_abort.go @@ -27,7 +27,7 @@ import ( type cmdAbort struct { Positional struct { - Id string `positional-arg-name:"change-id"` + ID string `positional-arg-name:"change-id"` } `positional-args:"yes" required:"yes"` } @@ -52,6 +52,6 @@ func (x *cmdAbort) Execute(args []string) error { } cli := Client() - _, err := cli.Abort(x.Positional.Id) + _, err := cli.Abort(x.Positional.ID) return err } diff --git a/cmd/snap/cmd_booted.go b/cmd/snap/cmd_booted.go index 179f655c30..6af94f3c62 100644 --- a/cmd/snap/cmd_booted.go +++ b/cmd/snap/cmd_booted.go @@ -24,6 +24,7 @@ import ( "github.com/jessevdk/go-flags" + "github.com/snapcore/snapd/i18n" "github.com/snapcore/snapd/overlord" "github.com/snapcore/snapd/overlord/boot" "github.com/snapcore/snapd/partition" @@ -48,13 +49,13 @@ func (x *cmdBooted) Execute(args []string) error { } if release.OnClassic { - fmt.Fprintf(Stdout, "Ignoring 'booted' on classic") + fmt.Fprintf(Stdout, i18n.G("Ignoring 'booted' on classic")) return nil } bootloader, err := partition.FindBootloader() if err != nil { - return fmt.Errorf("can not mark boot successful: %s", err) + return fmt.Errorf(i18n.G("cannot mark boot successful: %s"), err) } if err := partition.MarkBootSuccessful(bootloader); err != nil { diff --git a/cmd/snap/cmd_buy.go b/cmd/snap/cmd_buy.go index 853dbb6ffd..9d58ddd14f 100644 --- a/cmd/snap/cmd_buy.go +++ b/cmd/snap/cmd_buy.go @@ -100,7 +100,7 @@ func buySnap(opts *store.BuyOptions) error { } // TODO Remove this payment method filter once interactive payment methods are supported on the CLI - methods := make([]*store.PaymentMethod, 0) + var methods []*store.PaymentMethod for _, method := range paymentInfo.Methods { if !method.RequiresInteraction { methods = append(methods, method) @@ -185,7 +185,8 @@ func buySnap(opts *store.BuyOptions) error { return fmt.Errorf(i18n.G("cannot buy snap %q: the command line tools do not support interactive purchases"), snap.Name) } - fmt.Fprintf(Stdout, "%s bought\n", opts.SnapName) + // TRANSLATORS: %s is a snap name + fmt.Fprintf(Stdout, i18n.G("%s bought\n"), opts.SnapName) return nil } diff --git a/cmd/snap/cmd_changes.go b/cmd/snap/cmd_changes.go index 0df5733a57..afe9c79195 100644 --- a/cmd/snap/cmd_changes.go +++ b/cmd/snap/cmd_changes.go @@ -21,7 +21,6 @@ package main import ( "fmt" - "os" "regexp" "sort" "time" @@ -47,7 +46,7 @@ type cmdChanges struct { type cmdChange struct { Positional struct { - Id string `positional-arg-name:"<id>" required:"yes"` + ID string `positional-arg-name:"<id>" required:"yes"` } `positional-args:"yes"` } @@ -70,11 +69,12 @@ func (c *cmdChanges) Execute(args []string) error { } if allDigits(c.Positional.Snap) { - return fmt.Errorf(`%s changes command expects a snap name, try: %[1]s change %s`, os.Args[0], c.Positional.Snap) + // TRANSLATORS: the %s is the argument given by the user to "snap changes" + return fmt.Errorf(i18n.G(`"snap changes" command expects a snap name, try: "snap change %s"`), c.Positional.Snap) } if c.Positional.Snap == "everything" { - fmt.Fprintln(Stdout, "Yes, yes it does.") + fmt.Fprintln(Stdout, i18n.G("Yes, yes it does.")) return nil } @@ -115,7 +115,7 @@ func (c *cmdChanges) Execute(args []string) error { func (c *cmdChange) Execute([]string) error { cli := Client() - chg, err := cli.Change(c.Positional.Id) + chg, err := cli.Change(c.Positional.ID) if err != nil { return err } diff --git a/cmd/snap/cmd_create_key.go b/cmd/snap/cmd_create_key.go index 0963834a0e..89c2fc3988 100644 --- a/cmd/snap/cmd_create_key.go +++ b/cmd/snap/cmd_create_key.go @@ -56,7 +56,7 @@ func (x *cmdCreateKey) Execute(args []string) error { keyName = "default" } if !asserts.IsValidAccountKeyName(keyName) { - return fmt.Errorf("key name %q is not valid; only ASCII letters, digits, and hyphens are allowed", keyName) + return fmt.Errorf(i18n.G("key name %q is not valid; only ASCII letters, digits, and hyphens are allowed"), keyName) } fmt.Fprint(Stdout, i18n.G("Passphrase: ")) diff --git a/cmd/snap/cmd_download.go b/cmd/snap/cmd_download.go index 59dd81b3ae..0e2689e5f7 100644 --- a/cmd/snap/cmd_download.go +++ b/cmd/snap/cmd_download.go @@ -23,16 +23,15 @@ import ( "fmt" "io/ioutil" "os" - "path/filepath" "strings" "github.com/jessevdk/go-flags" "github.com/snapcore/snapd/asserts" + "github.com/snapcore/snapd/asserts/sysdb" "github.com/snapcore/snapd/i18n" - "github.com/snapcore/snapd/osutil" + "github.com/snapcore/snapd/image" "github.com/snapcore/snapd/overlord/auth" - "github.com/snapcore/snapd/progress" "github.com/snapcore/snapd/snap" "github.com/snapcore/snapd/store" ) @@ -50,7 +49,7 @@ type cmdDownload struct { var shortDownloadHelp = i18n.G("Download a given snap") var longDownloadHelp = i18n.G(` -The download command will download the given snap to the current directory. +The download command will download the given snap and its supporting assertions to the current directory. `) func init() { @@ -59,17 +58,13 @@ func init() { }) } -func makeStore() *store.Store { - // FIXME: set auth context - var authContext auth.AuthContext - - return store.New(nil, authContext) -} - func (x *cmdDownload) downloadAssertion() error { var user *auth.UserState - sto := makeStore() + // FIXME: set auth context + var authContext auth.AuthContext + + sto := store.New(nil, authContext) l := strings.Split(x.Positional.Snap, "/") as, err := sto.Assertion(asserts.Type(l[0]), l[1:], user) if err != nil { @@ -84,6 +79,30 @@ func (x *cmdDownload) downloadAssertion() error { return nil } +func fetchSnapAssertions(sto *store.Store, snapPath string, snapInfo *snap.Info, dlOpts *image.DownloadOptions) error { + db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{ + Backstore: asserts.NewMemoryBackstore(), + Trusted: sysdb.Trusted(), + }) + if err != nil { + return err + } + + w, err := os.Create(snapPath + ".assertions") + if err != nil { + return fmt.Errorf(i18n.G("cannot create assertions file: %v"), err) + } + defer w.Close() + + encoder := asserts.NewEncoder(w) + save := func(a asserts.Assertion) error { + return encoder.Encode(a) + } + f := image.StoreAssertionFetcher(sto, dlOpts, db, save) + + return image.FetchSnapAssertions(snapPath, snapInfo, f, db) +} + func (x *cmdDownload) Execute(args []string) error { if err := x.setChannelFromCommandline(); err != nil { return err @@ -110,21 +129,32 @@ func (x *cmdDownload) Execute(args []string) error { snapName := x.Positional.Snap + // FIXME: set auth context + var authContext auth.AuthContext var user *auth.UserState - sto := makeStore() - // we always allow devmode + + sto := store.New(nil, authContext) + // we always allow devmode for downloads devMode := true - snap, err := sto.Snap(snapName, x.Channel, devMode, revision, user) + + dlOpts := image.DownloadOptions{ + TargetDir: "", // cwd + DevMode: devMode, + Channel: x.Channel, + User: user, + } + + fmt.Fprintf(Stderr, i18n.G("Fetching snap %q\n"), snapName) + snapPath, snapInfo, err := image.DownloadSnap(sto, snapName, revision, &dlOpts) if err != nil { return err } - pb := progress.NewTextProgress() - tmpName, err := sto.Download(snapName, &snap.DownloadInfo, pb, user) + + fmt.Fprintf(Stderr, i18n.G("Fetching assertions for %q\n"), snapName) + err = fetchSnapAssertions(sto, snapPath, snapInfo, &dlOpts) if err != nil { return err } - defer os.Remove(tmpName) - targetPath := filepath.Base(snap.MountFile()) - return osutil.CopyFile(tmpName, targetPath, 0) + return nil } diff --git a/cmd/snap/cmd_find.go b/cmd/snap/cmd_find.go index b106746f18..bb93e2499f 100644 --- a/cmd/snap/cmd_find.go +++ b/cmd/snap/cmd_find.go @@ -20,6 +20,7 @@ package main import ( + "errors" "fmt" "github.com/snapcore/snapd/client" @@ -36,7 +37,8 @@ The find command queries the store for available packages. func getPrice(prices map[string]float64, currency string) (float64, string, error) { // If there are no prices, then the snap is free if len(prices) == 0 { - return 0, "", fmt.Errorf(i18n.G("snap is free")) + // TRANSLATORS: free as in gratis + return 0, "", errors.New(i18n.G("snap is free")) } // Look up the price by currency code @@ -100,6 +102,10 @@ func (x *cmdFind) Execute(args []string) error { return ErrExtraArgs } + if x.Positional.Query == "" { + return errors.New(i18n.G("you need to specify a query. Try \"snap find hello-world\".")) + } + return findSnaps(&client.FindOptions{ Private: x.Private, Query: x.Positional.Query, @@ -114,7 +120,8 @@ func findSnaps(opts *client.FindOptions) error { } if len(snaps) == 0 { - return fmt.Errorf("no snaps found for %q", opts.Query) + // TRANSLATORS: the %q is the (quoted) query the user entered + return fmt.Errorf(i18n.G("no snaps found for %q"), opts.Query) } w := tabWriter() diff --git a/cmd/snap/cmd_find_test.go b/cmd/snap/cmd_find_test.go index d2634ce5ff..be8b5636ae 100644 --- a/cmd/snap/cmd_find_test.go +++ b/cmd/snap/cmd_find_test.go @@ -28,7 +28,15 @@ import ( snap "github.com/snapcore/snapd/cmd/snap" ) -const findJson = ` +func (s *SnapSuite) TestFindNothingFails(c *check.C) { + s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { + c.Fatalf("it reached the server") + }) + _, err := snap.Parser().ParseArgs([]string{"find"}) + c.Assert(err, check.ErrorMatches, `you need to specify a query. Try.*`) +} + +const findJSON = ` { "type": "sync", "status-code": 200, @@ -100,14 +108,14 @@ func (s *SnapSuite) TestFind(c *check.C) { case 0: c.Check(r.Method, check.Equals, "GET") c.Check(r.URL.Path, check.Equals, "/v2/find") - fmt.Fprintln(w, findJson) + fmt.Fprintln(w, findJSON) default: c.Fatalf("expected to get 1 requests, now on %d", n+1) } n++ }) - rest, err := snap.Parser().ParseArgs([]string{"find"}) + rest, err := snap.Parser().ParseArgs([]string{"find", "hello"}) c.Assert(err, check.IsNil) c.Assert(rest, check.DeepEquals, []string{}) c.Check(s.Stdout(), check.Matches, `Name +Version +Developer +Notes +Summary @@ -118,7 +126,7 @@ hello-huge +1.0 +noise +- +a really big snap c.Check(s.Stderr(), check.Equals, "") } -const findHelloJson = ` +const findHelloJSON = ` { "type": "sync", "status-code": 200, @@ -175,7 +183,7 @@ func (s *SnapSuite) TestFindHello(c *check.C) { c.Check(r.URL.Path, check.Equals, "/v2/find") q := r.URL.Query() c.Check(q.Get("q"), check.Equals, "hello") - fmt.Fprintln(w, findHelloJson) + fmt.Fprintln(w, findHelloJSON) default: c.Fatalf("expected to get 1 requests, now on %d", n+1) } @@ -192,7 +200,7 @@ hello-huge +1.0 +noise +- +a really big snap c.Check(s.Stderr(), check.Equals, "") } -const findPricedJson = ` +const findPricedJSON = ` { "type": "sync", "status-code": 200, @@ -231,14 +239,14 @@ func (s *SnapSuite) TestFindPriced(c *check.C) { case 0: c.Check(r.Method, check.Equals, "GET") c.Check(r.URL.Path, check.Equals, "/v2/find") - fmt.Fprintln(w, findPricedJson) + fmt.Fprintln(w, findPricedJSON) default: c.Fatalf("expected to get 1 requests, now on %d", n+1) } n++ }) - rest, err := snap.Parser().ParseArgs([]string{"find"}) + rest, err := snap.Parser().ParseArgs([]string{"find", "hello"}) c.Assert(err, check.IsNil) c.Assert(rest, check.DeepEquals, []string{}) c.Check(s.Stdout(), check.Matches, `Name +Version +Developer +Notes +Summary @@ -247,7 +255,7 @@ hello +2.10 +canonical +1.99GBP +GNU Hello, the "hello world" snap c.Check(s.Stderr(), check.Equals, "") } -const findPricedAndBoughtJson = ` +const findPricedAndBoughtJSON = ` { "type": "sync", "status-code": 200, @@ -286,14 +294,14 @@ func (s *SnapSuite) TestFindPricedAndBought(c *check.C) { case 0: c.Check(r.Method, check.Equals, "GET") c.Check(r.URL.Path, check.Equals, "/v2/find") - fmt.Fprintln(w, findPricedAndBoughtJson) + fmt.Fprintln(w, findPricedAndBoughtJSON) default: c.Fatalf("expected to get 1 requests, now on %d", n+1) } n++ }) - rest, err := snap.Parser().ParseArgs([]string{"find"}) + rest, err := snap.Parser().ParseArgs([]string{"find", "hello"}) c.Assert(err, check.IsNil) c.Assert(rest, check.DeepEquals, []string{}) c.Check(s.Stdout(), check.Matches, `Name +Version +Developer +Notes +Summary diff --git a/cmd/snap/cmd_get.go b/cmd/snap/cmd_get.go index 9c90218716..bdc4e3ceba 100644 --- a/cmd/snap/cmd_get.go +++ b/cmd/snap/cmd_get.go @@ -48,7 +48,8 @@ func init() { func (x *cmdGet) Execute(args []string) error { if len(args) > 0 { - return fmt.Errorf("too many arguments: %s", strings.Join(args, " ")) + // TRANSLATORS: the %s is the list of extra arguments + return fmt.Errorf(i18n.G("too many arguments: %s"), strings.Join(args, " ")) } return getConf(x.Positional.Snap, x.Positional.Keys, x.Document) diff --git a/cmd/snap/cmd_known.go b/cmd/snap/cmd_known.go index 973d0aeca6..54e49c1fb0 100644 --- a/cmd/snap/cmd_known.go +++ b/cmd/snap/cmd_known.go @@ -61,7 +61,7 @@ func (x *cmdKnown) Execute(args []string) error { for _, headerFilter := range x.KnownOptions.HeaderFilters { parts := strings.SplitN(headerFilter, "=", 2) if len(parts) != 2 { - return fmt.Errorf("invalid header filter: %q (want key=value)", headerFilter) + return fmt.Errorf(i18n.G("invalid header filter: %q (want key=value)"), headerFilter) } headers[parts[0]] = parts[1] } diff --git a/cmd/snap/cmd_list.go b/cmd/snap/cmd_list.go index 98a4eb5dcf..2617abc5ec 100644 --- a/cmd/snap/cmd_list.go +++ b/cmd/snap/cmd_list.go @@ -64,7 +64,7 @@ func listSnaps(names []string) error { snaps, err := cli.List(names) if err != nil { if err == client.ErrNoSnapsInstalled { - fmt.Fprintln(Stderr, i18n.G("No snaps are installed yet. Try 'snap install hello-world'.")) + fmt.Fprintln(Stderr, i18n.G("No snaps are installed yet. Try \"snap install hello-world\".")) return nil } return err diff --git a/cmd/snap/cmd_list_test.go b/cmd/snap/cmd_list_test.go index 17232e6395..4017407d42 100644 --- a/cmd/snap/cmd_list_test.go +++ b/cmd/snap/cmd_list_test.go @@ -69,7 +69,7 @@ func (s *SnapSuite) TestListEmpty(c *check.C) { c.Assert(err, check.IsNil) c.Assert(rest, check.DeepEquals, []string{}) c.Check(s.Stdout(), check.Equals, "") - c.Check(s.Stderr(), check.Matches, "No snaps are installed yet. Try 'snap install hello-world'.\n") + c.Check(s.Stderr(), check.Equals, "No snaps are installed yet. Try \"snap install hello-world\".\n") } func (s *SnapSuite) TestListEmptyWithQuery(c *check.C) { @@ -90,7 +90,7 @@ func (s *SnapSuite) TestListEmptyWithQuery(c *check.C) { c.Assert(err, check.IsNil) c.Assert(rest, check.DeepEquals, []string{}) c.Check(s.Stdout(), check.Equals, "") - c.Check(s.Stderr(), check.Matches, "No snaps are installed yet. Try 'snap install hello-world'.\n") + c.Check(s.Stderr(), check.Equals, "No snaps are installed yet. Try \"snap install hello-world\".\n") } func (s *SnapSuite) TestListWithNoMatchingQuery(c *check.C) { diff --git a/cmd/snap/cmd_run.go b/cmd/snap/cmd_run.go index c79fefe772..f43ef5a6a5 100644 --- a/cmd/snap/cmd_run.go +++ b/cmd/snap/cmd_run.go @@ -57,20 +57,21 @@ func init() { func (x *cmdRun) Execute(args []string) error { if len(args) == 0 { - return fmt.Errorf("need the application to run as argument") + return fmt.Errorf(i18n.G("need the application to run as argument")) } snapApp := args[0] args = args[1:] // Catch some invalid parameter combinations, provide helpful errors if x.Hook != "" && x.Command != "" { - return fmt.Errorf("cannot use --hook and --command together") + return fmt.Errorf(i18n.G("cannot use --hook and --command together")) } if x.Revision != "unset" && x.Revision != "" && x.Hook == "" { - return fmt.Errorf("-r can only be used with --hook") + return fmt.Errorf(i18n.G("-r can only be used with --hook")) } if x.Hook != "" && len(args) > 0 { - return fmt.Errorf("too many arguments for hook %q: %s", x.Hook, strings.Join(args, " ")) + // TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments + return fmt.Errorf(i18n.G("too many arguments for hook %q: %s"), x.Hook, strings.Join(args, " ")) } // Now actually handle the dispatching @@ -98,7 +99,7 @@ func getSnapInfo(snapName string, revision snap.Revision) (*snap.Info, error) { return nil, fmt.Errorf("cannot find snap %q", snapName) } if len(snaps) > 1 { - return nil, fmt.Errorf("multiple snaps for %q: %d", snapName, len(snaps)) + return nil, fmt.Errorf(i18n.G("multiple snaps for %q: %d"), snapName, len(snaps)) } revision = snaps[0].Revision } @@ -135,7 +136,7 @@ func snapExecEnv(info *snap.Info) []string { func createUserDataDirs(info *snap.Info) error { usr, err := userCurrent() if err != nil { - return fmt.Errorf("cannot get the current user: %s", err) + return fmt.Errorf(i18n.G("cannot get the current user: %v"), err) } // see snapenv.User @@ -143,7 +144,8 @@ func createUserDataDirs(info *snap.Info) error { commonUserData := info.UserCommonDataDir(usr.HomeDir) for _, d := range []string{userData, commonUserData} { if err := os.MkdirAll(d, 0755); err != nil { - return fmt.Errorf("cannot create %q: %s", d, err) + // TRANSLATORS: %q is the directory whose creation failed, %v the error message + return fmt.Errorf(i18n.G("cannot create %q: %v"), d, err) } } return nil @@ -158,7 +160,7 @@ func snapRunApp(snapApp, command string, args []string) error { app := info.Apps[appName] if app == nil { - return fmt.Errorf("cannot find app %q in %q", appName, snapName) + return fmt.Errorf(i18n.G("cannot find app %q in %q"), appName, snapName) } return runSnapConfine(info, app.SecurityTag(), snapApp, command, "", args) diff --git a/cmd/snap/cmd_set.go b/cmd/snap/cmd_set.go index a1d604668e..f3762f3428 100644 --- a/cmd/snap/cmd_set.go +++ b/cmd/snap/cmd_set.go @@ -50,7 +50,7 @@ func (x *cmdSet) Execute(args []string) error { for _, patchValue := range x.Positional.ConfValues { parts := strings.SplitN(patchValue, "=", 2) if len(parts) != 2 { - return fmt.Errorf("invalid configuration: %q (want key=value)", patchValue) + return fmt.Errorf(i18n.G("invalid configuration: %q (want key=value)"), patchValue) } var value interface{} err := json.Unmarshal([]byte(parts[1]), &value) diff --git a/cmd/snap/cmd_sign.go b/cmd/snap/cmd_sign.go index 8eaa2b9e0b..97811251cb 100644 --- a/cmd/snap/cmd_sign.go +++ b/cmd/snap/cmd_sign.go @@ -52,7 +52,7 @@ func (x *cmdSign) Execute(args []string) error { statement, err := ioutil.ReadAll(Stdin) if err != nil { - return fmt.Errorf("cannot read assertion input: %v", err) + return fmt.Errorf(i18n.G("cannot read assertion input: %v"), err) } keypairMgr := asserts.NewGPGKeypairManager() diff --git a/cmd/snap/cmd_sign_build.go b/cmd/snap/cmd_sign_build.go index 88e70fb1ed..7e2b1fc18f 100644 --- a/cmd/snap/cmd_sign_build.go +++ b/cmd/snap/cmd_sign_build.go @@ -68,7 +68,8 @@ func (x *cmdSignBuild) Execute(args []string) error { gkm := asserts.NewGPGKeypairManager() privKey, err := gkm.GetByName(x.KeyName) if err != nil { - return fmt.Errorf("cannot use %q key: %v", x.KeyName, err) + // TRANSLATORS: %q is the key name, %v the error message + return fmt.Errorf(i18n.G("cannot use %q key: %v"), x.KeyName, err) } pubKey := privKey.PublicKey() @@ -88,12 +89,12 @@ func (x *cmdSignBuild) Execute(args []string) error { KeypairManager: gkm, }) if err != nil { - return fmt.Errorf("cannot open the assertions database: %v", err) + return fmt.Errorf(i18n.G("cannot open the assertions database: %v"), err) } a, err := adb.Sign(asserts.SnapBuildType, headers, nil, pubKey.ID()) if err != nil { - return fmt.Errorf("cannot sign assertion: %v", err) + return fmt.Errorf(i18n.G("cannot sign assertion: %v"), err) } _, err = Stdout.Write(asserts.Encode(a)) diff --git a/cmd/snap/cmd_snap_op.go b/cmd/snap/cmd_snap_op.go index 00a71ef3e6..0dbb796cfe 100644 --- a/cmd/snap/cmd_snap_op.go +++ b/cmd/snap/cmd_snap_op.go @@ -69,7 +69,7 @@ func wait(client *client.Client, id string) (*client.Change, error) { if now.After(tMax) { return nil, err } - pb.Spin("Waiting for server to restart") + pb.Spin(i18n.G("Waiting for server to restart")) time.Sleep(pollTime) continue } @@ -453,7 +453,8 @@ func (x *cmdTry) Execute([]string) error { path, err := filepath.Abs(name) if err != nil { - return fmt.Errorf("cannot get full path for %q: %s", name, err) + // TRANSLATORS: %q gets what the user entered, %v gets the resulting error message + return fmt.Errorf(i18n.G("cannot get full path for %q: %v"), name, err) } changeID, err := cli.Try(path, opts) @@ -469,7 +470,8 @@ func (x *cmdTry) Execute([]string) error { // extract the snap name var snapName string if err := chg.Get("snap-name", &snapName); err != nil { - return fmt.Errorf("cannot extract the snap-name from local file %q: %s", name, err) + // TRANSLATORS: %q gets the snap name, %v gets the resulting error message + return fmt.Errorf(i18n.G("cannot extract the snap-name from local file %q: %v"), name, err) } name = snapName @@ -479,9 +481,11 @@ func (x *cmdTry) Execute([]string) error { return err } if len(snaps) != 1 { - return fmt.Errorf("cannot get data for %q: %v", name, snaps) + // TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it + return fmt.Errorf(i18n.G("cannot get data for %q: %v"), name, snaps) } snap := snaps[0] + // TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). fmt.Fprintf(Stdout, i18n.G("%s %s mounted from %s\n"), name, snap.Version, path) return nil } @@ -535,6 +539,7 @@ func (x *cmdDisable) Execute([]string) error { } type cmdRevert struct { + modeMixin Positional struct { Snap string `positional-arg-name:"<snap>"` } `positional-args:"yes"` @@ -555,9 +560,14 @@ func (x *cmdRevert) Execute(args []string) error { return ErrExtraArgs } + if err := x.validateMode(); err != nil { + return err + } + cli := Client() name := x.Positional.Snap - changeID, err := cli.Revert(name, nil) + opts := &client.SnapOptions{DevMode: x.DevMode, JailMode: x.JailMode} + changeID, err := cli.Revert(name, opts) if err != nil { return err } @@ -572,7 +582,8 @@ func (x *cmdRevert) Execute(args []string) error { return err } if len(snaps) != 1 { - return fmt.Errorf("cannot get data for %q: %v", name, snaps) + // TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it + return fmt.Errorf(i18n.G("cannot get data for %q: %v"), name, snaps) } snap := snaps[0] fmt.Fprintf(Stdout, i18n.G("%s reverted to %s\n"), name, snap.Version) @@ -586,7 +597,5 @@ func init() { addCommand("try", shortTryHelp, longTryHelp, func() flags.Commander { return &cmdTry{} }) addCommand("enable", shortEnableHelp, longEnableHelp, func() flags.Commander { return &cmdEnable{} }) addCommand("disable", shortDisableHelp, longDisableHelp, func() flags.Commander { return &cmdDisable{} }) - // FIXME: make visible once everything has landed for revert - cmd := addCommand("revert", shortRevertHelp, longRevertHelp, func() flags.Commander { return &cmdRevert{} }) - cmd.hidden = true + addCommand("revert", shortRevertHelp, longRevertHelp, func() flags.Commander { return &cmdRevert{} }) } diff --git a/cmd/snap/interfaces_common.go b/cmd/snap/interfaces_common.go index e9dfbae381..fa819d4474 100644 --- a/cmd/snap/interfaces_common.go +++ b/cmd/snap/interfaces_common.go @@ -22,6 +22,8 @@ package main import ( "fmt" "strings" + + "github.com/snapcore/snapd/i18n" ) // AttributePair contains a pair of key-value strings @@ -38,7 +40,7 @@ func (ap *AttributePair) UnmarshalFlag(value string) error { if len(parts) < 2 || parts[0] == "" { ap.Key = "" ap.Value = "" - return fmt.Errorf("invalid attribute: %q (want key=value)", value) + return fmt.Errorf(i18n.G("invalid attribute: %q (want key=value)"), value) } ap.Key = parts[0] ap.Value = parts[1] @@ -81,7 +83,7 @@ func (sn *SnapAndName) UnmarshalFlag(value string) error { } } if sn.Snap == "" && sn.Name == "" { - return fmt.Errorf("invalid value: %q (want snap:name or snap)", value) + return fmt.Errorf(i18n.G("invalid value: %q (want snap:name or snap)"), value) } return nil } diff --git a/cmd/snap/main.go b/cmd/snap/main.go index d0b642af6c..5f6841e768 100644 --- a/cmd/snap/main.go +++ b/cmd/snap/main.go @@ -51,7 +51,7 @@ type options struct { var optionsData options // ErrExtraArgs is returned if extra arguments to a command are found -var ErrExtraArgs = fmt.Errorf("too many arguments for command") +var ErrExtraArgs = fmt.Errorf(i18n.G("too many arguments for command")) // cmdInfo holds information needed to call parser.AddCommand(...). type cmdInfo struct { @@ -125,10 +125,10 @@ func Parser() *flags.Parser { panic(&exitStatus{0}) } parser := flags.NewParser(&optionsData, flags.HelpFlag|flags.PassDoubleDash|flags.PassAfterNonOption) - parser.ShortDescription = "Tool to interact with snaps" - parser.LongDescription = ` + parser.ShortDescription = i18n.G("Tool to interact with snaps") + parser.LongDescription = i18n.G(` The snap tool interacts with the snapd daemon to control the snappy software platform. -` +`) // Add all regular commands for _, c := range commands { @@ -172,7 +172,7 @@ func Client() *client.Client { func init() { err := logger.SimpleSetup() if err != nil { - fmt.Fprintf(Stderr, "WARNING: failed to activate logging: %s\n", err) + fmt.Fprintf(Stderr, i18n.G("WARNING: failed to activate logging: %v\n"), err) } } @@ -189,7 +189,7 @@ func main() { // *unless* there is an error, i.e. we setup a wrong // symlink (or syscall.Exec() fails for strange reasons) err := cmd.Execute(args) - fmt.Fprintf(Stderr, "internal error, please report: running %q failed: %s\n", snapApp, err) + fmt.Fprintf(Stderr, i18n.G("internal error, please report: running %q failed: %v\n"), snapApp, err) os.Exit(46) } @@ -204,7 +204,7 @@ func main() { // no magic /o\ if err := run(); err != nil { - fmt.Fprintf(Stderr, "error: %v\n", err) + fmt.Fprintf(Stderr, i18n.G("error: %v\n"), err) os.Exit(1) } } @@ -232,11 +232,11 @@ func run() error { if e, ok := err.(*client.Error); ok && e.Kind == client.ErrorKindLoginRequired { u, _ := user.Current() if u != nil && u.Username == "root" { - return fmt.Errorf(`%s (see "snap login --help")`, e.Message) - } else { - return fmt.Errorf(`%s (try with sudo)`, e.Message) + return fmt.Errorf(i18n.G(`%s (see "snap login --help")`), e.Message) } + // TRANSLATORS: %s will be a message along the lines of "login required" + return fmt.Errorf(i18n.G(`%s (try with sudo)`), e.Message) } } diff --git a/cmd/snap/notes.go b/cmd/snap/notes.go index a453ff9209..74849dc64a 100644 --- a/cmd/snap/notes.go +++ b/cmd/snap/notes.go @@ -23,6 +23,8 @@ import ( "strings" ) +// Notes encapsulate everything that might be interesting about a +// snap, in order to present a brief summary of it. type Notes struct { Price string Private bool diff --git a/cmd/version.go b/cmd/version.go index 1b8cd74bd3..224cedf790 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -21,5 +21,5 @@ package cmd //go:generate mkversion.sh -// will be overwritten at build-time via mkversion.sh +// Version will be overwritten at build-time via mkversion.sh var Version = "unknown" diff --git a/daemon/api.go b/daemon/api.go index 4dbed8fcd6..64d6fb5319 100644 --- a/daemon/api.go +++ b/daemon/api.go @@ -809,8 +809,13 @@ func snapRemove(inst *snapInstruction, st *state.State) (string, []*state.TaskSe } func snapRevert(inst *snapInstruction, st *state.State) (string, []*state.TaskSet, error) { + flags, err := modeFlags(inst.DevMode, inst.JailMode) + if err != nil { + return "", nil, err + } + // TODO: bail if revision is given (and != current), or revert to that revision? - ts, err := snapstate.Revert(st, inst.Snaps[0]) + ts, err := snapstate.Revert(st, inst.Snaps[0], flags) if err != nil { return "", nil, err } diff --git a/i18n/i18n_test.go b/i18n/i18n_test.go index b94aebc0fc..afd90b24ca 100644 --- a/i18n/i18n_test.go +++ b/i18n/i18n_test.go @@ -49,7 +49,7 @@ func (s *i18nTestSuite) SetUpTest(c *C) { gettext.BindTextdomain("snappy-test", localeDir) locale := gettext.SetLocale(gettext.LC_ALL, "en_DK.UTF-8") if locale != "en_DK.UTF-8" { - c.Skip("can not init locale") + c.Skip("cannot init locale") } os.Setenv("LANGUAGE", "en_DK") diff --git a/image/helpers.go b/image/helpers.go new file mode 100644 index 0000000000..29d0b23472 --- /dev/null +++ b/image/helpers.go @@ -0,0 +1,123 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2014-2016 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +package image + +// TODO: put these in appropriate package(s) once they are clarified a bit more + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/snapcore/snapd/asserts" + "github.com/snapcore/snapd/asserts/snapasserts" + "github.com/snapcore/snapd/osutil" + "github.com/snapcore/snapd/overlord/auth" + "github.com/snapcore/snapd/progress" + "github.com/snapcore/snapd/snap" +) + +// DownloadOptions carries options for downloading snaps plus assertions. +type DownloadOptions struct { + TargetDir string + Channel string + DevMode bool + User *auth.UserState +} + +// A Store can find metadata on snaps, download snaps and fetch assertions. +type Store interface { + Snap(name, channel string, devmode bool, revision snap.Revision, user *auth.UserState) (*snap.Info, error) + Download(name string, downloadInfo *snap.DownloadInfo, pbar progress.Meter, user *auth.UserState) (path string, err error) + + Assertion(assertType *asserts.AssertionType, primaryKey []string, user *auth.UserState) (asserts.Assertion, error) +} + +// DownloadSnap downloads the snap with the given name and optionally revision using the provided store and options. It returns the final full path of the snap inside the opts.TargetDir and a snap.Info for the snap. +func DownloadSnap(sto Store, name string, revision snap.Revision, opts *DownloadOptions) (targetPath string, info *snap.Info, err error) { + if opts == nil { + opts = &DownloadOptions{} + } + + targetDir := opts.TargetDir + if targetDir == "" { + pwd, err := os.Getwd() + if err != nil { + return "", nil, err + } + targetDir = pwd + } + + snap, err := sto.Snap(name, opts.Channel, opts.DevMode, revision, opts.User) + if err != nil { + return "", nil, fmt.Errorf("cannot find snap %q: %v", name, err) + } + pb := progress.NewTextProgress() + tmpName, err := sto.Download(name, &snap.DownloadInfo, pb, opts.User) + if err != nil { + return "", nil, err + } + defer os.Remove(tmpName) + + baseName := filepath.Base(snap.MountFile()) + targetPath = filepath.Join(targetDir, baseName) + if err := osutil.CopyFile(tmpName, targetPath, 0); err != nil { + return "", nil, err + } + + return targetPath, snap, nil +} + +// StoreAssertionFetcher creates an asserts.Fetcher for assertions against the given store using dlOpts for authorization, the fetcher will save assertions in the given database and after that also call save for each of them. +func StoreAssertionFetcher(sto Store, dlOpts *DownloadOptions, db *asserts.Database, save func(asserts.Assertion) error) *asserts.Fetcher { + retrieve := func(ref *asserts.Ref) (asserts.Assertion, error) { + return sto.Assertion(ref.Type, ref.PrimaryKey, dlOpts.User) + } + save2 := func(a asserts.Assertion) error { + // for checking + err := db.Add(a) + if err != nil { + if _, ok := err.(*asserts.RevisionError); ok { + return nil + } + return fmt.Errorf("cannot add assertion %v: %v", a.Ref(), err) + } + return save(a) + } + return asserts.NewFetcher(db, retrieve, save2) +} + +// FetchSnapAssertions fetches and cross checks the snap assertions matching the given snap file using the provided asserts.Fetcher and assertion database. +func FetchSnapAssertions(snapPath string, info *snap.Info, f *asserts.Fetcher, db asserts.RODatabase) error { + sha3_384, size, err := asserts.SnapFileSHA3_384(snapPath) + if err != nil { + return err + } + ref := &asserts.Ref{ + Type: asserts.SnapRevisionType, + PrimaryKey: []string{sha3_384}, + } + if err := f.Fetch(ref); err != nil { + return fmt.Errorf("cannot fetch snap signatures/assertions: %v", err) + } + + // cross checks + return snapasserts.CrossCheck(info.Name(), sha3_384, size, &info.SideInfo, db) +} diff --git a/image/image.go b/image/image.go index e3563f513b..c19ecd9721 100644 --- a/image/image.go +++ b/image/image.go @@ -29,15 +29,12 @@ import ( "strings" "github.com/snapcore/snapd/asserts" - "github.com/snapcore/snapd/asserts/snapasserts" "github.com/snapcore/snapd/asserts/sysdb" "github.com/snapcore/snapd/boot" "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/logger" "github.com/snapcore/snapd/osutil" - "github.com/snapcore/snapd/overlord/auth" "github.com/snapcore/snapd/partition" - "github.com/snapcore/snapd/progress" "github.com/snapcore/snapd/snap" "github.com/snapcore/snapd/snap/squashfs" "github.com/snapcore/snapd/store" @@ -160,7 +157,7 @@ func downloadUnpackGadget(sto Store, model *asserts.Model, opts *Options, local return fmt.Errorf("cannot create gadget unpack dir %q: %s", opts.GadgetUnpackDir, err) } - dlOpts := &downloadOptions{ + dlOpts := &DownloadOptions{ TargetDir: opts.GadgetUnpackDir, Channel: opts.Channel, } @@ -174,7 +171,7 @@ func downloadUnpackGadget(sto Store, model *asserts.Model, opts *Options, local return snap.Unpack("*", opts.GadgetUnpackDir) } -func acquireSnap(sto Store, name string, dlOpts *downloadOptions, local *localInfos) (downloadedSnap string, info *snap.Info, err error) { +func acquireSnap(sto Store, name string, dlOpts *DownloadOptions, local *localInfos) (downloadedSnap string, info *snap.Info, err error) { if info := local.Info(name); info != nil { // local snap to install (unasserted only for now) p := local.Path(name) @@ -184,7 +181,7 @@ func acquireSnap(sto Store, name string, dlOpts *downloadOptions, local *localIn } return dst, info, nil } - return downloadSnapWithSideInfo(sto, name, dlOpts) + return DownloadSnap(sto, name, snap.R(0), dlOpts) } type addingFetcher struct { @@ -192,46 +189,17 @@ type addingFetcher struct { addedRefs []*asserts.Ref } -func makeFetcher(sto Store, db *asserts.Database) *addingFetcher { +func makeFetcher(sto Store, dlOpts *DownloadOptions, db *asserts.Database) *addingFetcher { var f addingFetcher - retrieve := func(ref *asserts.Ref) (asserts.Assertion, error) { - return sto.Assertion(ref.Type, ref.PrimaryKey, nil) - } save := func(a asserts.Assertion) error { - // for checking - err := db.Add(a) - if err != nil { - if _, ok := err.(*asserts.RevisionError); ok { - return nil - } - return fmt.Errorf("cannot add assertion %v: %v", a.Ref(), err) - } f.addedRefs = append(f.addedRefs, a.Ref()) return nil } - f.Fetcher = asserts.NewFetcher(db, retrieve, save) + f.Fetcher = StoreAssertionFetcher(sto, dlOpts, db, save) return &f } -func fetchSnapAssertions(fn string, info *snap.Info, f *addingFetcher, db asserts.RODatabase) error { - // TODO: share some of this code - sha3_384, size, err := asserts.SnapFileSHA3_384(fn) - if err != nil { - return err - } - ref := &asserts.Ref{ - Type: asserts.SnapRevisionType, - PrimaryKey: []string{sha3_384}, - } - if err := f.Fetch(ref); err != nil { - return fmt.Errorf("cannot fetch assertion %v: %s", ref, err) - } - - // cross checks - return snapasserts.CrossCheck(info.Name(), sha3_384, size, &info.SideInfo, db) -} - // one and only core snap for now const defaultCore = "ubuntu-core" @@ -250,14 +218,13 @@ func bootstrapToRootDir(sto Store, model *asserts.Model, opts *Options, local *l // TODO: developer database in home or use snapd (but need // a bit more API there, potential issues when crossing stores/series) db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{ - KeypairManager: asserts.NewMemoryKeypairManager(), - Backstore: asserts.NewMemoryBackstore(), - Trusted: sysdb.Trusted(), + Backstore: asserts.NewMemoryBackstore(), + Trusted: sysdb.Trusted(), }) if err != nil { return err } - f := makeFetcher(sto, db) + f := makeFetcher(sto, &DownloadOptions{}, db) if err := f.Save(model); err != nil { if os.Getenv("UBUNTU_IMAGE_SKIP_COPY_UNVERIFIED_MODEL") == "" { @@ -275,9 +242,10 @@ func bootstrapToRootDir(sto Store, model *asserts.Model, opts *Options, local *l snapSeedDir := filepath.Join(dirs.SnapSeedDir, "snaps") assertSeedDir := filepath.Join(dirs.SnapSeedDir, "assertions") - dlOpts := &downloadOptions{ + dlOpts := &DownloadOptions{ TargetDir: snapSeedDir, Channel: opts.Channel, + DevMode: false, // XXX: should this be true? } snaps := []string{} @@ -323,7 +291,7 @@ func bootstrapToRootDir(sto Store, model *asserts.Model, opts *Options, local *l // TODO: support somehow including available assertions // also for local snaps if info.SnapID != "" { - err = fetchSnapAssertions(fn, info, f, db) + err = FetchSnapAssertions(fn, info, f.Fetcher, db) if err != nil { return err } @@ -453,11 +421,6 @@ func copyLocalSnapFile(snapPath, targetDir string, info *snap.Info) (dstPath str return dst, osutil.CopyFile(snapPath, dst, 0) } -type downloadOptions struct { - TargetDir string - Channel string -} - func makeStore(model *asserts.Model) Store { cfg := store.DefaultConfig() cfg.Architecture = model.Architecture() @@ -465,44 +428,3 @@ func makeStore(model *asserts.Model) Store { cfg.StoreID = model.Store() return store.New(cfg, nil) } - -type Store interface { - Snap(name, channel string, devmode bool, revision snap.Revision, user *auth.UserState) (*snap.Info, error) - Download(name string, downloadInfo *snap.DownloadInfo, pbar progress.Meter, user *auth.UserState) (path string, err error) - - Assertion(assertType *asserts.AssertionType, primaryKey []string, user *auth.UserState) (asserts.Assertion, error) -} - -func downloadSnapWithSideInfo(sto Store, name string, opts *downloadOptions) (targetPath string, info *snap.Info, err error) { - if opts == nil { - opts = &downloadOptions{} - } - - targetDir := opts.TargetDir - if targetDir == "" { - pwd, err := os.Getwd() - if err != nil { - return "", nil, err - } - targetDir = pwd - } - - snap, err := sto.Snap(name, opts.Channel, false, snap.R(0), nil) - if err != nil { - return "", nil, fmt.Errorf("cannot find snap %q: %s", name, err) - } - pb := progress.NewTextProgress() - tmpName, err := sto.Download(name, &snap.DownloadInfo, pb, nil) - if err != nil { - return "", nil, err - } - defer os.Remove(tmpName) - - baseName := filepath.Base(snap.MountFile()) - targetPath = filepath.Join(targetDir, baseName) - if err := osutil.CopyFile(tmpName, targetPath, 0); err != nil { - return "", nil, err - } - - return targetPath, snap, nil -} diff --git a/interfaces/builtin/opengl.go b/interfaces/builtin/opengl.go index 577b7e3daf..83a81f2e8a 100644 --- a/interfaces/builtin/opengl.go +++ b/interfaces/builtin/opengl.go @@ -28,6 +28,7 @@ const openglConnectedPlugAppArmor = ` # Usage: reserved # specific gl libs + /var/lib/snapd/lib/gl/ r, /var/lib/snapd/lib/gl/** rm, /dev/dri/card0 rw, diff --git a/interfaces/builtin/serial_port.go b/interfaces/builtin/serial_port.go index 379acbf3f6..0c8a535a89 100644 --- a/interfaces/builtin/serial_port.go +++ b/interfaces/builtin/serial_port.go @@ -50,12 +50,6 @@ var serialDeviceNodePattern = regexp.MustCompile("^/dev/tty[A-Z]{1,3}[0-9]{1,3}$ // are also specified var serialUdevSymlinkPattern = regexp.MustCompile("^/dev/serial-port-[a-z0-9]+$") -// Strings used to build up the udev snippet -const udevHeader string = `IMPORT{builtin}="usb_id"` -const udevDevicePrefix string = `SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="%04x", ATTRS{idProduct}=="%04x"` -const udevSymlinkSuffix string = `, SYMLINK+="%s"` -const udevTagSuffix string = `, TAG+="%s"` - // SanitizeSlot checks validity of the defined slot func (iface *SerialPortInterface) SanitizeSlot(slot *interfaces.Slot) error { // Check slot is of right type @@ -134,12 +128,7 @@ func (iface *SerialPortInterface) PermanentSlotSnippet(slot *interfaces.Slot, se if !ok || path == "" { return nil, nil } - var udevSnippet bytes.Buffer - udevSnippet.WriteString(udevHeader + "\n") - udevSnippet.WriteString(fmt.Sprintf(udevDevicePrefix, usbVendor, usbProduct)) - udevSnippet.WriteString(fmt.Sprintf(udevSymlinkSuffix, strings.TrimPrefix(path, "/dev/"))) - udevSnippet.WriteString("\n") - return udevSnippet.Bytes(), nil + return udevUsbDeviceSnippet("tty", usbVendor, usbProduct, "SYMLINK", strings.TrimPrefix(path, "/dev/")), nil case interfaces.SecurityAppArmor, interfaces.SecuritySecComp, interfaces.SecurityDBus, interfaces.SecurityMount: return nil, nil default: @@ -194,11 +183,9 @@ func (iface *SerialPortInterface) ConnectedPlugSnippet(plug *interfaces.Plug, sl return nil, nil } var udevSnippet bytes.Buffer - udevSnippet.WriteString(udevHeader + "\n") for appName := range plug.Apps { - udevSnippet.WriteString(fmt.Sprintf(udevDevicePrefix, usbVendor, usbProduct)) tag := fmt.Sprintf("snap_%s_%s", plug.Snap.Name(), appName) - udevSnippet.WriteString(fmt.Sprintf(udevTagSuffix, tag) + "\n") + udevSnippet.Write(udevUsbDeviceSnippet("tty", usbVendor, usbProduct, "TAG", tag)) } return udevSnippet.Bytes(), nil case interfaces.SecuritySecComp, interfaces.SecurityDBus, interfaces.SecurityMount: diff --git a/logger/logger.go b/logger/logger.go index 2027c7a879..6e5fcbd1a9 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -134,7 +134,7 @@ func NewConsoleLog(w io.Writer, flag int) (*ConsoleLog, error) { sys, err := newSyslog() if err != nil { - clog.Output(3, "WARNING: can not create syslog logger") + clog.Output(3, "WARNING: cannot create syslog logger") sys = log.New(ioutil.Discard, "", flag) } diff --git a/logger/logger_test.go b/logger/logger_test.go index 2f20fe2bd4..2189442343 100644 --- a/logger/logger_test.go +++ b/logger/logger_test.go @@ -140,7 +140,7 @@ func (s *LogSuite) TestSyslogFails(c *C) { // ensure a warning is displayed l, err := NewConsoleLog(&logbuf, DefaultFlags) c.Assert(err, IsNil) - c.Check(logbuf.String(), Matches, `(?m).*:\d+: WARNING: can not create syslog logger`) + c.Check(logbuf.String(), Matches, `(?m).*:\d+: WARNING: cannot create syslog logger`) // ensure that even without a syslog the console log works and we // do not crash diff --git a/mkversion.sh b/mkversion.sh index 466511c4b2..0652ea56de 100755 --- a/mkversion.sh +++ b/mkversion.sh @@ -23,9 +23,10 @@ fi echo "*** Setting version to '$v' from $o." >&2 cat <<EOF > version_generated.go -// generated by mkversion.sh; do not edit package cmd +// generated by mkversion.sh; do not edit + func init() { Version = "$v" } diff --git a/overlord/boot/booted.go b/overlord/boot/booted.go index c2f861fccf..a813faec4d 100644 --- a/overlord/boot/booted.go +++ b/overlord/boot/booted.go @@ -94,7 +94,7 @@ func UpdateRevisions(ovld *overlord.Overlord) error { for snapName, snapState := range installed { if name == snapName { if rev != snapState.Current { - ts, err := snapstate.RevertToRevision(st, name, rev) + ts, err := snapstate.RevertToRevision(st, name, rev, snapstate.Flags(0)) if err != nil { return err } diff --git a/overlord/managers_test.go b/overlord/managers_test.go index 7429291944..a82f7a715b 100644 --- a/overlord/managers_test.go +++ b/overlord/managers_test.go @@ -668,7 +668,7 @@ apps: c.Assert(err, ErrorMatches, ".*no such file.*") // now do the revert - ts, err := snapstate.Revert(st, "foo") + ts, err := snapstate.Revert(st, "foo", snapstate.Flags(0)) c.Assert(err, IsNil) chg := st.NewChange("revert-snap", "...") chg.AddAll(ts) diff --git a/overlord/snapstate/snapmgr_test.go b/overlord/snapstate/snapmgr_test.go index 9b01c3dc56..48b79a424c 100644 --- a/overlord/snapstate/snapmgr_test.go +++ b/overlord/snapstate/snapmgr_test.go @@ -174,7 +174,7 @@ func (s *snapmgrTestSuite) TestRevertTasks(c *C) { Current: snap.R(11), }) - ts, err := snapstate.Revert(s.state, "some-snap") + ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags(0)) c.Assert(err, IsNil) i := 0 @@ -298,7 +298,7 @@ func (s *snapmgrTestSuite) TestRevertCreatesNoGCTasks(c *C) { Current: snap.R(2), }) - ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R(4)) + ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R(4), snapstate.Flags(0)) c.Assert(err, IsNil) // ensure that we do not run any form of garbage-collection @@ -2131,7 +2131,7 @@ func (s *snapmgrTestSuite) TestRevertNoRevertAgain(c *C) { Current: snap.R(7), }) - ts, err := snapstate.Revert(s.state, "some-snap") + ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags(0)) c.Assert(err, ErrorMatches, "no revision to revert to") c.Assert(ts, IsNil) } @@ -2151,7 +2151,7 @@ func (s *snapmgrTestSuite) TestRevertNothingToRevertTo(c *C) { Current: si.Revision, }) - ts, err := snapstate.Revert(s.state, "some-snap") + ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags(0)) c.Assert(err, ErrorMatches, "no revision to revert to") c.Assert(ts, IsNil) } @@ -2175,7 +2175,7 @@ func (s *snapmgrTestSuite) TestRevertToRevisionNoValidVersion(c *C) { Current: snap.R(77), }) - ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R("99")) + ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R("99"), snapstate.Flags(0)) c.Assert(err, ErrorMatches, `cannot find revision 99 for snap "some-snap"`) c.Assert(ts, IsNil) } @@ -2199,7 +2199,7 @@ func (s *snapmgrTestSuite) TestRevertToRevisionAlreadyCurrent(c *C) { Current: snap.R(77), }) - ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R("77")) + ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R("77"), snapstate.Flags(0)) c.Assert(err, ErrorMatches, `already on requested revision`) c.Assert(ts, IsNil) } @@ -2224,7 +2224,7 @@ func (s *snapmgrTestSuite) TestRevertRunThrough(c *C) { }) chg := s.state.NewChange("revert", "revert a snap backwards") - ts, err := snapstate.Revert(s.state, "some-snap") + ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags(0)) c.Assert(err, IsNil) chg.AddAll(ts) @@ -2306,7 +2306,7 @@ func (s *snapmgrTestSuite) TestRevertWithLocalRevisionRunThrough(c *C) { }) chg := s.state.NewChange("revert", "revert a snap backwards") - ts, err := snapstate.Revert(s.state, "some-snap") + ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags(0)) c.Assert(err, IsNil) chg.AddAll(ts) @@ -2346,7 +2346,7 @@ func (s *snapmgrTestSuite) TestRevertToRevisionNewVersion(c *C) { }) chg := s.state.NewChange("revert", "revert a snap forward") - ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R(7)) + ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R(7), snapstate.Flags(0)) c.Assert(err, IsNil) chg.AddAll(ts) @@ -2419,7 +2419,7 @@ func (s *snapmgrTestSuite) TestRevertTotalUndoRunThrough(c *C) { }) chg := s.state.NewChange("revert", "revert a snap") - ts, err := snapstate.Revert(s.state, "some-snap") + ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags(0)) c.Assert(err, IsNil) chg.AddAll(ts) @@ -2519,7 +2519,7 @@ func (s *snapmgrTestSuite) TestRevertUndoRunThrough(c *C) { }) chg := s.state.NewChange("revert", "install a revert") - ts, err := snapstate.Revert(s.state, "some-snap") + ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags(0)) c.Assert(err, IsNil) chg.AddAll(ts) diff --git a/overlord/snapstate/snapstate.go b/overlord/snapstate/snapstate.go index 440be2d736..8131fc5f60 100644 --- a/overlord/snapstate/snapstate.go +++ b/overlord/snapstate/snapstate.go @@ -671,7 +671,7 @@ func Remove(s *state.State, name string, revision snap.Revision) (*state.TaskSet // Revert returns a set of tasks for reverting to the pervious version of the snap. // Note that the state must be locked by the caller. -func Revert(s *state.State, name string) (*state.TaskSet, error) { +func Revert(s *state.State, name string, flags Flags) (*state.TaskSet, error) { var snapst SnapState err := Get(s, name, &snapst) if err != nil && err != state.ErrNoState { @@ -683,10 +683,10 @@ func Revert(s *state.State, name string) (*state.TaskSet, error) { return nil, fmt.Errorf("no revision to revert to") } - return RevertToRevision(s, name, pi.Revision) + return RevertToRevision(s, name, pi.Revision, flags) } -func RevertToRevision(s *state.State, name string, rev snap.Revision) (*state.TaskSet, error) { +func RevertToRevision(s *state.State, name string, rev snap.Revision, flags Flags) (*state.TaskSet, error) { var snapst SnapState err := Get(s, name, &snapst) if err != nil && err != state.ErrNoState { @@ -706,6 +706,7 @@ func RevertToRevision(s *state.State, name string, rev snap.Revision) (*state.Ta } ss := &SnapSetup{ SideInfo: snapst.Sequence[i], + Flags: SnapSetupFlags(flags), } return doInstall(s, &snapst, ss) } diff --git a/snap/snaptest/build.go b/snap/snaptest/build.go index c4c6155ca9..05aafa19a5 100644 --- a/snap/snaptest/build.go +++ b/snap/snaptest/build.go @@ -197,7 +197,7 @@ func copyToBuildDir(sourceDir, buildDir string) error { // fail if its unsupported if !info.Mode().IsRegular() { - return fmt.Errorf("can not handle type of file %s", path) + return fmt.Errorf("cannot handle type of file %s", path) } // it's a file. Maybe we can link it? diff --git a/snap/snaptest/build_test.go b/snap/snaptest/build_test.go index 3ab97b13c5..52883be108 100644 --- a/snap/snaptest/build_test.go +++ b/snap/snaptest/build_test.go @@ -215,7 +215,7 @@ version: 1.0.1 c.Assert(err, IsNil) _, err = snaptest.BuildSquashfsSnap(sourceDir, "") - c.Assert(err, ErrorMatches, "can not handle type of file .*") + c.Assert(err, ErrorMatches, "cannot handle type of file .*") } func (s *BuildTestSuite) TestBuildSquashfsSimple(c *C) { diff --git a/store/store.go b/store/store.go index 38fc3ab094..ce36a5a70d 100644 --- a/store/store.go +++ b/store/store.go @@ -1316,6 +1316,10 @@ type PaymentInformation struct { // PaymentMethods gets a list of the individual payment methods the user has registerd against their Ubuntu One account func (s *Store) PaymentMethods(user *auth.UserState) (*PaymentInformation, error) { + if user == nil { + return nil, ErrInvalidCredentials + } + reqOptions := &requestOptions{ Method: "GET", URL: s.paymentMethodsURI, @@ -1360,6 +1364,8 @@ func (s *Store) PaymentMethods(user *auth.UserState) (*PaymentInformation, error } return paymentMethods, nil + case http.StatusUnauthorized: + return nil, ErrInvalidCredentials default: var errorInfo buyError dec := json.NewDecoder(resp.Body) diff --git a/store/store_test.go b/store/store_test.go index b4b39f09b2..95d9453104 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -3131,6 +3131,50 @@ func (t *remoteRepoTestSuite) TestUbuntuStorePaymentMethods(c *C) { c.Check(purchaseServerGetCalled, Equals, 1) } +func (t *remoteRepoTestSuite) TestUbuntuStorePaymentMethodsNoAuth(c *C) { + authContext := &testAuthContext{c: c, device: t.device, user: t.user} + cfg := Config{} + repo := New(&cfg, authContext) + c.Assert(repo, NotNil) + + result, err := repo.PaymentMethods(nil) + c.Assert(result, IsNil) + c.Assert(err, NotNil) + c.Check(err.Error(), Equals, "invalid credentials") +} + +func (t *remoteRepoTestSuite) TestUbuntuStorePaymentMethodsHandles401(c *C) { + purchaseServerGetCalled := 0 + mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case "GET": + w.WriteHeader(http.StatusUnauthorized) + io.WriteString(w, "Authorization Required") + purchaseServerGetCalled++ + default: + c.Error("Unexpected request method: ", r.Method) + } + })) + + c.Assert(mockPurchasesServer, NotNil) + defer mockPurchasesServer.Close() + + paymentMethodsURI, err := url.Parse(mockPurchasesServer.URL + "/api/2.0/click/paymentmethods/") + c.Assert(err, IsNil) + + authContext := &testAuthContext{c: c, device: t.device, user: t.user} + cfg := Config{ + PaymentMethodsURI: paymentMethodsURI, + } + repo := New(&cfg, authContext) + c.Assert(repo, NotNil) + + result, err := repo.PaymentMethods(t.user) + c.Assert(result, IsNil) + c.Assert(err, NotNil) + c.Check(err.Error(), Equals, "invalid credentials") +} + func (t *remoteRepoTestSuite) TestUbuntuStorePaymentMethodsRefreshesAuth(c *C) { refresh, err := makeTestRefreshDischargeResponse() c.Assert(err, IsNil) diff --git a/tests/lib/fakestore/store/store.go b/tests/lib/fakestore/store/store.go index 7109633ed7..a92c5335be 100644 --- a/tests/lib/fakestore/store/store.go +++ b/tests/lib/fakestore/store/store.go @@ -164,7 +164,7 @@ var errInfo = errors.New("cannot get info") func snapEssentialInfo(w http.ResponseWriter, fn, snapID string, bs asserts.Backstore) (*essentialInfo, error) { snapFile, err := snap.Open(fn) if err != nil { - http.Error(w, fmt.Sprintf("can not read: %v: %v", fn, err), http.StatusBadRequest) + http.Error(w, fmt.Sprintf("cannot read: %v: %v", fn, err), http.StatusBadRequest) return nil, errInfo } @@ -282,7 +282,7 @@ func (s *Store) detailsEndpoint(w http.ResponseWriter, req *http.Request) { // should look nice out, err := json.MarshalIndent(details, "", " ") if err != nil { - http.Error(w, fmt.Sprintf("can't marshal: %v: %v", details, err), http.StatusBadRequest) + http.Error(w, fmt.Sprintf("cannot marshal: %v: %v", details, err), http.StatusBadRequest) return } w.Write(out) @@ -343,7 +343,7 @@ func (s *Store) bulkEndpoint(w http.ResponseWriter, req *http.Request) { decoder := json.NewDecoder(req.Body) if err := decoder.Decode(&pkgs); err != nil { - http.Error(w, fmt.Sprintf("can't decode request body: %v", err), http.StatusBadRequest) + http.Error(w, fmt.Sprintf("cannot decode request body: %v", err), http.StatusBadRequest) return } diff --git a/tests/lib/gadget.sh b/tests/lib/gadget.sh new file mode 100644 index 0000000000..6e8a0b04d5 --- /dev/null +++ b/tests/lib/gadget.sh @@ -0,0 +1,4 @@ +#!/bin/sh +get_gadget_name(){ + snap list | grep '^pc \|^pi2 \|^pi3 \|^dragonboard ' | head -n 1 | cut -d ' ' -f 1 +} diff --git a/tests/lib/prepare.sh b/tests/lib/prepare.sh index e8c789d8cf..75ca14518c 100755 --- a/tests/lib/prepare.sh +++ b/tests/lib/prepare.sh @@ -32,7 +32,7 @@ setup_reflash_magic() { # install the stuff we need apt install -y kpartx busybox-static apt install -y ${SPREAD_PATH}/../snapd_*.deb - + snap install --edge ubuntu-core # install ubuntu-image @@ -61,7 +61,7 @@ setup_reflash_magic() { if [ -e /.spread.yaml ]; then cp -av /.spread.yaml $UNPACKD fi - + # we need the test user in the image chroot $UNPACKD adduser --quiet --no-create-home --disabled-password --gecos '' test @@ -72,7 +72,7 @@ setup_reflash_magic() { # the image # unpack our freshly build snapd into the new core snap dpkg-deb -x ${SPREAD_PATH}/../snapd_*.deb $UNPACKD - + # build new core snap for the image snapbuild $UNPACKD $IMAGE_HOME @@ -146,7 +146,7 @@ EOF umount /mnt kpartx -d $IMAGE_HOME/$IMAGE - + # the reflash magic # FIXME: ideally in initrd, but this is good enough for now cat > $IMAGE_HOME/reflash.sh << EOF @@ -192,7 +192,9 @@ prepare_all_snap() { fi echo "Ensure fundamental snaps are still present" - for name in pc pc-kernel ubuntu-core; do + . $TESTSLIB/gadget.sh + gadget_name=$(get_gadget_name) + for name in $gadget_name ${gadget_name}-kernel ubuntu-core; do if ! snap list | grep $name; then echo "Not all fundamental snaps are available, all-snap image not valid" echo "Currently installed snaps" @@ -202,6 +204,5 @@ prepare_all_snap() { done echo "Kernel has a store revision" - snap list|grep ^pc-kernel|grep -E " [0-9]+\s+canonical" + snap list|grep ^${gadget_name}-kernel|grep -E " [0-9]+\s+canonical" } - diff --git a/tests/lib/reset.sh b/tests/lib/reset.sh index 4761f5a4f1..30e9ca991f 100755 --- a/tests/lib/reset.sh +++ b/tests/lib/reset.sh @@ -13,9 +13,9 @@ reset_classic() { ls -lR /snap/* /var/snap/* exit 1 fi - + rm -f /tmp/ubuntu-core* - + if [ "$1" = "--reuse-core" ]; then $(cd / && tar xzf $SPREAD_PATH/snapd-state.tar.gz) mounts="$(systemctl list-unit-files | grep '^snap[-.].*\.mount' | cut -f1 -d ' ')" @@ -32,8 +32,10 @@ reset_classic() { } reset_all_snap() { + . $TESTSLIB/gadget.sh + gadget_name=$(get_gadget_name) for snap in $(ls /snap); do - if [ "$snap" = "bin" ] || [ "$snap" = "pc" ] || [ "$snap" = "pc-kernel" ] || [ "$snap" = "ubuntu-core" ]; then + if [ "$snap" = "bin" ] || [ "$snap" = "$gadget_name" ] || [ "$snap" = "${gadget_name}-kernel" ] || [ "$snap" = "ubuntu-core" ]; then continue fi snap remove $snap diff --git a/tests/main/revert-devmode/task.yaml b/tests/main/revert-devmode/task.yaml new file mode 100644 index 0000000000..b63b10587f --- /dev/null +++ b/tests/main/revert-devmode/task.yaml @@ -0,0 +1,52 @@ +summary: Check that revert of a snap in devmode restores devmode + +environment: + STORE_TYPE/fake: fake + STORE_TYPE/staging: staging + STORE_TYPE/production: production + BLOB_DIR: $(pwd)/fake-store-blobdir + +prepare: | + if [ "$STORE_TYPE" = "fake" ]; then + echo "Given a snap is installed" + snap install --devmode test-snapd-tools + fi + + . $TESTSLIB/store.sh + setup_store $STORE_TYPE $BLOB_DIR + + if [ "$STORE_TYPE" = "fake" ]; then + echo "And a new version of that snap put in the controlled store" + fakestore -dir $BLOB_DIR -make-refreshable test-snapd-tools + fi + +restore: | + . $TESTSLIB/store.sh + teardown_store $STORE_TYPE $BLOB_DIR + +execute: | + echo "When a refresh is made" + snap refresh --devmode --edge test-snapd-tools + + echo "Then the new version is installed" + snap list | grep -Pq "test-snapd-tools +\d+\.\d+\+fake1" + LATEST=$(readlink /snap/test-snapd-tools/current) + + echo "When a revert is made without --devmode flag" + snap revert test-snapd-tools + + echo "Then the old version is active" + snap list | grep -Pq "test-snapd-tools +\d+\.\d+ " + + echo "And the snap runs confined" + snap list|grep test-snapd-tools|grep -q "-" + + echo "When the latest revision is installed again" + snap remove --revision=$LATEST test-snapd-tools + snap refresh --devmode --edge test-snapd-tools + + echo "And revert is made with --devmode flag" + snap revert --devmode test-snapd-tools + + echo "Then snap uses devmode" + snap list|grep test-snapd-tools|grep -q devmode diff --git a/tests/main/revert/task.yaml b/tests/main/revert/task.yaml index 9ecced842e..4d4f6c8166 100644 --- a/tests/main/revert/task.yaml +++ b/tests/main/revert/task.yaml @@ -41,6 +41,9 @@ execute: | ls /snap/test-snapd-tools | grep -q current ls /var/snap/test-snapd-tools | grep -q current + echo "And the snap runs confined" + snap list|grep test-snapd-tools|grep -q "-" + echo "And a new revert fails" if snap revert test-snapd-tools; then echo "A revert on an already reverted snap should fail" diff --git a/tests/main/searching/task.yaml b/tests/main/searching/task.yaml index c29ad02dad..c7ec9ccda3 100644 --- a/tests/main/searching/task.yaml +++ b/tests/main/searching/task.yaml @@ -2,7 +2,7 @@ summary: Check snap search execute: | echo "Try to list all snaps" - ( snap find 2>&1 || echo FAILED ) | grep -Pzq "(?ms)empty query.*FAILED" + ( snap find 2>&1 || echo FAILED ) | grep -Pzq "(?ms)you need to specify a query.*FAILED" echo "Exact matches" for snapName in test-snapd-tools xkcd-webserver diff --git a/tests/main/snap-download/task.yaml b/tests/main/snap-download/task.yaml index 4f12db396b..147cc88a6b 100644 --- a/tests/main/snap-download/task.yaml +++ b/tests/main/snap-download/task.yaml @@ -2,14 +2,24 @@ summary: Check that snap download works restore: | rm -f *.snap execute: | + verify_asserts() { + fn="$1" + grep "type: account-key" "$fn" + grep "type: snap-declaration" "$fn" + grep "type: snap-revision" "$fn" + } echo "Snap download can download snaps" snap download hello-world ls hello-world_*.snap + verify_asserts hello-world_*.snap.assertions echo "Snap download understand --edge" snap download --edge test-snapd-tools ls test-snapd-tools_*.snap + verify_asserts test-snapd-tools_*.snap.assertions echo "Snap download downloads devmode snaps" snap download --beta classic ls classic_*.snap + verify_asserts classic_*.snap.assertions + |
