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