-
Notifications
You must be signed in to change notification settings - Fork 562
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
snap: implement new snap reboot
command
#9021
Changes from 3 commits
4469021
b0e0833
1a5f7fe
2e4100f
bab0ca2
83ea27a
c98ad20
105ede5
4485991
5a34eff
5a99a04
c876309
4509e86
9da1973
688b602
421e70a
a37c40d
4a9c98e
e88e6d5
29d87b0
5b57db1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
// -*- Mode: Go; indent-tabs-mode: t -*- | ||
|
||
/* | ||
* Copyright (C) 2020 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 main | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/jessevdk/go-flags" | ||
|
||
"github.com/snapcore/snapd/client" | ||
"github.com/snapcore/snapd/i18n" | ||
) | ||
|
||
type cmdReboot struct { | ||
clientMixin | ||
Positional struct { | ||
Label string | ||
} `positional-args:"true" required:"true"` | ||
|
||
RunMode bool `long:"run"` | ||
InstallMode bool `long:"install"` | ||
RecoverMode bool `long:"recover"` | ||
} | ||
|
||
var shortRebootHelp = i18n.G("Reboot into selected system and mode") | ||
var longRebootHelp = i18n.G(` | ||
The reboot command reboots the system into a particular mode of the selected | ||
recovery system. | ||
`) | ||
|
||
func init() { | ||
addCommand("reboot", shortRebootHelp, longRebootHelp, func() flags.Commander { | ||
return &cmdReboot{} | ||
}, map[string]string{ | ||
// TRANSLATORS: This should not start with a lowercase letter. | ||
"run": i18n.G("Boot into run mode"), | ||
// TRANSLATORS: This should not start with a lowercase letter. | ||
"install": i18n.G("Boot into install mode"), | ||
// TRANSLATORS: This should not start with a lowercase letter. | ||
"recover": i18n.G("Boot into recover mode"), | ||
}, []argDesc{ | ||
{ | ||
// TRANSLATORS: This needs to begin with < and end with > | ||
name: i18n.G("<label>"), | ||
// TRANSLATORS: This should not start with a lowercase letter. | ||
desc: i18n.G("The recovery system label"), | ||
}, | ||
}) | ||
} | ||
|
||
func (x *cmdReboot) modeFromCommandline() (*client.SystemAction, error) { | ||
var action client.SystemAction | ||
for _, arg := range []struct { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice. |
||
enabled bool | ||
mode string | ||
}{ | ||
{x.RunMode, "run"}, | ||
{x.RecoverMode, "recover"}, | ||
{x.InstallMode, "install"}, | ||
} { | ||
if !arg.enabled { | ||
continue | ||
} | ||
if action.Mode != "" { | ||
return nil, fmt.Errorf("Please specify a single mode") | ||
} | ||
action.Mode = arg.mode | ||
} | ||
// XXX: should we have a default here? | ||
mvo5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if action.Mode == "" { | ||
return nil, fmt.Errorf("Please specify a mode, see --help") | ||
} | ||
|
||
return &action, nil | ||
} | ||
|
||
func (x *cmdReboot) Execute(args []string) error { | ||
if len(args) > 0 { | ||
return ErrExtraArgs | ||
} | ||
|
||
action, err := x.modeFromCommandline() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if err := x.client.DoSystemAction(x.Positional.Label, action); err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For run label can only be the current system, so it could also be omitted. Same for recover. In general the label can be left out and default to current. |
||
return fmt.Errorf("cannot reboot into system %q: %v", x.Positional.Label, err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we maybe want a client.RebootSystem(label, mode) method, that should map to a different variant of the API that has the |
||
} | ||
|
||
fmt.Fprintf(Stdout, "Reboot into %q with mode %q scheduled.\n", x.Positional.Label, action.Mode) | ||
mvo5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
// -*- Mode: Go; indent-tabs-mode: t -*- | ||
|
||
/* | ||
* Copyright (C) 2020 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 main_test | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
"strings" | ||
|
||
. "gopkg.in/check.v1" | ||
|
||
snap "github.com/snapcore/snapd/cmd/snap" | ||
) | ||
|
||
func (s *SnapSuite) TestRebootHelp(c *C) { | ||
msg := `Usage: | ||
snap.test reboot [reboot-OPTIONS] <label> | ||
|
||
The reboot command reboots the system into a particular mode of the selected | ||
recovery system. | ||
|
||
[reboot command options] | ||
--run Boot into run mode | ||
--install Boot into install mode | ||
--recover Boot into recover mode | ||
|
||
[reboot command arguments] | ||
<label>: The recovery system label | ||
` | ||
s.testSubCommandHelp(c, "reboot", msg) | ||
} | ||
|
||
func (s *SnapSuite) TestRebootHappy(c *C) { | ||
n := 0 | ||
s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { | ||
switch n { | ||
case 0: | ||
c.Check(r.Method, Equals, "POST") | ||
c.Check(r.URL.Path, Equals, "/v2/systems/20200101") | ||
c.Check(r.URL.RawQuery, Equals, "") | ||
fmt.Fprintln(w, `{"type": "sync", "result": {}}`) | ||
default: | ||
c.Fatalf("expected to get 1 requests, now on %d", n+1) | ||
} | ||
|
||
n++ | ||
}) | ||
rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"reboot", "--recover", "20200101"}) | ||
c.Assert(err, IsNil) | ||
c.Assert(rest, DeepEquals, []string{}) | ||
c.Check(s.Stdout(), Equals, `Reboot into "20200101" with mode "recover" scheduled. | ||
`) | ||
c.Check(s.Stderr(), Equals, "") | ||
} | ||
|
||
func (s *SnapSuite) TestRebootUnhappy(c *C) { | ||
s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { | ||
c.Fatalf("server should not be hit in this test") | ||
}) | ||
|
||
var tc = []struct { | ||
args []string | ||
errStr string | ||
}{ | ||
{ | ||
args: []string{"reboot", "20200101"}, | ||
errStr: "Please specify a mode, see --help", | ||
}, | ||
{ | ||
args: []string{"reboot", "--run", "--recover", "20200101"}, | ||
errStr: "Please specify a single mode", | ||
}, | ||
{ | ||
args: []string{"reboot", "--unknown-mode", "20200101"}, | ||
errStr: "unknown flag `unknown-mode'", | ||
}, | ||
} | ||
|
||
for _, t := range tc { | ||
_, err := snap.Parser(snap.Client()).ParseArgs(t.args) | ||
c.Check(err, ErrorMatches, t.errStr, Commentf(strings.Join(t.args, " "))) | ||
} | ||
} | ||
|
||
func (s *SnapSuite) TestRebootAPIFail(c *C) { | ||
n := 0 | ||
s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { | ||
switch n { | ||
case 0: | ||
c.Check(r.Method, Equals, "POST") | ||
c.Check(r.URL.Path, Equals, "/v2/systems/20200101") | ||
c.Check(r.URL.RawQuery, Equals, "") | ||
w.WriteHeader(404) | ||
fmt.Fprintln(w, `{"type": "error", "status-code":404, "result": {"message":"requested system does not exist"}}`) | ||
default: | ||
c.Fatalf("expected to get 1 requests, now on %d", n+1) | ||
} | ||
|
||
n++ | ||
}) | ||
_, err := snap.Parser(snap.Client()).ParseArgs([]string{"reboot", "--recover", "20200101"}) | ||
c.Assert(err, ErrorMatches, `cannot reboot into system "20200101": cannot request system action: requested system does not exist`) | ||
c.Check(s.Stdout(), Equals, "") | ||
c.Check(s.Stderr(), Equals, "") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should also explain what happens if mode or label are omitted