Skip to content

Commit

Permalink
add new snap switch command
Browse files Browse the repository at this point in the history
  • Loading branch information
mvo5 committed Feb 8, 2017
1 parent dd3adef commit 856b7db
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 1 deletion.
5 changes: 5 additions & 0 deletions client/snap_op.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ func (client *Client) Revert(name string, options *SnapOptions) (changeID string
return client.doSnapAction("revert", name, options)
}

// Switch moves the snap to a different channel without a refresh
func (client *Client) Switch(name string, options *SnapOptions) (changeID string, err error) {
return client.doSnapAction("switch", name, options)
}

var ErrDangerousNotApplicable = fmt.Errorf("dangerous option only meaningful when installing from a local file")

func (client *Client) doSnapAction(actionName string, snapName string, options *SnapOptions) (changeID string, err error) {
Expand Down
38 changes: 38 additions & 0 deletions cmd/snap/cmd_snap_op.go
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,42 @@ func (x *cmdRevert) Execute(args []string) error {
return nil
}

var shortSwitchHelp = i18n.G("Switch snap to a different channel")
var longSwitchHelp = i18n.G(`
The switch command switch the given snap to a different channel without
actually doing a refresh.
`)

type cmdSwitch struct {
channelMixin

Positional struct {
Snap installedSnapName `positional-arg-name:"<snap>" required:"1"`
Channel string `positional-arg-name:"<channel>" required:"1"`
} `positional-args:"yes" required:"yes"`
}

func (x cmdSwitch) Execute(args []string) error {
cli := Client()
name := string(x.Positional.Snap)
channel := string(x.Positional.Channel)
opts := &client.SnapOptions{
Channel: channel,
}
changeID, err := cli.Switch(name, opts)
if err != nil {
return err
}

_, err = wait(cli, changeID)
if err != nil {
return err
}

fmt.Fprintf(Stdout, i18n.G("%s switched to %s\n"), name, channel)
return nil
}

func init() {
addCommand("remove", shortRemoveHelp, longRemoveHelp, func() flags.Commander { return &cmdRemove{} },
map[string]string{"revision": i18n.G("Remove only the given revision")}, nil)
Expand All @@ -841,4 +877,6 @@ func init() {
addCommand("revert", shortRevertHelp, longRevertHelp, func() flags.Commander { return &cmdRevert{} }, modeDescs.also(map[string]string{
"revision": "Revert to the given revision",
}), nil)
addCommand("switch", shortSwitchHelp, longSwitchHelp, func() flags.Commander { return &cmdSwitch{} }, nil, nil)

}
15 changes: 15 additions & 0 deletions daemon/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,7 @@ var (
snapstateRemoveMany = snapstate.RemoveMany
snapstateRevert = snapstate.Revert
snapstateRevertToRevision = snapstate.RevertToRevision
snapstateSwitch = snapstate.Switch

assertstateRefreshSnapDeclarations = assertstate.RefreshSnapDeclarations
)
Expand Down Expand Up @@ -1009,6 +1010,19 @@ func snapDisable(inst *snapInstruction, st *state.State) (string, []*state.TaskS
return msg, []*state.TaskSet{ts}, nil
}

func snapSwitch(inst *snapInstruction, st *state.State) (string, []*state.TaskSet, error) {
if !inst.Revision.Unset() {
return "", nil, errors.New("switch takes no revision")
}
ts, err := snapstate.Switch(st, inst.Snaps[0], inst.Channel)
if err != nil {
return "", nil, err
}

msg := fmt.Sprintf(i18n.G("Switch %q snap to %s"), inst.Snaps[0], inst.Channel)
return msg, []*state.TaskSet{ts}, nil
}

type snapActionFunc func(*snapInstruction, *state.State) (string, []*state.TaskSet, error)

var snapInstructionDispTable = map[string]snapActionFunc{
Expand All @@ -1018,6 +1032,7 @@ var snapInstructionDispTable = map[string]snapActionFunc{
"revert": snapRevert,
"enable": snapEnable,
"disable": snapDisable,
"switch": snapSwitch,
}

func (inst *snapInstruction) dispatch() snapActionFunc {
Expand Down
1 change: 1 addition & 0 deletions daemon/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,7 @@ func (s *apiSuite) TestListIncludesAll(c *check.C) {
"snapstateRefreshCandidates",
"snapstateRevert",
"snapstateRevertToRevision",
"snapstateSwitch",
"assertstateRefreshSnapDeclarations",
"unsafeReadSnapInfo",
"osutilAddUser",
Expand Down
19 changes: 18 additions & 1 deletion overlord/snapstate/snapmgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,9 @@ func Manager(st *state.State) (*SnapManager, error) {
runner.AddHandler("setup-aliases", m.doSetupAliases, m.undoSetupAliases)
runner.AddHandler("remove-aliases", m.doRemoveAliases, m.doSetupAliases)

// misc
runner.AddHandler("switch-snap", m.doSwitchSnap, nil)

// control serialisation
runner.SetBlocked(m.blockedTask)

Expand Down Expand Up @@ -994,9 +997,23 @@ func (m *SnapManager) doCopySnapData(t *state.Task, _ *tomb.Tomb) error {
return m.backend.CopySnapData(newInfo, oldInfo, pb)
}

func (m *SnapManager) doLinkSnap(t *state.Task, _ *tomb.Tomb) error {
func (m *SnapManager) doSwitchSnap(t *state.Task, _ *tomb.Tomb) error {
st := t.State()
st.Lock()
defer st.Unlock()

snapsup, snapst, err := snapSetupAndState(t)
if err != nil {
return err
}
snapst.Channel = snapsup.Channel

Set(st, snapsup.Name(), snapst)
return nil
}

func (m *SnapManager) doLinkSnap(t *state.Task, _ *tomb.Tomb) error {
st := t.State()
st.Lock()
defer st.Unlock()

Expand Down
22 changes: 22 additions & 0 deletions overlord/snapstate/snapstate.go
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,28 @@ func autoAliasesUpdate(st *state.State, names []string, updates []*snap.Info) (n
return new, mustRetire, transferTargets, nil
}

// Switch switches a snap to a new channel
func Switch(st *state.State, name, channel string) (*state.TaskSet, error) {
var snapst SnapState
err := Get(st, name, &snapst)
if err != nil && err != state.ErrNoState {
return nil, err
}
if !snapst.HasCurrent() {
return nil, fmt.Errorf("cannot find snap %q", name)
}

snapsup := &SnapSetup{
SideInfo: snapst.CurrentSideInfo(),
Channel: channel,
}

switchSnap := st.NewTask("switch-snap", fmt.Sprintf(i18n.G("Switch snap %q to %s"), snapsup.Name(), snapsup.Channel))
switchSnap.Set("snap-setup", &snapsup)

return state.NewTaskSet(switchSnap), nil
}

// Update initiates a change updating a snap.
// Note that the state must be locked by the caller.
func Update(st *state.State, name, channel string, revision snap.Revision, userID int, flags Flags) (*state.TaskSet, error) {
Expand Down
8 changes: 8 additions & 0 deletions tests/main/snap-switch/task.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
summary: Ensure that the snap switch command works

execute: |
snap install test-snapd-tools
snap info test-snapd-tools|MATCH "tracking: +stable"
snap switch test-snapd-tools edge
snap info test-snapd-tools|MATCH "tracking: +edge"

0 comments on commit 856b7db

Please sign in to comment.