github.com/rigado/snapd@v2.42.5-go-mod+incompatible/overlord/snapshotstate/snapshotstate_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2018 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package snapshotstate_test
    21  
    22  import (
    23  	"context"
    24  	"errors"
    25  	"fmt"
    26  	"os"
    27  	"os/exec"
    28  	"os/user"
    29  	"path/filepath"
    30  	"sort"
    31  	"strings"
    32  	"testing"
    33  	"time"
    34  
    35  	"gopkg.in/check.v1"
    36  
    37  	"github.com/snapcore/snapd/client"
    38  	"github.com/snapcore/snapd/dirs"
    39  	"github.com/snapcore/snapd/osutil/sys"
    40  	"github.com/snapcore/snapd/overlord"
    41  	"github.com/snapcore/snapd/overlord/configstate/config"
    42  	"github.com/snapcore/snapd/overlord/snapshotstate"
    43  	"github.com/snapcore/snapd/overlord/snapshotstate/backend"
    44  	"github.com/snapcore/snapd/overlord/snapstate"
    45  	"github.com/snapcore/snapd/overlord/snapstate/snapstatetest"
    46  	"github.com/snapcore/snapd/overlord/state"
    47  	"github.com/snapcore/snapd/release"
    48  	"github.com/snapcore/snapd/snap"
    49  	"github.com/snapcore/snapd/snap/snaptest"
    50  	"github.com/snapcore/snapd/testutil"
    51  )
    52  
    53  type snapshotSuite struct{}
    54  
    55  var _ = check.Suite(&snapshotSuite{})
    56  
    57  // tie gocheck into testing
    58  func TestSnapshot(t *testing.T) { check.TestingT(t) }
    59  
    60  func (snapshotSuite) SetUpTest(c *check.C) {
    61  	dirs.SetRootDir(c.MkDir())
    62  }
    63  
    64  func (snapshotSuite) TearDownTest(c *check.C) {
    65  	dirs.SetRootDir("/")
    66  }
    67  
    68  func (snapshotSuite) TestNewSnapshotSetID(c *check.C) {
    69  	st := state.New(nil)
    70  	st.Lock()
    71  	defer st.Unlock()
    72  	sid, err := snapshotstate.NewSnapshotSetID(st)
    73  	c.Assert(err, check.IsNil)
    74  	c.Check(sid, check.Equals, uint64(1))
    75  
    76  	sid, err = snapshotstate.NewSnapshotSetID(st)
    77  	c.Assert(err, check.IsNil)
    78  	c.Check(sid, check.Equals, uint64(2))
    79  }
    80  
    81  func (snapshotSuite) TestAllActiveSnapNames(c *check.C) {
    82  	fakeSnapstateAll := func(*state.State) (map[string]*snapstate.SnapState, error) {
    83  		return map[string]*snapstate.SnapState{
    84  			"a-snap": {Active: true},
    85  			"b-snap": {},
    86  			"c-snap": {Active: true},
    87  		}, nil
    88  	}
    89  
    90  	defer snapshotstate.MockSnapstateAll(fakeSnapstateAll)()
    91  
    92  	// loop to check sortedness
    93  	for i := 0; i < 100; i++ {
    94  		names, err := snapshotstate.AllActiveSnapNames(nil)
    95  		c.Assert(err, check.IsNil)
    96  		c.Check(names, check.DeepEquals, []string{"a-snap", "c-snap"})
    97  	}
    98  }
    99  
   100  func (snapshotSuite) TestAllActiveSnapNamesError(c *check.C) {
   101  	errBad := errors.New("bad")
   102  	fakeSnapstateAll := func(*state.State) (map[string]*snapstate.SnapState, error) {
   103  		return nil, errBad
   104  	}
   105  
   106  	defer snapshotstate.MockSnapstateAll(fakeSnapstateAll)()
   107  
   108  	names, err := snapshotstate.AllActiveSnapNames(nil)
   109  	c.Check(err, check.Equals, errBad)
   110  	c.Check(names, check.IsNil)
   111  }
   112  
   113  func (snapshotSuite) TestSnapSummariesInSnapshotSet(c *check.C) {
   114  	shotfileA, err := os.Create(filepath.Join(c.MkDir(), "foo.zip"))
   115  	c.Assert(err, check.IsNil)
   116  	defer shotfileA.Close()
   117  	shotfileB, err := os.Create(filepath.Join(c.MkDir(), "foo.zip"))
   118  	c.Assert(err, check.IsNil)
   119  	defer shotfileB.Close()
   120  
   121  	setID := uint64(42)
   122  	fakeIter := func(_ context.Context, f func(*backend.Reader) error) error {
   123  		c.Assert(f(&backend.Reader{
   124  			// wanted
   125  			Snapshot: client.Snapshot{SetID: setID, Snap: "a-snap", SnapID: "a-id", Epoch: snap.Epoch{Read: []uint32{42}, Write: []uint32{17}}},
   126  			File:     shotfileA,
   127  		}), check.IsNil)
   128  		c.Assert(f(&backend.Reader{
   129  			// not wanted (bad set id)
   130  			Snapshot: client.Snapshot{SetID: setID + 1, Snap: "a-snap", SnapID: "a-id"},
   131  			File:     shotfileA,
   132  		}), check.IsNil)
   133  		c.Assert(f(&backend.Reader{
   134  			// wanted
   135  			Snapshot: client.Snapshot{SetID: setID, Snap: "b-snap", SnapID: "b-id"},
   136  			File:     shotfileB,
   137  		}), check.IsNil)
   138  		return nil
   139  	}
   140  	defer snapshotstate.MockBackendIter(fakeIter)()
   141  
   142  	summaries, err := snapshotstate.SnapSummariesInSnapshotSet(setID, nil)
   143  	c.Assert(err, check.IsNil)
   144  	c.Assert(summaries.AsMaps(), check.DeepEquals, []map[string]string{
   145  		{"snap": "a-snap", "snapID": "a-id", "filename": shotfileA.Name(), "epoch": `{"read":[42],"write":[17]}`},
   146  		{"snap": "b-snap", "snapID": "b-id", "filename": shotfileB.Name(), "epoch": "0"},
   147  	})
   148  }
   149  
   150  func (snapshotSuite) TestSnapSummariesInSnapshotSetSnaps(c *check.C) {
   151  	shotfile, err := os.Create(filepath.Join(c.MkDir(), "foo.zip"))
   152  	c.Assert(err, check.IsNil)
   153  	defer shotfile.Close()
   154  	setID := uint64(42)
   155  	fakeIter := func(_ context.Context, f func(*backend.Reader) error) error {
   156  		c.Assert(f(&backend.Reader{
   157  			// wanted
   158  			Snapshot: client.Snapshot{SetID: setID, Snap: "a-snap", SnapID: "a-id"},
   159  			File:     shotfile,
   160  		}), check.IsNil)
   161  		c.Assert(f(&backend.Reader{
   162  			// not wanted (bad set id)
   163  			Snapshot: client.Snapshot{SetID: setID + 1, Snap: "a-snap", SnapID: "a-id"},
   164  			File:     shotfile,
   165  		}), check.IsNil)
   166  		c.Assert(f(&backend.Reader{
   167  			// not wanted (bad snap name)
   168  			Snapshot: client.Snapshot{SetID: setID, Snap: "c-snap", SnapID: "c-id"},
   169  			File:     shotfile,
   170  		}), check.IsNil)
   171  		return nil
   172  	}
   173  	defer snapshotstate.MockBackendIter(fakeIter)()
   174  
   175  	summaries, err := snapshotstate.SnapSummariesInSnapshotSet(setID, []string{"a-snap"})
   176  	c.Assert(err, check.IsNil)
   177  	c.Check(summaries.AsMaps(), check.DeepEquals, []map[string]string{
   178  		{"snap": "a-snap", "snapID": "a-id", "filename": shotfile.Name(), "epoch": "0"},
   179  	})
   180  }
   181  
   182  func (snapshotSuite) TestSnapSummariesInSnapshotSetErrors(c *check.C) {
   183  	shotfile, err := os.Create(filepath.Join(c.MkDir(), "foo.zip"))
   184  	c.Assert(err, check.IsNil)
   185  	defer shotfile.Close()
   186  	setID := uint64(42)
   187  	errBad := errors.New("bad")
   188  	fakeIter := func(_ context.Context, f func(*backend.Reader) error) error {
   189  		c.Assert(f(&backend.Reader{
   190  			// wanted
   191  			Snapshot: client.Snapshot{SetID: setID, Snap: "a-snap"},
   192  			File:     shotfile,
   193  		}), check.IsNil)
   194  
   195  		return errBad
   196  	}
   197  	defer snapshotstate.MockBackendIter(fakeIter)()
   198  
   199  	summaries, err := snapshotstate.SnapSummariesInSnapshotSet(setID, nil)
   200  	c.Assert(err, check.Equals, errBad)
   201  	c.Check(summaries, check.IsNil)
   202  }
   203  
   204  func (snapshotSuite) TestSnapSummariesInSnapshotSetNotFound(c *check.C) {
   205  	setID := uint64(42)
   206  	shotfile, err := os.Create(filepath.Join(c.MkDir(), "foo.zip"))
   207  	c.Assert(err, check.IsNil)
   208  	defer shotfile.Close()
   209  	fakeIter := func(_ context.Context, f func(*backend.Reader) error) error {
   210  		c.Assert(f(&backend.Reader{
   211  			// not wanted
   212  			Snapshot: client.Snapshot{SetID: setID - 1, Snap: "a-snap"},
   213  			File:     shotfile,
   214  		}), check.IsNil)
   215  
   216  		return nil
   217  	}
   218  	defer snapshotstate.MockBackendIter(fakeIter)()
   219  
   220  	summaries, err := snapshotstate.SnapSummariesInSnapshotSet(setID, nil)
   221  	c.Assert(err, check.Equals, client.ErrSnapshotSetNotFound)
   222  	c.Check(summaries, check.IsNil)
   223  }
   224  
   225  func (snapshotSuite) TestSnapSummariesInSnapshotSetEmptyNotFound(c *check.C) {
   226  	fakeIter := func(_ context.Context, f func(*backend.Reader) error) error { return nil }
   227  	defer snapshotstate.MockBackendIter(fakeIter)()
   228  
   229  	summaries, err := snapshotstate.SnapSummariesInSnapshotSet(42, nil)
   230  	c.Assert(err, check.Equals, client.ErrSnapshotSetNotFound)
   231  	c.Check(summaries, check.IsNil)
   232  }
   233  
   234  func (snapshotSuite) TestSnapSummariesInSnapshotSetSnapNotFound(c *check.C) {
   235  	setID := uint64(42)
   236  	shotfile, err := os.Create(filepath.Join(c.MkDir(), "foo.zip"))
   237  	c.Assert(err, check.IsNil)
   238  	defer shotfile.Close()
   239  	fakeIter := func(_ context.Context, f func(*backend.Reader) error) error {
   240  		c.Assert(f(&backend.Reader{
   241  			// not wanted
   242  			Snapshot: client.Snapshot{SetID: setID, Snap: "a-snap"},
   243  			File:     shotfile,
   244  		}), check.IsNil)
   245  
   246  		return nil
   247  	}
   248  	defer snapshotstate.MockBackendIter(fakeIter)()
   249  
   250  	summaries, err := snapshotstate.SnapSummariesInSnapshotSet(setID, []string{"b-snap"})
   251  	c.Assert(err, check.Equals, client.ErrSnapshotSnapsNotFound)
   252  	c.Check(summaries, check.IsNil)
   253  }
   254  
   255  func (snapshotSuite) TestCheckConflict(c *check.C) {
   256  	st := state.New(nil)
   257  	st.Lock()
   258  	defer st.Unlock()
   259  	chg := st.NewChange("some-change", "...")
   260  	tsk := st.NewTask("some-task", "...")
   261  	tsk.SetStatus(state.DoingStatus)
   262  	chg.AddTask(tsk)
   263  
   264  	// no snapshot state
   265  	err := snapshotstate.CheckSnapshotTaskConflict(st, 42, "some-task")
   266  	c.Assert(err, check.ErrorMatches, "internal error: task 1 .some-task. is missing snapshot information")
   267  
   268  	// wrong snapshot state
   269  	tsk.Set("snapshot-setup", "hello")
   270  	err = snapshotstate.CheckSnapshotTaskConflict(st, 42, "some-task")
   271  	c.Assert(err, check.ErrorMatches, "internal error.* could not unmarshal.*")
   272  
   273  	tsk.Set("snapshot-setup", map[string]int{"set-id": 42})
   274  
   275  	err = snapshotstate.CheckSnapshotTaskConflict(st, 42, "some-task")
   276  	c.Assert(err, check.ErrorMatches, "cannot operate on snapshot set #42 while change \"1\" is in progress")
   277  
   278  	// no change with that label
   279  	c.Assert(snapshotstate.CheckSnapshotTaskConflict(st, 42, "some-other-task"), check.IsNil)
   280  
   281  	// no change with that snapshot id
   282  	c.Assert(snapshotstate.CheckSnapshotTaskConflict(st, 43, "some-task"), check.IsNil)
   283  
   284  	// no non-ready change
   285  	tsk.SetStatus(state.DoneStatus)
   286  	c.Assert(snapshotstate.CheckSnapshotTaskConflict(st, 42, "some-task"), check.IsNil)
   287  }
   288  
   289  func (snapshotSuite) TestSaveChecksSnapnamesError(c *check.C) {
   290  	defer snapshotstate.MockSnapstateAll(func(*state.State) (map[string]*snapstate.SnapState, error) {
   291  		return nil, errors.New("bzzt")
   292  	})()
   293  
   294  	st := state.New(nil)
   295  	st.Lock()
   296  	defer st.Unlock()
   297  	_, _, _, err := snapshotstate.Save(st, nil, nil)
   298  	c.Check(err, check.ErrorMatches, "bzzt")
   299  }
   300  
   301  func (snapshotSuite) createConflictingChange(c *check.C) (st *state.State, restore func()) {
   302  	shotfile, err := os.Create(filepath.Join(c.MkDir(), "foo.zip"))
   303  	c.Assert(err, check.IsNil)
   304  	shotfile.Close()
   305  
   306  	fakeIter := func(_ context.Context, f func(*backend.Reader) error) error {
   307  		c.Assert(f(&backend.Reader{
   308  			Snapshot: client.Snapshot{SetID: 42, Snap: "foo"},
   309  			File:     shotfile,
   310  		}), check.IsNil)
   311  
   312  		return nil
   313  	}
   314  	restoreIter := snapshotstate.MockBackendIter(fakeIter)
   315  
   316  	o := overlord.Mock()
   317  	st = o.State()
   318  
   319  	stmgr, err := snapstate.Manager(st, o.TaskRunner())
   320  	c.Assert(err, check.IsNil)
   321  	o.AddManager(stmgr)
   322  	shmgr := snapshotstate.Manager(st, o.TaskRunner())
   323  	o.AddManager(shmgr)
   324  
   325  	st.Lock()
   326  	defer func() {
   327  		if c.Failed() {
   328  			// something went wrong
   329  			st.Unlock()
   330  		}
   331  	}()
   332  
   333  	snapstate.Set(st, "foo", &snapstate.SnapState{
   334  		Active: true,
   335  		Sequence: []*snap.SideInfo{
   336  			{RealName: "foo", Revision: snap.R(1)},
   337  		},
   338  		Current:  snap.R(1),
   339  		SnapType: "app",
   340  	})
   341  
   342  	r := snapstatetest.UseFallbackDeviceModel()
   343  	defer r()
   344  
   345  	chg := st.NewChange("rm foo", "...")
   346  	rmTasks, err := snapstate.Remove(st, "foo", snap.R(0), nil)
   347  	c.Assert(err, check.IsNil)
   348  	c.Assert(rmTasks, check.NotNil)
   349  	chg.AddAll(rmTasks)
   350  
   351  	return st, func() {
   352  		shotfile.Close()
   353  		st.Unlock()
   354  		restoreIter()
   355  	}
   356  }
   357  
   358  func (s snapshotSuite) TestSaveChecksSnapstateConflict(c *check.C) {
   359  	st, restore := s.createConflictingChange(c)
   360  	defer restore()
   361  
   362  	_, _, _, err := snapshotstate.Save(st, []string{"foo"}, nil)
   363  	c.Assert(err, check.NotNil)
   364  	c.Check(err, check.FitsTypeOf, &snapstate.ChangeConflictError{})
   365  }
   366  
   367  func (snapshotSuite) TestSaveConflictsWithSnapstate(c *check.C) {
   368  	fakeSnapstateAll := func(*state.State) (map[string]*snapstate.SnapState, error) {
   369  		return map[string]*snapstate.SnapState{
   370  			"foo": {Active: true},
   371  		}, nil
   372  	}
   373  
   374  	defer snapshotstate.MockSnapstateAll(fakeSnapstateAll)()
   375  
   376  	o := overlord.Mock()
   377  	st := o.State()
   378  
   379  	stmgr, err := snapstate.Manager(st, o.TaskRunner())
   380  	c.Assert(err, check.IsNil)
   381  	o.AddManager(stmgr)
   382  	shmgr := snapshotstate.Manager(st, o.TaskRunner())
   383  	o.AddManager(shmgr)
   384  
   385  	st.Lock()
   386  	defer st.Unlock()
   387  
   388  	snapstate.Set(st, "foo", &snapstate.SnapState{
   389  		Active: true,
   390  		Sequence: []*snap.SideInfo{
   391  			{RealName: "foo", Revision: snap.R(1)},
   392  		},
   393  		Current:  snap.R(1),
   394  		SnapType: "app",
   395  	})
   396  
   397  	chg := st.NewChange("snapshot-save", "...")
   398  	_, _, saveTasks, err := snapshotstate.Save(st, nil, nil)
   399  	c.Assert(err, check.IsNil)
   400  	chg.AddAll(saveTasks)
   401  
   402  	_, err = snapstate.Disable(st, "foo")
   403  	c.Assert(err, check.ErrorMatches, `snap "foo" has "snapshot-save" change in progress`)
   404  }
   405  
   406  func (snapshotSuite) TestSaveChecksSnapstateConflictError(c *check.C) {
   407  	defer snapshotstate.MockSnapstateCheckChangeConflictMany(func(*state.State, []string, string) error {
   408  		return errors.New("bzzt")
   409  	})()
   410  
   411  	st := state.New(nil)
   412  	st.Lock()
   413  	defer st.Unlock()
   414  	_, _, _, err := snapshotstate.Save(st, nil, nil)
   415  	c.Check(err, check.ErrorMatches, "bzzt")
   416  }
   417  
   418  func (snapshotSuite) TestSaveChecksSetIDError(c *check.C) {
   419  	st := state.New(nil)
   420  	st.Lock()
   421  	defer st.Unlock()
   422  
   423  	st.Set("last-snapshot-set-id", "3/4")
   424  
   425  	_, _, _, err := snapshotstate.Save(st, nil, nil)
   426  	c.Check(err, check.ErrorMatches, ".* could not unmarshal .*")
   427  }
   428  
   429  func (snapshotSuite) TestSaveNoSnapsInState(c *check.C) {
   430  	st := state.New(nil)
   431  	st.Lock()
   432  	defer st.Unlock()
   433  
   434  	setID, saved, taskset, err := snapshotstate.Save(st, nil, nil)
   435  	c.Assert(err, check.IsNil)
   436  	c.Check(setID, check.Equals, uint64(1))
   437  	c.Check(saved, check.HasLen, 0)
   438  	c.Check(taskset.Tasks(), check.HasLen, 0)
   439  }
   440  
   441  func (snapshotSuite) TestSaveSomeSnaps(c *check.C) {
   442  	fakeSnapstateAll := func(*state.State) (map[string]*snapstate.SnapState, error) {
   443  		return map[string]*snapstate.SnapState{
   444  			"a-snap": {Active: true},
   445  			"b-snap": {},
   446  			"c-snap": {Active: true},
   447  		}, nil
   448  	}
   449  
   450  	defer snapshotstate.MockSnapstateAll(fakeSnapstateAll)()
   451  
   452  	st := state.New(nil)
   453  	st.Lock()
   454  	defer st.Unlock()
   455  
   456  	setID, saved, taskset, err := snapshotstate.Save(st, nil, nil)
   457  	c.Assert(err, check.IsNil)
   458  	c.Check(setID, check.Equals, uint64(1))
   459  	c.Check(saved, check.DeepEquals, []string{"a-snap", "c-snap"})
   460  	tasks := taskset.Tasks()
   461  	c.Assert(tasks, check.HasLen, 2)
   462  	c.Check(tasks[0].Kind(), check.Equals, "save-snapshot")
   463  	c.Check(tasks[0].Summary(), check.Equals, `Save data of snap "a-snap" in snapshot set #1`)
   464  	c.Check(tasks[1].Kind(), check.Equals, "save-snapshot")
   465  	c.Check(tasks[1].Summary(), check.Equals, `Save data of snap "c-snap" in snapshot set #1`)
   466  }
   467  
   468  func (snapshotSuite) TestSaveOneSnap(c *check.C) {
   469  	fakeSnapstateAll := func(*state.State) (map[string]*snapstate.SnapState, error) {
   470  		// snapstate.All isn't called when a snap name is passed in
   471  		return nil, errors.New("bzzt")
   472  	}
   473  
   474  	defer snapshotstate.MockSnapstateAll(fakeSnapstateAll)()
   475  
   476  	st := state.New(nil)
   477  	st.Lock()
   478  	defer st.Unlock()
   479  
   480  	setID, saved, taskset, err := snapshotstate.Save(st, []string{"a-snap"}, []string{"a-user"})
   481  	c.Assert(err, check.IsNil)
   482  	c.Check(setID, check.Equals, uint64(1))
   483  	c.Check(saved, check.DeepEquals, []string{"a-snap"})
   484  	tasks := taskset.Tasks()
   485  	c.Assert(tasks, check.HasLen, 1)
   486  	c.Check(tasks[0].Kind(), check.Equals, "save-snapshot")
   487  	c.Check(tasks[0].Summary(), check.Equals, `Save data of snap "a-snap" in snapshot set #1`)
   488  	var snapshot map[string]interface{}
   489  	c.Check(tasks[0].Get("snapshot-setup", &snapshot), check.IsNil)
   490  	c.Check(snapshot, check.DeepEquals, map[string]interface{}{
   491  		"set-id":  1.,
   492  		"snap":    "a-snap",
   493  		"users":   []interface{}{"a-user"},
   494  		"current": "unset",
   495  	})
   496  }
   497  
   498  func (snapshotSuite) TestSaveIntegration(c *check.C) {
   499  	if os.Geteuid() == 0 {
   500  		c.Skip("this test cannot run as root (runuser will fail)")
   501  	}
   502  
   503  	c.Assert(os.MkdirAll(dirs.SnapshotsDir, 0755), check.IsNil)
   504  	homedir := filepath.Join(dirs.GlobalRootDir, "home", "a-user")
   505  
   506  	defer backend.MockUserLookup(func(username string) (*user.User, error) {
   507  		if username != "a-user" {
   508  			c.Fatalf("unexpected user %q", username)
   509  		}
   510  		return &user.User{
   511  			Uid:      fmt.Sprint(sys.Geteuid()),
   512  			Username: username,
   513  			HomeDir:  homedir,
   514  		}, nil
   515  	})()
   516  
   517  	o := overlord.Mock()
   518  	st := o.State()
   519  
   520  	stmgr, err := snapstate.Manager(st, o.TaskRunner())
   521  	c.Assert(err, check.IsNil)
   522  	o.AddManager(stmgr)
   523  	shmgr := snapshotstate.Manager(st, o.TaskRunner())
   524  	o.AddManager(shmgr)
   525  	o.AddManager(o.TaskRunner())
   526  
   527  	st.Lock()
   528  	defer st.Unlock()
   529  
   530  	snapshots := make(map[string]*client.Snapshot, 3)
   531  	for i, name := range []string{"one-snap", "too-snap", "tri-snap"} {
   532  		sideInfo := &snap.SideInfo{RealName: name, Revision: snap.R(i + 1)}
   533  		snapstate.Set(st, name, &snapstate.SnapState{
   534  			Active:   true,
   535  			Sequence: []*snap.SideInfo{sideInfo},
   536  			Current:  sideInfo.Revision,
   537  			SnapType: "app",
   538  		})
   539  		snaptest.MockSnap(c, fmt.Sprintf("{name: %s, version: v1}", name), sideInfo)
   540  
   541  		c.Assert(os.MkdirAll(filepath.Join(homedir, "snap", name, fmt.Sprint(i+1), "canary-"+name), 0755), check.IsNil)
   542  		c.Assert(os.MkdirAll(filepath.Join(homedir, "snap", name, "common", "common-"+name), 0755), check.IsNil)
   543  
   544  		snapshots[name] = &client.Snapshot{
   545  			SetID:    1,
   546  			Snap:     name,
   547  			Version:  "v1",
   548  			Revision: sideInfo.Revision,
   549  			Epoch:    snap.E("0"),
   550  		}
   551  	}
   552  
   553  	setID, saved, taskset, err := snapshotstate.Save(st, nil, []string{"a-user"})
   554  	c.Assert(err, check.IsNil)
   555  	c.Check(setID, check.Equals, uint64(1))
   556  	c.Check(saved, check.DeepEquals, []string{"one-snap", "too-snap", "tri-snap"})
   557  
   558  	change := st.NewChange("save-snapshot", "...")
   559  	change.AddAll(taskset)
   560  
   561  	t0 := time.Now()
   562  
   563  	st.Unlock()
   564  	c.Assert(o.Settle(5*time.Second), check.IsNil)
   565  	st.Lock()
   566  	c.Check(change.Err(), check.IsNil)
   567  
   568  	tf := time.Now()
   569  	c.Assert(backend.Iter(context.TODO(), func(r *backend.Reader) error {
   570  		c.Check(r.Check(context.TODO(), nil), check.IsNil)
   571  
   572  		// check the unknowables, and zero them out
   573  		c.Check(r.Snapshot.Time.After(t0), check.Equals, true)
   574  		c.Check(r.Snapshot.Time.Before(tf), check.Equals, true)
   575  		c.Check(r.Snapshot.Size > 0, check.Equals, true)
   576  		c.Assert(r.Snapshot.SHA3_384, check.HasLen, 1)
   577  		c.Check(r.Snapshot.SHA3_384["user/a-user.tgz"], check.HasLen, 96)
   578  
   579  		r.Snapshot.Time = time.Time{}
   580  		r.Snapshot.Size = 0
   581  		r.Snapshot.SHA3_384 = nil
   582  
   583  		c.Check(&r.Snapshot, check.DeepEquals, snapshots[r.Snapshot.Snap])
   584  		return nil
   585  	}), check.IsNil)
   586  }
   587  
   588  func (snapshotSuite) TestSaveIntegrationFails(c *check.C) {
   589  	if os.Geteuid() == 0 {
   590  		c.Skip("this test cannot run as root (runuser will fail)")
   591  	}
   592  	c.Assert(os.MkdirAll(dirs.SnapshotsDir, 0755), check.IsNil)
   593  	// sanity check: no files in snapshot dir
   594  	out, err := exec.Command("find", dirs.SnapshotsDir, "-type", "f").CombinedOutput()
   595  	c.Assert(err, check.IsNil)
   596  	c.Check(string(out), check.Equals, "")
   597  
   598  	homedir := filepath.Join(dirs.GlobalRootDir, "home", "a-user")
   599  
   600  	// Mock "tar" so that the tars finish in the expected order.
   601  	// Locally .01s and .02s do the trick with count=1000;
   602  	// padded a lot bigger for slower systems.
   603  	mocktar := testutil.MockCommand(c, "tar", `
   604  case "$*" in
   605  */too-snap/*)
   606      sleep .5
   607      ;;
   608  */tri-snap/*)
   609      sleep 1
   610      ;;
   611  esac
   612  exec /bin/tar "$@"
   613  `)
   614  	defer mocktar.Restore()
   615  
   616  	defer backend.MockUserLookup(func(username string) (*user.User, error) {
   617  		if username != "a-user" {
   618  			c.Fatalf("unexpected user %q", username)
   619  		}
   620  		return &user.User{
   621  			Uid:      fmt.Sprint(sys.Geteuid()),
   622  			Username: username,
   623  			HomeDir:  homedir,
   624  		}, nil
   625  	})()
   626  
   627  	o := overlord.Mock()
   628  	st := o.State()
   629  
   630  	stmgr, err := snapstate.Manager(st, o.TaskRunner())
   631  	c.Assert(err, check.IsNil)
   632  	o.AddManager(stmgr)
   633  	shmgr := snapshotstate.Manager(st, o.TaskRunner())
   634  	o.AddManager(shmgr)
   635  	o.AddManager(o.TaskRunner())
   636  
   637  	st.Lock()
   638  	defer st.Unlock()
   639  
   640  	for i, name := range []string{"one-snap", "too-snap", "tri-snap"} {
   641  		sideInfo := &snap.SideInfo{RealName: name, Revision: snap.R(i + 1)}
   642  		snapstate.Set(st, name, &snapstate.SnapState{
   643  			Active:   true,
   644  			Sequence: []*snap.SideInfo{sideInfo},
   645  			Current:  sideInfo.Revision,
   646  			SnapType: "app",
   647  		})
   648  		snaptest.MockSnap(c, fmt.Sprintf("{name: %s, version: v1}", name), sideInfo)
   649  
   650  		c.Assert(os.MkdirAll(filepath.Join(homedir, "snap", name, fmt.Sprint(i+1), "canary-"+name), 0755), check.IsNil)
   651  		c.Assert(os.MkdirAll(filepath.Join(homedir, "snap", name, "common"), 0755), check.IsNil)
   652  		mode := os.FileMode(0755)
   653  		if i == 1 {
   654  			mode = 0
   655  		}
   656  		c.Assert(os.Mkdir(filepath.Join(homedir, "snap", name, "common", "common-"+name), mode), check.IsNil)
   657  	}
   658  
   659  	setID, saved, taskset, err := snapshotstate.Save(st, nil, []string{"a-user"})
   660  	c.Assert(err, check.IsNil)
   661  	c.Check(setID, check.Equals, uint64(1))
   662  	c.Check(saved, check.DeepEquals, []string{"one-snap", "too-snap", "tri-snap"})
   663  
   664  	change := st.NewChange("save-snapshot", "...")
   665  	change.AddAll(taskset)
   666  
   667  	st.Unlock()
   668  	c.Assert(o.Settle(5*time.Second), check.IsNil)
   669  	st.Lock()
   670  	c.Check(change.Err(), check.NotNil)
   671  	tasks := change.Tasks()
   672  	c.Assert(tasks, check.HasLen, 3)
   673  
   674  	// task 0 (for "one-snap") will have been undone
   675  	c.Check(tasks[0].Summary(), testutil.Contains, `"one-snap"`) // sanity check: task 0 is one-snap's
   676  	c.Check(tasks[0].Status(), check.Equals, state.UndoneStatus)
   677  
   678  	// task 1 (for "too-snap") will have errored
   679  	c.Check(tasks[1].Summary(), testutil.Contains, `"too-snap"`) // sanity check: task 1 is too-snap's
   680  	c.Check(tasks[1].Status(), check.Equals, state.ErrorStatus)
   681  	c.Check(strings.Join(tasks[1].Log(), "\n"), check.Matches, `\S+ ERROR cannot create archive: .* Permission denied .and \d+ more.`)
   682  
   683  	// task 2 (for "tri-snap") will have errored as well, hopefully, but it's a race (see the "tar" comment above)
   684  	c.Check(tasks[2].Summary(), testutil.Contains, `"tri-snap"`) // sanity check: task 2 is tri-snap's
   685  	c.Check(tasks[2].Status(), check.Equals, state.ErrorStatus, check.Commentf("if this ever fails, duplicate the fake tar sleeps please"))
   686  	// sometimes you'll get one, sometimes you'll get the other (depending on ordering of events)
   687  	c.Check(strings.Join(tasks[2].Log(), "\n"), check.Matches, `\S+ ERROR( tar failed:)? context canceled`)
   688  
   689  	// no zips left behind, not for errors, not for undos \o/
   690  	out, err = exec.Command("find", dirs.SnapshotsDir, "-type", "f").CombinedOutput()
   691  	c.Assert(err, check.IsNil)
   692  	c.Check(string(out), check.Equals, "")
   693  }
   694  
   695  func (snapshotSuite) TestRestoreChecksIterError(c *check.C) {
   696  	defer snapshotstate.MockBackendIter(func(context.Context, func(*backend.Reader) error) error {
   697  		return errors.New("bzzt")
   698  	})()
   699  
   700  	st := state.New(nil)
   701  	st.Lock()
   702  	defer st.Unlock()
   703  
   704  	_, _, err := snapshotstate.Restore(st, 42, nil, nil)
   705  	c.Assert(err, check.ErrorMatches, "bzzt")
   706  }
   707  
   708  func (s snapshotSuite) TestRestoreChecksSnapstateConflicts(c *check.C) {
   709  	st, restore := s.createConflictingChange(c)
   710  	defer restore()
   711  
   712  	_, _, err := snapshotstate.Restore(st, 42, nil, nil)
   713  	c.Assert(err, check.NotNil)
   714  	c.Check(err, check.FitsTypeOf, &snapstate.ChangeConflictError{})
   715  
   716  }
   717  
   718  func (snapshotSuite) TestRestoreConflictsWithSnapstate(c *check.C) {
   719  	shotfile, err := os.Create(filepath.Join(c.MkDir(), "foo.zip"))
   720  	c.Assert(err, check.IsNil)
   721  	defer shotfile.Close()
   722  
   723  	sideInfo := &snap.SideInfo{RealName: "foo", Revision: snap.R(1)}
   724  	fakeSnapstateAll := func(*state.State) (map[string]*snapstate.SnapState, error) {
   725  		return map[string]*snapstate.SnapState{
   726  			"foo": {
   727  				Active:   true,
   728  				Sequence: []*snap.SideInfo{sideInfo},
   729  				Current:  sideInfo.Revision,
   730  			},
   731  		}, nil
   732  	}
   733  	snaptest.MockSnap(c, "{name: foo, version: v1}", sideInfo)
   734  
   735  	defer snapshotstate.MockSnapstateAll(fakeSnapstateAll)()
   736  
   737  	fakeIter := func(_ context.Context, f func(*backend.Reader) error) error {
   738  		c.Assert(f(&backend.Reader{
   739  			Snapshot: client.Snapshot{SetID: 42, Snap: "foo"},
   740  			File:     shotfile,
   741  		}), check.IsNil)
   742  
   743  		return nil
   744  	}
   745  	defer snapshotstate.MockBackendIter(fakeIter)()
   746  
   747  	o := overlord.Mock()
   748  	st := o.State()
   749  
   750  	stmgr, err := snapstate.Manager(st, o.TaskRunner())
   751  	c.Assert(err, check.IsNil)
   752  	o.AddManager(stmgr)
   753  	shmgr := snapshotstate.Manager(st, o.TaskRunner())
   754  	o.AddManager(shmgr)
   755  
   756  	st.Lock()
   757  	defer st.Unlock()
   758  
   759  	snapstate.Set(st, "foo", &snapstate.SnapState{
   760  		Active: true,
   761  		Sequence: []*snap.SideInfo{
   762  			{RealName: "foo", Revision: snap.R(1)},
   763  		},
   764  		Current:  snap.R(1),
   765  		SnapType: "app",
   766  	})
   767  
   768  	chg := st.NewChange("snapshot-restore", "...")
   769  	_, restoreTasks, err := snapshotstate.Restore(st, 42, nil, nil)
   770  	c.Assert(err, check.IsNil)
   771  	chg.AddAll(restoreTasks)
   772  
   773  	_, err = snapstate.Disable(st, "foo")
   774  	c.Assert(err, check.ErrorMatches, `snap "foo" has "snapshot-restore" change in progress`)
   775  }
   776  
   777  func (snapshotSuite) TestRestoreChecksForgetConflicts(c *check.C) {
   778  	shotfile, err := os.Create(filepath.Join(c.MkDir(), "yadda.zip"))
   779  	c.Assert(err, check.IsNil)
   780  	defer shotfile.Close()
   781  	fakeIter := func(_ context.Context, f func(*backend.Reader) error) error {
   782  		c.Assert(f(&backend.Reader{
   783  			// not wanted
   784  			Snapshot: client.Snapshot{SetID: 42, Snap: "a-snap"},
   785  			File:     shotfile,
   786  		}), check.IsNil)
   787  
   788  		return nil
   789  	}
   790  	defer snapshotstate.MockBackendIter(fakeIter)()
   791  
   792  	st := state.New(nil)
   793  	st.Lock()
   794  	defer st.Unlock()
   795  	chg := st.NewChange("forget-snapshot-change", "...")
   796  	tsk := st.NewTask("forget-snapshot", "...")
   797  	tsk.SetStatus(state.DoingStatus)
   798  	tsk.Set("snapshot-setup", map[string]int{"set-id": 42})
   799  	chg.AddTask(tsk)
   800  
   801  	_, _, err = snapshotstate.Restore(st, 42, nil, nil)
   802  	c.Assert(err, check.ErrorMatches, `cannot operate on snapshot set #42 while change \"1\" is in progress`)
   803  }
   804  
   805  func (snapshotSuite) TestRestoreChecksChangesToSnapID(c *check.C) {
   806  	shotfile, err := os.Create(filepath.Join(c.MkDir(), "yadda.zip"))
   807  	c.Assert(err, check.IsNil)
   808  	defer shotfile.Close()
   809  	fakeSnapstateAll := func(*state.State) (map[string]*snapstate.SnapState, error) {
   810  		return map[string]*snapstate.SnapState{
   811  			"a-snap": {
   812  				Active: true,
   813  				Sequence: []*snap.SideInfo{
   814  					{RealName: "a-snap", Revision: snap.R(1), SnapID: "1234567890"},
   815  				},
   816  				Current: snap.R(1),
   817  			},
   818  		}, nil
   819  	}
   820  	defer snapshotstate.MockSnapstateAll(fakeSnapstateAll)()
   821  	fakeIter := func(_ context.Context, f func(*backend.Reader) error) error {
   822  		c.Assert(f(&backend.Reader{
   823  			// not wanted
   824  			Snapshot: client.Snapshot{SetID: 42, Snap: "a-snap", SnapID: "0987654321"},
   825  			File:     shotfile,
   826  		}), check.IsNil)
   827  
   828  		return nil
   829  	}
   830  	defer snapshotstate.MockBackendIter(fakeIter)()
   831  
   832  	st := state.New(nil)
   833  	st.Lock()
   834  	defer st.Unlock()
   835  
   836  	_, _, err = snapshotstate.Restore(st, 42, nil, nil)
   837  	c.Assert(err, check.ErrorMatches, `cannot restore snapshot for "a-snap": current snap \(ID 1234567…\) does not match snapshot \(ID 0987654…\)`)
   838  }
   839  
   840  func (snapshotSuite) TestRestoreChecksChangesToEpoch(c *check.C) {
   841  	shotfile, err := os.Create(filepath.Join(c.MkDir(), "yadda.zip"))
   842  	c.Assert(err, check.IsNil)
   843  	defer shotfile.Close()
   844  
   845  	sideInfo := &snap.SideInfo{RealName: "a-snap", Revision: snap.R(1)}
   846  	fakeSnapstateAll := func(*state.State) (map[string]*snapstate.SnapState, error) {
   847  		return map[string]*snapstate.SnapState{
   848  			"a-snap": {
   849  				Active:   true,
   850  				Sequence: []*snap.SideInfo{sideInfo},
   851  				Current:  sideInfo.Revision,
   852  			},
   853  		}, nil
   854  	}
   855  	defer snapshotstate.MockSnapstateAll(fakeSnapstateAll)()
   856  	snaptest.MockSnap(c, "{name: a-snap, version: v1, epoch: 17}", sideInfo)
   857  
   858  	fakeIter := func(_ context.Context, f func(*backend.Reader) error) error {
   859  		c.Assert(f(&backend.Reader{
   860  			// not wanted
   861  			Snapshot: client.Snapshot{
   862  				SetID: 42,
   863  				Snap:  "a-snap",
   864  				Epoch: snap.E("42"),
   865  			},
   866  			File: shotfile,
   867  		}), check.IsNil)
   868  
   869  		return nil
   870  	}
   871  	defer snapshotstate.MockBackendIter(fakeIter)()
   872  
   873  	st := state.New(nil)
   874  	st.Lock()
   875  	defer st.Unlock()
   876  
   877  	_, _, err = snapshotstate.Restore(st, 42, nil, nil)
   878  	c.Assert(err, check.ErrorMatches, `cannot restore snapshot for "a-snap": current snap \(epoch 17\) cannot read snapshot data \(epoch 42\)`)
   879  }
   880  
   881  func (snapshotSuite) TestRestoreWorksWithCompatibleEpoch(c *check.C) {
   882  	shotfile, err := os.Create(filepath.Join(c.MkDir(), "yadda.zip"))
   883  	c.Assert(err, check.IsNil)
   884  	defer shotfile.Close()
   885  
   886  	sideInfo := &snap.SideInfo{RealName: "a-snap", Revision: snap.R(1)}
   887  	fakeSnapstateAll := func(*state.State) (map[string]*snapstate.SnapState, error) {
   888  		return map[string]*snapstate.SnapState{
   889  			"a-snap": {
   890  				Active:   true,
   891  				Sequence: []*snap.SideInfo{sideInfo},
   892  				Current:  sideInfo.Revision,
   893  			},
   894  		}, nil
   895  	}
   896  	defer snapshotstate.MockSnapstateAll(fakeSnapstateAll)()
   897  	snaptest.MockSnap(c, "{name: a-snap, version: v1, epoch: {read: [17, 42], write: [42]}}", sideInfo)
   898  
   899  	fakeIter := func(_ context.Context, f func(*backend.Reader) error) error {
   900  		c.Assert(f(&backend.Reader{
   901  			// not wanted
   902  			Snapshot: client.Snapshot{
   903  				SetID: 42,
   904  				Snap:  "a-snap",
   905  				Epoch: snap.E("17"),
   906  			},
   907  			File: shotfile,
   908  		}), check.IsNil)
   909  
   910  		return nil
   911  	}
   912  	defer snapshotstate.MockBackendIter(fakeIter)()
   913  
   914  	st := state.New(nil)
   915  	st.Lock()
   916  	defer st.Unlock()
   917  
   918  	found, taskset, err := snapshotstate.Restore(st, 42, nil, nil)
   919  	c.Assert(err, check.IsNil)
   920  	c.Check(found, check.DeepEquals, []string{"a-snap"})
   921  	tasks := taskset.Tasks()
   922  	c.Assert(tasks, check.HasLen, 1)
   923  	c.Check(tasks[0].Kind(), check.Equals, "restore-snapshot")
   924  	c.Check(tasks[0].Summary(), check.Equals, `Restore data of snap "a-snap" from snapshot set #42`)
   925  	var snapshot map[string]interface{}
   926  	c.Check(tasks[0].Get("snapshot-setup", &snapshot), check.IsNil)
   927  	c.Check(snapshot, check.DeepEquals, map[string]interface{}{
   928  		"set-id":   42.,
   929  		"snap":     "a-snap",
   930  		"filename": shotfile.Name(),
   931  		"current":  "1",
   932  	})
   933  }
   934  
   935  func (snapshotSuite) TestRestore(c *check.C) {
   936  	shotfile, err := os.Create(filepath.Join(c.MkDir(), "yadda.zip"))
   937  	c.Assert(err, check.IsNil)
   938  	defer shotfile.Close()
   939  	fakeIter := func(_ context.Context, f func(*backend.Reader) error) error {
   940  		c.Assert(f(&backend.Reader{
   941  			// not wanted
   942  			Snapshot: client.Snapshot{SetID: 42, Snap: "a-snap"},
   943  			File:     shotfile,
   944  		}), check.IsNil)
   945  
   946  		return nil
   947  	}
   948  	defer snapshotstate.MockBackendIter(fakeIter)()
   949  
   950  	st := state.New(nil)
   951  	st.Lock()
   952  	defer st.Unlock()
   953  
   954  	found, taskset, err := snapshotstate.Restore(st, 42, []string{"a-snap", "b-snap"}, []string{"a-user"})
   955  	c.Assert(err, check.IsNil)
   956  	c.Check(found, check.DeepEquals, []string{"a-snap"})
   957  	tasks := taskset.Tasks()
   958  	c.Assert(tasks, check.HasLen, 1)
   959  	c.Check(tasks[0].Kind(), check.Equals, "restore-snapshot")
   960  	c.Check(tasks[0].Summary(), check.Equals, `Restore data of snap "a-snap" from snapshot set #42`)
   961  	var snapshot map[string]interface{}
   962  	c.Check(tasks[0].Get("snapshot-setup", &snapshot), check.IsNil)
   963  	c.Check(snapshot, check.DeepEquals, map[string]interface{}{
   964  		"set-id":   42.,
   965  		"snap":     "a-snap",
   966  		"filename": shotfile.Name(),
   967  		"users":    []interface{}{"a-user"},
   968  		"current":  "unset",
   969  	})
   970  }
   971  
   972  func (snapshotSuite) TestRestoreIntegration(c *check.C) {
   973  	if os.Geteuid() == 0 {
   974  		c.Skip("this test cannot run as root (runuser will fail)")
   975  	}
   976  
   977  	c.Assert(os.MkdirAll(dirs.SnapshotsDir, 0755), check.IsNil)
   978  	homedirA := filepath.Join(dirs.GlobalRootDir, "home", "a-user")
   979  	homedirB := filepath.Join(dirs.GlobalRootDir, "home", "b-user")
   980  
   981  	defer backend.MockUserLookup(func(username string) (*user.User, error) {
   982  		if username != "a-user" && username != "b-user" {
   983  			c.Fatalf("unexpected user %q", username)
   984  			return nil, user.UnknownUserError(username)
   985  		}
   986  		return &user.User{
   987  			Uid:      fmt.Sprint(sys.Geteuid()),
   988  			Username: username,
   989  			HomeDir:  filepath.Join(dirs.GlobalRootDir, "home", username),
   990  		}, nil
   991  
   992  	})()
   993  
   994  	o := overlord.Mock()
   995  	st := o.State()
   996  
   997  	stmgr, err := snapstate.Manager(st, o.TaskRunner())
   998  	c.Assert(err, check.IsNil)
   999  	o.AddManager(stmgr)
  1000  	shmgr := snapshotstate.Manager(st, o.TaskRunner())
  1001  	o.AddManager(shmgr)
  1002  	o.AddManager(o.TaskRunner())
  1003  
  1004  	st.Lock()
  1005  	defer st.Unlock()
  1006  
  1007  	for i, name := range []string{"one-snap", "too-snap", "tri-snap"} {
  1008  		sideInfo := &snap.SideInfo{RealName: name, Revision: snap.R(i + 1)}
  1009  		snapstate.Set(st, name, &snapstate.SnapState{
  1010  			Active:   true,
  1011  			Sequence: []*snap.SideInfo{sideInfo},
  1012  			Current:  sideInfo.Revision,
  1013  			SnapType: "app",
  1014  		})
  1015  		snapInfo := snaptest.MockSnap(c, fmt.Sprintf("{name: %s, version: v1}", name), sideInfo)
  1016  
  1017  		for _, home := range []string{homedirA, homedirB} {
  1018  			c.Assert(os.MkdirAll(filepath.Join(home, "snap", name, fmt.Sprint(i+1), "canary-"+name), 0755), check.IsNil)
  1019  			c.Assert(os.MkdirAll(filepath.Join(home, "snap", name, "common", "common-"+name), 0755), check.IsNil)
  1020  		}
  1021  
  1022  		_, err := backend.Save(context.TODO(), 42, snapInfo, nil, []string{"a-user", "b-user"}, nil)
  1023  		c.Assert(err, check.IsNil)
  1024  	}
  1025  
  1026  	// move the old away
  1027  	c.Assert(os.Rename(filepath.Join(homedirA, "snap"), filepath.Join(homedirA, "snap.old")), check.IsNil)
  1028  	// remove b-user's home
  1029  	c.Assert(os.RemoveAll(homedirB), check.IsNil)
  1030  
  1031  	found, taskset, err := snapshotstate.Restore(st, 42, nil, []string{"a-user", "b-user"})
  1032  	c.Assert(err, check.IsNil)
  1033  	sort.Strings(found)
  1034  	c.Check(found, check.DeepEquals, []string{"one-snap", "too-snap", "tri-snap"})
  1035  
  1036  	change := st.NewChange("restore-snapshot", "...")
  1037  	change.AddAll(taskset)
  1038  
  1039  	st.Unlock()
  1040  	c.Assert(o.Settle(5*time.Second), check.IsNil)
  1041  	st.Lock()
  1042  	c.Check(change.Err(), check.IsNil)
  1043  
  1044  	// the three restores warn about the missing home (but no errors, no panics)
  1045  	for _, task := range change.Tasks() {
  1046  		c.Check(strings.Join(task.Log(), "\n"), check.Matches, `.* Skipping restore of "[^"]+/home/b-user/[^"]+" as "[^"]+/home/b-user" doesn't exist.`)
  1047  	}
  1048  
  1049  	// check it was all brought back \o/
  1050  	out, err := exec.Command("diff", "-rN", filepath.Join(homedirA, "snap"), filepath.Join("snap.old")).CombinedOutput()
  1051  	c.Assert(err, check.IsNil)
  1052  	c.Check(string(out), check.Equals, "")
  1053  }
  1054  
  1055  func (snapshotSuite) TestRestoreIntegrationFails(c *check.C) {
  1056  	if os.Geteuid() == 0 {
  1057  		c.Skip("this test cannot run as root (runuser will fail)")
  1058  	}
  1059  	c.Assert(os.MkdirAll(dirs.SnapshotsDir, 0755), check.IsNil)
  1060  	homedir := filepath.Join(dirs.GlobalRootDir, "home", "a-user")
  1061  
  1062  	defer backend.MockUserLookup(func(username string) (*user.User, error) {
  1063  		if username != "a-user" {
  1064  			c.Fatalf("unexpected user %q", username)
  1065  		}
  1066  		return &user.User{
  1067  			Uid:      fmt.Sprint(sys.Geteuid()),
  1068  			Username: username,
  1069  			HomeDir:  homedir,
  1070  		}, nil
  1071  	})()
  1072  
  1073  	o := overlord.Mock()
  1074  	st := o.State()
  1075  
  1076  	stmgr, err := snapstate.Manager(st, o.TaskRunner())
  1077  	c.Assert(err, check.IsNil)
  1078  	o.AddManager(stmgr)
  1079  	shmgr := snapshotstate.Manager(st, o.TaskRunner())
  1080  	o.AddManager(shmgr)
  1081  	o.AddManager(o.TaskRunner())
  1082  
  1083  	st.Lock()
  1084  	defer st.Unlock()
  1085  
  1086  	for i, name := range []string{"one-snap", "too-snap", "tri-snap"} {
  1087  		sideInfo := &snap.SideInfo{RealName: name, Revision: snap.R(i + 1)}
  1088  		snapstate.Set(st, name, &snapstate.SnapState{
  1089  			Active:   true,
  1090  			Sequence: []*snap.SideInfo{sideInfo},
  1091  			Current:  sideInfo.Revision,
  1092  			SnapType: "app",
  1093  		})
  1094  		snapInfo := snaptest.MockSnap(c, fmt.Sprintf("{name: %s, version: vv1}", name), sideInfo)
  1095  
  1096  		c.Assert(os.MkdirAll(filepath.Join(homedir, "snap", name, fmt.Sprint(i+1), "canary-"+name), 0755), check.IsNil)
  1097  		c.Assert(os.MkdirAll(filepath.Join(homedir, "snap", name, "common", "common-"+name), 0755), check.IsNil)
  1098  
  1099  		_, err := backend.Save(context.TODO(), 42, snapInfo, nil, []string{"a-user"}, nil)
  1100  		c.Assert(err, check.IsNil)
  1101  	}
  1102  
  1103  	// move the old away
  1104  	c.Assert(os.Rename(filepath.Join(homedir, "snap"), filepath.Join(homedir, "snap.old")), check.IsNil)
  1105  	// but poison the well
  1106  	c.Assert(os.MkdirAll(filepath.Join(homedir, "snap"), 0755), check.IsNil)
  1107  	c.Assert(os.MkdirAll(filepath.Join(homedir, "snap", "too-snap"), 0), check.IsNil)
  1108  
  1109  	found, taskset, err := snapshotstate.Restore(st, 42, nil, []string{"a-user"})
  1110  	c.Assert(err, check.IsNil)
  1111  	sort.Strings(found)
  1112  	c.Check(found, check.DeepEquals, []string{"one-snap", "too-snap", "tri-snap"})
  1113  
  1114  	change := st.NewChange("restore-snapshot", "...")
  1115  	change.AddAll(taskset)
  1116  
  1117  	st.Unlock()
  1118  	c.Assert(o.Settle(5*time.Second), check.IsNil)
  1119  	st.Lock()
  1120  	c.Check(change.Err(), check.NotNil)
  1121  
  1122  	tasks := change.Tasks()
  1123  	c.Check(tasks, check.HasLen, 3)
  1124  	for _, task := range tasks {
  1125  		if strings.Contains(task.Summary(), `"too-snap"`) {
  1126  			// too-snap was set up to fail, should always fail with
  1127  			// 'permission denied' (see the mkdirall w/mode 0 above)
  1128  			c.Check(task.Status(), check.Equals, state.ErrorStatus)
  1129  			c.Check(strings.Join(task.Log(), "\n"), check.Matches, `\S+ ERROR mkdir \S+: permission denied`)
  1130  		} else {
  1131  			// the other two might fail (ErrorStatus) if they're
  1132  			// still running when too-snap fails, or they might have
  1133  			// finished and needed to be undone (UndoneStatus); it's
  1134  			// a race, but either is fine.
  1135  			if task.Status() == state.ErrorStatus {
  1136  				c.Check(strings.Join(task.Log(), "\n"), check.Matches, `\S+ ERROR.* context canceled`)
  1137  			} else {
  1138  				c.Check(task.Status(), check.Equals, state.UndoneStatus)
  1139  			}
  1140  		}
  1141  	}
  1142  
  1143  	// remove the poison
  1144  	c.Assert(os.Remove(filepath.Join(homedir, "snap", "too-snap")), check.IsNil)
  1145  
  1146  	// check that nothing else was put there
  1147  	out, err := exec.Command("find", filepath.Join(homedir, "snap")).CombinedOutput()
  1148  	c.Assert(err, check.IsNil)
  1149  	c.Check(strings.TrimSpace(string(out)), check.Equals, filepath.Join(homedir, "snap"))
  1150  }
  1151  
  1152  func (snapshotSuite) TestCheckChecksIterError(c *check.C) {
  1153  	defer snapshotstate.MockBackendIter(func(context.Context, func(*backend.Reader) error) error {
  1154  		return errors.New("bzzt")
  1155  	})()
  1156  
  1157  	st := state.New(nil)
  1158  	st.Lock()
  1159  	defer st.Unlock()
  1160  
  1161  	_, _, err := snapshotstate.Check(st, 42, nil, nil)
  1162  	c.Assert(err, check.ErrorMatches, "bzzt")
  1163  }
  1164  
  1165  func (s snapshotSuite) TestCheckDoesNotTriggerSnapstateConflict(c *check.C) {
  1166  	st, restore := s.createConflictingChange(c)
  1167  	defer restore()
  1168  
  1169  	_, _, err := snapshotstate.Check(st, 42, nil, nil)
  1170  	c.Assert(err, check.IsNil)
  1171  }
  1172  
  1173  func (snapshotSuite) TestCheckChecksForgetConflicts(c *check.C) {
  1174  	shotfile, err := os.Create(filepath.Join(c.MkDir(), "yadda.zip"))
  1175  	c.Assert(err, check.IsNil)
  1176  	defer shotfile.Close()
  1177  	fakeIter := func(_ context.Context, f func(*backend.Reader) error) error {
  1178  		c.Assert(f(&backend.Reader{
  1179  			// not wanted
  1180  			Snapshot: client.Snapshot{SetID: 42, Snap: "a-snap"},
  1181  			File:     shotfile,
  1182  		}), check.IsNil)
  1183  
  1184  		return nil
  1185  	}
  1186  	defer snapshotstate.MockBackendIter(fakeIter)()
  1187  
  1188  	st := state.New(nil)
  1189  	st.Lock()
  1190  	defer st.Unlock()
  1191  	chg := st.NewChange("forget-snapshot-change", "...")
  1192  	tsk := st.NewTask("forget-snapshot", "...")
  1193  	tsk.SetStatus(state.DoingStatus)
  1194  	tsk.Set("snapshot-setup", map[string]int{"set-id": 42})
  1195  	chg.AddTask(tsk)
  1196  
  1197  	_, _, err = snapshotstate.Check(st, 42, nil, nil)
  1198  	c.Assert(err, check.ErrorMatches, `cannot operate on snapshot set #42 while change \"1\" is in progress`)
  1199  }
  1200  
  1201  func (snapshotSuite) TestCheck(c *check.C) {
  1202  	shotfile, err := os.Create(filepath.Join(c.MkDir(), "yadda.zip"))
  1203  	c.Assert(err, check.IsNil)
  1204  	defer shotfile.Close()
  1205  	fakeIter := func(_ context.Context, f func(*backend.Reader) error) error {
  1206  		c.Assert(f(&backend.Reader{
  1207  			// not wanted
  1208  			Snapshot: client.Snapshot{SetID: 42, Snap: "a-snap"},
  1209  			File:     shotfile,
  1210  		}), check.IsNil)
  1211  
  1212  		return nil
  1213  	}
  1214  	defer snapshotstate.MockBackendIter(fakeIter)()
  1215  
  1216  	st := state.New(nil)
  1217  	st.Lock()
  1218  	defer st.Unlock()
  1219  
  1220  	found, taskset, err := snapshotstate.Check(st, 42, []string{"a-snap", "b-snap"}, []string{"a-user"})
  1221  	c.Assert(err, check.IsNil)
  1222  	c.Check(found, check.DeepEquals, []string{"a-snap"})
  1223  	tasks := taskset.Tasks()
  1224  	c.Assert(tasks, check.HasLen, 1)
  1225  	c.Check(tasks[0].Kind(), check.Equals, "check-snapshot")
  1226  	c.Check(tasks[0].Summary(), check.Equals, `Check data of snap "a-snap" in snapshot set #42`)
  1227  	var snapshot map[string]interface{}
  1228  	c.Check(tasks[0].Get("snapshot-setup", &snapshot), check.IsNil)
  1229  	c.Check(snapshot, check.DeepEquals, map[string]interface{}{
  1230  		"set-id":   42.,
  1231  		"snap":     "a-snap",
  1232  		"filename": shotfile.Name(),
  1233  		"users":    []interface{}{"a-user"},
  1234  		"current":  "unset",
  1235  	})
  1236  }
  1237  
  1238  func (snapshotSuite) TestForgetChecksIterError(c *check.C) {
  1239  	defer snapshotstate.MockBackendIter(func(context.Context, func(*backend.Reader) error) error {
  1240  		return errors.New("bzzt")
  1241  	})()
  1242  
  1243  	st := state.New(nil)
  1244  	st.Lock()
  1245  	defer st.Unlock()
  1246  
  1247  	_, _, err := snapshotstate.Forget(st, 42, nil)
  1248  	c.Assert(err, check.ErrorMatches, "bzzt")
  1249  }
  1250  
  1251  func (s snapshotSuite) TestForgetDoesNotTriggerSnapstateConflict(c *check.C) {
  1252  	st, restore := s.createConflictingChange(c)
  1253  	defer restore()
  1254  
  1255  	_, _, err := snapshotstate.Forget(st, 42, nil)
  1256  	c.Assert(err, check.IsNil)
  1257  }
  1258  
  1259  func (snapshotSuite) TestForgetChecksCheckConflicts(c *check.C) {
  1260  	shotfile, err := os.Create(filepath.Join(c.MkDir(), "yadda.zip"))
  1261  	c.Assert(err, check.IsNil)
  1262  	defer shotfile.Close()
  1263  	fakeIter := func(_ context.Context, f func(*backend.Reader) error) error {
  1264  		c.Assert(f(&backend.Reader{
  1265  			// not wanted
  1266  			Snapshot: client.Snapshot{SetID: 42, Snap: "a-snap"},
  1267  			File:     shotfile,
  1268  		}), check.IsNil)
  1269  
  1270  		return nil
  1271  	}
  1272  	defer snapshotstate.MockBackendIter(fakeIter)()
  1273  
  1274  	st := state.New(nil)
  1275  	st.Lock()
  1276  	defer st.Unlock()
  1277  	chg := st.NewChange("check-snapshot-change", "...")
  1278  	tsk := st.NewTask("check-snapshot", "...")
  1279  	tsk.SetStatus(state.DoingStatus)
  1280  	tsk.Set("snapshot-setup", map[string]int{"set-id": 42})
  1281  	chg.AddTask(tsk)
  1282  
  1283  	_, _, err = snapshotstate.Forget(st, 42, nil)
  1284  	c.Assert(err, check.ErrorMatches, `cannot operate on snapshot set #42 while change \"1\" is in progress`)
  1285  }
  1286  
  1287  func (snapshotSuite) TestForgetChecksRestoreConflicts(c *check.C) {
  1288  	shotfile, err := os.Create(filepath.Join(c.MkDir(), "yadda.zip"))
  1289  	c.Assert(err, check.IsNil)
  1290  	defer shotfile.Close()
  1291  	fakeIter := func(_ context.Context, f func(*backend.Reader) error) error {
  1292  		c.Assert(f(&backend.Reader{
  1293  			// not wanted
  1294  			Snapshot: client.Snapshot{SetID: 42, Snap: "a-snap"},
  1295  			File:     shotfile,
  1296  		}), check.IsNil)
  1297  
  1298  		return nil
  1299  	}
  1300  	defer snapshotstate.MockBackendIter(fakeIter)()
  1301  
  1302  	st := state.New(nil)
  1303  	st.Lock()
  1304  	defer st.Unlock()
  1305  	chg := st.NewChange("restore-snapshot-change", "...")
  1306  	tsk := st.NewTask("restore-snapshot", "...")
  1307  	tsk.SetStatus(state.DoingStatus)
  1308  	tsk.Set("snapshot-setup", map[string]int{"set-id": 42})
  1309  	chg.AddTask(tsk)
  1310  
  1311  	_, _, err = snapshotstate.Forget(st, 42, nil)
  1312  	c.Assert(err, check.ErrorMatches, `cannot operate on snapshot set #42 while change \"1\" is in progress`)
  1313  }
  1314  
  1315  func (snapshotSuite) TestForget(c *check.C) {
  1316  	shotfile, err := os.Create(filepath.Join(c.MkDir(), "yadda.zip"))
  1317  	c.Assert(err, check.IsNil)
  1318  	defer shotfile.Close()
  1319  	fakeIter := func(_ context.Context, f func(*backend.Reader) error) error {
  1320  		c.Assert(f(&backend.Reader{
  1321  			// not wanted
  1322  			Snapshot: client.Snapshot{SetID: 42, Snap: "a-snap"},
  1323  			File:     shotfile,
  1324  		}), check.IsNil)
  1325  
  1326  		return nil
  1327  	}
  1328  	defer snapshotstate.MockBackendIter(fakeIter)()
  1329  
  1330  	st := state.New(nil)
  1331  	st.Lock()
  1332  	defer st.Unlock()
  1333  
  1334  	found, taskset, err := snapshotstate.Forget(st, 42, []string{"a-snap", "b-snap"})
  1335  	c.Assert(err, check.IsNil)
  1336  	c.Check(found, check.DeepEquals, []string{"a-snap"})
  1337  	tasks := taskset.Tasks()
  1338  	c.Assert(tasks, check.HasLen, 1)
  1339  	c.Check(tasks[0].Kind(), check.Equals, "forget-snapshot")
  1340  	c.Check(tasks[0].Summary(), check.Equals, `Drop data of snap "a-snap" from snapshot set #42`)
  1341  	var snapshot map[string]interface{}
  1342  	c.Check(tasks[0].Get("snapshot-setup", &snapshot), check.IsNil)
  1343  	c.Check(snapshot, check.DeepEquals, map[string]interface{}{
  1344  		"set-id":   42.,
  1345  		"snap":     "a-snap",
  1346  		"filename": shotfile.Name(),
  1347  		"current":  "unset",
  1348  	})
  1349  }
  1350  
  1351  func (snapshotSuite) TestSaveExpiration(c *check.C) {
  1352  	st := state.New(nil)
  1353  	st.Lock()
  1354  	defer st.Unlock()
  1355  
  1356  	var expirations map[uint64]interface{}
  1357  	tm, err := time.Parse(time.RFC3339, "2019-03-11T11:24:00Z")
  1358  	c.Assert(err, check.IsNil)
  1359  	c.Assert(snapshotstate.SaveExpiration(st, 12, tm), check.IsNil)
  1360  
  1361  	tm, err = time.Parse(time.RFC3339, "2019-02-12T12:50:00Z")
  1362  	c.Assert(err, check.IsNil)
  1363  	c.Assert(snapshotstate.SaveExpiration(st, 13, tm), check.IsNil)
  1364  
  1365  	c.Assert(st.Get("snapshots", &expirations), check.IsNil)
  1366  	c.Check(expirations, check.DeepEquals, map[uint64]interface{}{
  1367  		12: map[string]interface{}{"expiry-time": "2019-03-11T11:24:00Z"},
  1368  		13: map[string]interface{}{"expiry-time": "2019-02-12T12:50:00Z"},
  1369  	})
  1370  }
  1371  
  1372  func (snapshotSuite) TestRemoveSnapshotState(c *check.C) {
  1373  	st := state.New(nil)
  1374  	st.Lock()
  1375  	defer st.Unlock()
  1376  
  1377  	st.Set("snapshots", map[uint64]interface{}{
  1378  		12: map[string]interface{}{"expiry-time": "2019-01-11T11:11:00Z"},
  1379  		13: map[string]interface{}{"expiry-time": "2019-02-12T12:11:00Z"},
  1380  		14: map[string]interface{}{"expiry-time": "2019-03-12T13:11:00Z"},
  1381  	})
  1382  
  1383  	snapshotstate.RemoveSnapshotState(st, 12, 14)
  1384  
  1385  	var snapshots map[uint64]interface{}
  1386  	c.Assert(st.Get("snapshots", &snapshots), check.IsNil)
  1387  	c.Check(snapshots, check.DeepEquals, map[uint64]interface{}{
  1388  		13: map[string]interface{}{"expiry-time": "2019-02-12T12:11:00Z"},
  1389  	})
  1390  }
  1391  
  1392  func (snapshotSuite) TestExpiredSnapshotSets(c *check.C) {
  1393  	st := state.New(nil)
  1394  	st.Lock()
  1395  	defer st.Unlock()
  1396  
  1397  	tm, err := time.Parse(time.RFC3339, "2019-03-11T11:24:00Z")
  1398  	c.Assert(err, check.IsNil)
  1399  	c.Assert(snapshotstate.SaveExpiration(st, 12, tm), check.IsNil)
  1400  
  1401  	tm, err = time.Parse(time.RFC3339, "2019-02-12T12:50:00Z")
  1402  	c.Assert(err, check.IsNil)
  1403  	c.Assert(snapshotstate.SaveExpiration(st, 13, tm), check.IsNil)
  1404  
  1405  	tm, err = time.Parse(time.RFC3339, "2020-03-11T11:24:00Z")
  1406  	c.Assert(err, check.IsNil)
  1407  	expired, err := snapshotstate.ExpiredSnapshotSets(st, tm)
  1408  	c.Assert(err, check.IsNil)
  1409  	c.Check(expired, check.DeepEquals, map[uint64]bool{12: true, 13: true})
  1410  
  1411  	tm, err = time.Parse(time.RFC3339, "2019-03-01T11:24:00Z")
  1412  	c.Assert(err, check.IsNil)
  1413  	expired, err = snapshotstate.ExpiredSnapshotSets(st, tm)
  1414  	c.Assert(err, check.IsNil)
  1415  	c.Check(expired, check.DeepEquals, map[uint64]bool{13: true})
  1416  }
  1417  
  1418  func (snapshotSuite) TestAutomaticSnapshotDisabled(c *check.C) {
  1419  	st := state.New(nil)
  1420  	st.Lock()
  1421  	defer st.Unlock()
  1422  
  1423  	tr := config.NewTransaction(st)
  1424  	tr.Set("core", "snapshots.automatic.retention", "no")
  1425  	tr.Commit()
  1426  
  1427  	_, err := snapshotstate.AutomaticSnapshot(st, "foo")
  1428  	c.Assert(err, check.Equals, snapstate.ErrNothingToDo)
  1429  }
  1430  
  1431  func (snapshotSuite) TestAutomaticSnapshot(c *check.C) {
  1432  	st := state.New(nil)
  1433  	st.Lock()
  1434  	defer st.Unlock()
  1435  
  1436  	tr := config.NewTransaction(st)
  1437  	tr.Set("core", "snapshots.automatic.retention", "24h")
  1438  	tr.Commit()
  1439  
  1440  	ts, err := snapshotstate.AutomaticSnapshot(st, "foo")
  1441  	c.Assert(err, check.IsNil)
  1442  
  1443  	tasks := ts.Tasks()
  1444  	c.Assert(tasks, check.HasLen, 1)
  1445  	c.Check(tasks[0].Kind(), check.Equals, "save-snapshot")
  1446  	c.Check(tasks[0].Summary(), check.Equals, `Save data of snap "foo" in automatic snapshot set #1`)
  1447  	var snapshot map[string]interface{}
  1448  	c.Check(tasks[0].Get("snapshot-setup", &snapshot), check.IsNil)
  1449  	c.Check(snapshot, check.DeepEquals, map[string]interface{}{
  1450  		"set-id":  1.,
  1451  		"snap":    "foo",
  1452  		"current": "unset",
  1453  		"auto":    true,
  1454  	})
  1455  }
  1456  
  1457  func (snapshotSuite) TestAutomaticSnapshotDefaultClassic(c *check.C) {
  1458  	release.MockOnClassic(true)
  1459  
  1460  	st := state.New(nil)
  1461  	st.Lock()
  1462  	defer st.Unlock()
  1463  
  1464  	du, err := snapshotstate.AutomaticSnapshotExpiration(st)
  1465  	c.Assert(err, check.IsNil)
  1466  	c.Assert(du, check.Equals, snapshotstate.DefaultAutomaticSnapshotExpiration)
  1467  }
  1468  
  1469  func (snapshotSuite) TestAutomaticSnapshotDefaultUbuntuCore(c *check.C) {
  1470  	release.MockOnClassic(false)
  1471  
  1472  	st := state.New(nil)
  1473  	st.Lock()
  1474  	defer st.Unlock()
  1475  
  1476  	du, err := snapshotstate.AutomaticSnapshotExpiration(st)
  1477  	c.Assert(err, check.IsNil)
  1478  	c.Assert(du, check.Equals, time.Duration(0))
  1479  }