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

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2019 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 overlord_test
    21  
    22  // test the various managers and their operation together through overlord
    23  
    24  import (
    25  	"bytes"
    26  	"context"
    27  	"encoding/json"
    28  	"fmt"
    29  	"io"
    30  	"io/ioutil"
    31  	"net/http"
    32  	"net/http/httptest"
    33  	"net/url"
    34  	"os"
    35  	"path/filepath"
    36  	"sort"
    37  	"strings"
    38  	"time"
    39  
    40  	. "gopkg.in/check.v1"
    41  	"gopkg.in/yaml.v2"
    42  
    43  	"github.com/snapcore/snapd/asserts"
    44  	"github.com/snapcore/snapd/asserts/assertstest"
    45  	"github.com/snapcore/snapd/asserts/sysdb"
    46  	"github.com/snapcore/snapd/bootloader"
    47  	"github.com/snapcore/snapd/bootloader/bootloadertest"
    48  	"github.com/snapcore/snapd/client"
    49  	"github.com/snapcore/snapd/dirs"
    50  	"github.com/snapcore/snapd/interfaces"
    51  	"github.com/snapcore/snapd/osutil"
    52  	"github.com/snapcore/snapd/overlord"
    53  	"github.com/snapcore/snapd/overlord/assertstate"
    54  	"github.com/snapcore/snapd/overlord/assertstate/assertstatetest"
    55  	"github.com/snapcore/snapd/overlord/auth"
    56  	"github.com/snapcore/snapd/overlord/configstate/config"
    57  	"github.com/snapcore/snapd/overlord/devicestate"
    58  	"github.com/snapcore/snapd/overlord/devicestate/devicestatetest"
    59  	"github.com/snapcore/snapd/overlord/hookstate"
    60  	"github.com/snapcore/snapd/overlord/ifacestate"
    61  	"github.com/snapcore/snapd/overlord/snapshotstate"
    62  	snapshotbackend "github.com/snapcore/snapd/overlord/snapshotstate/backend"
    63  	"github.com/snapcore/snapd/overlord/snapstate"
    64  	"github.com/snapcore/snapd/overlord/state"
    65  	"github.com/snapcore/snapd/release"
    66  	"github.com/snapcore/snapd/snap"
    67  	"github.com/snapcore/snapd/snap/snaptest"
    68  	"github.com/snapcore/snapd/store"
    69  	"github.com/snapcore/snapd/systemd"
    70  	"github.com/snapcore/snapd/testutil"
    71  )
    72  
    73  type automaticSnapshotCall struct {
    74  	InstanceName string
    75  	SnapConfig   map[string]interface{}
    76  	Usernames    []string
    77  	Flags        *snapshotbackend.Flags
    78  }
    79  
    80  type mgrsSuite struct {
    81  	testutil.BaseTest
    82  
    83  	tempdir string
    84  
    85  	storeSigning *assertstest.StoreStack
    86  	brands       *assertstest.SigningAccounts
    87  
    88  	devAcct *asserts.Account
    89  
    90  	serveIDtoName map[string]string
    91  	serveSnapPath map[string]string
    92  	serveRevision map[string]string
    93  	serveOldPaths map[string][]string
    94  	serveOldRevs  map[string][]string
    95  
    96  	hijackServeSnap func(http.ResponseWriter)
    97  
    98  	checkDeviceAndAuthContext func(store.DeviceAndAuthContext)
    99  	expectedSerial            string
   100  	expectedStore             string
   101  	sessionMacaroon           string
   102  
   103  	o *overlord.Overlord
   104  
   105  	failNextDownload string
   106  
   107  	automaticSnapshots []automaticSnapshotCall
   108  }
   109  
   110  var (
   111  	_ = Suite(&mgrsSuite{})
   112  	_ = Suite(&storeCtxSetupSuite{})
   113  )
   114  
   115  var (
   116  	brandPrivKey, _ = assertstest.GenerateKey(752)
   117  
   118  	develPrivKey, _ = assertstest.GenerateKey(752)
   119  
   120  	deviceKey, _ = assertstest.GenerateKey(752)
   121  )
   122  
   123  const (
   124  	aggressiveSettleTimeout = 50 * time.Millisecond
   125  	connectRetryTimeout     = 70 * time.Millisecond
   126  )
   127  
   128  func verifyLastTasksetIsRerefresh(c *C, tts []*state.TaskSet) {
   129  	ts := tts[len(tts)-1]
   130  	c.Assert(ts.Tasks(), HasLen, 1)
   131  	c.Check(ts.Tasks()[0].Kind(), Equals, "check-rerefresh")
   132  }
   133  
   134  func (s *mgrsSuite) SetUpTest(c *C) {
   135  	s.BaseTest.SetUpTest(c)
   136  
   137  	s.tempdir = c.MkDir()
   138  	dirs.SetRootDir(s.tempdir)
   139  	s.AddCleanup(func() { dirs.SetRootDir("") })
   140  
   141  	err := os.MkdirAll(filepath.Dir(dirs.SnapStateFile), 0755)
   142  	c.Assert(err, IsNil)
   143  
   144  	// needed by hooks
   145  	s.AddCleanup(testutil.MockCommand(c, "snap", "").Restore)
   146  
   147  	oldSetupInstallHook := snapstate.SetupInstallHook
   148  	oldSetupRemoveHook := snapstate.SetupRemoveHook
   149  	snapstate.SetupRemoveHook = hookstate.SetupRemoveHook
   150  	snapstate.SetupInstallHook = hookstate.SetupInstallHook
   151  	s.AddCleanup(func() {
   152  		snapstate.SetupRemoveHook = oldSetupRemoveHook
   153  		snapstate.SetupInstallHook = oldSetupInstallHook
   154  	})
   155  
   156  	s.automaticSnapshots = nil
   157  	r := snapshotstate.MockBackendSave(func(_ context.Context, id uint64, si *snap.Info, cfg map[string]interface{}, usernames []string, flags *snapshotbackend.Flags) (*client.Snapshot, error) {
   158  		s.automaticSnapshots = append(s.automaticSnapshots, automaticSnapshotCall{InstanceName: si.InstanceName(), SnapConfig: cfg, Usernames: usernames, Flags: flags})
   159  		return nil, nil
   160  	})
   161  	s.AddCleanup(r)
   162  
   163  	s.AddCleanup(ifacestate.MockConnectRetryTimeout(connectRetryTimeout))
   164  
   165  	os.Setenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS", "1")
   166  	s.AddCleanup(func() { os.Unsetenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS") })
   167  
   168  	// create a fake systemd environment
   169  	os.MkdirAll(filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants"), 0755)
   170  
   171  	r = systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
   172  		return []byte("ActiveState=inactive\n"), nil
   173  	})
   174  	s.AddCleanup(r)
   175  
   176  	s.storeSigning = assertstest.NewStoreStack("can0nical", nil)
   177  	s.brands = assertstest.NewSigningAccounts(s.storeSigning)
   178  	s.brands.Register("my-brand", brandPrivKey, map[string]interface{}{
   179  		"validation": "verified",
   180  	})
   181  	s.AddCleanup(sysdb.InjectTrusted(s.storeSigning.Trusted))
   182  
   183  	s.devAcct = assertstest.NewAccount(s.storeSigning, "devdevdev", map[string]interface{}{
   184  		"account-id": "devdevdev",
   185  	}, "")
   186  	err = s.storeSigning.Add(s.devAcct)
   187  	c.Assert(err, IsNil)
   188  
   189  	s.serveIDtoName = make(map[string]string)
   190  	s.serveSnapPath = make(map[string]string)
   191  	s.serveRevision = make(map[string]string)
   192  	s.serveOldPaths = make(map[string][]string)
   193  	s.serveOldRevs = make(map[string][]string)
   194  	s.hijackServeSnap = nil
   195  
   196  	s.checkDeviceAndAuthContext = nil
   197  	s.expectedSerial = ""
   198  	s.expectedStore = ""
   199  	s.sessionMacaroon = ""
   200  
   201  	s.AddCleanup(ifacestate.MockSecurityBackends(nil))
   202  
   203  	o, err := overlord.New(nil)
   204  	c.Assert(err, IsNil)
   205  	err = o.StartUp()
   206  	c.Assert(err, IsNil)
   207  	o.InterfaceManager().DisableUDevMonitor()
   208  	s.o = o
   209  	st := s.o.State()
   210  	st.Lock()
   211  	defer st.Unlock()
   212  	st.Set("seeded", true)
   213  	// registered
   214  	err = assertstate.Add(st, sysdb.GenericClassicModel())
   215  	c.Assert(err, IsNil)
   216  	devicestatetest.SetDevice(st, &auth.DeviceState{
   217  		Brand:  "generic",
   218  		Model:  "generic-classic",
   219  		Serial: "serialserial",
   220  	})
   221  
   222  	// add "core" snap declaration
   223  	headers := map[string]interface{}{
   224  		"series":       "16",
   225  		"snap-name":    "core",
   226  		"publisher-id": "can0nical",
   227  		"timestamp":    time.Now().Format(time.RFC3339),
   228  	}
   229  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   230  	err = assertstate.Add(st, s.storeSigning.StoreAccountKey(""))
   231  	c.Assert(err, IsNil)
   232  	a, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   233  	c.Assert(err, IsNil)
   234  	err = assertstate.Add(st, a)
   235  	c.Assert(err, IsNil)
   236  	s.serveRevision["core"] = "1"
   237  	s.serveIDtoName[fakeSnapID("core")] = "core"
   238  	err = s.storeSigning.Add(a)
   239  	c.Assert(err, IsNil)
   240  
   241  	// add "snap1" snap declaration
   242  	headers = map[string]interface{}{
   243  		"series":       "16",
   244  		"snap-name":    "snap1",
   245  		"publisher-id": "can0nical",
   246  		"timestamp":    time.Now().Format(time.RFC3339),
   247  	}
   248  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   249  	a2, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   250  	c.Assert(err, IsNil)
   251  	c.Assert(assertstate.Add(st, a2), IsNil)
   252  	c.Assert(s.storeSigning.Add(a2), IsNil)
   253  
   254  	// add "snap2" snap declaration
   255  	headers = map[string]interface{}{
   256  		"series":       "16",
   257  		"snap-name":    "snap2",
   258  		"publisher-id": "can0nical",
   259  		"timestamp":    time.Now().Format(time.RFC3339),
   260  	}
   261  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   262  	a3, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   263  	c.Assert(err, IsNil)
   264  	c.Assert(assertstate.Add(st, a3), IsNil)
   265  	c.Assert(s.storeSigning.Add(a3), IsNil)
   266  
   267  	// add "some-snap" snap declaration
   268  	headers = map[string]interface{}{
   269  		"series":       "16",
   270  		"snap-name":    "some-snap",
   271  		"publisher-id": "can0nical",
   272  		"timestamp":    time.Now().Format(time.RFC3339),
   273  	}
   274  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   275  	a4, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   276  	c.Assert(err, IsNil)
   277  	c.Assert(assertstate.Add(st, a4), IsNil)
   278  	c.Assert(s.storeSigning.Add(a4), IsNil)
   279  
   280  	// add "other-snap" snap declaration
   281  	headers = map[string]interface{}{
   282  		"series":       "16",
   283  		"snap-name":    "other-snap",
   284  		"publisher-id": "can0nical",
   285  		"timestamp":    time.Now().Format(time.RFC3339),
   286  	}
   287  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   288  	a5, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   289  	c.Assert(err, IsNil)
   290  	c.Assert(assertstate.Add(st, a5), IsNil)
   291  	c.Assert(s.storeSigning.Add(a5), IsNil)
   292  
   293  	// add pc-kernel snap declaration
   294  	headers = map[string]interface{}{
   295  		"series":       "16",
   296  		"snap-name":    "pc-kernel",
   297  		"publisher-id": "can0nical",
   298  		"timestamp":    time.Now().Format(time.RFC3339),
   299  	}
   300  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   301  	a6, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   302  	c.Assert(err, IsNil)
   303  	c.Assert(assertstate.Add(st, a6), IsNil)
   304  	c.Assert(s.storeSigning.Add(a6), IsNil)
   305  
   306  	// add core itself
   307  	snapstate.Set(st, "core", &snapstate.SnapState{
   308  		Active: true,
   309  		Sequence: []*snap.SideInfo{
   310  			{RealName: "core", SnapID: fakeSnapID("core"), Revision: snap.R(1)},
   311  		},
   312  		Current:  snap.R(1),
   313  		SnapType: "os",
   314  		Flags: snapstate.Flags{
   315  			Required: true,
   316  		},
   317  	})
   318  	// don't actually try to talk to the store on snapstate.Ensure
   319  	// needs doing after the call to devicestate.Manager (which happens in overlord.New)
   320  	snapstate.CanAutoRefresh = nil
   321  
   322  	st.Set("refresh-privacy-key", "privacy-key")
   323  }
   324  
   325  var settleTimeout = 15 * time.Second
   326  
   327  func makeTestSnap(c *C, snapYamlContent string) string {
   328  	info, err := snap.InfoFromSnapYaml([]byte(snapYamlContent))
   329  	c.Assert(err, IsNil)
   330  
   331  	var files [][]string
   332  	for _, app := range info.Apps {
   333  		// files is a list of (filename, content)
   334  		files = append(files, []string{app.Command, ""})
   335  	}
   336  
   337  	return snaptest.MakeTestSnapWithFiles(c, snapYamlContent, files)
   338  }
   339  
   340  func (s *mgrsSuite) TestHappyLocalInstall(c *C) {
   341  	snapYamlContent := `name: foo
   342  apps:
   343   bar:
   344    command: bin/bar
   345  `
   346  	snapPath := makeTestSnap(c, snapYamlContent+"version: 1.0")
   347  
   348  	st := s.o.State()
   349  	st.Lock()
   350  	defer st.Unlock()
   351  
   352  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "foo"}, snapPath, "", "", snapstate.Flags{DevMode: true})
   353  	c.Assert(err, IsNil)
   354  	chg := st.NewChange("install-snap", "...")
   355  	chg.AddAll(ts)
   356  
   357  	st.Unlock()
   358  	err = s.o.Settle(settleTimeout)
   359  	st.Lock()
   360  	c.Assert(err, IsNil)
   361  
   362  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
   363  
   364  	snap, err := snapstate.CurrentInfo(st, "foo")
   365  	c.Assert(err, IsNil)
   366  
   367  	// ensure that the binary wrapper file got generated with the right
   368  	// name
   369  	binaryWrapper := filepath.Join(dirs.SnapBinariesDir, "foo.bar")
   370  	c.Assert(osutil.IsSymlink(binaryWrapper), Equals, true)
   371  
   372  	// data dirs
   373  	c.Assert(osutil.IsDirectory(snap.DataDir()), Equals, true)
   374  	c.Assert(osutil.IsDirectory(snap.CommonDataDir()), Equals, true)
   375  
   376  	// snap file and its mounting
   377  
   378  	// after install the snap file is in the right dir
   379  	c.Assert(osutil.FileExists(filepath.Join(dirs.SnapBlobDir, "foo_x1.snap")), Equals, true)
   380  
   381  	// ensure the right unit is created
   382  	mup := systemd.MountUnitPath(filepath.Join(dirs.StripRootDir(dirs.SnapMountDir), "foo/x1"))
   383  	c.Assert(mup, testutil.FileMatches, fmt.Sprintf("(?ms).*^Where=%s/foo/x1", dirs.StripRootDir(dirs.SnapMountDir)))
   384  	c.Assert(mup, testutil.FileMatches, "(?ms).*^What=/var/lib/snapd/snaps/foo_x1.snap")
   385  }
   386  
   387  func (s *mgrsSuite) TestHappyRemove(c *C) {
   388  	st := s.o.State()
   389  	st.Lock()
   390  	defer st.Unlock()
   391  
   392  	snapYamlContent := `name: foo
   393  apps:
   394   bar:
   395    command: bin/bar
   396  `
   397  	snapInfo := s.installLocalTestSnap(c, snapYamlContent+"version: 1.0")
   398  
   399  	// set config
   400  	tr := config.NewTransaction(st)
   401  	c.Assert(tr.Set("foo", "key", "value"), IsNil)
   402  	tr.Commit()
   403  
   404  	ts, err := snapstate.Remove(st, "foo", snap.R(0), nil)
   405  	c.Assert(err, IsNil)
   406  	chg := st.NewChange("remove-snap", "...")
   407  	chg.AddAll(ts)
   408  
   409  	st.Unlock()
   410  	err = s.o.Settle(settleTimeout)
   411  	st.Lock()
   412  	c.Assert(err, IsNil)
   413  
   414  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("remove-snap change failed with: %v", chg.Err()))
   415  
   416  	// ensure that the binary wrapper file got removed
   417  	binaryWrapper := filepath.Join(dirs.SnapBinariesDir, "foo.bar")
   418  	c.Assert(osutil.FileExists(binaryWrapper), Equals, false)
   419  
   420  	// data dirs
   421  	c.Assert(osutil.FileExists(snapInfo.DataDir()), Equals, false)
   422  	c.Assert(osutil.FileExists(snapInfo.CommonDataDir()), Equals, false)
   423  
   424  	// snap file and its mount
   425  	c.Assert(osutil.FileExists(filepath.Join(dirs.SnapBlobDir, "foo_x1.snap")), Equals, false)
   426  	mup := systemd.MountUnitPath(filepath.Join(dirs.StripRootDir(dirs.SnapMountDir), "foo/x1"))
   427  	c.Assert(osutil.FileExists(mup), Equals, false)
   428  
   429  	// automatic snapshot was created
   430  	c.Assert(s.automaticSnapshots, DeepEquals, []automaticSnapshotCall{{"foo", map[string]interface{}{"key": "value"}, nil, &snapshotbackend.Flags{Auto: true}}})
   431  }
   432  
   433  func fakeSnapID(name string) string {
   434  	const suffix = "idididididididididididididididid"
   435  	return name + suffix[len(name)+1:]
   436  }
   437  
   438  const (
   439  	snapV2 = `{
   440  	"architectures": [
   441  	    "all"
   442  	],
   443          "download": {
   444              "url": "@URL@"
   445          },
   446          "epoch": @EPOCH@,
   447          "type": "@TYPE@",
   448  	"name": "@NAME@",
   449  	"revision": @REVISION@,
   450  	"snap-id": "@SNAPID@",
   451  	"summary": "Foo",
   452  	"description": "this is a description",
   453  	"version": "@VERSION@",
   454          "publisher": {
   455             "id": "devdevdev",
   456             "name": "bar"
   457           },
   458           "media": [
   459              {"type": "icon", "url": "@ICON@"}
   460           ]
   461  }`
   462  )
   463  
   464  var fooSnapID = fakeSnapID("foo")
   465  
   466  func (s *mgrsSuite) prereqSnapAssertions(c *C, extraHeaders ...map[string]interface{}) *asserts.SnapDeclaration {
   467  	if len(extraHeaders) == 0 {
   468  		extraHeaders = []map[string]interface{}{{}}
   469  	}
   470  	var snapDecl *asserts.SnapDeclaration
   471  	for _, extraHeaders := range extraHeaders {
   472  		headers := map[string]interface{}{
   473  			"series":       "16",
   474  			"snap-name":    "foo",
   475  			"publisher-id": "devdevdev",
   476  			"timestamp":    time.Now().Format(time.RFC3339),
   477  		}
   478  		for h, v := range extraHeaders {
   479  			headers[h] = v
   480  		}
   481  		headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   482  		a, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   483  		c.Assert(err, IsNil)
   484  		err = s.storeSigning.Add(a)
   485  		c.Assert(err, IsNil)
   486  		snapDecl = a.(*asserts.SnapDeclaration)
   487  	}
   488  	return snapDecl
   489  }
   490  
   491  func (s *mgrsSuite) makeStoreTestSnap(c *C, snapYaml string, revno string) (path, digest string) {
   492  	info, err := snap.InfoFromSnapYaml([]byte(snapYaml))
   493  	c.Assert(err, IsNil)
   494  
   495  	snapPath := makeTestSnap(c, snapYaml)
   496  
   497  	snapDigest, size, err := asserts.SnapFileSHA3_384(snapPath)
   498  	c.Assert(err, IsNil)
   499  
   500  	headers := map[string]interface{}{
   501  		"snap-id":       fakeSnapID(info.SnapName()),
   502  		"snap-sha3-384": snapDigest,
   503  		"snap-size":     fmt.Sprintf("%d", size),
   504  		"snap-revision": revno,
   505  		"developer-id":  "devdevdev",
   506  		"timestamp":     time.Now().Format(time.RFC3339),
   507  	}
   508  	snapRev, err := s.storeSigning.Sign(asserts.SnapRevisionType, headers, nil, "")
   509  	c.Assert(err, IsNil)
   510  	err = s.storeSigning.Add(snapRev)
   511  	c.Assert(err, IsNil)
   512  
   513  	return snapPath, snapDigest
   514  }
   515  
   516  func (s *mgrsSuite) pathFor(name, revno string) string {
   517  	if revno == s.serveRevision[name] {
   518  		return s.serveSnapPath[name]
   519  	}
   520  	for i, r := range s.serveOldRevs[name] {
   521  		if r == revno {
   522  			return s.serveOldPaths[name][i]
   523  		}
   524  	}
   525  	return "/not/found"
   526  }
   527  
   528  func (s *mgrsSuite) newestThatCanRead(name string, epoch snap.Epoch) (info *snap.Info, rev string) {
   529  	if s.serveSnapPath[name] == "" {
   530  		return nil, ""
   531  	}
   532  	idx := len(s.serveOldPaths[name])
   533  	rev = s.serveRevision[name]
   534  	path := s.serveSnapPath[name]
   535  	for {
   536  		snapf, err := snap.Open(path)
   537  		if err != nil {
   538  			panic(err)
   539  		}
   540  		info, err := snap.ReadInfoFromSnapFile(snapf, nil)
   541  		if err != nil {
   542  			panic(err)
   543  		}
   544  		if info.Epoch.CanRead(epoch) {
   545  			return info, rev
   546  		}
   547  		idx--
   548  		if idx < 0 {
   549  			return nil, ""
   550  		}
   551  		path = s.serveOldPaths[name][idx]
   552  		rev = s.serveOldRevs[name][idx]
   553  	}
   554  }
   555  
   556  func (s *mgrsSuite) mockStore(c *C) *httptest.Server {
   557  	var baseURL *url.URL
   558  	fillHit := func(hitTemplate, revno string, info *snap.Info) string {
   559  		epochBuf, err := json.Marshal(info.Epoch)
   560  		if err != nil {
   561  			panic(err)
   562  		}
   563  		name := info.SnapName()
   564  
   565  		hit := strings.Replace(hitTemplate, "@URL@", baseURL.String()+"/api/v1/snaps/download/"+name+"/"+revno, -1)
   566  		hit = strings.Replace(hit, "@NAME@", name, -1)
   567  		hit = strings.Replace(hit, "@SNAPID@", fakeSnapID(name), -1)
   568  		hit = strings.Replace(hit, "@ICON@", baseURL.String()+"/icon", -1)
   569  		hit = strings.Replace(hit, "@VERSION@", info.Version, -1)
   570  		hit = strings.Replace(hit, "@REVISION@", revno, -1)
   571  		hit = strings.Replace(hit, `@TYPE@`, string(info.GetType()), -1)
   572  		hit = strings.Replace(hit, `@EPOCH@`, string(epochBuf), -1)
   573  		return hit
   574  	}
   575  
   576  	mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   577  		// all URLS are /api/v1/snaps/... or /v2/snaps/... so
   578  		// check the url is sane and discard the common prefix
   579  		// to simplify indexing into the comps slice.
   580  		comps := strings.Split(r.URL.Path, "/")
   581  		if len(comps) < 2 {
   582  			panic("unexpected url path: " + r.URL.Path)
   583  		}
   584  		if comps[1] == "api" { //v1
   585  			if len(comps) <= 4 {
   586  				panic("unexpected url path: " + r.URL.Path)
   587  			}
   588  			comps = comps[4:]
   589  			if comps[0] == "auth" {
   590  				comps[0] = "auth:" + comps[1]
   591  			}
   592  		} else { // v2
   593  			if len(comps) <= 3 {
   594  				panic("unexpected url path: " + r.URL.Path)
   595  			}
   596  			comps = comps[3:]
   597  			comps[0] = "v2:" + comps[0]
   598  		}
   599  
   600  		switch comps[0] {
   601  		case "auth:nonces":
   602  			w.Write([]byte(`{"nonce": "NONCE"}`))
   603  			return
   604  		case "auth:sessions":
   605  			// quick sanity check
   606  			reqBody, err := ioutil.ReadAll(r.Body)
   607  			c.Check(err, IsNil)
   608  			c.Check(bytes.Contains(reqBody, []byte("nonce: NONCE")), Equals, true)
   609  			c.Check(bytes.Contains(reqBody, []byte(fmt.Sprintf("serial: %s", s.expectedSerial))), Equals, true)
   610  			c.Check(bytes.Contains(reqBody, []byte(fmt.Sprintf("store: %s", s.expectedStore))), Equals, true)
   611  
   612  			c.Check(s.sessionMacaroon, Not(Equals), "")
   613  			w.WriteHeader(200)
   614  			w.Write([]byte(fmt.Sprintf(`{"macaroon": "%s"}`, s.sessionMacaroon)))
   615  			return
   616  		case "assertions":
   617  			ref := &asserts.Ref{
   618  				Type:       asserts.Type(comps[1]),
   619  				PrimaryKey: comps[2:],
   620  			}
   621  			a, err := ref.Resolve(s.storeSigning.Find)
   622  			if asserts.IsNotFound(err) {
   623  				w.Header().Set("Content-Type", "application/json")
   624  				w.WriteHeader(404)
   625  				w.Write([]byte(`{"status": 404}`))
   626  				return
   627  			}
   628  			if err != nil {
   629  				panic(err)
   630  			}
   631  			w.Header().Set("Content-Type", asserts.MediaType)
   632  			w.WriteHeader(200)
   633  			w.Write(asserts.Encode(a))
   634  			return
   635  		case "download":
   636  			if s.sessionMacaroon != "" {
   637  				// FIXME: download is still using the old headers!
   638  				c.Check(r.Header.Get("X-Device-Authorization"), Equals, fmt.Sprintf(`Macaroon root="%s"`, s.sessionMacaroon))
   639  			}
   640  			if s.failNextDownload == comps[1] {
   641  				s.failNextDownload = ""
   642  				w.WriteHeader(418)
   643  				return
   644  			}
   645  			if s.hijackServeSnap != nil {
   646  				s.hijackServeSnap(w)
   647  				return
   648  			}
   649  			snapR, err := os.Open(s.pathFor(comps[1], comps[2]))
   650  			if err != nil {
   651  				panic(err)
   652  			}
   653  			io.Copy(w, snapR)
   654  		case "v2:refresh":
   655  			if s.sessionMacaroon != "" {
   656  				c.Check(r.Header.Get("Snap-Device-Authorization"), Equals, fmt.Sprintf(`Macaroon root="%s"`, s.sessionMacaroon))
   657  			}
   658  			dec := json.NewDecoder(r.Body)
   659  			var input struct {
   660  				Actions []struct {
   661  					Action      string     `json:"action"`
   662  					SnapID      string     `json:"snap-id"`
   663  					Name        string     `json:"name"`
   664  					InstanceKey string     `json:"instance-key"`
   665  					Epoch       snap.Epoch `json:"epoch"`
   666  				} `json:"actions"`
   667  				Context []struct {
   668  					SnapID string     `json:"snap-id"`
   669  					Epoch  snap.Epoch `json:"epoch"`
   670  				} `json:"context"`
   671  			}
   672  			if err := dec.Decode(&input); err != nil {
   673  				panic(err)
   674  			}
   675  			id2epoch := make(map[string]snap.Epoch, len(input.Context))
   676  			for _, s := range input.Context {
   677  				id2epoch[s.SnapID] = s.Epoch
   678  			}
   679  			type resultJSON struct {
   680  				Result      string          `json:"result"`
   681  				SnapID      string          `json:"snap-id"`
   682  				Name        string          `json:"name"`
   683  				Snap        json.RawMessage `json:"snap"`
   684  				InstanceKey string          `json:"instance-key"`
   685  			}
   686  			var results []resultJSON
   687  			for _, a := range input.Actions {
   688  				name := s.serveIDtoName[a.SnapID]
   689  				epoch := id2epoch[a.SnapID]
   690  				if a.Action == "install" {
   691  					name = a.Name
   692  					epoch = a.Epoch
   693  				}
   694  
   695  				info, revno := s.newestThatCanRead(name, epoch)
   696  				if info == nil {
   697  					// no match
   698  					continue
   699  				}
   700  				results = append(results, resultJSON{
   701  					Result:      a.Action,
   702  					SnapID:      a.SnapID,
   703  					InstanceKey: a.InstanceKey,
   704  					Name:        name,
   705  					Snap:        json.RawMessage(fillHit(snapV2, revno, info)),
   706  				})
   707  			}
   708  			w.WriteHeader(200)
   709  			output, err := json.Marshal(map[string]interface{}{
   710  				"results": results,
   711  			})
   712  			if err != nil {
   713  				panic(err)
   714  			}
   715  			w.Write(output)
   716  
   717  		default:
   718  			panic("unexpected url path: " + r.URL.Path)
   719  		}
   720  	}))
   721  	c.Assert(mockServer, NotNil)
   722  
   723  	baseURL, _ = url.Parse(mockServer.URL)
   724  	storeCfg := store.Config{
   725  		StoreBaseURL: baseURL,
   726  	}
   727  
   728  	mStore := store.New(&storeCfg, nil)
   729  	st := s.o.State()
   730  	st.Lock()
   731  	snapstate.ReplaceStore(s.o.State(), mStore)
   732  	st.Unlock()
   733  
   734  	// this will be used by remodeling cases
   735  	storeNew := func(cfg *store.Config, dac store.DeviceAndAuthContext) *store.Store {
   736  		cfg.StoreBaseURL = baseURL
   737  		if s.checkDeviceAndAuthContext != nil {
   738  			s.checkDeviceAndAuthContext(dac)
   739  		}
   740  		return store.New(cfg, dac)
   741  	}
   742  
   743  	s.AddCleanup(overlord.MockStoreNew(storeNew))
   744  
   745  	return mockServer
   746  }
   747  
   748  // serveSnap starts serving the snap at snapPath, moving the current
   749  // one onto the list of previous ones if already set.
   750  func (s *mgrsSuite) serveSnap(snapPath, revno string) {
   751  	snapf, err := snap.Open(snapPath)
   752  	if err != nil {
   753  		panic(err)
   754  	}
   755  	info, err := snap.ReadInfoFromSnapFile(snapf, nil)
   756  	if err != nil {
   757  		panic(err)
   758  	}
   759  	name := info.SnapName()
   760  	s.serveIDtoName[fakeSnapID(name)] = name
   761  
   762  	if oldPath := s.serveSnapPath[name]; oldPath != "" {
   763  		oldRev := s.serveRevision[name]
   764  		if oldRev == "" {
   765  			panic("old path set but not old revision")
   766  		}
   767  		s.serveOldPaths[name] = append(s.serveOldPaths[name], oldPath)
   768  		s.serveOldRevs[name] = append(s.serveOldRevs[name], oldRev)
   769  	}
   770  	s.serveSnapPath[name] = snapPath
   771  	s.serveRevision[name] = revno
   772  }
   773  
   774  func (s *mgrsSuite) TestHappyRemoteInstallAndUpgradeSvc(c *C) {
   775  	// test install through store and update, plus some mechanics
   776  	// of update
   777  	// TODO: ok to split if it gets too messy to maintain
   778  
   779  	s.prereqSnapAssertions(c)
   780  
   781  	snapYamlContent := `name: foo
   782  version: @VERSION@
   783  apps:
   784   bar:
   785    command: bin/bar
   786   svc:
   787    command: svc
   788    daemon: forking
   789  `
   790  
   791  	ver := "1.0"
   792  	revno := "42"
   793  	snapPath, digest := s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno)
   794  	s.serveSnap(snapPath, revno)
   795  
   796  	mockServer := s.mockStore(c)
   797  	defer mockServer.Close()
   798  
   799  	st := s.o.State()
   800  	st.Lock()
   801  	defer st.Unlock()
   802  
   803  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
   804  	c.Assert(err, IsNil)
   805  	chg := st.NewChange("install-snap", "...")
   806  	chg.AddAll(ts)
   807  
   808  	st.Unlock()
   809  	err = s.o.Settle(settleTimeout)
   810  	st.Lock()
   811  	c.Assert(err, IsNil)
   812  
   813  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
   814  
   815  	info, err := snapstate.CurrentInfo(st, "foo")
   816  	c.Assert(err, IsNil)
   817  
   818  	c.Check(info.Revision, Equals, snap.R(42))
   819  	c.Check(info.SnapID, Equals, fooSnapID)
   820  	c.Check(info.Version, Equals, "1.0")
   821  	c.Check(info.Summary(), Equals, "Foo")
   822  	c.Check(info.Description(), Equals, "this is a description")
   823  	c.Assert(osutil.FileExists(info.MountFile()), Equals, true)
   824  
   825  	pubAcct, err := assertstate.Publisher(st, info.SnapID)
   826  	c.Assert(err, IsNil)
   827  	c.Check(pubAcct.AccountID(), Equals, "devdevdev")
   828  	c.Check(pubAcct.Username(), Equals, "devdevdev")
   829  
   830  	snapRev42, err := assertstate.DB(st).Find(asserts.SnapRevisionType, map[string]string{
   831  		"snap-sha3-384": digest,
   832  	})
   833  	c.Assert(err, IsNil)
   834  	c.Check(snapRev42.(*asserts.SnapRevision).SnapID(), Equals, fooSnapID)
   835  	c.Check(snapRev42.(*asserts.SnapRevision).SnapRevision(), Equals, 42)
   836  
   837  	// check service was setup properly
   838  	svcFile := filepath.Join(dirs.SnapServicesDir, "snap.foo.svc.service")
   839  	c.Assert(osutil.FileExists(svcFile), Equals, true)
   840  	stat, err := os.Stat(svcFile)
   841  	c.Assert(err, IsNil)
   842  	// should _not_ be executable
   843  	c.Assert(stat.Mode().String(), Equals, "-rw-r--r--")
   844  
   845  	// Refresh
   846  
   847  	ver = "2.0"
   848  	revno = "50"
   849  	snapPath, digest = s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno)
   850  	s.serveSnap(snapPath, revno)
   851  
   852  	ts, err = snapstate.Update(st, "foo", nil, 0, snapstate.Flags{})
   853  	c.Assert(err, IsNil)
   854  	chg = st.NewChange("upgrade-snap", "...")
   855  	chg.AddAll(ts)
   856  
   857  	st.Unlock()
   858  	err = s.o.Settle(settleTimeout)
   859  	st.Lock()
   860  	c.Assert(err, IsNil)
   861  
   862  	c.Assert(chg.Err(), IsNil)
   863  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
   864  
   865  	info, err = snapstate.CurrentInfo(st, "foo")
   866  	c.Assert(err, IsNil)
   867  
   868  	c.Check(info.Revision, Equals, snap.R(50))
   869  	c.Check(info.SnapID, Equals, fooSnapID)
   870  	c.Check(info.Version, Equals, "2.0")
   871  
   872  	snapRev50, err := assertstate.DB(st).Find(asserts.SnapRevisionType, map[string]string{
   873  		"snap-sha3-384": digest,
   874  	})
   875  	c.Assert(err, IsNil)
   876  	c.Check(snapRev50.(*asserts.SnapRevision).SnapID(), Equals, fooSnapID)
   877  	c.Check(snapRev50.(*asserts.SnapRevision).SnapRevision(), Equals, 50)
   878  
   879  	// check updated wrapper
   880  	symlinkTarget, err := os.Readlink(info.Apps["bar"].WrapperPath())
   881  	c.Assert(err, IsNil)
   882  	c.Assert(symlinkTarget, Equals, "/usr/bin/snap")
   883  
   884  	// check updated service file
   885  	c.Assert(svcFile, testutil.FileContains, "/var/snap/foo/"+revno)
   886  }
   887  
   888  func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateWithEpochBump(c *C) {
   889  	// test install through store and update, where there's an epoch bump in the upgrade
   890  	// this does less checks on the details of install/update than TestHappyRemoteInstallAndUpgradeSvc
   891  
   892  	s.prereqSnapAssertions(c)
   893  
   894  	snapPath, _ := s.makeStoreTestSnap(c, "{name: foo, version: 0}", "1")
   895  	s.serveSnap(snapPath, "1")
   896  
   897  	mockServer := s.mockStore(c)
   898  	defer mockServer.Close()
   899  
   900  	st := s.o.State()
   901  	st.Lock()
   902  	defer st.Unlock()
   903  
   904  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
   905  	c.Assert(err, IsNil)
   906  	chg := st.NewChange("install-snap", "...")
   907  	chg.AddAll(ts)
   908  
   909  	st.Unlock()
   910  	err = s.o.Settle(settleTimeout)
   911  	st.Lock()
   912  	c.Assert(err, IsNil)
   913  
   914  	// confirm it worked
   915  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
   916  
   917  	// sanity checks
   918  	info, err := snapstate.CurrentInfo(st, "foo")
   919  	c.Assert(err, IsNil)
   920  	c.Assert(info.Revision, Equals, snap.R(1))
   921  	c.Assert(info.SnapID, Equals, fooSnapID)
   922  	c.Assert(info.Epoch.String(), Equals, "0")
   923  
   924  	// now add some more snaps
   925  	for i, epoch := range []string{"1*", "2*", "3*"} {
   926  		revno := fmt.Sprint(i + 2)
   927  		snapPath, _ := s.makeStoreTestSnap(c, "{name: foo, version: 0, epoch: "+epoch+"}", revno)
   928  		s.serveSnap(snapPath, revno)
   929  	}
   930  
   931  	// refresh
   932  
   933  	ts, err = snapstate.Update(st, "foo", nil, 0, snapstate.Flags{})
   934  	c.Assert(err, IsNil)
   935  	chg = st.NewChange("upgrade-snap", "...")
   936  	chg.AddAll(ts)
   937  
   938  	st.Unlock()
   939  	err = s.o.Settle(settleTimeout)
   940  	st.Lock()
   941  	c.Assert(err, IsNil)
   942  
   943  	c.Assert(chg.Err(), IsNil)
   944  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
   945  
   946  	info, err = snapstate.CurrentInfo(st, "foo")
   947  	c.Assert(err, IsNil)
   948  
   949  	c.Check(info.Revision, Equals, snap.R(4))
   950  	c.Check(info.SnapID, Equals, fooSnapID)
   951  	c.Check(info.Epoch.String(), Equals, "3*")
   952  }
   953  
   954  func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateWithPostHocEpochBump(c *C) {
   955  	// test install through store and update, where there is an epoch
   956  	// bump in the upgrade that comes in after the initial update is
   957  	// computed.
   958  
   959  	// this is mostly checking the same as TestHappyRemoteInstallAndUpdateWithEpochBump
   960  	// but serves as a sanity check for the Without case that follows
   961  	// (these two together serve as a test for the refresh filtering)
   962  	s.testHappyRemoteInstallAndUpdateWithMaybeEpochBump(c, true)
   963  }
   964  
   965  func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateWithoutEpochBump(c *C) {
   966  	// test install through store and update, where there _isn't_ an epoch bump in the upgrade
   967  	// note that there _are_ refreshes available after the refresh,
   968  	// but they're not an epoch bump so they're ignored
   969  	s.testHappyRemoteInstallAndUpdateWithMaybeEpochBump(c, false)
   970  }
   971  
   972  func (s *mgrsSuite) testHappyRemoteInstallAndUpdateWithMaybeEpochBump(c *C, doBump bool) {
   973  	s.prereqSnapAssertions(c)
   974  
   975  	snapPath, _ := s.makeStoreTestSnap(c, "{name: foo, version: 1}", "1")
   976  	s.serveSnap(snapPath, "1")
   977  
   978  	mockServer := s.mockStore(c)
   979  	defer mockServer.Close()
   980  
   981  	st := s.o.State()
   982  	st.Lock()
   983  	defer st.Unlock()
   984  
   985  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
   986  	c.Assert(err, IsNil)
   987  	chg := st.NewChange("install-snap", "...")
   988  	chg.AddAll(ts)
   989  
   990  	st.Unlock()
   991  	err = s.o.Settle(settleTimeout)
   992  	st.Lock()
   993  	c.Assert(err, IsNil)
   994  
   995  	// confirm it worked
   996  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
   997  
   998  	// sanity checks
   999  	info, err := snapstate.CurrentInfo(st, "foo")
  1000  	c.Assert(err, IsNil)
  1001  	c.Assert(info.Revision, Equals, snap.R(1))
  1002  	c.Assert(info.SnapID, Equals, fooSnapID)
  1003  	c.Assert(info.Epoch.String(), Equals, "0")
  1004  
  1005  	// add a new revision
  1006  	snapPath, _ = s.makeStoreTestSnap(c, "{name: foo, version: 2}", "2")
  1007  	s.serveSnap(snapPath, "2")
  1008  
  1009  	// refresh
  1010  
  1011  	ts, err = snapstate.Update(st, "foo", nil, 0, snapstate.Flags{})
  1012  	c.Assert(err, IsNil)
  1013  	chg = st.NewChange("upgrade-snap", "...")
  1014  	chg.AddAll(ts)
  1015  
  1016  	// add another new revision, after the update was computed (maybe with an epoch bump)
  1017  	if doBump {
  1018  		snapPath, _ = s.makeStoreTestSnap(c, "{name: foo, version: 3, epoch: 1*}", "3")
  1019  	} else {
  1020  		snapPath, _ = s.makeStoreTestSnap(c, "{name: foo, version: 3}", "3")
  1021  	}
  1022  	s.serveSnap(snapPath, "3")
  1023  
  1024  	st.Unlock()
  1025  	err = s.o.Settle(settleTimeout)
  1026  	st.Lock()
  1027  	c.Assert(err, IsNil)
  1028  
  1029  	c.Assert(chg.Err(), IsNil)
  1030  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  1031  
  1032  	info, err = snapstate.CurrentInfo(st, "foo")
  1033  	c.Assert(err, IsNil)
  1034  
  1035  	if doBump {
  1036  		// if the epoch bumped, then we should've re-refreshed
  1037  		c.Check(info.Revision, Equals, snap.R(3))
  1038  		c.Check(info.SnapID, Equals, fooSnapID)
  1039  		c.Check(info.Epoch.String(), Equals, "1*")
  1040  	} else {
  1041  		// if the epoch did not bump, then we should _not_ have re-refreshed
  1042  		c.Check(info.Revision, Equals, snap.R(2))
  1043  		c.Check(info.SnapID, Equals, fooSnapID)
  1044  		c.Check(info.Epoch.String(), Equals, "0")
  1045  	}
  1046  }
  1047  
  1048  func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateManyWithEpochBump(c *C) {
  1049  	// test install through store and update many, where there's an epoch bump in the upgrade
  1050  	// this does less checks on the details of install/update than TestHappyRemoteInstallAndUpgradeSvc
  1051  
  1052  	snapNames := []string{"aaaa", "bbbb", "cccc"}
  1053  	for _, name := range snapNames {
  1054  		s.prereqSnapAssertions(c, map[string]interface{}{"snap-name": name})
  1055  		snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 0}", name), "1")
  1056  		s.serveSnap(snapPath, "1")
  1057  	}
  1058  
  1059  	mockServer := s.mockStore(c)
  1060  	defer mockServer.Close()
  1061  
  1062  	st := s.o.State()
  1063  	st.Lock()
  1064  	defer st.Unlock()
  1065  
  1066  	affected, tasksets, err := snapstate.InstallMany(st, snapNames, 0)
  1067  	c.Assert(err, IsNil)
  1068  	sort.Strings(affected)
  1069  	c.Check(affected, DeepEquals, snapNames)
  1070  	chg := st.NewChange("install-snaps", "...")
  1071  	for _, taskset := range tasksets {
  1072  		chg.AddAll(taskset)
  1073  	}
  1074  
  1075  	st.Unlock()
  1076  	err = s.o.Settle(settleTimeout)
  1077  	st.Lock()
  1078  	c.Assert(err, IsNil)
  1079  
  1080  	// confirm it worked
  1081  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1082  
  1083  	// sanity checks
  1084  	for _, name := range snapNames {
  1085  		info, err := snapstate.CurrentInfo(st, name)
  1086  		c.Assert(err, IsNil)
  1087  		c.Assert(info.Revision, Equals, snap.R(1))
  1088  		c.Assert(info.SnapID, Equals, fakeSnapID(name))
  1089  		c.Assert(info.Epoch.String(), Equals, "0")
  1090  	}
  1091  
  1092  	// now add some more snap revisions with increasing epochs
  1093  	for _, name := range snapNames {
  1094  		for i, epoch := range []string{"1*", "2*", "3*"} {
  1095  			revno := fmt.Sprint(i + 2)
  1096  			snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 0, epoch: %s}", name, epoch), revno)
  1097  			s.serveSnap(snapPath, revno)
  1098  		}
  1099  	}
  1100  
  1101  	// refresh
  1102  
  1103  	affected, tasksets, err = snapstate.UpdateMany(context.TODO(), st, nil, 0, &snapstate.Flags{})
  1104  	c.Assert(err, IsNil)
  1105  	sort.Strings(affected)
  1106  	c.Check(affected, DeepEquals, snapNames)
  1107  	chg = st.NewChange("upgrade-snaps", "...")
  1108  	for _, taskset := range tasksets {
  1109  		chg.AddAll(taskset)
  1110  	}
  1111  
  1112  	st.Unlock()
  1113  	err = s.o.Settle(settleTimeout)
  1114  	st.Lock()
  1115  	c.Assert(err, IsNil)
  1116  
  1117  	c.Assert(chg.Err(), IsNil)
  1118  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  1119  
  1120  	for _, name := range snapNames {
  1121  		info, err := snapstate.CurrentInfo(st, name)
  1122  		c.Assert(err, IsNil)
  1123  
  1124  		c.Check(info.Revision, Equals, snap.R(4))
  1125  		c.Check(info.SnapID, Equals, fakeSnapID(name))
  1126  		c.Check(info.Epoch.String(), Equals, "3*")
  1127  	}
  1128  }
  1129  
  1130  func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateManyWithEpochBumpAndOneFailing(c *C) {
  1131  	// test install through store and update, where there's an epoch bump in the upgrade and one of them fails
  1132  
  1133  	snapNames := []string{"aaaa", "bbbb", "cccc"}
  1134  	for _, name := range snapNames {
  1135  		s.prereqSnapAssertions(c, map[string]interface{}{"snap-name": name})
  1136  		snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 0}", name), "1")
  1137  		s.serveSnap(snapPath, "1")
  1138  	}
  1139  
  1140  	mockServer := s.mockStore(c)
  1141  	defer mockServer.Close()
  1142  
  1143  	st := s.o.State()
  1144  	st.Lock()
  1145  	defer st.Unlock()
  1146  
  1147  	affected, tasksets, err := snapstate.InstallMany(st, snapNames, 0)
  1148  	c.Assert(err, IsNil)
  1149  	sort.Strings(affected)
  1150  	c.Check(affected, DeepEquals, snapNames)
  1151  	chg := st.NewChange("install-snaps", "...")
  1152  	for _, taskset := range tasksets {
  1153  		chg.AddAll(taskset)
  1154  	}
  1155  
  1156  	st.Unlock()
  1157  	err = s.o.Settle(settleTimeout)
  1158  	st.Lock()
  1159  	c.Assert(err, IsNil)
  1160  
  1161  	// confirm it worked
  1162  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1163  
  1164  	// sanity checks
  1165  	for _, name := range snapNames {
  1166  		info, err := snapstate.CurrentInfo(st, name)
  1167  		c.Assert(err, IsNil)
  1168  		c.Assert(info.Revision, Equals, snap.R(1))
  1169  		c.Assert(info.SnapID, Equals, fakeSnapID(name))
  1170  		c.Assert(info.Epoch.String(), Equals, "0")
  1171  	}
  1172  
  1173  	// now add some more snap revisions with increasing epochs
  1174  	for _, name := range snapNames {
  1175  		for i, epoch := range []string{"1*", "2*", "3*"} {
  1176  			revno := fmt.Sprint(i + 2)
  1177  			snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 0, epoch: %s}", name, epoch), revno)
  1178  			s.serveSnap(snapPath, revno)
  1179  		}
  1180  	}
  1181  
  1182  	// refresh
  1183  	affected, tasksets, err = snapstate.UpdateMany(context.TODO(), st, nil, 0, &snapstate.Flags{})
  1184  	c.Assert(err, IsNil)
  1185  	sort.Strings(affected)
  1186  	c.Check(affected, DeepEquals, snapNames)
  1187  	chg = st.NewChange("upgrade-snaps", "...")
  1188  	for _, taskset := range tasksets {
  1189  		chg.AddAll(taskset)
  1190  	}
  1191  
  1192  	st.Unlock()
  1193  	// the download for the refresh above will be performed below, during 'settle'.
  1194  	// fail the refresh of cccc by failing its download
  1195  	s.failNextDownload = "cccc"
  1196  	err = s.o.Settle(settleTimeout)
  1197  	st.Lock()
  1198  	c.Assert(err, IsNil)
  1199  
  1200  	c.Assert(chg.Err(), NotNil)
  1201  	c.Assert(chg.Status(), Equals, state.ErrorStatus)
  1202  
  1203  	for _, name := range snapNames {
  1204  		comment := Commentf("%q", name)
  1205  		info, err := snapstate.CurrentInfo(st, name)
  1206  		c.Assert(err, IsNil, comment)
  1207  
  1208  		if name == "cccc" {
  1209  			// the failed one: still on rev 1 (epoch 0)
  1210  			c.Assert(info.Revision, Equals, snap.R(1))
  1211  			c.Assert(info.SnapID, Equals, fakeSnapID(name))
  1212  			c.Assert(info.Epoch.String(), Equals, "0")
  1213  		} else {
  1214  			// the non-failed ones: refreshed to rev 4 (epoch 3*)
  1215  			c.Check(info.Revision, Equals, snap.R(4), comment)
  1216  			c.Check(info.SnapID, Equals, fakeSnapID(name), comment)
  1217  			c.Check(info.Epoch.String(), Equals, "3*", comment)
  1218  		}
  1219  	}
  1220  }
  1221  
  1222  func (s *mgrsSuite) TestHappyLocalInstallWithStoreMetadata(c *C) {
  1223  	snapDecl := s.prereqSnapAssertions(c)
  1224  
  1225  	snapYamlContent := `name: foo
  1226  apps:
  1227   bar:
  1228    command: bin/bar
  1229  `
  1230  	snapPath := makeTestSnap(c, snapYamlContent+"version: 1.5")
  1231  
  1232  	si := &snap.SideInfo{
  1233  		RealName: "foo",
  1234  		SnapID:   fooSnapID,
  1235  		Revision: snap.R(55),
  1236  	}
  1237  
  1238  	st := s.o.State()
  1239  	st.Lock()
  1240  	defer st.Unlock()
  1241  
  1242  	// have the snap-declaration in the system db
  1243  	err := assertstate.Add(st, s.devAcct)
  1244  	c.Assert(err, IsNil)
  1245  	err = assertstate.Add(st, snapDecl)
  1246  	c.Assert(err, IsNil)
  1247  
  1248  	ts, _, err := snapstate.InstallPath(st, si, snapPath, "", "", snapstate.Flags{DevMode: true})
  1249  	c.Assert(err, IsNil)
  1250  	chg := st.NewChange("install-snap", "...")
  1251  	chg.AddAll(ts)
  1252  
  1253  	st.Unlock()
  1254  	err = s.o.Settle(settleTimeout)
  1255  	st.Lock()
  1256  	c.Assert(err, IsNil)
  1257  
  1258  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1259  
  1260  	info, err := snapstate.CurrentInfo(st, "foo")
  1261  	c.Assert(err, IsNil)
  1262  	c.Check(info.Revision, Equals, snap.R(55))
  1263  	c.Check(info.SnapID, Equals, fooSnapID)
  1264  	c.Check(info.Version, Equals, "1.5")
  1265  
  1266  	// ensure that the binary wrapper file got generated with the right
  1267  	// name
  1268  	binaryWrapper := filepath.Join(dirs.SnapBinariesDir, "foo.bar")
  1269  	c.Assert(osutil.IsSymlink(binaryWrapper), Equals, true)
  1270  
  1271  	// data dirs
  1272  	c.Assert(osutil.IsDirectory(info.DataDir()), Equals, true)
  1273  	c.Assert(osutil.IsDirectory(info.CommonDataDir()), Equals, true)
  1274  
  1275  	// snap file and its mounting
  1276  
  1277  	// after install the snap file is in the right dir
  1278  	c.Assert(osutil.FileExists(filepath.Join(dirs.SnapBlobDir, "foo_55.snap")), Equals, true)
  1279  
  1280  	// ensure the right unit is created
  1281  	mup := systemd.MountUnitPath(filepath.Join(dirs.StripRootDir(dirs.SnapMountDir), "foo/55"))
  1282  	c.Assert(mup, testutil.FileMatches, fmt.Sprintf("(?ms).*^Where=%s/foo/55", dirs.StripRootDir(dirs.SnapMountDir)))
  1283  	c.Assert(mup, testutil.FileMatches, "(?ms).*^What=/var/lib/snapd/snaps/foo_55.snap")
  1284  }
  1285  
  1286  func (s *mgrsSuite) TestParallelInstanceLocalInstallSnapNameMismatch(c *C) {
  1287  	snapDecl := s.prereqSnapAssertions(c)
  1288  
  1289  	snapYamlContent := `name: foo
  1290  apps:
  1291   bar:
  1292    command: bin/bar
  1293  `
  1294  	snapPath := makeTestSnap(c, snapYamlContent+"version: 1.5")
  1295  
  1296  	si := &snap.SideInfo{
  1297  		RealName: "foo",
  1298  		SnapID:   fooSnapID,
  1299  		Revision: snap.R(55),
  1300  	}
  1301  
  1302  	st := s.o.State()
  1303  	st.Lock()
  1304  	defer st.Unlock()
  1305  
  1306  	// have the snap-declaration in the system db
  1307  	err := assertstate.Add(st, s.devAcct)
  1308  	c.Assert(err, IsNil)
  1309  	err = assertstate.Add(st, snapDecl)
  1310  	c.Assert(err, IsNil)
  1311  
  1312  	_, _, err = snapstate.InstallPath(st, si, snapPath, "bar_instance", "", snapstate.Flags{DevMode: true})
  1313  	c.Assert(err, ErrorMatches, `cannot install snap "bar_instance", the name does not match the metadata "foo"`)
  1314  }
  1315  
  1316  func (s *mgrsSuite) TestParallelInstanceLocalInstallInvalidInstanceName(c *C) {
  1317  	snapDecl := s.prereqSnapAssertions(c)
  1318  
  1319  	snapYamlContent := `name: foo
  1320  apps:
  1321   bar:
  1322    command: bin/bar
  1323  `
  1324  	snapPath := makeTestSnap(c, snapYamlContent+"version: 1.5")
  1325  
  1326  	si := &snap.SideInfo{
  1327  		RealName: "foo",
  1328  		SnapID:   fooSnapID,
  1329  		Revision: snap.R(55),
  1330  	}
  1331  
  1332  	st := s.o.State()
  1333  	st.Lock()
  1334  	defer st.Unlock()
  1335  
  1336  	// have the snap-declaration in the system db
  1337  	err := assertstate.Add(st, s.devAcct)
  1338  	c.Assert(err, IsNil)
  1339  	err = assertstate.Add(st, snapDecl)
  1340  	c.Assert(err, IsNil)
  1341  
  1342  	_, _, err = snapstate.InstallPath(st, si, snapPath, "bar_invalid_instance_name", "", snapstate.Flags{DevMode: true})
  1343  	c.Assert(err, ErrorMatches, `invalid instance name: invalid instance key: "invalid_instance_name"`)
  1344  }
  1345  
  1346  func (s *mgrsSuite) TestCheckInterfaces(c *C) {
  1347  	snapDecl := s.prereqSnapAssertions(c)
  1348  
  1349  	snapYamlContent := `name: foo
  1350  apps:
  1351   bar:
  1352    command: bin/bar
  1353  slots:
  1354   network:
  1355  `
  1356  	snapPath := makeTestSnap(c, snapYamlContent+"version: 1.5")
  1357  
  1358  	si := &snap.SideInfo{
  1359  		RealName: "foo",
  1360  		SnapID:   fooSnapID,
  1361  		Revision: snap.R(55),
  1362  	}
  1363  
  1364  	st := s.o.State()
  1365  	st.Lock()
  1366  	defer st.Unlock()
  1367  
  1368  	// have the snap-declaration in the system db
  1369  	err := assertstate.Add(st, s.devAcct)
  1370  	c.Assert(err, IsNil)
  1371  	err = assertstate.Add(st, snapDecl)
  1372  	c.Assert(err, IsNil)
  1373  
  1374  	// mock SanitizePlugsSlots so that unknown interfaces are not rejected
  1375  	restoreSanitize := snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})
  1376  	defer restoreSanitize()
  1377  
  1378  	ts, _, err := snapstate.InstallPath(st, si, snapPath, "", "", snapstate.Flags{DevMode: true})
  1379  	c.Assert(err, IsNil)
  1380  	chg := st.NewChange("install-snap", "...")
  1381  	chg.AddAll(ts)
  1382  
  1383  	st.Unlock()
  1384  	err = s.o.Settle(settleTimeout)
  1385  	st.Lock()
  1386  	c.Assert(err, IsNil)
  1387  
  1388  	c.Assert(chg.Err(), ErrorMatches, `(?s).*installation not allowed by "network" slot rule of interface "network".*`)
  1389  	c.Check(chg.Status(), Equals, state.ErrorStatus)
  1390  }
  1391  
  1392  func (s *mgrsSuite) TestHappyRefreshControl(c *C) {
  1393  	// test install through store and update, plus some mechanics
  1394  	// of update
  1395  	// TODO: ok to split if it gets too messy to maintain
  1396  
  1397  	s.prereqSnapAssertions(c)
  1398  
  1399  	snapYamlContent := `name: foo
  1400  version: @VERSION@
  1401  `
  1402  
  1403  	ver := "1.0"
  1404  	revno := "42"
  1405  	snapPath, _ := s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno)
  1406  	s.serveSnap(snapPath, revno)
  1407  
  1408  	mockServer := s.mockStore(c)
  1409  	defer mockServer.Close()
  1410  
  1411  	st := s.o.State()
  1412  	st.Lock()
  1413  	defer st.Unlock()
  1414  
  1415  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  1416  	c.Assert(err, IsNil)
  1417  	chg := st.NewChange("install-snap", "...")
  1418  	chg.AddAll(ts)
  1419  
  1420  	st.Unlock()
  1421  	err = s.o.Settle(settleTimeout)
  1422  	st.Lock()
  1423  	c.Assert(err, IsNil)
  1424  
  1425  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1426  
  1427  	info, err := snapstate.CurrentInfo(st, "foo")
  1428  	c.Assert(err, IsNil)
  1429  
  1430  	c.Check(info.Revision, Equals, snap.R(42))
  1431  
  1432  	// Refresh
  1433  
  1434  	// Setup refresh control
  1435  
  1436  	headers := map[string]interface{}{
  1437  		"series":          "16",
  1438  		"snap-id":         "bar-id",
  1439  		"snap-name":       "bar",
  1440  		"publisher-id":    "devdevdev",
  1441  		"refresh-control": []interface{}{fooSnapID},
  1442  		"timestamp":       time.Now().Format(time.RFC3339),
  1443  	}
  1444  	snapDeclBar, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
  1445  	c.Assert(err, IsNil)
  1446  	err = s.storeSigning.Add(snapDeclBar)
  1447  	c.Assert(err, IsNil)
  1448  	err = assertstate.Add(st, snapDeclBar)
  1449  	c.Assert(err, IsNil)
  1450  
  1451  	snapstate.Set(st, "bar", &snapstate.SnapState{
  1452  		Active: true,
  1453  		Sequence: []*snap.SideInfo{
  1454  			{RealName: "bar", SnapID: "bar-id", Revision: snap.R(1)},
  1455  		},
  1456  		Current:  snap.R(1),
  1457  		SnapType: "app",
  1458  	})
  1459  
  1460  	develSigning := assertstest.NewSigningDB("devdevdev", develPrivKey)
  1461  
  1462  	develAccKey := assertstest.NewAccountKey(s.storeSigning, s.devAcct, nil, develPrivKey.PublicKey(), "")
  1463  	err = s.storeSigning.Add(develAccKey)
  1464  	c.Assert(err, IsNil)
  1465  
  1466  	ver = "2.0"
  1467  	revno = "50"
  1468  	snapPath, _ = s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno)
  1469  	s.serveSnap(snapPath, revno)
  1470  
  1471  	updated, tss, err := snapstate.UpdateMany(context.TODO(), st, []string{"foo"}, 0, nil)
  1472  	c.Check(updated, IsNil)
  1473  	c.Check(tss, IsNil)
  1474  	// no validation we, get an error
  1475  	c.Check(err, ErrorMatches, `cannot refresh "foo" to revision 50: no validation by "bar"`)
  1476  
  1477  	// setup validation
  1478  	headers = map[string]interface{}{
  1479  		"series":                 "16",
  1480  		"snap-id":                "bar-id",
  1481  		"approved-snap-id":       fooSnapID,
  1482  		"approved-snap-revision": "50",
  1483  		"timestamp":              time.Now().Format(time.RFC3339),
  1484  	}
  1485  	barValidation, err := develSigning.Sign(asserts.ValidationType, headers, nil, "")
  1486  	c.Assert(err, IsNil)
  1487  	err = s.storeSigning.Add(barValidation)
  1488  	c.Assert(err, IsNil)
  1489  
  1490  	// ... and try again
  1491  	updated, tss, err = snapstate.UpdateMany(context.TODO(), st, []string{"foo"}, 0, nil)
  1492  	c.Assert(err, IsNil)
  1493  	c.Assert(updated, DeepEquals, []string{"foo"})
  1494  	c.Assert(tss, HasLen, 2)
  1495  	verifyLastTasksetIsRerefresh(c, tss)
  1496  	chg = st.NewChange("upgrade-snaps", "...")
  1497  	chg.AddAll(tss[0])
  1498  
  1499  	st.Unlock()
  1500  	err = s.o.Settle(settleTimeout)
  1501  	st.Lock()
  1502  	c.Assert(err, IsNil)
  1503  
  1504  	c.Assert(chg.Err(), IsNil)
  1505  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  1506  
  1507  	info, err = snapstate.CurrentInfo(st, "foo")
  1508  	c.Assert(err, IsNil)
  1509  
  1510  	c.Check(info.Revision, Equals, snap.R(50))
  1511  }
  1512  
  1513  // core & kernel
  1514  
  1515  var modelDefaults = map[string]interface{}{
  1516  	"architecture": "amd64",
  1517  	"store":        "my-brand-store-id",
  1518  	"gadget":       "pc",
  1519  	"kernel":       "pc-kernel",
  1520  }
  1521  
  1522  func findKind(chg *state.Change, kind string) *state.Task {
  1523  	for _, t := range chg.Tasks() {
  1524  		if t.Kind() == kind {
  1525  			return t
  1526  		}
  1527  	}
  1528  	return nil
  1529  }
  1530  
  1531  func (s *mgrsSuite) TestInstallCoreSnapUpdatesBootloaderAndSplitsAcrossRestart(c *C) {
  1532  	bloader := bootloadertest.Mock("mock", c.MkDir())
  1533  	bootloader.Force(bloader)
  1534  	defer bootloader.Force(nil)
  1535  
  1536  	restore := release.MockOnClassic(false)
  1537  	defer restore()
  1538  
  1539  	model := s.brands.Model("my-brand", "my-model", modelDefaults)
  1540  
  1541  	const packageOS = `
  1542  name: core
  1543  version: 16.04-1
  1544  type: os
  1545  `
  1546  	snapPath := makeTestSnap(c, packageOS)
  1547  
  1548  	st := s.o.State()
  1549  	st.Lock()
  1550  	defer st.Unlock()
  1551  
  1552  	// setup model assertion
  1553  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  1554  	devicestatetest.SetDevice(st, &auth.DeviceState{
  1555  		Brand:  "my-brand",
  1556  		Model:  "my-model",
  1557  		Serial: "serialserialserial",
  1558  	})
  1559  	err := assertstate.Add(st, model)
  1560  	c.Assert(err, IsNil)
  1561  
  1562  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "core"}, snapPath, "", "", snapstate.Flags{})
  1563  	c.Assert(err, IsNil)
  1564  	chg := st.NewChange("install-snap", "...")
  1565  	chg.AddAll(ts)
  1566  
  1567  	st.Unlock()
  1568  	err = s.o.Settle(settleTimeout)
  1569  	st.Lock()
  1570  	c.Assert(err, IsNil)
  1571  
  1572  	// final steps will are post poned until we are in the restarted snapd
  1573  	ok, rst := st.Restarting()
  1574  	c.Assert(ok, Equals, true)
  1575  	c.Assert(rst, Equals, state.RestartSystem)
  1576  
  1577  	t := findKind(chg, "auto-connect")
  1578  	c.Assert(t, NotNil)
  1579  	c.Assert(t.Status(), Equals, state.DoingStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1580  
  1581  	// this is already set
  1582  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  1583  		"snap_try_core": "core_x1.snap",
  1584  		"snap_mode":     "try",
  1585  	})
  1586  
  1587  	// simulate successful restart happened
  1588  	state.MockRestarting(st, state.RestartUnset)
  1589  	bloader.BootVars["snap_mode"] = ""
  1590  	bloader.SetBootBase("core_x1.snap")
  1591  
  1592  	st.Unlock()
  1593  	err = s.o.Settle(settleTimeout)
  1594  	st.Lock()
  1595  	c.Assert(err, IsNil)
  1596  
  1597  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1598  
  1599  }
  1600  
  1601  func (s *mgrsSuite) TestInstallKernelSnapUpdatesBootloader(c *C) {
  1602  	bloader := bootloadertest.Mock("mock", c.MkDir())
  1603  	bootloader.Force(bloader)
  1604  	defer bootloader.Force(nil)
  1605  
  1606  	restore := release.MockOnClassic(false)
  1607  	defer restore()
  1608  
  1609  	model := s.brands.Model("my-brand", "my-model", modelDefaults)
  1610  
  1611  	const packageKernel = `
  1612  name: pc-kernel
  1613  version: 4.0-1
  1614  type: kernel`
  1615  
  1616  	files := [][]string{
  1617  		{"kernel.img", "I'm a kernel"},
  1618  		{"initrd.img", "...and I'm an initrd"},
  1619  		{"meta/kernel.yaml", "version: 4.2"},
  1620  	}
  1621  	snapPath := snaptest.MakeTestSnapWithFiles(c, packageKernel, files)
  1622  
  1623  	st := s.o.State()
  1624  	st.Lock()
  1625  	defer st.Unlock()
  1626  
  1627  	// setup model assertion
  1628  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  1629  	devicestatetest.SetDevice(st, &auth.DeviceState{
  1630  		Brand:  "my-brand",
  1631  		Model:  "my-model",
  1632  		Serial: "serialserialserial",
  1633  	})
  1634  	err := assertstate.Add(st, model)
  1635  	c.Assert(err, IsNil)
  1636  
  1637  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "pc-kernel"}, snapPath, "", "", snapstate.Flags{})
  1638  	c.Assert(err, IsNil)
  1639  	chg := st.NewChange("install-snap", "...")
  1640  	chg.AddAll(ts)
  1641  
  1642  	// run, this will trigger a wait for the restart
  1643  	st.Unlock()
  1644  	err = s.o.Settle(settleTimeout)
  1645  	st.Lock()
  1646  	c.Assert(err, IsNil)
  1647  
  1648  	// we are in restarting state and the change is not done yet
  1649  	restarting, _ := st.Restarting()
  1650  	c.Check(restarting, Equals, true)
  1651  	c.Check(chg.Status(), Equals, state.DoingStatus)
  1652  	// pretend we restarted
  1653  	state.MockRestarting(st, state.RestartUnset)
  1654  
  1655  	st.Unlock()
  1656  	err = s.o.Settle(settleTimeout)
  1657  	st.Lock()
  1658  	c.Assert(err, IsNil)
  1659  
  1660  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1661  
  1662  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  1663  		"snap_try_kernel": "pc-kernel_x1.snap",
  1664  		"snap_mode":       "try",
  1665  	})
  1666  }
  1667  
  1668  func (s *mgrsSuite) installLocalTestSnap(c *C, snapYamlContent string) *snap.Info {
  1669  	st := s.o.State()
  1670  
  1671  	snapPath := makeTestSnap(c, snapYamlContent)
  1672  	snapf, err := snap.Open(snapPath)
  1673  	c.Assert(err, IsNil)
  1674  	info, err := snap.ReadInfoFromSnapFile(snapf, nil)
  1675  	c.Assert(err, IsNil)
  1676  
  1677  	// store current state
  1678  	snapName := info.InstanceName()
  1679  	var snapst snapstate.SnapState
  1680  	snapstate.Get(st, snapName, &snapst)
  1681  
  1682  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: snapName}, snapPath, "", "", snapstate.Flags{DevMode: true})
  1683  	c.Assert(err, IsNil)
  1684  	chg := st.NewChange("install-snap", "...")
  1685  	chg.AddAll(ts)
  1686  
  1687  	st.Unlock()
  1688  	err = s.o.Settle(settleTimeout)
  1689  	st.Lock()
  1690  	c.Assert(err, IsNil)
  1691  
  1692  	c.Assert(chg.Err(), IsNil)
  1693  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1694  
  1695  	return info
  1696  }
  1697  
  1698  func (s *mgrsSuite) removeSnap(c *C, name string) {
  1699  	st := s.o.State()
  1700  
  1701  	ts, err := snapstate.Remove(st, name, snap.R(0), nil)
  1702  	c.Assert(err, IsNil)
  1703  	chg := st.NewChange("remove-snap", "...")
  1704  	chg.AddAll(ts)
  1705  
  1706  	st.Unlock()
  1707  	err = s.o.Settle(settleTimeout)
  1708  	st.Lock()
  1709  	c.Assert(err, IsNil)
  1710  
  1711  	c.Assert(chg.Err(), IsNil)
  1712  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("remove-snap change failed with: %v", chg.Err()))
  1713  }
  1714  
  1715  func (s *mgrsSuite) TestHappyRevert(c *C) {
  1716  	st := s.o.State()
  1717  	st.Lock()
  1718  	defer st.Unlock()
  1719  
  1720  	x1Yaml := `name: foo
  1721  version: 1.0
  1722  apps:
  1723   x1:
  1724    command: bin/bar
  1725  `
  1726  	x1binary := filepath.Join(dirs.SnapBinariesDir, "foo.x1")
  1727  
  1728  	x2Yaml := `name: foo
  1729  version: 2.0
  1730  apps:
  1731   x2:
  1732    command: bin/bar
  1733  `
  1734  	x2binary := filepath.Join(dirs.SnapBinariesDir, "foo.x2")
  1735  
  1736  	s.installLocalTestSnap(c, x1Yaml)
  1737  	s.installLocalTestSnap(c, x2Yaml)
  1738  
  1739  	// ensure we are on x2
  1740  	_, err := os.Lstat(x2binary)
  1741  	c.Assert(err, IsNil)
  1742  	_, err = os.Lstat(x1binary)
  1743  	c.Assert(err, ErrorMatches, ".*no such file.*")
  1744  
  1745  	// now do the revert
  1746  	ts, err := snapstate.Revert(st, "foo", snapstate.Flags{})
  1747  	c.Assert(err, IsNil)
  1748  	chg := st.NewChange("revert-snap", "...")
  1749  	chg.AddAll(ts)
  1750  
  1751  	st.Unlock()
  1752  	err = s.o.Settle(settleTimeout)
  1753  	st.Lock()
  1754  	c.Assert(err, IsNil)
  1755  
  1756  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("revert-snap change failed with: %v", chg.Err()))
  1757  
  1758  	// ensure that we use x1 now
  1759  	_, err = os.Lstat(x1binary)
  1760  	c.Assert(err, IsNil)
  1761  	_, err = os.Lstat(x2binary)
  1762  	c.Assert(err, ErrorMatches, ".*no such file.*")
  1763  
  1764  	// ensure that x1,x2 is still there, revert just moves the "current"
  1765  	// pointer
  1766  	for _, fn := range []string{"foo_x2.snap", "foo_x1.snap"} {
  1767  		p := filepath.Join(dirs.SnapBlobDir, fn)
  1768  		c.Assert(osutil.FileExists(p), Equals, true)
  1769  	}
  1770  }
  1771  
  1772  func (s *mgrsSuite) TestHappyAlias(c *C) {
  1773  	st := s.o.State()
  1774  	st.Lock()
  1775  	defer st.Unlock()
  1776  
  1777  	fooYaml := `name: foo
  1778  version: 1.0
  1779  apps:
  1780      foo:
  1781          command: bin/foo
  1782  `
  1783  	s.installLocalTestSnap(c, fooYaml)
  1784  
  1785  	ts, err := snapstate.Alias(st, "foo", "foo", "foo_")
  1786  	c.Assert(err, IsNil)
  1787  	chg := st.NewChange("alias", "...")
  1788  	chg.AddAll(ts)
  1789  
  1790  	st.Unlock()
  1791  	err = s.o.Settle(settleTimeout)
  1792  	st.Lock()
  1793  	c.Assert(err, IsNil)
  1794  
  1795  	c.Assert(chg.Err(), IsNil)
  1796  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("alias change failed with: %v", chg.Err()))
  1797  
  1798  	foo_Alias := filepath.Join(dirs.SnapBinariesDir, "foo_")
  1799  	dest, err := os.Readlink(foo_Alias)
  1800  	c.Assert(err, IsNil)
  1801  
  1802  	c.Check(dest, Equals, "foo")
  1803  
  1804  	var snapst snapstate.SnapState
  1805  	err = snapstate.Get(st, "foo", &snapst)
  1806  	c.Assert(err, IsNil)
  1807  	c.Check(snapst.AutoAliasesDisabled, Equals, false)
  1808  	c.Check(snapst.AliasesPending, Equals, false)
  1809  	c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  1810  		"foo_": {Manual: "foo"},
  1811  	})
  1812  
  1813  	s.removeSnap(c, "foo")
  1814  
  1815  	c.Check(osutil.IsSymlink(foo_Alias), Equals, false)
  1816  }
  1817  
  1818  func (s *mgrsSuite) TestHappyUnalias(c *C) {
  1819  	st := s.o.State()
  1820  	st.Lock()
  1821  	defer st.Unlock()
  1822  
  1823  	fooYaml := `name: foo
  1824  version: 1.0
  1825  apps:
  1826      foo:
  1827          command: bin/foo
  1828  `
  1829  	s.installLocalTestSnap(c, fooYaml)
  1830  
  1831  	ts, err := snapstate.Alias(st, "foo", "foo", "foo_")
  1832  	c.Assert(err, IsNil)
  1833  	chg := st.NewChange("alias", "...")
  1834  	chg.AddAll(ts)
  1835  
  1836  	st.Unlock()
  1837  	err = s.o.Settle(settleTimeout)
  1838  	st.Lock()
  1839  	c.Assert(err, IsNil)
  1840  
  1841  	c.Assert(chg.Err(), IsNil)
  1842  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("alias change failed with: %v", chg.Err()))
  1843  
  1844  	foo_Alias := filepath.Join(dirs.SnapBinariesDir, "foo_")
  1845  	dest, err := os.Readlink(foo_Alias)
  1846  	c.Assert(err, IsNil)
  1847  
  1848  	c.Check(dest, Equals, "foo")
  1849  
  1850  	ts, snapName, err := snapstate.RemoveManualAlias(st, "foo_")
  1851  	c.Assert(err, IsNil)
  1852  	c.Check(snapName, Equals, "foo")
  1853  	chg = st.NewChange("unalias", "...")
  1854  	chg.AddAll(ts)
  1855  
  1856  	st.Unlock()
  1857  	err = s.o.Settle(settleTimeout)
  1858  	st.Lock()
  1859  	c.Assert(err, IsNil)
  1860  
  1861  	c.Assert(chg.Err(), IsNil)
  1862  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("unalias change failed with: %v", chg.Err()))
  1863  
  1864  	c.Check(osutil.IsSymlink(foo_Alias), Equals, false)
  1865  
  1866  	var snapst snapstate.SnapState
  1867  	err = snapstate.Get(st, "foo", &snapst)
  1868  	c.Assert(err, IsNil)
  1869  	c.Check(snapst.AutoAliasesDisabled, Equals, false)
  1870  	c.Check(snapst.AliasesPending, Equals, false)
  1871  	c.Check(snapst.Aliases, HasLen, 0)
  1872  }
  1873  
  1874  func (s *mgrsSuite) TestHappyRemoteInstallAutoAliases(c *C) {
  1875  	s.prereqSnapAssertions(c, map[string]interface{}{
  1876  		"snap-name": "foo",
  1877  		"aliases": []interface{}{
  1878  			map[string]interface{}{"name": "app1", "target": "app1"},
  1879  			map[string]interface{}{"name": "app2", "target": "app2"},
  1880  		},
  1881  	})
  1882  
  1883  	snapYamlContent := `name: foo
  1884  version: @VERSION@
  1885  apps:
  1886   app1:
  1887    command: bin/app1
  1888   app2:
  1889    command: bin/app2
  1890  `
  1891  
  1892  	ver := "1.0"
  1893  	revno := "42"
  1894  	snapPath, _ := s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno)
  1895  	s.serveSnap(snapPath, revno)
  1896  
  1897  	mockServer := s.mockStore(c)
  1898  	defer mockServer.Close()
  1899  
  1900  	st := s.o.State()
  1901  	st.Lock()
  1902  	defer st.Unlock()
  1903  
  1904  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  1905  	c.Assert(err, IsNil)
  1906  	chg := st.NewChange("install-snap", "...")
  1907  	chg.AddAll(ts)
  1908  
  1909  	st.Unlock()
  1910  	err = s.o.Settle(settleTimeout)
  1911  	st.Lock()
  1912  	c.Assert(err, IsNil)
  1913  
  1914  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1915  
  1916  	var snapst snapstate.SnapState
  1917  	err = snapstate.Get(st, "foo", &snapst)
  1918  	c.Assert(err, IsNil)
  1919  	c.Check(snapst.AutoAliasesDisabled, Equals, false)
  1920  	c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  1921  		"app1": {Auto: "app1"},
  1922  		"app2": {Auto: "app2"},
  1923  	})
  1924  
  1925  	// check disk
  1926  	app1Alias := filepath.Join(dirs.SnapBinariesDir, "app1")
  1927  	dest, err := os.Readlink(app1Alias)
  1928  	c.Assert(err, IsNil)
  1929  	c.Check(dest, Equals, "foo.app1")
  1930  
  1931  	app2Alias := filepath.Join(dirs.SnapBinariesDir, "app2")
  1932  	dest, err = os.Readlink(app2Alias)
  1933  	c.Assert(err, IsNil)
  1934  	c.Check(dest, Equals, "foo.app2")
  1935  }
  1936  
  1937  func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateAutoAliases(c *C) {
  1938  	s.prereqSnapAssertions(c, map[string]interface{}{
  1939  		"snap-name": "foo",
  1940  		"aliases": []interface{}{
  1941  			map[string]interface{}{"name": "app1", "target": "app1"},
  1942  		},
  1943  	})
  1944  
  1945  	fooYaml := `name: foo
  1946  version: @VERSION@
  1947  apps:
  1948   app1:
  1949    command: bin/app1
  1950   app2:
  1951    command: bin/app2
  1952  `
  1953  
  1954  	fooPath, _ := s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.0", -1), "10")
  1955  	s.serveSnap(fooPath, "10")
  1956  
  1957  	mockServer := s.mockStore(c)
  1958  	defer mockServer.Close()
  1959  
  1960  	st := s.o.State()
  1961  	st.Lock()
  1962  	defer st.Unlock()
  1963  
  1964  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  1965  	c.Assert(err, IsNil)
  1966  	chg := st.NewChange("install-snap", "...")
  1967  	chg.AddAll(ts)
  1968  
  1969  	st.Unlock()
  1970  	err = s.o.Settle(settleTimeout)
  1971  	st.Lock()
  1972  	c.Assert(err, IsNil)
  1973  
  1974  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1975  
  1976  	info, err := snapstate.CurrentInfo(st, "foo")
  1977  	c.Assert(err, IsNil)
  1978  	c.Check(info.Revision, Equals, snap.R(10))
  1979  	c.Check(info.Version, Equals, "1.0")
  1980  
  1981  	var snapst snapstate.SnapState
  1982  	err = snapstate.Get(st, "foo", &snapst)
  1983  	c.Assert(err, IsNil)
  1984  	c.Check(snapst.AutoAliasesDisabled, Equals, false)
  1985  	c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  1986  		"app1": {Auto: "app1"},
  1987  	})
  1988  
  1989  	app1Alias := filepath.Join(dirs.SnapBinariesDir, "app1")
  1990  	dest, err := os.Readlink(app1Alias)
  1991  	c.Assert(err, IsNil)
  1992  	c.Check(dest, Equals, "foo.app1")
  1993  
  1994  	s.prereqSnapAssertions(c, map[string]interface{}{
  1995  		"snap-name": "foo",
  1996  		"aliases": []interface{}{
  1997  			map[string]interface{}{"name": "app2", "target": "app2"},
  1998  		},
  1999  		"revision": "1",
  2000  	})
  2001  
  2002  	// new foo version/revision
  2003  	fooPath, _ = s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.5", -1), "15")
  2004  	s.serveSnap(fooPath, "15")
  2005  
  2006  	// refresh all
  2007  	updated, tss, err := snapstate.UpdateMany(context.TODO(), st, nil, 0, nil)
  2008  	c.Assert(err, IsNil)
  2009  	c.Assert(updated, DeepEquals, []string{"foo"})
  2010  	c.Assert(tss, HasLen, 2)
  2011  	verifyLastTasksetIsRerefresh(c, tss)
  2012  	chg = st.NewChange("upgrade-snaps", "...")
  2013  	chg.AddAll(tss[0])
  2014  
  2015  	st.Unlock()
  2016  	err = s.o.Settle(settleTimeout)
  2017  	st.Lock()
  2018  	c.Assert(err, IsNil)
  2019  
  2020  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  2021  
  2022  	info, err = snapstate.CurrentInfo(st, "foo")
  2023  	c.Assert(err, IsNil)
  2024  	c.Check(info.Revision, Equals, snap.R(15))
  2025  	c.Check(info.Version, Equals, "1.5")
  2026  
  2027  	var snapst2 snapstate.SnapState
  2028  	err = snapstate.Get(st, "foo", &snapst2)
  2029  	c.Assert(err, IsNil)
  2030  	c.Check(snapst2.AutoAliasesDisabled, Equals, false)
  2031  	c.Check(snapst2.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  2032  		"app2": {Auto: "app2"},
  2033  	})
  2034  
  2035  	c.Check(osutil.IsSymlink(app1Alias), Equals, false)
  2036  
  2037  	app2Alias := filepath.Join(dirs.SnapBinariesDir, "app2")
  2038  	dest, err = os.Readlink(app2Alias)
  2039  	c.Assert(err, IsNil)
  2040  	c.Check(dest, Equals, "foo.app2")
  2041  }
  2042  
  2043  func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateAutoAliasesUnaliased(c *C) {
  2044  	s.prereqSnapAssertions(c, map[string]interface{}{
  2045  		"snap-name": "foo",
  2046  		"aliases": []interface{}{
  2047  			map[string]interface{}{"name": "app1", "target": "app1"},
  2048  		},
  2049  	})
  2050  
  2051  	fooYaml := `name: foo
  2052  version: @VERSION@
  2053  apps:
  2054   app1:
  2055    command: bin/app1
  2056   app2:
  2057    command: bin/app2
  2058  `
  2059  
  2060  	fooPath, _ := s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.0", -1), "10")
  2061  	s.serveSnap(fooPath, "10")
  2062  
  2063  	mockServer := s.mockStore(c)
  2064  	defer mockServer.Close()
  2065  
  2066  	st := s.o.State()
  2067  	st.Lock()
  2068  	defer st.Unlock()
  2069  
  2070  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{Unaliased: true})
  2071  	c.Assert(err, IsNil)
  2072  	chg := st.NewChange("install-snap", "...")
  2073  	chg.AddAll(ts)
  2074  
  2075  	st.Unlock()
  2076  	err = s.o.Settle(settleTimeout)
  2077  	st.Lock()
  2078  	c.Assert(err, IsNil)
  2079  
  2080  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  2081  
  2082  	info, err := snapstate.CurrentInfo(st, "foo")
  2083  	c.Assert(err, IsNil)
  2084  	c.Check(info.Revision, Equals, snap.R(10))
  2085  	c.Check(info.Version, Equals, "1.0")
  2086  
  2087  	var snapst snapstate.SnapState
  2088  	err = snapstate.Get(st, "foo", &snapst)
  2089  	c.Assert(err, IsNil)
  2090  	c.Check(snapst.AutoAliasesDisabled, Equals, true)
  2091  	c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  2092  		"app1": {Auto: "app1"},
  2093  	})
  2094  
  2095  	app1Alias := filepath.Join(dirs.SnapBinariesDir, "app1")
  2096  	c.Check(osutil.IsSymlink(app1Alias), Equals, false)
  2097  
  2098  	s.prereqSnapAssertions(c, map[string]interface{}{
  2099  		"snap-name": "foo",
  2100  		"aliases": []interface{}{
  2101  			map[string]interface{}{"name": "app2", "target": "app2"},
  2102  		},
  2103  		"revision": "1",
  2104  	})
  2105  
  2106  	// new foo version/revision
  2107  	fooPath, _ = s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.5", -1), "15")
  2108  	s.serveSnap(fooPath, "15")
  2109  
  2110  	// refresh foo
  2111  	ts, err = snapstate.Update(st, "foo", nil, 0, snapstate.Flags{})
  2112  	c.Assert(err, IsNil)
  2113  	chg = st.NewChange("upgrade-snap", "...")
  2114  	chg.AddAll(ts)
  2115  
  2116  	st.Unlock()
  2117  	err = s.o.Settle(settleTimeout)
  2118  	st.Lock()
  2119  	c.Assert(err, IsNil)
  2120  
  2121  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  2122  
  2123  	info, err = snapstate.CurrentInfo(st, "foo")
  2124  	c.Assert(err, IsNil)
  2125  	c.Check(info.Revision, Equals, snap.R(15))
  2126  	c.Check(info.Version, Equals, "1.5")
  2127  
  2128  	var snapst2 snapstate.SnapState
  2129  	err = snapstate.Get(st, "foo", &snapst2)
  2130  	c.Assert(err, IsNil)
  2131  	c.Check(snapst2.AutoAliasesDisabled, Equals, true)
  2132  	c.Check(snapst2.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  2133  		"app2": {Auto: "app2"},
  2134  	})
  2135  
  2136  	c.Check(osutil.IsSymlink(app1Alias), Equals, false)
  2137  
  2138  	app2Alias := filepath.Join(dirs.SnapBinariesDir, "app2")
  2139  	c.Check(osutil.IsSymlink(app2Alias), Equals, false)
  2140  }
  2141  
  2142  func (s *mgrsSuite) TestHappyOrthogonalRefreshAutoAliases(c *C) {
  2143  	s.prereqSnapAssertions(c, map[string]interface{}{
  2144  		"snap-name": "foo",
  2145  		"aliases": []interface{}{
  2146  			map[string]interface{}{"name": "app1", "target": "app1"},
  2147  		},
  2148  	}, map[string]interface{}{
  2149  		"snap-name": "bar",
  2150  	})
  2151  
  2152  	fooYaml := `name: foo
  2153  version: @VERSION@
  2154  apps:
  2155   app1:
  2156    command: bin/app1
  2157   app2:
  2158    command: bin/app2
  2159  `
  2160  
  2161  	barYaml := `name: bar
  2162  version: @VERSION@
  2163  apps:
  2164   app1:
  2165    command: bin/app1
  2166   app3:
  2167    command: bin/app3
  2168  `
  2169  
  2170  	fooPath, _ := s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.0", -1), "10")
  2171  	s.serveSnap(fooPath, "10")
  2172  
  2173  	barPath, _ := s.makeStoreTestSnap(c, strings.Replace(barYaml, "@VERSION@", "2.0", -1), "20")
  2174  	s.serveSnap(barPath, "20")
  2175  
  2176  	mockServer := s.mockStore(c)
  2177  	defer mockServer.Close()
  2178  
  2179  	st := s.o.State()
  2180  	st.Lock()
  2181  	defer st.Unlock()
  2182  
  2183  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  2184  	c.Assert(err, IsNil)
  2185  	chg := st.NewChange("install-snap", "...")
  2186  	chg.AddAll(ts)
  2187  
  2188  	st.Unlock()
  2189  	err = s.o.Settle(settleTimeout)
  2190  	st.Lock()
  2191  	c.Assert(err, IsNil)
  2192  
  2193  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  2194  
  2195  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  2196  
  2197  	ts, err = snapstate.Install(context.TODO(), st, "bar", nil, 0, snapstate.Flags{})
  2198  	c.Assert(err, IsNil)
  2199  	chg = st.NewChange("install-snap", "...")
  2200  	chg.AddAll(ts)
  2201  
  2202  	st.Unlock()
  2203  	err = s.o.Settle(settleTimeout)
  2204  	st.Lock()
  2205  	c.Assert(err, IsNil)
  2206  
  2207  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  2208  
  2209  	info, err := snapstate.CurrentInfo(st, "foo")
  2210  	c.Assert(err, IsNil)
  2211  	c.Check(info.Revision, Equals, snap.R(10))
  2212  	c.Check(info.Version, Equals, "1.0")
  2213  
  2214  	info, err = snapstate.CurrentInfo(st, "bar")
  2215  	c.Assert(err, IsNil)
  2216  	c.Check(info.Revision, Equals, snap.R(20))
  2217  	c.Check(info.Version, Equals, "2.0")
  2218  
  2219  	var snapst snapstate.SnapState
  2220  	err = snapstate.Get(st, "foo", &snapst)
  2221  	c.Assert(err, IsNil)
  2222  	c.Check(snapst.AutoAliasesDisabled, Equals, false)
  2223  	c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  2224  		"app1": {Auto: "app1"},
  2225  	})
  2226  
  2227  	// foo gets a new version/revision and a change of automatic aliases
  2228  	// bar gets only the latter
  2229  	// app1 is transferred from foo to bar
  2230  	// UpdateMany after a snap-declaration refresh handles all of this
  2231  	s.prereqSnapAssertions(c, map[string]interface{}{
  2232  		"snap-name": "foo",
  2233  		"aliases": []interface{}{
  2234  			map[string]interface{}{"name": "app2", "target": "app2"},
  2235  		},
  2236  		"revision": "1",
  2237  	}, map[string]interface{}{
  2238  		"snap-name": "bar",
  2239  		"aliases": []interface{}{
  2240  			map[string]interface{}{"name": "app1", "target": "app1"},
  2241  			map[string]interface{}{"name": "app3", "target": "app3"},
  2242  		},
  2243  		"revision": "1",
  2244  	})
  2245  
  2246  	// new foo version/revision
  2247  	fooPath, _ = s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.5", -1), "15")
  2248  	s.serveSnap(fooPath, "15")
  2249  
  2250  	// refresh all
  2251  	err = assertstate.RefreshSnapDeclarations(st, 0)
  2252  	c.Assert(err, IsNil)
  2253  
  2254  	updated, tss, err := snapstate.UpdateMany(context.TODO(), st, nil, 0, nil)
  2255  	c.Assert(err, IsNil)
  2256  	sort.Strings(updated)
  2257  	c.Assert(updated, DeepEquals, []string{"bar", "foo"})
  2258  	c.Assert(tss, HasLen, 4)
  2259  	verifyLastTasksetIsRerefresh(c, tss)
  2260  	chg = st.NewChange("upgrade-snaps", "...")
  2261  	chg.AddAll(tss[0])
  2262  	chg.AddAll(tss[1])
  2263  	chg.AddAll(tss[2])
  2264  
  2265  	st.Unlock()
  2266  	err = s.o.Settle(settleTimeout)
  2267  	st.Lock()
  2268  	c.Assert(err, IsNil)
  2269  
  2270  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  2271  
  2272  	info, err = snapstate.CurrentInfo(st, "foo")
  2273  	c.Assert(err, IsNil)
  2274  	c.Check(info.Revision, Equals, snap.R(15))
  2275  	c.Check(info.Version, Equals, "1.5")
  2276  
  2277  	var snapst2 snapstate.SnapState
  2278  	err = snapstate.Get(st, "foo", &snapst2)
  2279  	c.Assert(err, IsNil)
  2280  	c.Check(snapst2.AutoAliasesDisabled, Equals, false)
  2281  	c.Check(snapst2.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  2282  		"app2": {Auto: "app2"},
  2283  	})
  2284  	var snapst3 snapstate.SnapState
  2285  	err = snapstate.Get(st, "bar", &snapst3)
  2286  	c.Assert(err, IsNil)
  2287  	c.Check(snapst3.AutoAliasesDisabled, Equals, false)
  2288  	c.Check(snapst3.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  2289  		"app1": {Auto: "app1"},
  2290  		"app3": {Auto: "app3"},
  2291  	})
  2292  
  2293  	app2Alias := filepath.Join(dirs.SnapBinariesDir, "app2")
  2294  	dest, err := os.Readlink(app2Alias)
  2295  	c.Assert(err, IsNil)
  2296  	c.Check(dest, Equals, "foo.app2")
  2297  
  2298  	app1Alias := filepath.Join(dirs.SnapBinariesDir, "app1")
  2299  	dest, err = os.Readlink(app1Alias)
  2300  	c.Assert(err, IsNil)
  2301  	c.Check(dest, Equals, "bar.app1")
  2302  	app3Alias := filepath.Join(dirs.SnapBinariesDir, "app3")
  2303  	dest, err = os.Readlink(app3Alias)
  2304  	c.Assert(err, IsNil)
  2305  	c.Check(dest, Equals, "bar.app3")
  2306  }
  2307  
  2308  func (s *mgrsSuite) TestHappyStopWhileDownloadingHeader(c *C) {
  2309  	s.prereqSnapAssertions(c)
  2310  
  2311  	snapYamlContent := `name: foo
  2312  version: 1.0
  2313  `
  2314  	snapPath, _ := s.makeStoreTestSnap(c, snapYamlContent, "42")
  2315  	s.serveSnap(snapPath, "42")
  2316  
  2317  	stopped := make(chan struct{})
  2318  	s.hijackServeSnap = func(_ http.ResponseWriter) {
  2319  		s.o.Stop()
  2320  		close(stopped)
  2321  	}
  2322  
  2323  	mockServer := s.mockStore(c)
  2324  	defer mockServer.Close()
  2325  
  2326  	st := s.o.State()
  2327  	st.Lock()
  2328  	defer st.Unlock()
  2329  
  2330  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  2331  	c.Assert(err, IsNil)
  2332  	chg := st.NewChange("install-snap", "...")
  2333  	chg.AddAll(ts)
  2334  
  2335  	st.Unlock()
  2336  	s.o.Loop()
  2337  
  2338  	<-stopped
  2339  
  2340  	st.Lock()
  2341  	c.Assert(chg.Status(), Equals, state.DoingStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  2342  }
  2343  
  2344  func (s *mgrsSuite) TestHappyStopWhileDownloadingBody(c *C) {
  2345  	s.prereqSnapAssertions(c)
  2346  
  2347  	snapYamlContent := `name: foo
  2348  version: 1.0
  2349  `
  2350  	snapPath, _ := s.makeStoreTestSnap(c, snapYamlContent, "42")
  2351  	s.serveSnap(snapPath, "42")
  2352  
  2353  	stopped := make(chan struct{})
  2354  	s.hijackServeSnap = func(w http.ResponseWriter) {
  2355  		w.WriteHeader(200)
  2356  		// best effort to reach the body reading part in the client
  2357  		w.Write(make([]byte, 10000))
  2358  		time.Sleep(100 * time.Millisecond)
  2359  		w.Write(make([]byte, 10000))
  2360  		s.o.Stop()
  2361  		close(stopped)
  2362  	}
  2363  
  2364  	mockServer := s.mockStore(c)
  2365  	defer mockServer.Close()
  2366  
  2367  	st := s.o.State()
  2368  	st.Lock()
  2369  	defer st.Unlock()
  2370  
  2371  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  2372  	c.Assert(err, IsNil)
  2373  	chg := st.NewChange("install-snap", "...")
  2374  	chg.AddAll(ts)
  2375  
  2376  	st.Unlock()
  2377  	s.o.Loop()
  2378  
  2379  	<-stopped
  2380  
  2381  	st.Lock()
  2382  	c.Assert(chg.Status(), Equals, state.DoingStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  2383  }
  2384  
  2385  type storeCtxSetupSuite struct {
  2386  	o  *overlord.Overlord
  2387  	sc store.DeviceAndAuthContext
  2388  
  2389  	storeSigning   *assertstest.StoreStack
  2390  	restoreTrusted func()
  2391  
  2392  	brands *assertstest.SigningAccounts
  2393  
  2394  	deviceKey asserts.PrivateKey
  2395  
  2396  	model  *asserts.Model
  2397  	serial *asserts.Serial
  2398  
  2399  	restoreBackends func()
  2400  }
  2401  
  2402  func (s *storeCtxSetupSuite) SetUpTest(c *C) {
  2403  	tempdir := c.MkDir()
  2404  	dirs.SetRootDir(tempdir)
  2405  	err := os.MkdirAll(filepath.Dir(dirs.SnapStateFile), 0755)
  2406  	c.Assert(err, IsNil)
  2407  
  2408  	captureStoreCtx := func(_ *store.Config, dac store.DeviceAndAuthContext) *store.Store {
  2409  		s.sc = dac
  2410  		return store.New(nil, nil)
  2411  	}
  2412  	r := overlord.MockStoreNew(captureStoreCtx)
  2413  	defer r()
  2414  
  2415  	s.storeSigning = assertstest.NewStoreStack("can0nical", nil)
  2416  	s.restoreTrusted = sysdb.InjectTrusted(s.storeSigning.Trusted)
  2417  
  2418  	s.brands = assertstest.NewSigningAccounts(s.storeSigning)
  2419  	s.brands.Register("my-brand", brandPrivKey, map[string]interface{}{
  2420  		"verification": "verified",
  2421  	})
  2422  	assertstest.AddMany(s.storeSigning, s.brands.AccountsAndKeys("my-brand")...)
  2423  
  2424  	s.model = s.brands.Model("my-brand", "my-model", modelDefaults)
  2425  
  2426  	encDevKey, err := asserts.EncodePublicKey(deviceKey.PublicKey())
  2427  	c.Assert(err, IsNil)
  2428  	serial, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, map[string]interface{}{
  2429  		"authority-id":        "my-brand",
  2430  		"brand-id":            "my-brand",
  2431  		"model":               "my-model",
  2432  		"serial":              "7878",
  2433  		"device-key":          string(encDevKey),
  2434  		"device-key-sha3-384": deviceKey.PublicKey().ID(),
  2435  		"timestamp":           time.Now().Format(time.RFC3339),
  2436  	}, nil, "")
  2437  	c.Assert(err, IsNil)
  2438  	s.serial = serial.(*asserts.Serial)
  2439  
  2440  	s.restoreBackends = ifacestate.MockSecurityBackends(nil)
  2441  
  2442  	o, err := overlord.New(nil)
  2443  	c.Assert(err, IsNil)
  2444  	o.InterfaceManager().DisableUDevMonitor()
  2445  	s.o = o
  2446  
  2447  	st := o.State()
  2448  	st.Lock()
  2449  	defer st.Unlock()
  2450  
  2451  	assertstatetest.AddMany(st, s.storeSigning.StoreAccountKey(""))
  2452  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  2453  }
  2454  
  2455  func (s *storeCtxSetupSuite) TearDownTest(c *C) {
  2456  	dirs.SetRootDir("")
  2457  	s.restoreBackends()
  2458  	s.restoreTrusted()
  2459  }
  2460  
  2461  func (s *storeCtxSetupSuite) TestStoreID(c *C) {
  2462  	st := s.o.State()
  2463  	st.Lock()
  2464  	defer st.Unlock()
  2465  
  2466  	st.Unlock()
  2467  	storeID, err := s.sc.StoreID("fallback")
  2468  	st.Lock()
  2469  	c.Assert(err, IsNil)
  2470  	c.Check(storeID, Equals, "fallback")
  2471  
  2472  	// setup model in system statey
  2473  	devicestatetest.SetDevice(st, &auth.DeviceState{
  2474  		Brand:  s.serial.BrandID(),
  2475  		Model:  s.serial.Model(),
  2476  		Serial: s.serial.Serial(),
  2477  	})
  2478  	err = assertstate.Add(st, s.model)
  2479  	c.Assert(err, IsNil)
  2480  
  2481  	st.Unlock()
  2482  	storeID, err = s.sc.StoreID("fallback")
  2483  	st.Lock()
  2484  	c.Assert(err, IsNil)
  2485  	c.Check(storeID, Equals, "my-brand-store-id")
  2486  }
  2487  
  2488  func (s *storeCtxSetupSuite) TestDeviceSessionRequestParams(c *C) {
  2489  	st := s.o.State()
  2490  	st.Lock()
  2491  	defer st.Unlock()
  2492  
  2493  	st.Unlock()
  2494  	_, err := s.sc.DeviceSessionRequestParams("NONCE")
  2495  	st.Lock()
  2496  	c.Check(err, Equals, store.ErrNoSerial)
  2497  
  2498  	// setup model, serial and key in system state
  2499  	err = assertstate.Add(st, s.model)
  2500  	c.Assert(err, IsNil)
  2501  	err = assertstate.Add(st, s.serial)
  2502  	c.Assert(err, IsNil)
  2503  	kpMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir)
  2504  	c.Assert(err, IsNil)
  2505  	err = kpMgr.Put(deviceKey)
  2506  	c.Assert(err, IsNil)
  2507  	devicestatetest.SetDevice(st, &auth.DeviceState{
  2508  		Brand:  s.serial.BrandID(),
  2509  		Model:  s.serial.Model(),
  2510  		Serial: s.serial.Serial(),
  2511  		KeyID:  deviceKey.PublicKey().ID(),
  2512  	})
  2513  
  2514  	st.Unlock()
  2515  	params, err := s.sc.DeviceSessionRequestParams("NONCE")
  2516  	st.Lock()
  2517  	c.Assert(err, IsNil)
  2518  	c.Check(strings.HasPrefix(params.EncodedRequest(), "type: device-session-request\n"), Equals, true)
  2519  	c.Check(params.EncodedSerial(), DeepEquals, string(asserts.Encode(s.serial)))
  2520  	c.Check(params.EncodedModel(), DeepEquals, string(asserts.Encode(s.model)))
  2521  
  2522  }
  2523  
  2524  func (s *storeCtxSetupSuite) TestProxyStoreParams(c *C) {
  2525  	st := s.o.State()
  2526  	st.Lock()
  2527  	defer st.Unlock()
  2528  
  2529  	defURL, err := url.Parse("http://store")
  2530  	c.Assert(err, IsNil)
  2531  
  2532  	st.Unlock()
  2533  	proxyStoreID, proxyStoreURL, err := s.sc.ProxyStoreParams(defURL)
  2534  	st.Lock()
  2535  	c.Assert(err, IsNil)
  2536  	c.Check(proxyStoreID, Equals, "")
  2537  	c.Check(proxyStoreURL, Equals, defURL)
  2538  
  2539  	// setup proxy store reference and assertion
  2540  	operatorAcct := assertstest.NewAccount(s.storeSigning, "foo-operator", nil, "")
  2541  	err = assertstate.Add(st, operatorAcct)
  2542  	c.Assert(err, IsNil)
  2543  	stoAs, err := s.storeSigning.Sign(asserts.StoreType, map[string]interface{}{
  2544  		"store":       "foo",
  2545  		"operator-id": operatorAcct.AccountID(),
  2546  		"url":         "http://foo.internal",
  2547  		"timestamp":   time.Now().Format(time.RFC3339),
  2548  	}, nil, "")
  2549  	c.Assert(err, IsNil)
  2550  	err = assertstate.Add(st, stoAs)
  2551  	c.Assert(err, IsNil)
  2552  	tr := config.NewTransaction(st)
  2553  	err = tr.Set("core", "proxy.store", "foo")
  2554  	c.Assert(err, IsNil)
  2555  	tr.Commit()
  2556  
  2557  	fooURL, err := url.Parse("http://foo.internal")
  2558  	c.Assert(err, IsNil)
  2559  
  2560  	st.Unlock()
  2561  	proxyStoreID, proxyStoreURL, err = s.sc.ProxyStoreParams(defURL)
  2562  	st.Lock()
  2563  	c.Assert(err, IsNil)
  2564  	c.Check(proxyStoreID, Equals, "foo")
  2565  	c.Check(proxyStoreURL, DeepEquals, fooURL)
  2566  }
  2567  
  2568  const snapYamlContent1 = `name: snap1
  2569  plugs:
  2570   shared-data-plug:
  2571    interface: content
  2572    target: import
  2573    content: mylib
  2574  apps:
  2575   bar:
  2576    command: bin/bar
  2577  `
  2578  const snapYamlContent2 = `name: snap2
  2579  slots:
  2580   shared-data-slot:
  2581    interface: content
  2582    content: mylib
  2583    read:
  2584     - /
  2585  apps:
  2586   bar:
  2587    command: bin/bar
  2588  `
  2589  
  2590  func (s *mgrsSuite) testTwoInstalls(c *C, snapName1, snapYaml1, snapName2, snapYaml2 string) {
  2591  	snapPath1 := makeTestSnap(c, snapYaml1+"version: 1.0")
  2592  	snapPath2 := makeTestSnap(c, snapYaml2+"version: 1.0")
  2593  
  2594  	st := s.o.State()
  2595  	st.Lock()
  2596  	defer st.Unlock()
  2597  
  2598  	ts1, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: snapName1, SnapID: fakeSnapID(snapName1), Revision: snap.R(3)}, snapPath1, "", "", snapstate.Flags{DevMode: true})
  2599  	c.Assert(err, IsNil)
  2600  	chg := st.NewChange("install-snap", "...")
  2601  	chg.AddAll(ts1)
  2602  
  2603  	ts2, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: snapName2, SnapID: fakeSnapID(snapName2), Revision: snap.R(3)}, snapPath2, "", "", snapstate.Flags{DevMode: true})
  2604  	c.Assert(err, IsNil)
  2605  
  2606  	ts2.WaitAll(ts1)
  2607  	chg.AddAll(ts2)
  2608  
  2609  	st.Unlock()
  2610  	err = s.o.Settle(settleTimeout)
  2611  	st.Lock()
  2612  	c.Assert(err, IsNil)
  2613  
  2614  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  2615  
  2616  	tasks := chg.Tasks()
  2617  	connectTask := tasks[len(tasks)-2]
  2618  	c.Assert(connectTask.Kind(), Equals, "connect")
  2619  
  2620  	setupProfilesTask := tasks[len(tasks)-1]
  2621  	c.Assert(setupProfilesTask.Kind(), Equals, "setup-profiles")
  2622  
  2623  	// verify connect task data
  2624  	var plugRef interfaces.PlugRef
  2625  	var slotRef interfaces.SlotRef
  2626  	c.Assert(connectTask.Get("plug", &plugRef), IsNil)
  2627  	c.Assert(connectTask.Get("slot", &slotRef), IsNil)
  2628  	c.Assert(plugRef.Snap, Equals, "snap1")
  2629  	c.Assert(plugRef.Name, Equals, "shared-data-plug")
  2630  	c.Assert(slotRef.Snap, Equals, "snap2")
  2631  	c.Assert(slotRef.Name, Equals, "shared-data-slot")
  2632  
  2633  	// verify that connection was made
  2634  	var conns map[string]interface{}
  2635  	c.Assert(st.Get("conns", &conns), IsNil)
  2636  	c.Assert(conns, HasLen, 1)
  2637  
  2638  	repo := s.o.InterfaceManager().Repository()
  2639  	cn, err := repo.Connected("snap1", "shared-data-plug")
  2640  	c.Assert(err, IsNil)
  2641  	c.Assert(cn, HasLen, 1)
  2642  	c.Assert(cn, DeepEquals, []*interfaces.ConnRef{{
  2643  		PlugRef: interfaces.PlugRef{Snap: "snap1", Name: "shared-data-plug"},
  2644  		SlotRef: interfaces.SlotRef{Snap: "snap2", Name: "shared-data-slot"},
  2645  	}})
  2646  }
  2647  
  2648  func (s *mgrsSuite) TestTwoInstallsWithAutoconnectPlugSnapFirst(c *C) {
  2649  	s.testTwoInstalls(c, "snap1", snapYamlContent1, "snap2", snapYamlContent2)
  2650  }
  2651  
  2652  func (s *mgrsSuite) TestTwoInstallsWithAutoconnectSlotSnapFirst(c *C) {
  2653  	s.testTwoInstalls(c, "snap2", snapYamlContent2, "snap1", snapYamlContent1)
  2654  }
  2655  
  2656  func (s *mgrsSuite) TestRemoveAndInstallWithAutoconnectHappy(c *C) {
  2657  	st := s.o.State()
  2658  	st.Lock()
  2659  	defer st.Unlock()
  2660  
  2661  	_ = s.installLocalTestSnap(c, snapYamlContent1+"version: 1.0")
  2662  
  2663  	ts, err := snapstate.Remove(st, "snap1", snap.R(0), nil)
  2664  	c.Assert(err, IsNil)
  2665  	chg := st.NewChange("remove-snap", "...")
  2666  	chg.AddAll(ts)
  2667  
  2668  	snapPath := makeTestSnap(c, snapYamlContent2+"version: 1.0")
  2669  	chg2 := st.NewChange("install-snap", "...")
  2670  	ts2, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "snap2", SnapID: fakeSnapID("snap2"), Revision: snap.R(3)}, snapPath, "", "", snapstate.Flags{DevMode: true})
  2671  	chg2.AddAll(ts2)
  2672  	c.Assert(err, IsNil)
  2673  
  2674  	st.Unlock()
  2675  	err = s.o.Settle(settleTimeout)
  2676  	st.Lock()
  2677  	c.Assert(err, IsNil)
  2678  
  2679  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("remove-snap change failed with: %v", chg.Err()))
  2680  	c.Assert(chg2.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  2681  }
  2682  
  2683  const otherSnapYaml = `name: other-snap
  2684  version: 1.0
  2685  apps:
  2686     baz:
  2687          command: bin/bar
  2688          plugs: [media-hub]
  2689  `
  2690  
  2691  func (s *mgrsSuite) TestUpdateManyWithAutoconnect(c *C) {
  2692  	const someSnapYaml = `name: some-snap
  2693  version: 1.0
  2694  apps:
  2695     foo:
  2696          command: bin/bar
  2697          plugs: [network,home]
  2698          slots: [media-hub]
  2699  `
  2700  
  2701  	const coreSnapYaml = `name: core
  2702  type: os
  2703  version: @VERSION@`
  2704  
  2705  	snapPath, _ := s.makeStoreTestSnap(c, someSnapYaml, "40")
  2706  	s.serveSnap(snapPath, "40")
  2707  
  2708  	snapPath, _ = s.makeStoreTestSnap(c, otherSnapYaml, "50")
  2709  	s.serveSnap(snapPath, "50")
  2710  
  2711  	corePath, _ := s.makeStoreTestSnap(c, strings.Replace(coreSnapYaml, "@VERSION@", "30", -1), "30")
  2712  	s.serveSnap(corePath, "30")
  2713  
  2714  	mockServer := s.mockStore(c)
  2715  	defer mockServer.Close()
  2716  
  2717  	st := s.o.State()
  2718  	st.Lock()
  2719  	defer st.Unlock()
  2720  
  2721  	st.Set("conns", map[string]interface{}{})
  2722  
  2723  	si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)}
  2724  	snapInfo := snaptest.MockSnap(c, someSnapYaml, si)
  2725  	c.Assert(snapInfo.Plugs, HasLen, 2)
  2726  
  2727  	oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)}
  2728  	otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi)
  2729  	c.Assert(otherInfo.Plugs, HasLen, 1)
  2730  
  2731  	csi := &snap.SideInfo{RealName: "core", SnapID: fakeSnapID("core"), Revision: snap.R(1)}
  2732  	coreInfo := snaptest.MockSnap(c, strings.Replace(coreSnapYaml, "@VERSION@", "1", -1), csi)
  2733  
  2734  	// add implicit slots
  2735  	coreInfo.Slots["network"] = &snap.SlotInfo{
  2736  		Name:      "network",
  2737  		Snap:      coreInfo,
  2738  		Interface: "network",
  2739  	}
  2740  	coreInfo.Slots["home"] = &snap.SlotInfo{
  2741  		Name:      "home",
  2742  		Snap:      coreInfo,
  2743  		Interface: "home",
  2744  	}
  2745  
  2746  	snapstate.Set(st, "some-snap", &snapstate.SnapState{
  2747  		Active:   true,
  2748  		Sequence: []*snap.SideInfo{si},
  2749  		Current:  snap.R(1),
  2750  		SnapType: "app",
  2751  	})
  2752  	snapstate.Set(st, "other-snap", &snapstate.SnapState{
  2753  		Active:   true,
  2754  		Sequence: []*snap.SideInfo{oi},
  2755  		Current:  snap.R(1),
  2756  		SnapType: "app",
  2757  	})
  2758  
  2759  	repo := s.o.InterfaceManager().Repository()
  2760  
  2761  	// add snaps to the repo to have plugs/slots
  2762  	c.Assert(repo.AddSnap(snapInfo), IsNil)
  2763  	c.Assert(repo.AddSnap(otherInfo), IsNil)
  2764  	c.Assert(repo.AddSnap(coreInfo), IsNil)
  2765  
  2766  	// refresh all
  2767  	err := assertstate.RefreshSnapDeclarations(st, 0)
  2768  	c.Assert(err, IsNil)
  2769  
  2770  	updates, tts, err := snapstate.UpdateMany(context.TODO(), st, []string{"core", "some-snap", "other-snap"}, 0, nil)
  2771  	c.Assert(err, IsNil)
  2772  	c.Check(updates, HasLen, 3)
  2773  	c.Assert(tts, HasLen, 4)
  2774  	verifyLastTasksetIsRerefresh(c, tts)
  2775  
  2776  	// to make TaskSnapSetup work
  2777  	chg := st.NewChange("refresh", "...")
  2778  	for _, ts := range tts[:len(tts)-1] {
  2779  		chg.AddAll(ts)
  2780  	}
  2781  
  2782  	// force hold state to hit ignore status of findSymmetricAutoconnect
  2783  	tts[2].Tasks()[0].SetStatus(state.HoldStatus)
  2784  
  2785  	st.Unlock()
  2786  	err = s.o.Settle(3 * time.Second)
  2787  	st.Lock()
  2788  	c.Assert(err, IsNil)
  2789  
  2790  	// simulate successful restart happened
  2791  	state.MockRestarting(st, state.RestartUnset)
  2792  	tts[2].Tasks()[0].SetStatus(state.DefaultStatus)
  2793  	st.Unlock()
  2794  
  2795  	err = s.o.Settle(settleTimeout)
  2796  	st.Lock()
  2797  
  2798  	c.Assert(err, IsNil)
  2799  
  2800  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  2801  
  2802  	// check connections
  2803  	var conns map[string]interface{}
  2804  	st.Get("conns", &conns)
  2805  	c.Assert(conns, DeepEquals, map[string]interface{}{
  2806  		"some-snap:home core:home":                 map[string]interface{}{"interface": "home", "auto": true},
  2807  		"some-snap:network core:network":           map[string]interface{}{"interface": "network", "auto": true},
  2808  		"other-snap:media-hub some-snap:media-hub": map[string]interface{}{"interface": "media-hub", "auto": true},
  2809  	})
  2810  
  2811  	connections, err := repo.Connections("some-snap")
  2812  	c.Assert(err, IsNil)
  2813  	c.Assert(connections, HasLen, 3)
  2814  }
  2815  
  2816  func (s *mgrsSuite) TestUpdateWithAutoconnectAndInactiveRevisions(c *C) {
  2817  	const someSnapYaml = `name: some-snap
  2818  version: 1.0
  2819  apps:
  2820     foo:
  2821          command: bin/bar
  2822          plugs: [network]
  2823  `
  2824  	const coreSnapYaml = `name: core
  2825  type: os
  2826  version: 1`
  2827  
  2828  	snapPath, _ := s.makeStoreTestSnap(c, someSnapYaml, "40")
  2829  	s.serveSnap(snapPath, "40")
  2830  
  2831  	mockServer := s.mockStore(c)
  2832  	defer mockServer.Close()
  2833  
  2834  	st := s.o.State()
  2835  	st.Lock()
  2836  	defer st.Unlock()
  2837  
  2838  	si1 := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)}
  2839  	snapInfo := snaptest.MockSnap(c, someSnapYaml, si1)
  2840  	c.Assert(snapInfo.Plugs, HasLen, 1)
  2841  
  2842  	csi := &snap.SideInfo{RealName: "core", SnapID: fakeSnapID("core"), Revision: snap.R(1)}
  2843  	coreInfo := snaptest.MockSnap(c, coreSnapYaml, csi)
  2844  
  2845  	// add implicit slots
  2846  	coreInfo.Slots["network"] = &snap.SlotInfo{
  2847  		Name:      "network",
  2848  		Snap:      coreInfo,
  2849  		Interface: "network",
  2850  	}
  2851  
  2852  	// some-snap has inactive revisions
  2853  	si0 := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(0)}
  2854  	si2 := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(2)}
  2855  	snapstate.Set(st, "some-snap", &snapstate.SnapState{
  2856  		Active:   true,
  2857  		Sequence: []*snap.SideInfo{si0, si1, si2},
  2858  		Current:  snap.R(1),
  2859  		SnapType: "app",
  2860  	})
  2861  
  2862  	repo := s.o.InterfaceManager().Repository()
  2863  
  2864  	// add snaps to the repo to have plugs/slots
  2865  	c.Assert(repo.AddSnap(snapInfo), IsNil)
  2866  	c.Assert(repo.AddSnap(coreInfo), IsNil)
  2867  
  2868  	// refresh all
  2869  	err := assertstate.RefreshSnapDeclarations(st, 0)
  2870  	c.Assert(err, IsNil)
  2871  
  2872  	updates, tts, err := snapstate.UpdateMany(context.TODO(), st, []string{"some-snap"}, 0, nil)
  2873  	c.Assert(err, IsNil)
  2874  	c.Check(updates, HasLen, 1)
  2875  	c.Assert(tts, HasLen, 2)
  2876  	verifyLastTasksetIsRerefresh(c, tts)
  2877  
  2878  	// to make TaskSnapSetup work
  2879  	chg := st.NewChange("refresh", "...")
  2880  	chg.AddAll(tts[0])
  2881  
  2882  	st.Unlock()
  2883  	err = s.o.Settle(settleTimeout)
  2884  	st.Lock()
  2885  
  2886  	c.Assert(err, IsNil)
  2887  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  2888  
  2889  	// check connections
  2890  	var conns map[string]interface{}
  2891  	st.Get("conns", &conns)
  2892  	c.Assert(conns, DeepEquals, map[string]interface{}{
  2893  		"some-snap:network core:network": map[string]interface{}{"interface": "network", "auto": true},
  2894  	})
  2895  }
  2896  
  2897  const someSnapYaml = `name: some-snap
  2898  version: 1.0
  2899  apps:
  2900     foo:
  2901          command: bin/bar
  2902          slots: [media-hub]
  2903  `
  2904  
  2905  func (s *mgrsSuite) testUpdateWithAutoconnectRetry(c *C, updateSnapName, removeSnapName string) {
  2906  	snapPath, _ := s.makeStoreTestSnap(c, someSnapYaml, "40")
  2907  	s.serveSnap(snapPath, "40")
  2908  
  2909  	snapPath, _ = s.makeStoreTestSnap(c, otherSnapYaml, "50")
  2910  	s.serveSnap(snapPath, "50")
  2911  
  2912  	mockServer := s.mockStore(c)
  2913  	defer mockServer.Close()
  2914  
  2915  	st := s.o.State()
  2916  	st.Lock()
  2917  	defer st.Unlock()
  2918  
  2919  	st.Set("conns", map[string]interface{}{})
  2920  
  2921  	si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)}
  2922  	snapInfo := snaptest.MockSnap(c, someSnapYaml, si)
  2923  	c.Assert(snapInfo.Slots, HasLen, 1)
  2924  
  2925  	oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)}
  2926  	otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi)
  2927  	c.Assert(otherInfo.Plugs, HasLen, 1)
  2928  
  2929  	snapstate.Set(st, "some-snap", &snapstate.SnapState{
  2930  		Active:   true,
  2931  		Sequence: []*snap.SideInfo{si},
  2932  		Current:  snap.R(1),
  2933  		SnapType: "app",
  2934  	})
  2935  	snapstate.Set(st, "other-snap", &snapstate.SnapState{
  2936  		Active:   true,
  2937  		Sequence: []*snap.SideInfo{oi},
  2938  		Current:  snap.R(1),
  2939  		SnapType: "app",
  2940  	})
  2941  
  2942  	repo := s.o.InterfaceManager().Repository()
  2943  
  2944  	// add snaps to the repo to have plugs/slots
  2945  	c.Assert(repo.AddSnap(snapInfo), IsNil)
  2946  	c.Assert(repo.AddSnap(otherInfo), IsNil)
  2947  
  2948  	// refresh all
  2949  	err := assertstate.RefreshSnapDeclarations(st, 0)
  2950  	c.Assert(err, IsNil)
  2951  
  2952  	ts, err := snapstate.Update(st, updateSnapName, nil, 0, snapstate.Flags{})
  2953  	c.Assert(err, IsNil)
  2954  
  2955  	// to make TaskSnapSetup work
  2956  	chg := st.NewChange("refresh", "...")
  2957  	chg.AddAll(ts)
  2958  
  2959  	// remove other-snap
  2960  	ts2, err := snapstate.Remove(st, removeSnapName, snap.R(0), nil)
  2961  	c.Assert(err, IsNil)
  2962  	chg2 := st.NewChange("remove-snap", "...")
  2963  	chg2.AddAll(ts2)
  2964  
  2965  	// force hold state on first removal task to hit Retry error
  2966  	ts2.Tasks()[0].SetStatus(state.HoldStatus)
  2967  
  2968  	// Settle is not converging here because of the task in Hold status, therefore
  2969  	// it always hits given timeout before we carry on with the test. We're
  2970  	// interested in hitting the retry condition on auto-connect task, so
  2971  	// instead of passing a generous timeout to Settle(), repeat Settle() a number
  2972  	// of times with an aggressive timeout and break as soon as we reach the desired
  2973  	// state of auto-connect task.
  2974  	var retryCheck bool
  2975  	var autoconnectLog string
  2976  	for i := 0; i < 50 && !retryCheck; i++ {
  2977  		st.Unlock()
  2978  		s.o.Settle(aggressiveSettleTimeout)
  2979  		st.Lock()
  2980  
  2981  		for _, t := range st.Tasks() {
  2982  			if t.Kind() == "auto-connect" && t.Status() == state.DoingStatus && strings.Contains(strings.Join(t.Log(), ""), "Waiting") {
  2983  				autoconnectLog = strings.Join(t.Log(), "")
  2984  				retryCheck = true
  2985  				break
  2986  			}
  2987  		}
  2988  	}
  2989  
  2990  	c.Check(retryCheck, Equals, true)
  2991  	c.Assert(autoconnectLog, Matches, `.*Waiting for conflicting change in progress: conflicting snap.*`)
  2992  
  2993  	// back to default state, that will unblock autoconnect
  2994  	ts2.Tasks()[0].SetStatus(state.DefaultStatus)
  2995  	st.Unlock()
  2996  	err = s.o.Settle(settleTimeout)
  2997  	st.Lock()
  2998  	c.Assert(err, IsNil)
  2999  
  3000  	c.Check(chg.Err(), IsNil)
  3001  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  3002  
  3003  	// check connections
  3004  	var conns map[string]interface{}
  3005  	st.Get("conns", &conns)
  3006  	c.Assert(conns, HasLen, 0)
  3007  }
  3008  
  3009  func (s *mgrsSuite) TestUpdateWithAutoconnectRetrySlotSide(c *C) {
  3010  	s.testUpdateWithAutoconnectRetry(c, "some-snap", "other-snap")
  3011  }
  3012  
  3013  func (s *mgrsSuite) TestUpdateWithAutoconnectRetryPlugSide(c *C) {
  3014  	s.testUpdateWithAutoconnectRetry(c, "other-snap", "some-snap")
  3015  }
  3016  
  3017  func (s *mgrsSuite) TestDisconnectIgnoredOnSymmetricRemove(c *C) {
  3018  	const someSnapYaml = `name: some-snap
  3019  version: 1.0
  3020  apps:
  3021     foo:
  3022          command: bin/bar
  3023          slots: [media-hub]
  3024  hooks:
  3025     disconnect-slot-media-hub:
  3026  `
  3027  	const otherSnapYaml = `name: other-snap
  3028  version: 1.0
  3029  apps:
  3030     baz:
  3031          command: bin/bar
  3032          plugs: [media-hub]
  3033  hooks:
  3034     disconnect-plug-media-hub:
  3035  `
  3036  	st := s.o.State()
  3037  	st.Lock()
  3038  	defer st.Unlock()
  3039  
  3040  	st.Set("conns", map[string]interface{}{
  3041  		"other-snap:media-hub some-snap:media-hub": map[string]interface{}{"interface": "media-hub", "auto": false},
  3042  	})
  3043  
  3044  	si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)}
  3045  	snapInfo := snaptest.MockSnap(c, someSnapYaml, si)
  3046  	c.Assert(snapInfo.Slots, HasLen, 1)
  3047  
  3048  	oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)}
  3049  	otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi)
  3050  	c.Assert(otherInfo.Plugs, HasLen, 1)
  3051  
  3052  	snapstate.Set(st, "some-snap", &snapstate.SnapState{
  3053  		Active:   true,
  3054  		Sequence: []*snap.SideInfo{si},
  3055  		Current:  snap.R(1),
  3056  		SnapType: "app",
  3057  	})
  3058  	snapstate.Set(st, "other-snap", &snapstate.SnapState{
  3059  		Active:   true,
  3060  		Sequence: []*snap.SideInfo{oi},
  3061  		Current:  snap.R(1),
  3062  		SnapType: "app",
  3063  	})
  3064  
  3065  	repo := s.o.InterfaceManager().Repository()
  3066  
  3067  	// add snaps to the repo to have plugs/slots
  3068  	c.Assert(repo.AddSnap(snapInfo), IsNil)
  3069  	c.Assert(repo.AddSnap(otherInfo), IsNil)
  3070  	repo.Connect(&interfaces.ConnRef{
  3071  		PlugRef: interfaces.PlugRef{Snap: "other-snap", Name: "media-hub"},
  3072  		SlotRef: interfaces.SlotRef{Snap: "some-snap", Name: "media-hub"},
  3073  	}, nil, nil, nil, nil, nil)
  3074  
  3075  	ts, err := snapstate.Remove(st, "some-snap", snap.R(0), nil)
  3076  	c.Assert(err, IsNil)
  3077  	chg := st.NewChange("uninstall", "...")
  3078  	chg.AddAll(ts)
  3079  
  3080  	// remove other-snap
  3081  	ts2, err := snapstate.Remove(st, "other-snap", snap.R(0), nil)
  3082  	c.Assert(err, IsNil)
  3083  	chg2 := st.NewChange("uninstall", "...")
  3084  	chg2.AddAll(ts2)
  3085  
  3086  	st.Unlock()
  3087  	err = s.o.Settle(settleTimeout)
  3088  	st.Lock()
  3089  	c.Assert(err, IsNil)
  3090  
  3091  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  3092  
  3093  	// check connections
  3094  	var conns map[string]interface{}
  3095  	st.Get("conns", &conns)
  3096  	c.Assert(conns, HasLen, 0)
  3097  
  3098  	var disconnectInterfacesCount, slotHookCount, plugHookCount int
  3099  	for _, t := range st.Tasks() {
  3100  		if t.Kind() == "auto-disconnect" {
  3101  			disconnectInterfacesCount++
  3102  		}
  3103  		if t.Kind() == "run-hook" {
  3104  			var hsup hookstate.HookSetup
  3105  			c.Assert(t.Get("hook-setup", &hsup), IsNil)
  3106  			if hsup.Hook == "disconnect-plug-media-hub" {
  3107  				plugHookCount++
  3108  			}
  3109  			if hsup.Hook == "disconnect-slot-media-hub" {
  3110  				slotHookCount++
  3111  			}
  3112  		}
  3113  	}
  3114  	c.Assert(plugHookCount, Equals, 1)
  3115  	c.Assert(slotHookCount, Equals, 1)
  3116  	c.Assert(disconnectInterfacesCount, Equals, 2)
  3117  
  3118  	var snst snapstate.SnapState
  3119  	err = snapstate.Get(st, "other-snap", &snst)
  3120  	c.Assert(err, Equals, state.ErrNoState)
  3121  	_, err = repo.Connected("other-snap", "media-hub")
  3122  	c.Assert(err, ErrorMatches, `snap "other-snap" has no plug or slot named "media-hub"`)
  3123  }
  3124  
  3125  func (s *mgrsSuite) TestDisconnectOnUninstallRemovesAutoconnection(c *C) {
  3126  	st := s.o.State()
  3127  	st.Lock()
  3128  	defer st.Unlock()
  3129  
  3130  	st.Set("conns", map[string]interface{}{
  3131  		"other-snap:media-hub some-snap:media-hub": map[string]interface{}{"interface": "media-hub", "auto": true},
  3132  	})
  3133  
  3134  	si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)}
  3135  	snapInfo := snaptest.MockSnap(c, someSnapYaml, si)
  3136  
  3137  	oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)}
  3138  	otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi)
  3139  
  3140  	snapstate.Set(st, "some-snap", &snapstate.SnapState{
  3141  		Active:   true,
  3142  		Sequence: []*snap.SideInfo{si},
  3143  		Current:  snap.R(1),
  3144  		SnapType: "app",
  3145  	})
  3146  	snapstate.Set(st, "other-snap", &snapstate.SnapState{
  3147  		Active:   true,
  3148  		Sequence: []*snap.SideInfo{oi},
  3149  		Current:  snap.R(1),
  3150  		SnapType: "app",
  3151  	})
  3152  
  3153  	repo := s.o.InterfaceManager().Repository()
  3154  
  3155  	// add snaps to the repo to have plugs/slots
  3156  	c.Assert(repo.AddSnap(snapInfo), IsNil)
  3157  	c.Assert(repo.AddSnap(otherInfo), IsNil)
  3158  	repo.Connect(&interfaces.ConnRef{
  3159  		PlugRef: interfaces.PlugRef{Snap: "other-snap", Name: "media-hub"},
  3160  		SlotRef: interfaces.SlotRef{Snap: "some-snap", Name: "media-hub"},
  3161  	}, nil, nil, nil, nil, nil)
  3162  
  3163  	ts, err := snapstate.Remove(st, "some-snap", snap.R(0), nil)
  3164  	c.Assert(err, IsNil)
  3165  	chg := st.NewChange("uninstall", "...")
  3166  	chg.AddAll(ts)
  3167  
  3168  	st.Unlock()
  3169  	err = s.o.Settle(settleTimeout)
  3170  	st.Lock()
  3171  	c.Assert(err, IsNil)
  3172  
  3173  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  3174  
  3175  	// check connections; auto-connection should be removed completely from conns on uninstall.
  3176  	var conns map[string]interface{}
  3177  	st.Get("conns", &conns)
  3178  	c.Assert(conns, HasLen, 0)
  3179  }
  3180  
  3181  // TODO: add a custom checker in testutils for this and similar
  3182  func validateDownloadCheckTasks(c *C, tasks []*state.Task, name, revno, channel string) int {
  3183  	var i int
  3184  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Ensure prerequisites for "%s" are available`, name))
  3185  	i++
  3186  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Download snap "%s" (%s) from channel "%s"`, name, revno, channel))
  3187  	i++
  3188  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Fetch and check assertions for snap "%s" (%s)`, name, revno))
  3189  	i++
  3190  	return i
  3191  }
  3192  
  3193  func validateInstallTasks(c *C, tasks []*state.Task, name, revno string) int {
  3194  	var i int
  3195  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Mount snap "%s" (%s)`, name, revno))
  3196  	i++
  3197  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Copy snap "%s" data`, name))
  3198  	i++
  3199  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Setup snap "%s" (%s) security profiles`, name, revno))
  3200  	i++
  3201  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Make snap "%s" (%s) available to the system`, name, revno))
  3202  	i++
  3203  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Automatically connect eligible plugs and slots of snap "%s"`, name))
  3204  	i++
  3205  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Set automatic aliases for snap "%s"`, name))
  3206  	i++
  3207  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Setup snap "%s" aliases`, name))
  3208  	i++
  3209  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run install hook of "%s" snap if present`, name))
  3210  	i++
  3211  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Start snap "%s" (%s) services`, name, revno))
  3212  	i++
  3213  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run configure hook of "%s" snap if present`, name))
  3214  	i++
  3215  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run health check of "%s" snap`, name))
  3216  	i++
  3217  	return i
  3218  }
  3219  
  3220  func validateRefreshTasks(c *C, tasks []*state.Task, name, revno string) int {
  3221  	var i int
  3222  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Mount snap "%s" (%s)`, name, revno))
  3223  	i++
  3224  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run pre-refresh hook of "%s" snap if present`, name))
  3225  	i++
  3226  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Stop snap "%s" services`, name))
  3227  	i++
  3228  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Remove aliases for snap "%s"`, name))
  3229  	i++
  3230  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Make current revision for snap "%s" unavailable`, name))
  3231  	i++
  3232  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Copy snap "%s" data`, name))
  3233  	i++
  3234  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Setup snap "%s" (%s) security profiles`, name, revno))
  3235  	i++
  3236  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Make snap "%s" (%s) available to the system`, name, revno))
  3237  	i++
  3238  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Automatically connect eligible plugs and slots of snap "%s"`, name))
  3239  	i++
  3240  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Set automatic aliases for snap "%s"`, name))
  3241  	i++
  3242  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Setup snap "%s" aliases`, name))
  3243  	i++
  3244  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run post-refresh hook of "%s" snap if present`, name))
  3245  	i++
  3246  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Start snap "%s" (%s) services`, name, revno))
  3247  	i++
  3248  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Clean up "%s" (%s) install`, name, revno))
  3249  	i++
  3250  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run configure hook of "%s" snap if present`, name))
  3251  	i++
  3252  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run health check of "%s" snap`, name))
  3253  	i++
  3254  	return i
  3255  }
  3256  
  3257  // byReadyTime sorts a list of tasks by their "ready" time
  3258  type byReadyTime []*state.Task
  3259  
  3260  func (a byReadyTime) Len() int           { return len(a) }
  3261  func (a byReadyTime) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
  3262  func (a byReadyTime) Less(i, j int) bool { return a[i].ReadyTime().Before(a[j].ReadyTime()) }
  3263  
  3264  func (s *mgrsSuite) TestRemodelRequiredSnapsAdded(c *C) {
  3265  	for _, name := range []string{"foo", "bar", "baz"} {
  3266  		s.prereqSnapAssertions(c, map[string]interface{}{
  3267  			"snap-name": name,
  3268  		})
  3269  		snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 1.0}", name), "1")
  3270  		s.serveSnap(snapPath, "1")
  3271  	}
  3272  
  3273  	mockServer := s.mockStore(c)
  3274  	defer mockServer.Close()
  3275  
  3276  	st := s.o.State()
  3277  	st.Lock()
  3278  	defer st.Unlock()
  3279  
  3280  	// pretend we have an old required snap installed
  3281  	si1 := &snap.SideInfo{RealName: "old-required-snap-1", Revision: snap.R(1)}
  3282  	snapstate.Set(st, "old-required-snap-1", &snapstate.SnapState{
  3283  		SnapType: "app",
  3284  		Active:   true,
  3285  		Sequence: []*snap.SideInfo{si1},
  3286  		Current:  si1.Revision,
  3287  		Flags:    snapstate.Flags{Required: true},
  3288  	})
  3289  
  3290  	// create/set custom model assertion
  3291  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  3292  
  3293  	model := s.brands.Model("my-brand", "my-model", modelDefaults)
  3294  
  3295  	// setup model assertion
  3296  	devicestatetest.SetDevice(st, &auth.DeviceState{
  3297  		Brand:  "my-brand",
  3298  		Model:  "my-model",
  3299  		Serial: "serialserialserial",
  3300  	})
  3301  	err := assertstate.Add(st, model)
  3302  	c.Assert(err, IsNil)
  3303  
  3304  	// create a new model
  3305  	newModel := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{
  3306  		"required-snaps": []interface{}{"foo", "bar", "baz"},
  3307  		"revision":       "1",
  3308  	})
  3309  
  3310  	chg, err := devicestate.Remodel(st, newModel)
  3311  	c.Assert(err, IsNil)
  3312  
  3313  	c.Check(devicestate.Remodeling(st), Equals, true)
  3314  
  3315  	st.Unlock()
  3316  	err = s.o.Settle(settleTimeout)
  3317  	st.Lock()
  3318  	c.Assert(err, IsNil)
  3319  
  3320  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  3321  
  3322  	c.Check(devicestate.Remodeling(st), Equals, false)
  3323  
  3324  	// the new required-snap "foo" is installed
  3325  	var snapst snapstate.SnapState
  3326  	err = snapstate.Get(st, "foo", &snapst)
  3327  	c.Assert(err, IsNil)
  3328  	info, err := snapst.CurrentInfo()
  3329  	c.Assert(err, IsNil)
  3330  	c.Check(info.Revision, Equals, snap.R(1))
  3331  	c.Check(info.Version, Equals, "1.0")
  3332  
  3333  	// and marked required
  3334  	c.Check(snapst.Required, Equals, true)
  3335  
  3336  	// and core is still marked required
  3337  	err = snapstate.Get(st, "core", &snapst)
  3338  	c.Assert(err, IsNil)
  3339  	c.Check(snapst.Required, Equals, true)
  3340  
  3341  	// but old-required-snap-1 is no longer marked required
  3342  	err = snapstate.Get(st, "old-required-snap-1", &snapst)
  3343  	c.Assert(err, IsNil)
  3344  	c.Check(snapst.Required, Equals, false)
  3345  
  3346  	// ensure sorting is correct
  3347  	tasks := chg.Tasks()
  3348  	sort.Sort(byReadyTime(tasks))
  3349  
  3350  	var i int
  3351  	// first all downloads/checks in sequential order
  3352  	for _, name := range []string{"foo", "bar", "baz"} {
  3353  		i += validateDownloadCheckTasks(c, tasks[i:], name, "1", "stable")
  3354  	}
  3355  	// then all installs in sequential order
  3356  	for _, name := range []string{"foo", "bar", "baz"} {
  3357  		i += validateInstallTasks(c, tasks[i:], name, "1")
  3358  	}
  3359  	// ensure that we only have the tasks we checked (plus the one
  3360  	// extra "set-model" task)
  3361  	c.Assert(tasks, HasLen, i+1)
  3362  }
  3363  
  3364  func (s *mgrsSuite) TestRemodelDifferentBase(c *C) {
  3365  	// make "core18" snap available in the store
  3366  	s.prereqSnapAssertions(c, map[string]interface{}{
  3367  		"snap-name": "core18",
  3368  	})
  3369  	snapYamlContent := `name: core18
  3370  version: 18.04
  3371  type: base`
  3372  	snapPath, _ := s.makeStoreTestSnap(c, snapYamlContent, "18")
  3373  	s.serveSnap(snapPath, "18")
  3374  
  3375  	mockServer := s.mockStore(c)
  3376  	defer mockServer.Close()
  3377  
  3378  	st := s.o.State()
  3379  	st.Lock()
  3380  	defer st.Unlock()
  3381  
  3382  	// create/set custom model assertion
  3383  	model := s.brands.Model("can0nical", "my-model", modelDefaults)
  3384  	// setup model assertion
  3385  	devicestatetest.SetDevice(st, &auth.DeviceState{
  3386  		Brand:  "can0nical",
  3387  		Model:  "my-model",
  3388  		Serial: "serialserialserial",
  3389  	})
  3390  	err := assertstate.Add(st, model)
  3391  	c.Assert(err, IsNil)
  3392  
  3393  	// create a new model
  3394  	newModel := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  3395  		"base":     "core18",
  3396  		"revision": "1",
  3397  	})
  3398  
  3399  	chg, err := devicestate.Remodel(st, newModel)
  3400  	c.Assert(err, ErrorMatches, "cannot remodel to different bases yet")
  3401  	c.Assert(chg, IsNil)
  3402  }
  3403  
  3404  func (s *mgrsSuite) TestRemodelSwitchKernelTrack(c *C) {
  3405  	bloader := bootloadertest.Mock("mock", c.MkDir())
  3406  	bloader.SetBootKernel("pc-kernel_1.snap")
  3407  	bloader.SetBootBase("core_1.snap")
  3408  	bootloader.Force(bloader)
  3409  	defer bootloader.Force(nil)
  3410  
  3411  	restore := release.MockOnClassic(false)
  3412  	defer restore()
  3413  
  3414  	mockServer := s.mockStore(c)
  3415  	defer mockServer.Close()
  3416  
  3417  	st := s.o.State()
  3418  	st.Lock()
  3419  	defer st.Unlock()
  3420  
  3421  	si := &snap.SideInfo{RealName: "pc-kernel", SnapID: fakeSnapID("pc-kernel"), Revision: snap.R(1)}
  3422  	snapstate.Set(st, "pc-kernel", &snapstate.SnapState{
  3423  		Active:   true,
  3424  		Sequence: []*snap.SideInfo{si},
  3425  		Current:  snap.R(1),
  3426  		SnapType: "kernel",
  3427  	})
  3428  
  3429  	const kernelYaml = `name: pc-kernel
  3430  type: kernel
  3431  version: 2.0`
  3432  	snapPath, _ := s.makeStoreTestSnap(c, kernelYaml, "2")
  3433  	s.serveSnap(snapPath, "2")
  3434  
  3435  	s.prereqSnapAssertions(c, map[string]interface{}{
  3436  		"snap-name": "foo",
  3437  	})
  3438  	snapPath, _ = s.makeStoreTestSnap(c, `{name: "foo", version: 1.0}`, "1")
  3439  	s.serveSnap(snapPath, "1")
  3440  
  3441  	// create/set custom model assertion
  3442  	model := s.brands.Model("can0nical", "my-model", modelDefaults)
  3443  	// setup model assertion
  3444  	devicestatetest.SetDevice(st, &auth.DeviceState{
  3445  		Brand:  "can0nical",
  3446  		Model:  "my-model",
  3447  		Serial: "serialserialserial",
  3448  	})
  3449  	err := assertstate.Add(st, model)
  3450  	c.Assert(err, IsNil)
  3451  
  3452  	// create a new model
  3453  	newModel := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  3454  		"kernel":         "pc-kernel=18",
  3455  		"revision":       "1",
  3456  		"required-snaps": []interface{}{"foo"},
  3457  	})
  3458  
  3459  	chg, err := devicestate.Remodel(st, newModel)
  3460  	c.Assert(err, IsNil)
  3461  
  3462  	st.Unlock()
  3463  	err = s.o.Settle(settleTimeout)
  3464  	st.Lock()
  3465  	c.Assert(err, IsNil)
  3466  
  3467  	// system waits for a restart because of the new kernel
  3468  	t := findKind(chg, "auto-connect")
  3469  	c.Assert(t, NotNil)
  3470  	c.Assert(t.Status(), Equals, state.DoingStatus)
  3471  
  3472  	// simulate successful restart happened
  3473  	state.MockRestarting(st, state.RestartUnset)
  3474  
  3475  	// continue
  3476  	st.Unlock()
  3477  	err = s.o.Settle(settleTimeout)
  3478  	st.Lock()
  3479  	c.Assert(err, IsNil)
  3480  
  3481  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  3482  
  3483  	// ensure tasks were run in the right order
  3484  	tasks := chg.Tasks()
  3485  	sort.Sort(byReadyTime(tasks))
  3486  
  3487  	// first all downloads/checks in sequential order
  3488  	var i int
  3489  	i += validateDownloadCheckTasks(c, tasks[i:], "pc-kernel", "2", "18")
  3490  	i += validateDownloadCheckTasks(c, tasks[i:], "foo", "1", "stable")
  3491  
  3492  	// then all installs in sequential order
  3493  	i += validateRefreshTasks(c, tasks[i:], "pc-kernel", "2")
  3494  	i += validateInstallTasks(c, tasks[i:], "foo", "1")
  3495  
  3496  	// ensure that we only have the tasks we checked (plus the one
  3497  	// extra "set-model" task)
  3498  	c.Assert(tasks, HasLen, i+1)
  3499  }
  3500  
  3501  func (ms *mgrsSuite) TestRemodelSwitchToDifferentKernel(c *C) {
  3502  	bloader := bootloadertest.Mock("mock", c.MkDir())
  3503  	bootloader.Force(bloader)
  3504  	defer bootloader.Force(nil)
  3505  
  3506  	restore := release.MockOnClassic(false)
  3507  	defer restore()
  3508  
  3509  	mockServer := ms.mockStore(c)
  3510  	defer mockServer.Close()
  3511  
  3512  	st := ms.o.State()
  3513  	st.Lock()
  3514  	defer st.Unlock()
  3515  
  3516  	si := &snap.SideInfo{RealName: "pc-kernel", SnapID: fakeSnapID("pc-kernel"), Revision: snap.R(1)}
  3517  	snapstate.Set(st, "pc-kernel", &snapstate.SnapState{
  3518  		Active:   true,
  3519  		Sequence: []*snap.SideInfo{si},
  3520  		Current:  snap.R(1),
  3521  		SnapType: "kernel",
  3522  	})
  3523  	bloader.SetBootVars(map[string]string{
  3524  		"snap_mode":   "",
  3525  		"snap_core":   "core_1.snap",
  3526  		"snap_kernel": "pc-kernel_1.snap",
  3527  	})
  3528  	si2 := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)}
  3529  	gadgetSnapYaml := "name: pc\nversion: 1.0\ntype: gadget"
  3530  	snapstate.Set(st, "pc", &snapstate.SnapState{
  3531  		Active:   true,
  3532  		Sequence: []*snap.SideInfo{si2},
  3533  		Current:  snap.R(1),
  3534  		SnapType: "gadget",
  3535  	})
  3536  	gadgetYaml := `
  3537  volumes:
  3538      volume-id:
  3539          bootloader: grub
  3540  `
  3541  	snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si2, [][]string{
  3542  		{"meta/gadget.yaml", gadgetYaml},
  3543  	})
  3544  
  3545  	// add "brand-kernel" snap to fake store
  3546  	const brandKernelYaml = `name: brand-kernel
  3547  type: kernel
  3548  version: 1.0`
  3549  	ms.prereqSnapAssertions(c, map[string]interface{}{
  3550  		"snap-name":    "brand-kernel",
  3551  		"publisher-id": "can0nical",
  3552  	})
  3553  	snapPath, _ := ms.makeStoreTestSnap(c, brandKernelYaml, "2")
  3554  	ms.serveSnap(snapPath, "2")
  3555  
  3556  	// add "foo" snap to fake store
  3557  	ms.prereqSnapAssertions(c, map[string]interface{}{
  3558  		"snap-name": "foo",
  3559  	})
  3560  	snapPath, _ = ms.makeStoreTestSnap(c, `{name: "foo", version: 1.0}`, "1")
  3561  	ms.serveSnap(snapPath, "1")
  3562  
  3563  	// create/set custom model assertion
  3564  	model := ms.brands.Model("can0nical", "my-model", modelDefaults)
  3565  
  3566  	// setup model assertion
  3567  	devicestatetest.SetDevice(st, &auth.DeviceState{
  3568  		Brand:  "can0nical",
  3569  		Model:  "my-model",
  3570  		Serial: "serialserialserial",
  3571  	})
  3572  	err := assertstate.Add(st, model)
  3573  	c.Assert(err, IsNil)
  3574  
  3575  	// create a new model
  3576  	newModel := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  3577  		"kernel":         "brand-kernel",
  3578  		"revision":       "1",
  3579  		"required-snaps": []interface{}{"foo"},
  3580  	})
  3581  
  3582  	chg, err := devicestate.Remodel(st, newModel)
  3583  	c.Assert(err, IsNil)
  3584  
  3585  	st.Unlock()
  3586  	// regular settleTimeout is not enough on arm buildds :/
  3587  	err = ms.o.Settle(4 * settleTimeout)
  3588  	st.Lock()
  3589  	c.Assert(err, IsNil)
  3590  	c.Assert(chg.Err(), IsNil)
  3591  
  3592  	// system waits for a restart because of the new kernel
  3593  	t := findKind(chg, "auto-connect")
  3594  	c.Assert(t, NotNil)
  3595  	c.Assert(t.Status(), Equals, state.DoingStatus)
  3596  
  3597  	// simulate successful restart happened
  3598  	state.MockRestarting(st, state.RestartUnset)
  3599  
  3600  	// continue
  3601  	st.Unlock()
  3602  	// regular settleTimeout is not enough on arm buildds :/
  3603  	err = ms.o.Settle(4 * settleTimeout)
  3604  	st.Lock()
  3605  	c.Assert(err, IsNil)
  3606  
  3607  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  3608  
  3609  	// ensure tasks were run in the right order
  3610  	tasks := chg.Tasks()
  3611  	sort.Sort(byReadyTime(tasks))
  3612  
  3613  	// first all downloads/checks in sequential order
  3614  	var i int
  3615  	i += validateDownloadCheckTasks(c, tasks[i:], "brand-kernel", "2", "stable")
  3616  	i += validateDownloadCheckTasks(c, tasks[i:], "foo", "1", "stable")
  3617  
  3618  	// then all installs in sequential order
  3619  	i += validateInstallTasks(c, tasks[i:], "brand-kernel", "2")
  3620  	i += validateInstallTasks(c, tasks[i:], "foo", "1")
  3621  
  3622  	// ensure that we only have the tasks we checked (plus the one
  3623  	// extra "set-model" task)
  3624  	c.Assert(tasks, HasLen, i+1)
  3625  
  3626  	// ensure we did not try device registration
  3627  	for _, t := range st.Tasks() {
  3628  		if t.Kind() == "request-serial" {
  3629  			c.Fatalf("test should not create a request-serial task but did")
  3630  		}
  3631  	}
  3632  }
  3633  
  3634  func (s *mgrsSuite) TestRemodelStoreSwitch(c *C) {
  3635  	s.prereqSnapAssertions(c, map[string]interface{}{
  3636  		"snap-name": "foo",
  3637  	})
  3638  	snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 1.0}", "foo"), "1")
  3639  	s.serveSnap(snapPath, "1")
  3640  
  3641  	// track the creation of new DeviceAndAutContext (for new Store)
  3642  	newDAC := false
  3643  
  3644  	mockServer := s.mockStore(c)
  3645  	defer mockServer.Close()
  3646  
  3647  	st := s.o.State()
  3648  	st.Lock()
  3649  	defer st.Unlock()
  3650  
  3651  	s.checkDeviceAndAuthContext = func(dac store.DeviceAndAuthContext) {
  3652  		// the DeviceAndAuthContext assumes state is unlocked
  3653  		st.Unlock()
  3654  		defer st.Lock()
  3655  		c.Check(dac, NotNil)
  3656  		stoID, err := dac.StoreID("")
  3657  		c.Assert(err, IsNil)
  3658  		c.Check(stoID, Equals, "switched-store")
  3659  		newDAC = true
  3660  	}
  3661  
  3662  	// create/set custom model assertion
  3663  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  3664  
  3665  	model := s.brands.Model("my-brand", "my-model", modelDefaults)
  3666  
  3667  	// setup model assertion
  3668  	err := assertstate.Add(st, model)
  3669  	c.Assert(err, IsNil)
  3670  
  3671  	// have a serial as well
  3672  	kpMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir)
  3673  	c.Assert(err, IsNil)
  3674  	err = kpMgr.Put(deviceKey)
  3675  	c.Assert(err, IsNil)
  3676  
  3677  	encDevKey, err := asserts.EncodePublicKey(deviceKey.PublicKey())
  3678  	c.Assert(err, IsNil)
  3679  	serial, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, map[string]interface{}{
  3680  		"authority-id":        "my-brand",
  3681  		"brand-id":            "my-brand",
  3682  		"model":               "my-model",
  3683  		"serial":              "store-switch-serial",
  3684  		"device-key":          string(encDevKey),
  3685  		"device-key-sha3-384": deviceKey.PublicKey().ID(),
  3686  		"timestamp":           time.Now().Format(time.RFC3339),
  3687  	}, nil, "")
  3688  	c.Assert(err, IsNil)
  3689  	err = assertstate.Add(st, serial)
  3690  	c.Assert(err, IsNil)
  3691  
  3692  	devicestatetest.SetDevice(st, &auth.DeviceState{
  3693  		Brand:  "my-brand",
  3694  		Model:  "my-model",
  3695  		KeyID:  deviceKey.PublicKey().ID(),
  3696  		Serial: "store-switch-serial",
  3697  	})
  3698  
  3699  	// create a new model
  3700  	newModel := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{
  3701  		"store":          "switched-store",
  3702  		"required-snaps": []interface{}{"foo"},
  3703  		"revision":       "1",
  3704  	})
  3705  
  3706  	s.expectedSerial = "store-switch-serial"
  3707  	s.expectedStore = "switched-store"
  3708  	s.sessionMacaroon = "switched-store-session"
  3709  
  3710  	chg, err := devicestate.Remodel(st, newModel)
  3711  	c.Assert(err, IsNil)
  3712  
  3713  	st.Unlock()
  3714  	err = s.o.Settle(settleTimeout)
  3715  	st.Lock()
  3716  	c.Assert(err, IsNil)
  3717  
  3718  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  3719  
  3720  	// the new required-snap "foo" is installed
  3721  	var snapst snapstate.SnapState
  3722  	err = snapstate.Get(st, "foo", &snapst)
  3723  	c.Assert(err, IsNil)
  3724  
  3725  	// and marked required
  3726  	c.Check(snapst.Required, Equals, true)
  3727  
  3728  	// a new store was made
  3729  	c.Check(newDAC, Equals, true)
  3730  
  3731  	// we have a session with the new store
  3732  	device, err := devicestatetest.Device(st)
  3733  	c.Assert(err, IsNil)
  3734  	c.Check(device.Serial, Equals, "store-switch-serial")
  3735  	c.Check(device.SessionMacaroon, Equals, "switched-store-session")
  3736  }
  3737  
  3738  func (s *mgrsSuite) TestHappyDeviceRegistrationWithPrepareDeviceHook(c *C) {
  3739  	// just to 404 locally eager account-key requests
  3740  	mockStoreServer := s.mockStore(c)
  3741  	defer mockStoreServer.Close()
  3742  
  3743  	model := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{
  3744  		"gadget": "gadget",
  3745  	})
  3746  
  3747  	// reset as seeded but not registered
  3748  	// shortcut: have already device key
  3749  	kpMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir)
  3750  	c.Assert(err, IsNil)
  3751  	err = kpMgr.Put(deviceKey)
  3752  	c.Assert(err, IsNil)
  3753  
  3754  	st := s.o.State()
  3755  	st.Lock()
  3756  	defer st.Unlock()
  3757  
  3758  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  3759  	devicestatetest.SetDevice(st, &auth.DeviceState{
  3760  		Brand: "my-brand",
  3761  		Model: "my-model",
  3762  		KeyID: deviceKey.PublicKey().ID(),
  3763  	})
  3764  	err = assertstate.Add(st, model)
  3765  	c.Assert(err, IsNil)
  3766  
  3767  	signSerial := func(c *C, bhv *devicestatetest.DeviceServiceBehavior, headers map[string]interface{}, body []byte) (serial asserts.Assertion, ancillary []asserts.Assertion, err error) {
  3768  		brandID := headers["brand-id"].(string)
  3769  		model := headers["model"].(string)
  3770  		c.Check(brandID, Equals, "my-brand")
  3771  		c.Check(model, Equals, "my-model")
  3772  		headers["authority-id"] = brandID
  3773  		a, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, headers, body, "")
  3774  		return a, nil, err
  3775  	}
  3776  
  3777  	bhv := &devicestatetest.DeviceServiceBehavior{
  3778  		ReqID:            "REQID-1",
  3779  		RequestIDURLPath: "/svc/request-id",
  3780  		SerialURLPath:    "/svc/serial",
  3781  		SignSerial:       signSerial,
  3782  	}
  3783  
  3784  	mockServer := devicestatetest.MockDeviceService(c, bhv)
  3785  	defer mockServer.Close()
  3786  
  3787  	pDBhv := &devicestatetest.PrepareDeviceBehavior{
  3788  		DeviceSvcURL: mockServer.URL + "/svc/",
  3789  		Headers: map[string]string{
  3790  			"x-extra-header": "extra",
  3791  		},
  3792  		RegBody: map[string]string{
  3793  			"mac": "00:00:00:00:ff:00",
  3794  		},
  3795  		ProposedSerial: "12000",
  3796  	}
  3797  
  3798  	r := devicestatetest.MockGadget(c, st, "gadget", snap.R(2), pDBhv)
  3799  	defer r()
  3800  
  3801  	// run the whole device registration process
  3802  	st.Unlock()
  3803  	err = s.o.Settle(settleTimeout)
  3804  	st.Lock()
  3805  	c.Assert(err, IsNil)
  3806  
  3807  	var becomeOperational *state.Change
  3808  	for _, chg := range st.Changes() {
  3809  		if chg.Kind() == "become-operational" {
  3810  			becomeOperational = chg
  3811  			break
  3812  		}
  3813  	}
  3814  	c.Assert(becomeOperational, NotNil)
  3815  
  3816  	c.Check(becomeOperational.Status().Ready(), Equals, true)
  3817  	c.Check(becomeOperational.Err(), IsNil)
  3818  
  3819  	device, err := devicestatetest.Device(st)
  3820  	c.Assert(err, IsNil)
  3821  	c.Check(device.Brand, Equals, "my-brand")
  3822  	c.Check(device.Model, Equals, "my-model")
  3823  	c.Check(device.Serial, Equals, "12000")
  3824  
  3825  	a, err := assertstate.DB(st).Find(asserts.SerialType, map[string]string{
  3826  		"brand-id": "my-brand",
  3827  		"model":    "my-model",
  3828  		"serial":   "12000",
  3829  	})
  3830  	c.Assert(err, IsNil)
  3831  	serial := a.(*asserts.Serial)
  3832  
  3833  	var details map[string]interface{}
  3834  	err = yaml.Unmarshal(serial.Body(), &details)
  3835  	c.Assert(err, IsNil)
  3836  
  3837  	c.Check(details, DeepEquals, map[string]interface{}{
  3838  		"mac": "00:00:00:00:ff:00",
  3839  	})
  3840  
  3841  	c.Check(serial.DeviceKey().ID(), Equals, device.KeyID)
  3842  }
  3843  
  3844  func (s *mgrsSuite) TestRemodelReregistration(c *C) {
  3845  	s.prereqSnapAssertions(c, map[string]interface{}{
  3846  		"snap-name": "foo",
  3847  	})
  3848  	snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 1.0}", "foo"), "1")
  3849  	s.serveSnap(snapPath, "1")
  3850  
  3851  	// track the creation of new DeviceAndAutContext (for new Store)
  3852  	newDAC := false
  3853  
  3854  	mockServer := s.mockStore(c)
  3855  	defer mockServer.Close()
  3856  
  3857  	st := s.o.State()
  3858  	st.Lock()
  3859  	defer st.Unlock()
  3860  
  3861  	s.checkDeviceAndAuthContext = func(dac store.DeviceAndAuthContext) {
  3862  		// the DeviceAndAuthContext assumes state is unlocked
  3863  		st.Unlock()
  3864  		defer st.Lock()
  3865  		c.Check(dac, NotNil)
  3866  		stoID, err := dac.StoreID("")
  3867  		c.Assert(err, IsNil)
  3868  		c.Check(stoID, Equals, "my-brand-substore")
  3869  		newDAC = true
  3870  	}
  3871  
  3872  	model := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{
  3873  		"gadget": "gadget",
  3874  	})
  3875  
  3876  	// setup initial device identity
  3877  	kpMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir)
  3878  	c.Assert(err, IsNil)
  3879  	err = kpMgr.Put(deviceKey)
  3880  	c.Assert(err, IsNil)
  3881  
  3882  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  3883  	devicestatetest.SetDevice(st, &auth.DeviceState{
  3884  		Brand:  "my-brand",
  3885  		Model:  "my-model",
  3886  		KeyID:  deviceKey.PublicKey().ID(),
  3887  		Serial: "orig-serial",
  3888  	})
  3889  	err = assertstate.Add(st, model)
  3890  	c.Assert(err, IsNil)
  3891  
  3892  	encDevKey, err := asserts.EncodePublicKey(deviceKey.PublicKey())
  3893  	c.Assert(err, IsNil)
  3894  	serialHeaders := map[string]interface{}{
  3895  		"brand-id":            "my-brand",
  3896  		"model":               "my-model",
  3897  		"serial":              "orig-serial",
  3898  		"device-key":          string(encDevKey),
  3899  		"device-key-sha3-384": deviceKey.PublicKey().ID(),
  3900  		"timestamp":           time.Now().Format(time.RFC3339),
  3901  	}
  3902  	serialA, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, serialHeaders, nil, "")
  3903  	c.Assert(err, IsNil)
  3904  	serial := serialA.(*asserts.Serial)
  3905  	err = assertstate.Add(st, serial)
  3906  	c.Assert(err, IsNil)
  3907  
  3908  	signSerial := func(c *C, bhv *devicestatetest.DeviceServiceBehavior, headers map[string]interface{}, body []byte) (serial asserts.Assertion, ancillary []asserts.Assertion, err error) {
  3909  		brandID := headers["brand-id"].(string)
  3910  		model := headers["model"].(string)
  3911  		c.Check(brandID, Equals, "my-brand")
  3912  		c.Check(model, Equals, "other-model")
  3913  		headers["authority-id"] = brandID
  3914  		a, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, headers, body, "")
  3915  		return a, nil, err
  3916  	}
  3917  
  3918  	bhv := &devicestatetest.DeviceServiceBehavior{
  3919  		ReqID:            "REQID-1",
  3920  		RequestIDURLPath: "/svc/request-id",
  3921  		SerialURLPath:    "/svc/serial",
  3922  		SignSerial:       signSerial,
  3923  	}
  3924  
  3925  	mockDeviceService := devicestatetest.MockDeviceService(c, bhv)
  3926  	defer mockDeviceService.Close()
  3927  
  3928  	r := devicestatetest.MockGadget(c, st, "gadget", snap.R(2), nil)
  3929  	defer r()
  3930  
  3931  	// set registration config on gadget
  3932  	tr := config.NewTransaction(st)
  3933  	c.Assert(tr.Set("gadget", "device-service.url", mockDeviceService.URL+"/svc/"), IsNil)
  3934  	c.Assert(tr.Set("gadget", "registration.proposed-serial", "orig-serial"), IsNil)
  3935  	tr.Commit()
  3936  
  3937  	// run the remodel
  3938  	// create a new model
  3939  	newModel := s.brands.Model("my-brand", "other-model", modelDefaults, map[string]interface{}{
  3940  		"store":          "my-brand-substore",
  3941  		"gadget":         "gadget",
  3942  		"required-snaps": []interface{}{"foo"},
  3943  	})
  3944  
  3945  	s.expectedSerial = "orig-serial"
  3946  	s.expectedStore = "my-brand-substore"
  3947  	s.sessionMacaroon = "other-store-session"
  3948  
  3949  	chg, err := devicestate.Remodel(st, newModel)
  3950  	c.Assert(err, IsNil)
  3951  
  3952  	st.Unlock()
  3953  	err = s.o.Settle(settleTimeout)
  3954  	st.Lock()
  3955  	c.Assert(err, IsNil)
  3956  
  3957  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  3958  
  3959  	device, err := devicestatetest.Device(st)
  3960  	c.Assert(err, IsNil)
  3961  	c.Check(device.Brand, Equals, "my-brand")
  3962  	c.Check(device.Model, Equals, "other-model")
  3963  	c.Check(device.Serial, Equals, "orig-serial")
  3964  
  3965  	a, err := assertstate.DB(st).Find(asserts.SerialType, map[string]string{
  3966  		"brand-id": "my-brand",
  3967  		"model":    "other-model",
  3968  		"serial":   "orig-serial",
  3969  	})
  3970  	c.Assert(err, IsNil)
  3971  	serial = a.(*asserts.Serial)
  3972  
  3973  	c.Check(serial.Body(), HasLen, 0)
  3974  	c.Check(serial.DeviceKey().ID(), Equals, device.KeyID)
  3975  
  3976  	// the new required-snap "foo" is installed
  3977  	var snapst snapstate.SnapState
  3978  	err = snapstate.Get(st, "foo", &snapst)
  3979  	c.Assert(err, IsNil)
  3980  
  3981  	// and marked required
  3982  	c.Check(snapst.Required, Equals, true)
  3983  
  3984  	// a new store was made
  3985  	c.Check(newDAC, Equals, true)
  3986  
  3987  	// we have a session with the new store
  3988  	c.Check(device.SessionMacaroon, Equals, "other-store-session")
  3989  }
  3990  
  3991  func (s *mgrsSuite) TestCheckRefreshFailureWithConcurrentRemoveOfConnectedSnap(c *C) {
  3992  	hookMgr := s.o.HookManager()
  3993  	c.Assert(hookMgr, NotNil)
  3994  
  3995  	// force configure hook failure for some-snap.
  3996  	hookMgr.RegisterHijack("configure", "some-snap", func(ctx *hookstate.Context) error {
  3997  		return fmt.Errorf("failing configure hook")
  3998  	})
  3999  
  4000  	snapPath, _ := s.makeStoreTestSnap(c, someSnapYaml, "40")
  4001  	s.serveSnap(snapPath, "40")
  4002  	snapPath, _ = s.makeStoreTestSnap(c, otherSnapYaml, "50")
  4003  	s.serveSnap(snapPath, "50")
  4004  
  4005  	mockServer := s.mockStore(c)
  4006  	defer mockServer.Close()
  4007  
  4008  	st := s.o.State()
  4009  	st.Lock()
  4010  	defer st.Unlock()
  4011  
  4012  	st.Set("conns", map[string]interface{}{
  4013  		"other-snap:media-hub some-snap:media-hub": map[string]interface{}{"interface": "media-hub", "auto": false},
  4014  	})
  4015  
  4016  	si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)}
  4017  	snapInfo := snaptest.MockSnap(c, someSnapYaml, si)
  4018  
  4019  	oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)}
  4020  	otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi)
  4021  
  4022  	snapstate.Set(st, "some-snap", &snapstate.SnapState{
  4023  		Active:   true,
  4024  		Sequence: []*snap.SideInfo{si},
  4025  		Current:  snap.R(1),
  4026  		SnapType: "app",
  4027  	})
  4028  	snapstate.Set(st, "other-snap", &snapstate.SnapState{
  4029  		Active:   true,
  4030  		Sequence: []*snap.SideInfo{oi},
  4031  		Current:  snap.R(1),
  4032  		SnapType: "app",
  4033  	})
  4034  
  4035  	// add snaps to the repo and connect them
  4036  	repo := s.o.InterfaceManager().Repository()
  4037  	c.Assert(repo.AddSnap(snapInfo), IsNil)
  4038  	c.Assert(repo.AddSnap(otherInfo), IsNil)
  4039  	_, err := repo.Connect(&interfaces.ConnRef{
  4040  		PlugRef: interfaces.PlugRef{Snap: "other-snap", Name: "media-hub"},
  4041  		SlotRef: interfaces.SlotRef{Snap: "some-snap", Name: "media-hub"},
  4042  	}, nil, nil, nil, nil, nil)
  4043  	c.Assert(err, IsNil)
  4044  
  4045  	// refresh all
  4046  	c.Assert(assertstate.RefreshSnapDeclarations(st, 0), IsNil)
  4047  
  4048  	ts, err := snapstate.Update(st, "some-snap", nil, 0, snapstate.Flags{})
  4049  	c.Assert(err, IsNil)
  4050  	chg := st.NewChange("refresh", "...")
  4051  	chg.AddAll(ts)
  4052  
  4053  	// remove other-snap
  4054  	ts2, err := snapstate.Remove(st, "other-snap", snap.R(0), nil)
  4055  	c.Assert(err, IsNil)
  4056  	chg2 := st.NewChange("remove-snap", "...")
  4057  	chg2.AddAll(ts2)
  4058  
  4059  	st.Unlock()
  4060  	err = s.o.Settle(settleTimeout)
  4061  	st.Lock()
  4062  
  4063  	c.Check(err, IsNil)
  4064  
  4065  	// the refresh change has failed due to configure hook error
  4066  	c.Check(chg.Err(), ErrorMatches, `cannot perform the following tasks:\n.*failing configure hook.*`)
  4067  	c.Check(chg.Status(), Equals, state.ErrorStatus)
  4068  
  4069  	// download-snap is one of the first tasks in the refresh change, check that it was undone
  4070  	var downloadSnapStatus state.Status
  4071  	for _, t := range chg.Tasks() {
  4072  		if t.Kind() == "download-snap" {
  4073  			downloadSnapStatus = t.Status()
  4074  			break
  4075  		}
  4076  	}
  4077  	c.Check(downloadSnapStatus, Equals, state.UndoneStatus)
  4078  
  4079  	// the remove change succeeded
  4080  	c.Check(chg2.Err(), IsNil)
  4081  	c.Check(chg2.Status(), Equals, state.DoneStatus)
  4082  }