gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/overlord/managers_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2021 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  	"errors"
    29  	"fmt"
    30  	"io"
    31  	"io/ioutil"
    32  	"net/http"
    33  	"net/http/httptest"
    34  	"net/url"
    35  	"os"
    36  	"path/filepath"
    37  	"sort"
    38  	"strings"
    39  	"time"
    40  
    41  	. "gopkg.in/check.v1"
    42  	"gopkg.in/tomb.v2"
    43  	"gopkg.in/yaml.v2"
    44  
    45  	"github.com/snapcore/snapd/asserts"
    46  	"github.com/snapcore/snapd/asserts/assertstest"
    47  	"github.com/snapcore/snapd/asserts/sysdb"
    48  	"github.com/snapcore/snapd/boot"
    49  	"github.com/snapcore/snapd/boot/boottest"
    50  	"github.com/snapcore/snapd/bootloader"
    51  	"github.com/snapcore/snapd/bootloader/bootloadertest"
    52  	"github.com/snapcore/snapd/bootloader/grubenv"
    53  	"github.com/snapcore/snapd/client"
    54  	"github.com/snapcore/snapd/dirs"
    55  	"github.com/snapcore/snapd/gadget"
    56  	"github.com/snapcore/snapd/gadget/quantity"
    57  	"github.com/snapcore/snapd/interfaces"
    58  	"github.com/snapcore/snapd/logger"
    59  	"github.com/snapcore/snapd/osutil"
    60  	"github.com/snapcore/snapd/overlord"
    61  	"github.com/snapcore/snapd/overlord/assertstate"
    62  	"github.com/snapcore/snapd/overlord/assertstate/assertstatetest"
    63  	"github.com/snapcore/snapd/overlord/auth"
    64  	"github.com/snapcore/snapd/overlord/configstate/config"
    65  	"github.com/snapcore/snapd/overlord/devicestate"
    66  	"github.com/snapcore/snapd/overlord/devicestate/devicestatetest"
    67  	"github.com/snapcore/snapd/overlord/hookstate"
    68  	"github.com/snapcore/snapd/overlord/hookstate/ctlcmd"
    69  	"github.com/snapcore/snapd/overlord/ifacestate"
    70  	"github.com/snapcore/snapd/overlord/servicestate"
    71  	"github.com/snapcore/snapd/overlord/servicestate/servicestatetest"
    72  	"github.com/snapcore/snapd/overlord/snapshotstate"
    73  	"github.com/snapcore/snapd/overlord/snapstate"
    74  	"github.com/snapcore/snapd/overlord/state"
    75  	"github.com/snapcore/snapd/release"
    76  	"github.com/snapcore/snapd/secboot"
    77  	"github.com/snapcore/snapd/seed"
    78  	"github.com/snapcore/snapd/seed/seedtest"
    79  	"github.com/snapcore/snapd/seed/seedwriter"
    80  	"github.com/snapcore/snapd/snap"
    81  	"github.com/snapcore/snapd/snap/naming"
    82  	"github.com/snapcore/snapd/snap/snapfile"
    83  	"github.com/snapcore/snapd/snap/snaptest"
    84  	"github.com/snapcore/snapd/store"
    85  	"github.com/snapcore/snapd/systemd"
    86  	"github.com/snapcore/snapd/systemd/systemdtest"
    87  	"github.com/snapcore/snapd/testutil"
    88  	"github.com/snapcore/snapd/wrappers"
    89  )
    90  
    91  var (
    92  	settleTimeout           = testutil.HostScaledTimeout(45 * time.Second)
    93  	aggressiveSettleTimeout = testutil.HostScaledTimeout(50 * time.Millisecond)
    94  	connectRetryTimeout     = testutil.HostScaledTimeout(70 * time.Millisecond)
    95  )
    96  
    97  type automaticSnapshotCall struct {
    98  	InstanceName string
    99  	SnapConfig   map[string]interface{}
   100  	Usernames    []string
   101  }
   102  
   103  type baseMgrsSuite struct {
   104  	testutil.BaseTest
   105  
   106  	tempdir string
   107  
   108  	storeSigning *assertstest.StoreStack
   109  	brands       *assertstest.SigningAccounts
   110  
   111  	devAcct *asserts.Account
   112  
   113  	serveIDtoName map[string]string
   114  	serveSnapPath map[string]string
   115  	serveRevision map[string]string
   116  	serveOldPaths map[string][]string
   117  	serveOldRevs  map[string][]string
   118  
   119  	hijackServeSnap func(http.ResponseWriter)
   120  
   121  	checkDeviceAndAuthContext func(store.DeviceAndAuthContext)
   122  	expectedSerial            string
   123  	expectedStore             string
   124  	sessionMacaroon           string
   125  
   126  	o *overlord.Overlord
   127  
   128  	failNextDownload string
   129  
   130  	automaticSnapshots []automaticSnapshotCall
   131  
   132  	logbuf *bytes.Buffer
   133  }
   134  
   135  var (
   136  	_ = Suite(&mgrsSuite{})
   137  	_ = Suite(&storeCtxSetupSuite{})
   138  )
   139  
   140  var (
   141  	brandPrivKey, _ = assertstest.GenerateKey(752)
   142  
   143  	develPrivKey, _ = assertstest.GenerateKey(752)
   144  
   145  	deviceKey, _ = assertstest.GenerateKey(752)
   146  )
   147  
   148  func verifyLastTasksetIsRerefresh(c *C, tts []*state.TaskSet) {
   149  	ts := tts[len(tts)-1]
   150  	c.Assert(ts.Tasks(), HasLen, 1)
   151  	c.Check(ts.Tasks()[0].Kind(), Equals, "check-rerefresh")
   152  }
   153  
   154  func (s *baseMgrsSuite) SetUpTest(c *C) {
   155  	s.BaseTest.SetUpTest(c)
   156  
   157  	s.tempdir = c.MkDir()
   158  	dirs.SetRootDir(s.tempdir)
   159  	s.AddCleanup(func() { dirs.SetRootDir("") })
   160  
   161  	// needed for system key generation
   162  	s.AddCleanup(osutil.MockMountInfo(""))
   163  
   164  	err := os.MkdirAll(filepath.Dir(dirs.SnapStateFile), 0755)
   165  	c.Assert(err, IsNil)
   166  
   167  	// needed by hooks
   168  	s.AddCleanup(testutil.MockCommand(c, "snap", "").Restore)
   169  
   170  	restoreCheckFreeSpace := snapstate.MockOsutilCheckFreeSpace(func(string, uint64) error { return nil })
   171  	s.AddCleanup(restoreCheckFreeSpace)
   172  
   173  	oldSetupInstallHook := snapstate.SetupInstallHook
   174  	oldSetupRemoveHook := snapstate.SetupRemoveHook
   175  	snapstate.SetupRemoveHook = hookstate.SetupRemoveHook
   176  	snapstate.SetupInstallHook = hookstate.SetupInstallHook
   177  	s.AddCleanup(func() {
   178  		snapstate.SetupRemoveHook = oldSetupRemoveHook
   179  		snapstate.SetupInstallHook = oldSetupInstallHook
   180  	})
   181  
   182  	s.automaticSnapshots = nil
   183  	r := snapshotstate.MockBackendSave(func(_ context.Context, id uint64, si *snap.Info, cfg map[string]interface{}, usernames []string) (*client.Snapshot, error) {
   184  		s.automaticSnapshots = append(s.automaticSnapshots, automaticSnapshotCall{InstanceName: si.InstanceName(), SnapConfig: cfg, Usernames: usernames})
   185  		return nil, nil
   186  	})
   187  	s.AddCleanup(r)
   188  
   189  	s.AddCleanup(ifacestate.MockConnectRetryTimeout(connectRetryTimeout))
   190  
   191  	os.Setenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS", "1")
   192  	s.AddCleanup(func() { os.Unsetenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS") })
   193  
   194  	// create a fake systemd environment
   195  	os.MkdirAll(filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants"), 0755)
   196  
   197  	r = systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
   198  		if out := systemdtest.HandleMockAllUnitsActiveOutput(cmd, nil); out != nil {
   199  			return out, nil
   200  		}
   201  		return []byte("ActiveState=inactive\n"), nil
   202  	})
   203  	s.AddCleanup(r)
   204  
   205  	s.storeSigning = assertstest.NewStoreStack("can0nical", nil)
   206  	s.brands = assertstest.NewSigningAccounts(s.storeSigning)
   207  	s.brands.Register("my-brand", brandPrivKey, map[string]interface{}{
   208  		"validation": "verified",
   209  	})
   210  	s.AddCleanup(sysdb.InjectTrusted(s.storeSigning.Trusted))
   211  
   212  	s.devAcct = assertstest.NewAccount(s.storeSigning, "devdevdev", map[string]interface{}{
   213  		"account-id": "devdevdev",
   214  	}, "")
   215  	err = s.storeSigning.Add(s.devAcct)
   216  	c.Assert(err, IsNil)
   217  
   218  	s.serveIDtoName = make(map[string]string)
   219  	s.serveSnapPath = make(map[string]string)
   220  	s.serveRevision = make(map[string]string)
   221  	s.serveOldPaths = make(map[string][]string)
   222  	s.serveOldRevs = make(map[string][]string)
   223  	s.hijackServeSnap = nil
   224  
   225  	s.checkDeviceAndAuthContext = nil
   226  	s.expectedSerial = ""
   227  	s.expectedStore = ""
   228  	s.sessionMacaroon = ""
   229  
   230  	s.AddCleanup(ifacestate.MockSecurityBackends(nil))
   231  
   232  	o, err := overlord.New(nil)
   233  	c.Assert(err, IsNil)
   234  	st := o.State()
   235  	st.Lock()
   236  	st.Set("seeded", true)
   237  	st.Unlock()
   238  	err = o.StartUp()
   239  	c.Assert(err, IsNil)
   240  	o.InterfaceManager().DisableUDevMonitor()
   241  	s.o = o
   242  
   243  	st.Lock()
   244  	defer st.Unlock()
   245  	// registered
   246  	err = assertstate.Add(st, sysdb.GenericClassicModel())
   247  	c.Assert(err, IsNil)
   248  	devicestatetest.SetDevice(st, &auth.DeviceState{
   249  		Brand:  "generic",
   250  		Model:  "generic-classic",
   251  		Serial: "serialserial",
   252  	})
   253  
   254  	// add "core" snap declaration
   255  	headers := map[string]interface{}{
   256  		"series":       "16",
   257  		"snap-name":    "core",
   258  		"publisher-id": "can0nical",
   259  		"timestamp":    time.Now().Format(time.RFC3339),
   260  	}
   261  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   262  	err = assertstate.Add(st, s.storeSigning.StoreAccountKey(""))
   263  	c.Assert(err, IsNil)
   264  	a, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   265  	c.Assert(err, IsNil)
   266  	err = assertstate.Add(st, a)
   267  	c.Assert(err, IsNil)
   268  	s.serveRevision["core"] = "1"
   269  	s.serveIDtoName[fakeSnapID("core")] = "core"
   270  	err = s.storeSigning.Add(a)
   271  	c.Assert(err, IsNil)
   272  
   273  	// add "snap1" snap declaration
   274  	headers = map[string]interface{}{
   275  		"series":       "16",
   276  		"snap-name":    "snap1",
   277  		"publisher-id": "can0nical",
   278  		"timestamp":    time.Now().Format(time.RFC3339),
   279  	}
   280  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   281  	a2, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   282  	c.Assert(err, IsNil)
   283  	c.Assert(assertstate.Add(st, a2), IsNil)
   284  	c.Assert(s.storeSigning.Add(a2), IsNil)
   285  
   286  	// add "snap2" snap declaration
   287  	headers = map[string]interface{}{
   288  		"series":       "16",
   289  		"snap-name":    "snap2",
   290  		"publisher-id": "can0nical",
   291  		"timestamp":    time.Now().Format(time.RFC3339),
   292  	}
   293  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   294  	a3, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   295  	c.Assert(err, IsNil)
   296  	c.Assert(assertstate.Add(st, a3), IsNil)
   297  	c.Assert(s.storeSigning.Add(a3), IsNil)
   298  
   299  	// add "some-snap" snap declaration
   300  	headers = map[string]interface{}{
   301  		"series":       "16",
   302  		"snap-name":    "some-snap",
   303  		"publisher-id": "can0nical",
   304  		"timestamp":    time.Now().Format(time.RFC3339),
   305  	}
   306  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   307  	a4, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   308  	c.Assert(err, IsNil)
   309  	c.Assert(assertstate.Add(st, a4), IsNil)
   310  	c.Assert(s.storeSigning.Add(a4), IsNil)
   311  
   312  	// add "other-snap" snap declaration
   313  	headers = map[string]interface{}{
   314  		"series":       "16",
   315  		"snap-name":    "other-snap",
   316  		"publisher-id": "can0nical",
   317  		"timestamp":    time.Now().Format(time.RFC3339),
   318  	}
   319  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   320  	a5, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   321  	c.Assert(err, IsNil)
   322  	c.Assert(assertstate.Add(st, a5), IsNil)
   323  	c.Assert(s.storeSigning.Add(a5), IsNil)
   324  
   325  	// add pc-kernel snap declaration
   326  	headers = map[string]interface{}{
   327  		"series":       "16",
   328  		"snap-name":    "pc-kernel",
   329  		"publisher-id": "can0nical",
   330  		"timestamp":    time.Now().Format(time.RFC3339),
   331  	}
   332  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   333  	a6, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   334  	c.Assert(err, IsNil)
   335  	c.Assert(assertstate.Add(st, a6), IsNil)
   336  	c.Assert(s.storeSigning.Add(a6), IsNil)
   337  
   338  	// add pc snap declaration
   339  	headers = map[string]interface{}{
   340  		"series":       "16",
   341  		"snap-name":    "pc",
   342  		"publisher-id": "can0nical",
   343  		"timestamp":    time.Now().Format(time.RFC3339),
   344  	}
   345  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   346  	a7, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   347  	c.Assert(err, IsNil)
   348  	c.Assert(assertstate.Add(st, a7), IsNil)
   349  	c.Assert(s.storeSigning.Add(a7), IsNil)
   350  
   351  	// add pi snap declaration
   352  	headers = map[string]interface{}{
   353  		"series":       "16",
   354  		"snap-name":    "pi",
   355  		"publisher-id": "can0nical",
   356  		"timestamp":    time.Now().Format(time.RFC3339),
   357  	}
   358  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   359  	a8, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   360  	c.Assert(err, IsNil)
   361  	c.Assert(assertstate.Add(st, a8), IsNil)
   362  	c.Assert(s.storeSigning.Add(a8), IsNil)
   363  
   364  	// add pi-kernel snap declaration
   365  	headers = map[string]interface{}{
   366  		"series":       "16",
   367  		"snap-name":    "pi-kernel",
   368  		"publisher-id": "can0nical",
   369  		"timestamp":    time.Now().Format(time.RFC3339),
   370  	}
   371  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   372  	a9, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   373  	c.Assert(err, IsNil)
   374  	c.Assert(assertstate.Add(st, a9), IsNil)
   375  	c.Assert(s.storeSigning.Add(a9), IsNil)
   376  
   377  	// add core18 snap declaration
   378  	headers = map[string]interface{}{
   379  		"series":       "16",
   380  		"snap-name":    "core18",
   381  		"publisher-id": "can0nical",
   382  		"timestamp":    time.Now().Format(time.RFC3339),
   383  	}
   384  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   385  	a10, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   386  	c.Assert(err, IsNil)
   387  	c.Assert(assertstate.Add(st, a10), IsNil)
   388  	c.Assert(s.storeSigning.Add(a10), IsNil)
   389  
   390  	// add core20 snap declaration
   391  	headers = map[string]interface{}{
   392  		"series":       "16",
   393  		"snap-name":    "core20",
   394  		"publisher-id": "can0nical",
   395  		"timestamp":    time.Now().Format(time.RFC3339),
   396  	}
   397  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   398  	a11, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   399  	c.Assert(err, IsNil)
   400  	c.Assert(assertstate.Add(st, a11), IsNil)
   401  	c.Assert(s.storeSigning.Add(a11), IsNil)
   402  
   403  	// add snapd snap declaration
   404  	headers = map[string]interface{}{
   405  		"series":       "16",
   406  		"snap-name":    "snapd",
   407  		"publisher-id": "can0nical",
   408  		"timestamp":    time.Now().Format(time.RFC3339),
   409  	}
   410  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   411  	a12, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   412  	c.Assert(err, IsNil)
   413  	c.Assert(assertstate.Add(st, a12), IsNil)
   414  	c.Assert(s.storeSigning.Add(a12), IsNil)
   415  
   416  	// add core itself
   417  	snapstate.Set(st, "core", &snapstate.SnapState{
   418  		Active: true,
   419  		Sequence: []*snap.SideInfo{
   420  			{RealName: "core", SnapID: fakeSnapID("core"), Revision: snap.R(1)},
   421  		},
   422  		Current:  snap.R(1),
   423  		SnapType: "os",
   424  		Flags: snapstate.Flags{
   425  			Required: true,
   426  		},
   427  	})
   428  
   429  	// don't actually try to talk to the store on snapstate.Ensure
   430  	// needs doing after the call to devicestate.Manager (which happens in overlord.New)
   431  	snapstate.CanAutoRefresh = nil
   432  
   433  	st.Set("refresh-privacy-key", "privacy-key")
   434  
   435  	// For triggering errors
   436  	erroringHandler := func(task *state.Task, _ *tomb.Tomb) error {
   437  		return errors.New("error out")
   438  	}
   439  	s.o.TaskRunner().AddHandler("error-trigger", erroringHandler, nil)
   440  
   441  	// setup cloud-init as restricted so that tests by default don't run the
   442  	// full EnsureCloudInitRestricted logic in the devicestate mgr
   443  	snapdCloudInitRestrictedFile := filepath.Join(dirs.GlobalRootDir, "etc/cloud/cloud.cfg.d/zzzz_snapd.cfg")
   444  	err = os.MkdirAll(filepath.Dir(snapdCloudInitRestrictedFile), 0755)
   445  	c.Assert(err, IsNil)
   446  	err = ioutil.WriteFile(snapdCloudInitRestrictedFile, nil, 0644)
   447  	c.Assert(err, IsNil)
   448  
   449  	logbuf, restore := logger.MockLogger()
   450  	s.AddCleanup(restore)
   451  	s.logbuf = logbuf
   452  }
   453  
   454  func (s *baseMgrsSuite) makeSerialAssertionInState(c *C, st *state.State, brandID, model, serialN string) *asserts.Serial {
   455  	encDevKey, err := asserts.EncodePublicKey(deviceKey.PublicKey())
   456  	c.Assert(err, IsNil)
   457  	serial, err := s.brands.Signing(brandID).Sign(asserts.SerialType, map[string]interface{}{
   458  		"brand-id":            brandID,
   459  		"model":               model,
   460  		"serial":              serialN,
   461  		"device-key":          string(encDevKey),
   462  		"device-key-sha3-384": deviceKey.PublicKey().ID(),
   463  		"timestamp":           time.Now().Format(time.RFC3339),
   464  	}, nil, "")
   465  	c.Assert(err, IsNil)
   466  	err = assertstate.Add(st, serial)
   467  	c.Assert(err, IsNil)
   468  	return serial.(*asserts.Serial)
   469  }
   470  
   471  // XXX: We have some very similar code in hookstate/ctlcmd/is_connected_test.go
   472  //      should this be moved to overlord/snapstate/snapstatetest as a common
   473  //      helper
   474  func (ms *baseMgrsSuite) mockInstalledSnapWithFiles(c *C, snapYaml string, files [][]string) *snap.Info {
   475  	return ms.mockInstalledSnapWithRevAndFiles(c, snapYaml, snap.R(1), files)
   476  }
   477  
   478  func (ms *baseMgrsSuite) mockInstalledSnapWithRevAndFiles(c *C, snapYaml string, rev snap.Revision, files [][]string) *snap.Info {
   479  	st := ms.o.State()
   480  
   481  	info := snaptest.MockSnapWithFiles(c, snapYaml, &snap.SideInfo{Revision: snap.R(1)}, files)
   482  	si := &snap.SideInfo{
   483  		RealName: info.SnapName(),
   484  		SnapID:   fakeSnapID(info.SnapName()),
   485  		Revision: info.Revision,
   486  	}
   487  	snapstate.Set(st, info.InstanceName(), &snapstate.SnapState{
   488  		Active:   true,
   489  		Sequence: []*snap.SideInfo{si},
   490  		Current:  info.Revision,
   491  		SnapType: string(info.Type()),
   492  	})
   493  	return info
   494  }
   495  
   496  type mgrsSuite struct {
   497  	baseMgrsSuite
   498  }
   499  
   500  func makeTestSnapWithFiles(c *C, snapYamlContent string, files [][]string) string {
   501  	info, err := snap.InfoFromSnapYaml([]byte(snapYamlContent))
   502  	c.Assert(err, IsNil)
   503  
   504  	for _, app := range info.Apps {
   505  		// files is a list of (filename, content)
   506  		files = append(files, []string{app.Command, ""})
   507  	}
   508  
   509  	return snaptest.MakeTestSnapWithFiles(c, snapYamlContent, files)
   510  }
   511  
   512  func makeTestSnap(c *C, snapYamlContent string) string {
   513  	return makeTestSnapWithFiles(c, snapYamlContent, nil)
   514  }
   515  
   516  func (s *mgrsSuite) TestHappyLocalInstall(c *C) {
   517  	snapYamlContent := `name: foo
   518  apps:
   519   bar:
   520    command: bin/bar
   521  `
   522  	snapPath := makeTestSnap(c, snapYamlContent+"version: 1.0")
   523  
   524  	st := s.o.State()
   525  	st.Lock()
   526  	defer st.Unlock()
   527  
   528  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "foo"}, snapPath, "", "", snapstate.Flags{DevMode: true})
   529  	c.Assert(err, IsNil)
   530  	chg := st.NewChange("install-snap", "...")
   531  	chg.AddAll(ts)
   532  
   533  	st.Unlock()
   534  	err = s.o.Settle(settleTimeout)
   535  	st.Lock()
   536  	c.Assert(err, IsNil)
   537  
   538  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
   539  
   540  	snap, err := snapstate.CurrentInfo(st, "foo")
   541  	c.Assert(err, IsNil)
   542  
   543  	// ensure that the binary wrapper file got generated with the right
   544  	// name
   545  	binaryWrapper := filepath.Join(dirs.SnapBinariesDir, "foo.bar")
   546  	c.Assert(osutil.IsSymlink(binaryWrapper), Equals, true)
   547  
   548  	// data dirs
   549  	c.Assert(osutil.IsDirectory(snap.DataDir()), Equals, true)
   550  	c.Assert(osutil.IsDirectory(snap.CommonDataDir()), Equals, true)
   551  
   552  	// snap file and its mounting
   553  
   554  	// after install the snap file is in the right dir
   555  	c.Assert(osutil.FileExists(filepath.Join(dirs.SnapBlobDir, "foo_x1.snap")), Equals, true)
   556  
   557  	// ensure the right unit is created
   558  	mup := systemd.MountUnitPath(filepath.Join(dirs.StripRootDir(dirs.SnapMountDir), "foo/x1"))
   559  	c.Assert(mup, testutil.FileMatches, fmt.Sprintf("(?ms).*^Where=%s/foo/x1", dirs.StripRootDir(dirs.SnapMountDir)))
   560  	c.Assert(mup, testutil.FileMatches, "(?ms).*^What=/var/lib/snapd/snaps/foo_x1.snap")
   561  }
   562  
   563  func (s *mgrsSuite) TestLocalInstallUndo(c *C) {
   564  	snapYamlContent := `name: foo
   565  apps:
   566   bar:
   567    command: bin/bar
   568  hooks:
   569    install:
   570    configure:
   571  `
   572  	snapPath := makeTestSnap(c, snapYamlContent+"version: 1.0")
   573  
   574  	installHook := false
   575  	defer hookstate.MockRunHook(func(ctx *hookstate.Context, _ *tomb.Tomb) ([]byte, error) {
   576  		switch ctx.HookName() {
   577  		case "install":
   578  			installHook = true
   579  			_, _, err := ctlcmd.Run(ctx, []string{"set", "installed=true"}, 0)
   580  			c.Assert(err, IsNil)
   581  			return nil, nil
   582  		case "configure":
   583  			return nil, errors.New("configure failed")
   584  		}
   585  		return nil, nil
   586  	})()
   587  
   588  	st := s.o.State()
   589  	st.Lock()
   590  	defer st.Unlock()
   591  
   592  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "foo"}, snapPath, "", "", snapstate.Flags{DevMode: true})
   593  	c.Assert(err, IsNil)
   594  	chg := st.NewChange("install-snap", "...")
   595  	chg.AddAll(ts)
   596  
   597  	st.Unlock()
   598  	err = s.o.Settle(settleTimeout)
   599  	st.Lock()
   600  	c.Assert(err, IsNil)
   601  
   602  	c.Assert(chg.Status(), Equals, state.ErrorStatus, Commentf("install-snap unexpectedly succeeded"))
   603  
   604  	// check undo statutes
   605  	for _, t := range chg.Tasks() {
   606  		which := t.Kind()
   607  		expectedStatus := state.UndoneStatus
   608  		switch t.Kind() {
   609  		case "prerequisites":
   610  			expectedStatus = state.DoneStatus
   611  		case "run-hook":
   612  			var hs hookstate.HookSetup
   613  			err := t.Get("hook-setup", &hs)
   614  			c.Assert(err, IsNil)
   615  			switch hs.Hook {
   616  			case "install":
   617  				expectedStatus = state.UndoneStatus
   618  			case "configure":
   619  				expectedStatus = state.ErrorStatus
   620  			case "check-health":
   621  				expectedStatus = state.HoldStatus
   622  			}
   623  			which += fmt.Sprintf("[%s]", hs.Hook)
   624  		}
   625  		c.Assert(t.Status(), Equals, expectedStatus, Commentf("%s", which))
   626  	}
   627  
   628  	// install hooks was called
   629  	c.Check(installHook, Equals, true)
   630  
   631  	// nothing in snaps
   632  	all, err := snapstate.All(st)
   633  	c.Assert(err, IsNil)
   634  	c.Check(all, HasLen, 1)
   635  	_, ok := all["core"]
   636  	c.Check(ok, Equals, true)
   637  
   638  	// nothing in config
   639  	var config map[string]*json.RawMessage
   640  	err = st.Get("config", &config)
   641  	c.Assert(err, IsNil)
   642  	c.Check(config, HasLen, 1)
   643  	_, ok = config["core"]
   644  	c.Check(ok, Equals, true)
   645  
   646  	snapdirs, err := filepath.Glob(filepath.Join(dirs.SnapMountDir, "*"))
   647  	c.Assert(err, IsNil)
   648  	// just README and bin
   649  	c.Check(snapdirs, HasLen, 2)
   650  	for _, d := range snapdirs {
   651  		c.Check(filepath.Base(d), Not(Equals), "foo")
   652  	}
   653  }
   654  
   655  func (s *mgrsSuite) TestHappyRemove(c *C) {
   656  	oldEstimateSnapshotSize := snapstate.EstimateSnapshotSize
   657  	snapstate.EstimateSnapshotSize = func(st *state.State, instanceName string, users []string) (uint64, error) {
   658  		return 0, nil
   659  	}
   660  	defer func() {
   661  		snapstate.EstimateSnapshotSize = oldEstimateSnapshotSize
   662  	}()
   663  
   664  	st := s.o.State()
   665  	st.Lock()
   666  	defer st.Unlock()
   667  
   668  	snapYamlContent := `name: foo
   669  apps:
   670   bar:
   671    command: bin/bar
   672  `
   673  	snapInfo := s.installLocalTestSnap(c, snapYamlContent+"version: 1.0")
   674  
   675  	// set config
   676  	tr := config.NewTransaction(st)
   677  	c.Assert(tr.Set("foo", "key", "value"), IsNil)
   678  	tr.Commit()
   679  
   680  	ts, err := snapstate.Remove(st, "foo", snap.R(0), nil)
   681  	c.Assert(err, IsNil)
   682  	chg := st.NewChange("remove-snap", "...")
   683  	chg.AddAll(ts)
   684  
   685  	st.Unlock()
   686  	err = s.o.Settle(settleTimeout)
   687  	st.Lock()
   688  	c.Assert(err, IsNil)
   689  
   690  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("remove-snap change failed with: %v", chg.Err()))
   691  
   692  	// ensure that the binary wrapper file got removed
   693  	binaryWrapper := filepath.Join(dirs.SnapBinariesDir, "foo.bar")
   694  	c.Assert(osutil.FileExists(binaryWrapper), Equals, false)
   695  
   696  	// data dirs
   697  	c.Assert(osutil.FileExists(snapInfo.DataDir()), Equals, false)
   698  	c.Assert(osutil.FileExists(snapInfo.CommonDataDir()), Equals, false)
   699  
   700  	// snap file and its mount
   701  	c.Assert(osutil.FileExists(filepath.Join(dirs.SnapBlobDir, "foo_x1.snap")), Equals, false)
   702  	mup := systemd.MountUnitPath(filepath.Join(dirs.StripRootDir(dirs.SnapMountDir), "foo/x1"))
   703  	c.Assert(osutil.FileExists(mup), Equals, false)
   704  
   705  	// automatic snapshot was created
   706  	c.Assert(s.automaticSnapshots, DeepEquals, []automaticSnapshotCall{{"foo", map[string]interface{}{"key": "value"}, nil}})
   707  }
   708  
   709  func (s *mgrsSuite) TestHappyRemoveWithQuotas(c *C) {
   710  	r := servicestate.MockSystemdVersion(248)
   711  	defer r()
   712  
   713  	st := s.o.State()
   714  	st.Lock()
   715  	defer st.Unlock()
   716  
   717  	snapYamlContent := `name: foo
   718  apps:
   719   bar:
   720    command: bin/bar
   721    daemon: simple
   722  `
   723  	s.installLocalTestSnap(c, snapYamlContent+"version: 1.0")
   724  
   725  	tr := config.NewTransaction(st)
   726  	c.Assert(tr.Set("core", "experimental.quota-groups", "true"), IsNil)
   727  	tr.Commit()
   728  
   729  	// put the snap in a quota group
   730  	err := servicestatetest.MockQuotaInState(st, "quota-grp", "", []string{"foo"}, quantity.SizeMiB)
   731  	c.Assert(err, IsNil)
   732  
   733  	ts, err := snapstate.Remove(st, "foo", snap.R(0), &snapstate.RemoveFlags{Purge: true})
   734  	c.Assert(err, IsNil)
   735  	chg := st.NewChange("remove-snap", "...")
   736  	chg.AddAll(ts)
   737  
   738  	st.Unlock()
   739  	err = s.o.Settle(settleTimeout)
   740  	st.Lock()
   741  	c.Assert(err, IsNil)
   742  
   743  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("remove-snap change failed with: %v", chg.Err()))
   744  
   745  	// ensure that the quota group no longer contains the snap we removed
   746  	grp, err := servicestate.AllQuotas(st)
   747  	c.Assert(grp, HasLen, 1)
   748  	c.Assert(grp["quota-grp"].Snaps, HasLen, 0)
   749  	c.Assert(err, IsNil)
   750  }
   751  
   752  func fakeSnapID(name string) string {
   753  	if id := naming.WellKnownSnapID(name); id != "" {
   754  		return id
   755  	}
   756  	return snaptest.AssertedSnapID(name)
   757  }
   758  
   759  const (
   760  	snapV2 = `{
   761  	"architectures": [
   762  	    "all"
   763  	],
   764          "download": {
   765  			"url": "@URL@",
   766  			"size": 123
   767          },
   768          "epoch": @EPOCH@,
   769          "type": "@TYPE@",
   770  	"name": "@NAME@",
   771  	"revision": @REVISION@,
   772  	"snap-id": "@SNAPID@",
   773  	"snap-yaml": @SNAP_YAML@,
   774  	"summary": "Foo",
   775  	"description": "this is a description",
   776  	"version": "@VERSION@",
   777          "publisher": {
   778             "id": "devdevdev",
   779             "name": "bar"
   780           },
   781           "media": [
   782              {"type": "icon", "url": "@ICON@"}
   783           ]
   784  }`
   785  )
   786  
   787  var fooSnapID = fakeSnapID("foo")
   788  
   789  func (s *baseMgrsSuite) prereqSnapAssertions(c *C, extraHeaders ...map[string]interface{}) *asserts.SnapDeclaration {
   790  	if len(extraHeaders) == 0 {
   791  		extraHeaders = []map[string]interface{}{{}}
   792  	}
   793  	var snapDecl *asserts.SnapDeclaration
   794  	for _, extraHeaders := range extraHeaders {
   795  		headers := map[string]interface{}{
   796  			"series":       "16",
   797  			"snap-name":    "foo",
   798  			"publisher-id": "devdevdev",
   799  			"timestamp":    time.Now().Format(time.RFC3339),
   800  		}
   801  		for h, v := range extraHeaders {
   802  			headers[h] = v
   803  		}
   804  		headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   805  		a, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   806  		c.Assert(err, IsNil)
   807  		err = s.storeSigning.Add(a)
   808  		c.Assert(err, IsNil)
   809  		snapDecl = a.(*asserts.SnapDeclaration)
   810  	}
   811  	return snapDecl
   812  }
   813  
   814  func (s *baseMgrsSuite) makeStoreTestSnapWithFiles(c *C, snapYaml string, revno string, files [][]string) (path, digest string) {
   815  	info, err := snap.InfoFromSnapYaml([]byte(snapYaml))
   816  	c.Assert(err, IsNil)
   817  
   818  	snapPath := makeTestSnapWithFiles(c, snapYaml, files)
   819  
   820  	snapDigest, size, err := asserts.SnapFileSHA3_384(snapPath)
   821  	c.Assert(err, IsNil)
   822  
   823  	s.makeStoreSnapRevision(c, info.SnapName(), revno, snapDigest, size)
   824  
   825  	return snapPath, snapDigest
   826  }
   827  
   828  func (s *baseMgrsSuite) makeStoreTestSnap(c *C, snapYaml string, revno string) (path, digest string) {
   829  	return s.makeStoreTestSnapWithFiles(c, snapYaml, revno, nil)
   830  }
   831  
   832  func (s *baseMgrsSuite) makeStoreSnapRevision(c *C, name, revno, digest string, size uint64) asserts.Assertion {
   833  	headers := map[string]interface{}{
   834  		"snap-id":       fakeSnapID(name),
   835  		"snap-sha3-384": digest,
   836  		"snap-size":     fmt.Sprintf("%d", size),
   837  		"snap-revision": revno,
   838  		"developer-id":  "devdevdev",
   839  		"timestamp":     time.Now().Format(time.RFC3339),
   840  	}
   841  	snapRev, err := s.storeSigning.Sign(asserts.SnapRevisionType, headers, nil, "")
   842  	c.Assert(err, IsNil)
   843  	err = s.storeSigning.Add(snapRev)
   844  	c.Assert(err, IsNil)
   845  	return snapRev
   846  }
   847  
   848  func (s *baseMgrsSuite) pathFor(name, revno string) string {
   849  	if revno == s.serveRevision[name] {
   850  		return s.serveSnapPath[name]
   851  	}
   852  	for i, r := range s.serveOldRevs[name] {
   853  		if r == revno {
   854  			return s.serveOldPaths[name][i]
   855  		}
   856  	}
   857  	return "/not/found"
   858  }
   859  
   860  func (s *baseMgrsSuite) newestThatCanRead(name string, epoch snap.Epoch) (info *snap.Info, rawInfo, rev string) {
   861  	if s.serveSnapPath[name] == "" {
   862  		return nil, "", ""
   863  	}
   864  	idx := len(s.serveOldPaths[name])
   865  	rev = s.serveRevision[name]
   866  	path := s.serveSnapPath[name]
   867  	for {
   868  		snapf, err := snapfile.Open(path)
   869  		if err != nil {
   870  			panic(err)
   871  		}
   872  		info, err := snap.ReadInfoFromSnapFile(snapf, nil)
   873  		if err != nil {
   874  			panic(err)
   875  		}
   876  		rawInfo, err := snapf.ReadFile("meta/snap.yaml")
   877  		if err != nil {
   878  			panic(err)
   879  		}
   880  		if info.Epoch.CanRead(epoch) {
   881  			return info, string(rawInfo), rev
   882  		}
   883  		idx--
   884  		if idx < 0 {
   885  			return nil, "", ""
   886  		}
   887  		path = s.serveOldPaths[name][idx]
   888  		rev = s.serveOldRevs[name][idx]
   889  	}
   890  }
   891  
   892  func (s *baseMgrsSuite) mockStore(c *C) *httptest.Server {
   893  	var baseURL *url.URL
   894  	fillHit := func(hitTemplate, revno string, info *snap.Info, rawInfo string) string {
   895  		epochBuf, err := json.Marshal(info.Epoch)
   896  		if err != nil {
   897  			panic(err)
   898  		}
   899  		rawInfoBuf, err := json.Marshal(rawInfo)
   900  		if err != nil {
   901  			panic(err)
   902  		}
   903  
   904  		name := info.SnapName()
   905  
   906  		hit := strings.Replace(hitTemplate, "@URL@", baseURL.String()+"/api/v1/snaps/download/"+name+"/"+revno, -1)
   907  		hit = strings.Replace(hit, "@NAME@", name, -1)
   908  		hit = strings.Replace(hit, "@SNAPID@", fakeSnapID(name), -1)
   909  		hit = strings.Replace(hit, "@ICON@", baseURL.String()+"/icon", -1)
   910  		hit = strings.Replace(hit, "@VERSION@", info.Version, -1)
   911  		hit = strings.Replace(hit, "@REVISION@", revno, -1)
   912  		hit = strings.Replace(hit, `@TYPE@`, string(info.Type()), -1)
   913  		hit = strings.Replace(hit, `@EPOCH@`, string(epochBuf), -1)
   914  		hit = strings.Replace(hit, `@SNAP_YAML@`, string(rawInfoBuf), -1)
   915  		return hit
   916  	}
   917  
   918  	mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   919  		// all URLS are /api/v1/snaps/... or /v2/snaps/ or /v2/assertions/... so
   920  		// check the url is sane and discard the common prefix
   921  		// to simplify indexing into the comps slice.
   922  		comps := strings.Split(r.URL.Path, "/")
   923  		if len(comps) < 2 {
   924  			panic("unexpected url path: " + r.URL.Path)
   925  		}
   926  		if comps[1] == "api" { //v1
   927  			if len(comps) <= 4 {
   928  				panic("unexpected url path: " + r.URL.Path)
   929  			}
   930  			comps = comps[4:]
   931  			if comps[0] == "auth" {
   932  				comps[0] = "auth:" + comps[1]
   933  			}
   934  		} else { // v2
   935  			if len(comps) <= 3 {
   936  				panic("unexpected url path: " + r.URL.Path)
   937  			}
   938  			if comps[2] == "assertions" {
   939  				// preserve "assertions" component
   940  				comps = comps[2:]
   941  			} else {
   942  				// drop common "snap" component
   943  				comps = comps[3:]
   944  			}
   945  			comps[0] = "v2:" + comps[0]
   946  		}
   947  
   948  		switch comps[0] {
   949  		case "auth:nonces":
   950  			w.Write([]byte(`{"nonce": "NONCE"}`))
   951  			return
   952  		case "auth:sessions":
   953  			// quick sanity check
   954  			reqBody, err := ioutil.ReadAll(r.Body)
   955  			c.Check(err, IsNil)
   956  			c.Check(bytes.Contains(reqBody, []byte("nonce: NONCE")), Equals, true)
   957  			c.Check(bytes.Contains(reqBody, []byte(fmt.Sprintf("serial: %s", s.expectedSerial))), Equals, true)
   958  			c.Check(bytes.Contains(reqBody, []byte(fmt.Sprintf("store: %s", s.expectedStore))), Equals, true)
   959  
   960  			c.Check(s.sessionMacaroon, Not(Equals), "")
   961  			w.WriteHeader(200)
   962  			w.Write([]byte(fmt.Sprintf(`{"macaroon": "%s"}`, s.sessionMacaroon)))
   963  			return
   964  		case "v2:assertions":
   965  			ref := &asserts.Ref{
   966  				Type:       asserts.Type(comps[1]),
   967  				PrimaryKey: comps[2:],
   968  			}
   969  			a, err := ref.Resolve(s.storeSigning.Find)
   970  			if asserts.IsNotFound(err) {
   971  				w.Header().Set("Content-Type", "application/problem+json")
   972  				w.WriteHeader(404)
   973  				w.Write([]byte(`{"error-list":[{"code":"not-found","message":"..."}]}`))
   974  				return
   975  			}
   976  			if err != nil {
   977  				panic(err)
   978  			}
   979  			w.Header().Set("Content-Type", asserts.MediaType)
   980  			w.WriteHeader(200)
   981  			w.Write(asserts.Encode(a))
   982  			return
   983  		case "download":
   984  			if s.sessionMacaroon != "" {
   985  				// FIXME: download is still using the old headers!
   986  				c.Check(r.Header.Get("X-Device-Authorization"), Equals, fmt.Sprintf(`Macaroon root="%s"`, s.sessionMacaroon))
   987  			}
   988  			if s.failNextDownload == comps[1] {
   989  				s.failNextDownload = ""
   990  				w.WriteHeader(418)
   991  				return
   992  			}
   993  			if s.hijackServeSnap != nil {
   994  				s.hijackServeSnap(w)
   995  				return
   996  			}
   997  			snapR, err := os.Open(s.pathFor(comps[1], comps[2]))
   998  			if err != nil {
   999  				panic(err)
  1000  			}
  1001  			io.Copy(w, snapR)
  1002  		case "v2:refresh":
  1003  			if s.sessionMacaroon != "" {
  1004  				c.Check(r.Header.Get("Snap-Device-Authorization"), Equals, fmt.Sprintf(`Macaroon root="%s"`, s.sessionMacaroon))
  1005  			}
  1006  			dec := json.NewDecoder(r.Body)
  1007  			var input struct {
  1008  				Actions []struct {
  1009  					Action      string     `json:"action"`
  1010  					SnapID      string     `json:"snap-id"`
  1011  					Name        string     `json:"name"`
  1012  					InstanceKey string     `json:"instance-key"`
  1013  					Epoch       snap.Epoch `json:"epoch"`
  1014  					// assertions
  1015  					Key        string `json:"key"`
  1016  					Assertions []struct {
  1017  						Type        string   `json:"type"`
  1018  						PrimaryKey  []string `json:"primary-key"`
  1019  						IfNewerThan *int     `json:"if-newer-than"`
  1020  					}
  1021  				} `json:"actions"`
  1022  				Context []struct {
  1023  					SnapID string     `json:"snap-id"`
  1024  					Epoch  snap.Epoch `json:"epoch"`
  1025  				} `json:"context"`
  1026  			}
  1027  			if err := dec.Decode(&input); err != nil {
  1028  				panic(err)
  1029  			}
  1030  			id2epoch := make(map[string]snap.Epoch, len(input.Context))
  1031  			for _, s := range input.Context {
  1032  				id2epoch[s.SnapID] = s.Epoch
  1033  			}
  1034  			type resultJSON struct {
  1035  				Result      string          `json:"result"`
  1036  				SnapID      string          `json:"snap-id"`
  1037  				Name        string          `json:"name"`
  1038  				Snap        json.RawMessage `json:"snap"`
  1039  				InstanceKey string          `json:"instance-key"`
  1040  				// For assertions
  1041  				Key           string   `json:"key"`
  1042  				AssertionURLs []string `json:"assertion-stream-urls"`
  1043  			}
  1044  			var results []resultJSON
  1045  			for _, a := range input.Actions {
  1046  				if a.Action == "fetch-assertions" {
  1047  					urls := []string{}
  1048  					for _, ar := range a.Assertions {
  1049  						ref := &asserts.Ref{
  1050  							Type:       asserts.Type(ar.Type),
  1051  							PrimaryKey: ar.PrimaryKey,
  1052  						}
  1053  						_, err := ref.Resolve(s.storeSigning.Find)
  1054  						if err != nil {
  1055  							panic("missing assertions not supported")
  1056  						}
  1057  						urls = append(urls, fmt.Sprintf("%s/v2/assertions/%s", baseURL.String(), ref.Unique()))
  1058  
  1059  					}
  1060  					results = append(results, resultJSON{
  1061  						Result:        "fetch-assertions",
  1062  						Key:           a.Key,
  1063  						AssertionURLs: urls,
  1064  					})
  1065  					continue
  1066  				}
  1067  				name := s.serveIDtoName[a.SnapID]
  1068  				epoch := id2epoch[a.SnapID]
  1069  				if a.Action == "install" {
  1070  					name = a.Name
  1071  					epoch = a.Epoch
  1072  				}
  1073  
  1074  				info, rawInfo, revno := s.newestThatCanRead(name, epoch)
  1075  				if info == nil {
  1076  					// no match
  1077  					continue
  1078  				}
  1079  				results = append(results, resultJSON{
  1080  					Result:      a.Action,
  1081  					SnapID:      a.SnapID,
  1082  					InstanceKey: a.InstanceKey,
  1083  					Name:        name,
  1084  					Snap:        json.RawMessage(fillHit(snapV2, revno, info, rawInfo)),
  1085  				})
  1086  			}
  1087  			w.WriteHeader(200)
  1088  			output, err := json.Marshal(map[string]interface{}{
  1089  				"results": results,
  1090  			})
  1091  			if err != nil {
  1092  				panic(err)
  1093  			}
  1094  			w.Write(output)
  1095  
  1096  		default:
  1097  			panic("unexpected url path: " + r.URL.Path)
  1098  		}
  1099  	}))
  1100  	c.Assert(mockServer, NotNil)
  1101  
  1102  	baseURL, _ = url.Parse(mockServer.URL)
  1103  	storeCfg := store.Config{
  1104  		StoreBaseURL: baseURL,
  1105  	}
  1106  
  1107  	mStore := store.New(&storeCfg, nil)
  1108  	st := s.o.State()
  1109  	st.Lock()
  1110  	snapstate.ReplaceStore(s.o.State(), mStore)
  1111  	st.Unlock()
  1112  
  1113  	// this will be used by remodeling cases
  1114  	storeNew := func(cfg *store.Config, dac store.DeviceAndAuthContext) *store.Store {
  1115  		cfg.StoreBaseURL = baseURL
  1116  		if s.checkDeviceAndAuthContext != nil {
  1117  			s.checkDeviceAndAuthContext(dac)
  1118  		}
  1119  		return store.New(cfg, dac)
  1120  	}
  1121  
  1122  	s.AddCleanup(overlord.MockStoreNew(storeNew))
  1123  
  1124  	return mockServer
  1125  }
  1126  
  1127  // serveSnap starts serving the snap at snapPath, moving the current
  1128  // one onto the list of previous ones if already set.
  1129  func (s *baseMgrsSuite) serveSnap(snapPath, revno string) {
  1130  	snapf, err := snapfile.Open(snapPath)
  1131  	if err != nil {
  1132  		panic(err)
  1133  	}
  1134  	info, err := snap.ReadInfoFromSnapFile(snapf, nil)
  1135  	if err != nil {
  1136  		panic(err)
  1137  	}
  1138  	name := info.SnapName()
  1139  	s.serveIDtoName[fakeSnapID(name)] = name
  1140  
  1141  	if oldPath := s.serveSnapPath[name]; oldPath != "" {
  1142  		oldRev := s.serveRevision[name]
  1143  		if oldRev == "" {
  1144  			panic("old path set but not old revision")
  1145  		}
  1146  		s.serveOldPaths[name] = append(s.serveOldPaths[name], oldPath)
  1147  		s.serveOldRevs[name] = append(s.serveOldRevs[name], oldRev)
  1148  	}
  1149  	s.serveSnapPath[name] = snapPath
  1150  	s.serveRevision[name] = revno
  1151  }
  1152  
  1153  func (s *mgrsSuite) TestHappyRemoteInstallAndUpgradeSvc(c *C) {
  1154  	// test install through store and update, plus some mechanics
  1155  	// of update
  1156  	// TODO: ok to split if it gets too messy to maintain
  1157  
  1158  	s.prereqSnapAssertions(c)
  1159  
  1160  	snapYamlContent := `name: foo
  1161  version: @VERSION@
  1162  apps:
  1163   bar:
  1164    command: bin/bar
  1165   svc:
  1166    command: svc
  1167    daemon: forking
  1168  `
  1169  
  1170  	ver := "1.0"
  1171  	revno := "42"
  1172  	snapPath, digest := s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno)
  1173  	s.serveSnap(snapPath, revno)
  1174  
  1175  	mockServer := s.mockStore(c)
  1176  	defer mockServer.Close()
  1177  
  1178  	st := s.o.State()
  1179  	st.Lock()
  1180  	defer st.Unlock()
  1181  
  1182  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  1183  	c.Assert(err, IsNil)
  1184  	chg := st.NewChange("install-snap", "...")
  1185  	chg.AddAll(ts)
  1186  
  1187  	st.Unlock()
  1188  	err = s.o.Settle(settleTimeout)
  1189  	st.Lock()
  1190  	c.Assert(err, IsNil)
  1191  
  1192  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1193  
  1194  	info, err := snapstate.CurrentInfo(st, "foo")
  1195  	c.Assert(err, IsNil)
  1196  
  1197  	c.Check(info.Revision, Equals, snap.R(42))
  1198  	c.Check(info.SnapID, Equals, fooSnapID)
  1199  	c.Check(info.Version, Equals, "1.0")
  1200  	c.Check(info.Summary(), Equals, "Foo")
  1201  	c.Check(info.Description(), Equals, "this is a description")
  1202  	c.Assert(osutil.FileExists(info.MountFile()), Equals, true)
  1203  
  1204  	pubAcct, err := assertstate.Publisher(st, info.SnapID)
  1205  	c.Assert(err, IsNil)
  1206  	c.Check(pubAcct.AccountID(), Equals, "devdevdev")
  1207  	c.Check(pubAcct.Username(), Equals, "devdevdev")
  1208  
  1209  	snapRev42, err := assertstate.DB(st).Find(asserts.SnapRevisionType, map[string]string{
  1210  		"snap-sha3-384": digest,
  1211  	})
  1212  	c.Assert(err, IsNil)
  1213  	c.Check(snapRev42.(*asserts.SnapRevision).SnapID(), Equals, fooSnapID)
  1214  	c.Check(snapRev42.(*asserts.SnapRevision).SnapRevision(), Equals, 42)
  1215  
  1216  	// check service was setup properly
  1217  	svcFile := filepath.Join(dirs.SnapServicesDir, "snap.foo.svc.service")
  1218  	c.Assert(osutil.FileExists(svcFile), Equals, true)
  1219  	stat, err := os.Stat(svcFile)
  1220  	c.Assert(err, IsNil)
  1221  	// should _not_ be executable
  1222  	c.Assert(stat.Mode().String(), Equals, "-rw-r--r--")
  1223  
  1224  	// Refresh
  1225  
  1226  	ver = "2.0"
  1227  	revno = "50"
  1228  	snapPath, digest = s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno)
  1229  	s.serveSnap(snapPath, revno)
  1230  
  1231  	ts, err = snapstate.Update(st, "foo", nil, 0, snapstate.Flags{})
  1232  	c.Assert(err, IsNil)
  1233  	chg = st.NewChange("upgrade-snap", "...")
  1234  	chg.AddAll(ts)
  1235  
  1236  	st.Unlock()
  1237  	err = s.o.Settle(settleTimeout)
  1238  	st.Lock()
  1239  	c.Assert(err, IsNil)
  1240  
  1241  	c.Assert(chg.Err(), IsNil)
  1242  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  1243  
  1244  	info, err = snapstate.CurrentInfo(st, "foo")
  1245  	c.Assert(err, IsNil)
  1246  
  1247  	c.Check(info.Revision, Equals, snap.R(50))
  1248  	c.Check(info.SnapID, Equals, fooSnapID)
  1249  	c.Check(info.Version, Equals, "2.0")
  1250  
  1251  	snapRev50, err := assertstate.DB(st).Find(asserts.SnapRevisionType, map[string]string{
  1252  		"snap-sha3-384": digest,
  1253  	})
  1254  	c.Assert(err, IsNil)
  1255  	c.Check(snapRev50.(*asserts.SnapRevision).SnapID(), Equals, fooSnapID)
  1256  	c.Check(snapRev50.(*asserts.SnapRevision).SnapRevision(), Equals, 50)
  1257  
  1258  	// check updated wrapper
  1259  	symlinkTarget, err := os.Readlink(info.Apps["bar"].WrapperPath())
  1260  	c.Assert(err, IsNil)
  1261  	c.Assert(symlinkTarget, Equals, "/usr/bin/snap")
  1262  
  1263  	// check updated service file
  1264  	c.Assert(svcFile, testutil.FileContains, "/var/snap/foo/"+revno)
  1265  }
  1266  
  1267  func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateWithEpochBump(c *C) {
  1268  	// test install through store and update, where there's an epoch bump in the upgrade
  1269  	// this does less checks on the details of install/update than TestHappyRemoteInstallAndUpgradeSvc
  1270  
  1271  	s.prereqSnapAssertions(c)
  1272  
  1273  	snapPath, _ := s.makeStoreTestSnap(c, "{name: foo, version: 0}", "1")
  1274  	s.serveSnap(snapPath, "1")
  1275  
  1276  	mockServer := s.mockStore(c)
  1277  	defer mockServer.Close()
  1278  
  1279  	st := s.o.State()
  1280  	st.Lock()
  1281  	defer st.Unlock()
  1282  
  1283  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  1284  	c.Assert(err, IsNil)
  1285  	chg := st.NewChange("install-snap", "...")
  1286  	chg.AddAll(ts)
  1287  
  1288  	st.Unlock()
  1289  	err = s.o.Settle(settleTimeout)
  1290  	st.Lock()
  1291  	c.Assert(err, IsNil)
  1292  
  1293  	// confirm it worked
  1294  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1295  
  1296  	// sanity checks
  1297  	info, err := snapstate.CurrentInfo(st, "foo")
  1298  	c.Assert(err, IsNil)
  1299  	c.Assert(info.Revision, Equals, snap.R(1))
  1300  	c.Assert(info.SnapID, Equals, fooSnapID)
  1301  	c.Assert(info.Epoch.String(), Equals, "0")
  1302  
  1303  	// now add some more snaps
  1304  	for i, epoch := range []string{"1*", "2*", "3*"} {
  1305  		revno := fmt.Sprint(i + 2)
  1306  		snapPath, _ := s.makeStoreTestSnap(c, "{name: foo, version: 0, epoch: "+epoch+"}", revno)
  1307  		s.serveSnap(snapPath, revno)
  1308  	}
  1309  
  1310  	// refresh
  1311  
  1312  	ts, err = snapstate.Update(st, "foo", nil, 0, snapstate.Flags{})
  1313  	c.Assert(err, IsNil)
  1314  	chg = st.NewChange("upgrade-snap", "...")
  1315  	chg.AddAll(ts)
  1316  
  1317  	st.Unlock()
  1318  	err = s.o.Settle(settleTimeout)
  1319  	st.Lock()
  1320  	c.Assert(err, IsNil)
  1321  
  1322  	c.Assert(chg.Err(), IsNil)
  1323  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  1324  
  1325  	info, err = snapstate.CurrentInfo(st, "foo")
  1326  	c.Assert(err, IsNil)
  1327  
  1328  	c.Check(info.Revision, Equals, snap.R(4))
  1329  	c.Check(info.SnapID, Equals, fooSnapID)
  1330  	c.Check(info.Epoch.String(), Equals, "3*")
  1331  }
  1332  
  1333  func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateWithPostHocEpochBump(c *C) {
  1334  	// test install through store and update, where there is an epoch
  1335  	// bump in the upgrade that comes in after the initial update is
  1336  	// computed.
  1337  
  1338  	// this is mostly checking the same as TestHappyRemoteInstallAndUpdateWithEpochBump
  1339  	// but serves as a sanity check for the Without case that follows
  1340  	// (these two together serve as a test for the refresh filtering)
  1341  	s.testHappyRemoteInstallAndUpdateWithMaybeEpochBump(c, true)
  1342  }
  1343  
  1344  func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateWithoutEpochBump(c *C) {
  1345  	// test install through store and update, where there _isn't_ an epoch bump in the upgrade
  1346  	// note that there _are_ refreshes available after the refresh,
  1347  	// but they're not an epoch bump so they're ignored
  1348  	s.testHappyRemoteInstallAndUpdateWithMaybeEpochBump(c, false)
  1349  }
  1350  
  1351  func (s *mgrsSuite) testHappyRemoteInstallAndUpdateWithMaybeEpochBump(c *C, doBump bool) {
  1352  	s.prereqSnapAssertions(c)
  1353  
  1354  	snapPath, _ := s.makeStoreTestSnap(c, "{name: foo, version: 1}", "1")
  1355  	s.serveSnap(snapPath, "1")
  1356  
  1357  	mockServer := s.mockStore(c)
  1358  	defer mockServer.Close()
  1359  
  1360  	st := s.o.State()
  1361  	st.Lock()
  1362  	defer st.Unlock()
  1363  
  1364  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  1365  	c.Assert(err, IsNil)
  1366  	chg := st.NewChange("install-snap", "...")
  1367  	chg.AddAll(ts)
  1368  
  1369  	st.Unlock()
  1370  	err = s.o.Settle(settleTimeout)
  1371  	st.Lock()
  1372  	c.Assert(err, IsNil)
  1373  
  1374  	// confirm it worked
  1375  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1376  
  1377  	// sanity checks
  1378  	info, err := snapstate.CurrentInfo(st, "foo")
  1379  	c.Assert(err, IsNil)
  1380  	c.Assert(info.Revision, Equals, snap.R(1))
  1381  	c.Assert(info.SnapID, Equals, fooSnapID)
  1382  	c.Assert(info.Epoch.String(), Equals, "0")
  1383  
  1384  	// add a new revision
  1385  	snapPath, _ = s.makeStoreTestSnap(c, "{name: foo, version: 2}", "2")
  1386  	s.serveSnap(snapPath, "2")
  1387  
  1388  	// refresh
  1389  
  1390  	ts, err = snapstate.Update(st, "foo", nil, 0, snapstate.Flags{})
  1391  	c.Assert(err, IsNil)
  1392  	chg = st.NewChange("upgrade-snap", "...")
  1393  	chg.AddAll(ts)
  1394  
  1395  	// add another new revision, after the update was computed (maybe with an epoch bump)
  1396  	if doBump {
  1397  		snapPath, _ = s.makeStoreTestSnap(c, "{name: foo, version: 3, epoch: 1*}", "3")
  1398  	} else {
  1399  		snapPath, _ = s.makeStoreTestSnap(c, "{name: foo, version: 3}", "3")
  1400  	}
  1401  	s.serveSnap(snapPath, "3")
  1402  
  1403  	st.Unlock()
  1404  	err = s.o.Settle(settleTimeout)
  1405  	st.Lock()
  1406  	c.Assert(err, IsNil)
  1407  
  1408  	c.Assert(chg.Err(), IsNil)
  1409  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  1410  
  1411  	info, err = snapstate.CurrentInfo(st, "foo")
  1412  	c.Assert(err, IsNil)
  1413  
  1414  	if doBump {
  1415  		// if the epoch bumped, then we should've re-refreshed
  1416  		c.Check(info.Revision, Equals, snap.R(3))
  1417  		c.Check(info.SnapID, Equals, fooSnapID)
  1418  		c.Check(info.Epoch.String(), Equals, "1*")
  1419  	} else {
  1420  		// if the epoch did not bump, then we should _not_ have re-refreshed
  1421  		c.Check(info.Revision, Equals, snap.R(2))
  1422  		c.Check(info.SnapID, Equals, fooSnapID)
  1423  		c.Check(info.Epoch.String(), Equals, "0")
  1424  	}
  1425  }
  1426  
  1427  func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateManyWithEpochBump(c *C) {
  1428  	// test install through store and update many, where there's an epoch bump in the upgrade
  1429  	// this does less checks on the details of install/update than TestHappyRemoteInstallAndUpgradeSvc
  1430  
  1431  	snapNames := []string{"aaaa", "bbbb", "cccc"}
  1432  	for _, name := range snapNames {
  1433  		s.prereqSnapAssertions(c, map[string]interface{}{"snap-name": name})
  1434  		snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 0}", name), "1")
  1435  		s.serveSnap(snapPath, "1")
  1436  	}
  1437  
  1438  	mockServer := s.mockStore(c)
  1439  	defer mockServer.Close()
  1440  
  1441  	st := s.o.State()
  1442  	st.Lock()
  1443  	defer st.Unlock()
  1444  
  1445  	affected, tasksets, err := snapstate.InstallMany(st, snapNames, 0)
  1446  	c.Assert(err, IsNil)
  1447  	sort.Strings(affected)
  1448  	c.Check(affected, DeepEquals, snapNames)
  1449  	chg := st.NewChange("install-snaps", "...")
  1450  	for _, taskset := range tasksets {
  1451  		chg.AddAll(taskset)
  1452  	}
  1453  
  1454  	st.Unlock()
  1455  	err = s.o.Settle(settleTimeout)
  1456  	st.Lock()
  1457  	c.Assert(err, IsNil)
  1458  
  1459  	// confirm it worked
  1460  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1461  
  1462  	// sanity checks
  1463  	for _, name := range snapNames {
  1464  		info, err := snapstate.CurrentInfo(st, name)
  1465  		c.Assert(err, IsNil)
  1466  		c.Assert(info.Revision, Equals, snap.R(1))
  1467  		c.Assert(info.SnapID, Equals, fakeSnapID(name))
  1468  		c.Assert(info.Epoch.String(), Equals, "0")
  1469  	}
  1470  
  1471  	// now add some more snap revisions with increasing epochs
  1472  	for _, name := range snapNames {
  1473  		for i, epoch := range []string{"1*", "2*", "3*"} {
  1474  			revno := fmt.Sprint(i + 2)
  1475  			snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 0, epoch: %s}", name, epoch), revno)
  1476  			s.serveSnap(snapPath, revno)
  1477  		}
  1478  	}
  1479  
  1480  	// refresh
  1481  
  1482  	affected, tasksets, err = snapstate.UpdateMany(context.TODO(), st, nil, 0, &snapstate.Flags{})
  1483  	c.Assert(err, IsNil)
  1484  	sort.Strings(affected)
  1485  	c.Check(affected, DeepEquals, snapNames)
  1486  	chg = st.NewChange("upgrade-snaps", "...")
  1487  	for _, taskset := range tasksets {
  1488  		chg.AddAll(taskset)
  1489  	}
  1490  
  1491  	st.Unlock()
  1492  	err = s.o.Settle(settleTimeout)
  1493  	st.Lock()
  1494  	c.Assert(err, IsNil)
  1495  
  1496  	c.Assert(chg.Err(), IsNil)
  1497  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  1498  
  1499  	for _, name := range snapNames {
  1500  		info, err := snapstate.CurrentInfo(st, name)
  1501  		c.Assert(err, IsNil)
  1502  
  1503  		c.Check(info.Revision, Equals, snap.R(4))
  1504  		c.Check(info.SnapID, Equals, fakeSnapID(name))
  1505  		c.Check(info.Epoch.String(), Equals, "3*")
  1506  	}
  1507  }
  1508  
  1509  func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateManyWithEpochBumpAndOneFailing(c *C) {
  1510  	// test install through store and update, where there's an epoch bump in the upgrade and one of them fails
  1511  
  1512  	snapNames := []string{"aaaa", "bbbb", "cccc"}
  1513  	for _, name := range snapNames {
  1514  		s.prereqSnapAssertions(c, map[string]interface{}{"snap-name": name})
  1515  		snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 0}", name), "1")
  1516  		s.serveSnap(snapPath, "1")
  1517  	}
  1518  
  1519  	mockServer := s.mockStore(c)
  1520  	defer mockServer.Close()
  1521  
  1522  	st := s.o.State()
  1523  	st.Lock()
  1524  	defer st.Unlock()
  1525  
  1526  	affected, tasksets, err := snapstate.InstallMany(st, snapNames, 0)
  1527  	c.Assert(err, IsNil)
  1528  	sort.Strings(affected)
  1529  	c.Check(affected, DeepEquals, snapNames)
  1530  	chg := st.NewChange("install-snaps", "...")
  1531  	for _, taskset := range tasksets {
  1532  		chg.AddAll(taskset)
  1533  	}
  1534  
  1535  	st.Unlock()
  1536  	err = s.o.Settle(settleTimeout)
  1537  	st.Lock()
  1538  	c.Assert(err, IsNil)
  1539  
  1540  	// confirm it worked
  1541  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1542  
  1543  	// sanity checks
  1544  	for _, name := range snapNames {
  1545  		info, err := snapstate.CurrentInfo(st, name)
  1546  		c.Assert(err, IsNil)
  1547  		c.Assert(info.Revision, Equals, snap.R(1))
  1548  		c.Assert(info.SnapID, Equals, fakeSnapID(name))
  1549  		c.Assert(info.Epoch.String(), Equals, "0")
  1550  	}
  1551  
  1552  	// now add some more snap revisions with increasing epochs
  1553  	for _, name := range snapNames {
  1554  		for i, epoch := range []string{"1*", "2*", "3*"} {
  1555  			revno := fmt.Sprint(i + 2)
  1556  			snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 0, epoch: %s}", name, epoch), revno)
  1557  			s.serveSnap(snapPath, revno)
  1558  		}
  1559  	}
  1560  
  1561  	// refresh
  1562  	affected, tasksets, err = snapstate.UpdateMany(context.TODO(), st, nil, 0, &snapstate.Flags{})
  1563  	c.Assert(err, IsNil)
  1564  	sort.Strings(affected)
  1565  	c.Check(affected, DeepEquals, snapNames)
  1566  	chg = st.NewChange("upgrade-snaps", "...")
  1567  	for _, taskset := range tasksets {
  1568  		chg.AddAll(taskset)
  1569  	}
  1570  
  1571  	st.Unlock()
  1572  	// the download for the refresh above will be performed below, during 'settle'.
  1573  	// fail the refresh of cccc by failing its download
  1574  	s.failNextDownload = "cccc"
  1575  	err = s.o.Settle(settleTimeout)
  1576  	st.Lock()
  1577  	c.Assert(err, IsNil)
  1578  
  1579  	c.Assert(chg.Err(), NotNil)
  1580  	c.Assert(chg.Status(), Equals, state.ErrorStatus)
  1581  
  1582  	for _, name := range snapNames {
  1583  		comment := Commentf("%q", name)
  1584  		info, err := snapstate.CurrentInfo(st, name)
  1585  		c.Assert(err, IsNil, comment)
  1586  
  1587  		if name == "cccc" {
  1588  			// the failed one: still on rev 1 (epoch 0)
  1589  			c.Assert(info.Revision, Equals, snap.R(1))
  1590  			c.Assert(info.SnapID, Equals, fakeSnapID(name))
  1591  			c.Assert(info.Epoch.String(), Equals, "0")
  1592  		} else {
  1593  			// the non-failed ones: refreshed to rev 4 (epoch 3*)
  1594  			c.Check(info.Revision, Equals, snap.R(4), comment)
  1595  			c.Check(info.SnapID, Equals, fakeSnapID(name), comment)
  1596  			c.Check(info.Epoch.String(), Equals, "3*", comment)
  1597  		}
  1598  	}
  1599  }
  1600  
  1601  func (s *mgrsSuite) TestHappyLocalInstallWithStoreMetadata(c *C) {
  1602  	snapDecl := s.prereqSnapAssertions(c)
  1603  
  1604  	snapYamlContent := `name: foo
  1605  apps:
  1606   bar:
  1607    command: bin/bar
  1608  `
  1609  	snapPath := makeTestSnap(c, snapYamlContent+"version: 1.5")
  1610  
  1611  	si := &snap.SideInfo{
  1612  		RealName: "foo",
  1613  		SnapID:   fooSnapID,
  1614  		Revision: snap.R(55),
  1615  	}
  1616  
  1617  	st := s.o.State()
  1618  	st.Lock()
  1619  	defer st.Unlock()
  1620  
  1621  	// have the snap-declaration in the system db
  1622  	err := assertstate.Add(st, s.devAcct)
  1623  	c.Assert(err, IsNil)
  1624  	err = assertstate.Add(st, snapDecl)
  1625  	c.Assert(err, IsNil)
  1626  
  1627  	ts, _, err := snapstate.InstallPath(st, si, snapPath, "", "", snapstate.Flags{DevMode: true})
  1628  	c.Assert(err, IsNil)
  1629  	chg := st.NewChange("install-snap", "...")
  1630  	chg.AddAll(ts)
  1631  
  1632  	st.Unlock()
  1633  	err = s.o.Settle(settleTimeout)
  1634  	st.Lock()
  1635  	c.Assert(err, IsNil)
  1636  
  1637  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1638  
  1639  	info, err := snapstate.CurrentInfo(st, "foo")
  1640  	c.Assert(err, IsNil)
  1641  	c.Check(info.Revision, Equals, snap.R(55))
  1642  	c.Check(info.SnapID, Equals, fooSnapID)
  1643  	c.Check(info.Version, Equals, "1.5")
  1644  
  1645  	// ensure that the binary wrapper file got generated with the right
  1646  	// name
  1647  	binaryWrapper := filepath.Join(dirs.SnapBinariesDir, "foo.bar")
  1648  	c.Assert(osutil.IsSymlink(binaryWrapper), Equals, true)
  1649  
  1650  	// data dirs
  1651  	c.Assert(osutil.IsDirectory(info.DataDir()), Equals, true)
  1652  	c.Assert(osutil.IsDirectory(info.CommonDataDir()), Equals, true)
  1653  
  1654  	// snap file and its mounting
  1655  
  1656  	// after install the snap file is in the right dir
  1657  	c.Assert(osutil.FileExists(filepath.Join(dirs.SnapBlobDir, "foo_55.snap")), Equals, true)
  1658  
  1659  	// ensure the right unit is created
  1660  	mup := systemd.MountUnitPath(filepath.Join(dirs.StripRootDir(dirs.SnapMountDir), "foo/55"))
  1661  	c.Assert(mup, testutil.FileMatches, fmt.Sprintf("(?ms).*^Where=%s/foo/55", dirs.StripRootDir(dirs.SnapMountDir)))
  1662  	c.Assert(mup, testutil.FileMatches, "(?ms).*^What=/var/lib/snapd/snaps/foo_55.snap")
  1663  }
  1664  
  1665  func (s *mgrsSuite) TestParallelInstanceLocalInstallSnapNameMismatch(c *C) {
  1666  	snapDecl := s.prereqSnapAssertions(c)
  1667  
  1668  	snapYamlContent := `name: foo
  1669  apps:
  1670   bar:
  1671    command: bin/bar
  1672  `
  1673  	snapPath := makeTestSnap(c, snapYamlContent+"version: 1.5")
  1674  
  1675  	si := &snap.SideInfo{
  1676  		RealName: "foo",
  1677  		SnapID:   fooSnapID,
  1678  		Revision: snap.R(55),
  1679  	}
  1680  
  1681  	st := s.o.State()
  1682  	st.Lock()
  1683  	defer st.Unlock()
  1684  
  1685  	// have the snap-declaration in the system db
  1686  	err := assertstate.Add(st, s.devAcct)
  1687  	c.Assert(err, IsNil)
  1688  	err = assertstate.Add(st, snapDecl)
  1689  	c.Assert(err, IsNil)
  1690  
  1691  	_, _, err = snapstate.InstallPath(st, si, snapPath, "bar_instance", "", snapstate.Flags{DevMode: true})
  1692  	c.Assert(err, ErrorMatches, `cannot install snap "bar_instance", the name does not match the metadata "foo"`)
  1693  }
  1694  
  1695  func (s *mgrsSuite) TestParallelInstanceLocalInstallInvalidInstanceName(c *C) {
  1696  	snapDecl := s.prereqSnapAssertions(c)
  1697  
  1698  	snapYamlContent := `name: foo
  1699  apps:
  1700   bar:
  1701    command: bin/bar
  1702  `
  1703  	snapPath := makeTestSnap(c, snapYamlContent+"version: 1.5")
  1704  
  1705  	si := &snap.SideInfo{
  1706  		RealName: "foo",
  1707  		SnapID:   fooSnapID,
  1708  		Revision: snap.R(55),
  1709  	}
  1710  
  1711  	st := s.o.State()
  1712  	st.Lock()
  1713  	defer st.Unlock()
  1714  
  1715  	// have the snap-declaration in the system db
  1716  	err := assertstate.Add(st, s.devAcct)
  1717  	c.Assert(err, IsNil)
  1718  	err = assertstate.Add(st, snapDecl)
  1719  	c.Assert(err, IsNil)
  1720  
  1721  	_, _, err = snapstate.InstallPath(st, si, snapPath, "bar_invalid_instance_name", "", snapstate.Flags{DevMode: true})
  1722  	c.Assert(err, ErrorMatches, `invalid instance name: invalid instance key: "invalid_instance_name"`)
  1723  }
  1724  
  1725  func (s *mgrsSuite) TestCheckInterfaces(c *C) {
  1726  	snapDecl := s.prereqSnapAssertions(c)
  1727  
  1728  	snapYamlContent := `name: foo
  1729  apps:
  1730   bar:
  1731    command: bin/bar
  1732  slots:
  1733   network:
  1734  `
  1735  	snapPath := makeTestSnap(c, snapYamlContent+"version: 1.5")
  1736  
  1737  	si := &snap.SideInfo{
  1738  		RealName: "foo",
  1739  		SnapID:   fooSnapID,
  1740  		Revision: snap.R(55),
  1741  	}
  1742  
  1743  	st := s.o.State()
  1744  	st.Lock()
  1745  	defer st.Unlock()
  1746  
  1747  	// have the snap-declaration in the system db
  1748  	err := assertstate.Add(st, s.devAcct)
  1749  	c.Assert(err, IsNil)
  1750  	err = assertstate.Add(st, snapDecl)
  1751  	c.Assert(err, IsNil)
  1752  
  1753  	// mock SanitizePlugsSlots so that unknown interfaces are not rejected
  1754  	restoreSanitize := snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})
  1755  	defer restoreSanitize()
  1756  
  1757  	ts, _, err := snapstate.InstallPath(st, si, snapPath, "", "", snapstate.Flags{DevMode: true})
  1758  	c.Assert(err, IsNil)
  1759  	chg := st.NewChange("install-snap", "...")
  1760  	chg.AddAll(ts)
  1761  
  1762  	st.Unlock()
  1763  	err = s.o.Settle(settleTimeout)
  1764  	st.Lock()
  1765  	c.Assert(err, IsNil)
  1766  
  1767  	c.Assert(chg.Err(), ErrorMatches, `(?s).*installation not allowed by "network" slot rule of interface "network".*`)
  1768  	c.Check(chg.Status(), Equals, state.ErrorStatus)
  1769  }
  1770  
  1771  func (s *mgrsSuite) TestHappyRefreshControl(c *C) {
  1772  	// test install through store and update, plus some mechanics
  1773  	// of update
  1774  	// TODO: ok to split if it gets too messy to maintain
  1775  
  1776  	s.prereqSnapAssertions(c)
  1777  
  1778  	snapYamlContent := `name: foo
  1779  version: @VERSION@
  1780  `
  1781  
  1782  	ver := "1.0"
  1783  	revno := "42"
  1784  	snapPath, _ := s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno)
  1785  	s.serveSnap(snapPath, revno)
  1786  
  1787  	mockServer := s.mockStore(c)
  1788  	defer mockServer.Close()
  1789  
  1790  	st := s.o.State()
  1791  	st.Lock()
  1792  	defer st.Unlock()
  1793  
  1794  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  1795  	c.Assert(err, IsNil)
  1796  	chg := st.NewChange("install-snap", "...")
  1797  	chg.AddAll(ts)
  1798  
  1799  	st.Unlock()
  1800  	err = s.o.Settle(settleTimeout)
  1801  	st.Lock()
  1802  	c.Assert(err, IsNil)
  1803  
  1804  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1805  
  1806  	info, err := snapstate.CurrentInfo(st, "foo")
  1807  	c.Assert(err, IsNil)
  1808  
  1809  	c.Check(info.Revision, Equals, snap.R(42))
  1810  
  1811  	// Refresh
  1812  
  1813  	// Setup refresh control
  1814  
  1815  	headers := map[string]interface{}{
  1816  		"series":          "16",
  1817  		"snap-id":         "bar-id",
  1818  		"snap-name":       "bar",
  1819  		"publisher-id":    "devdevdev",
  1820  		"refresh-control": []interface{}{fooSnapID},
  1821  		"timestamp":       time.Now().Format(time.RFC3339),
  1822  	}
  1823  	snapDeclBar, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
  1824  	c.Assert(err, IsNil)
  1825  	err = s.storeSigning.Add(snapDeclBar)
  1826  	c.Assert(err, IsNil)
  1827  	err = assertstate.Add(st, snapDeclBar)
  1828  	c.Assert(err, IsNil)
  1829  
  1830  	snapstate.Set(st, "bar", &snapstate.SnapState{
  1831  		Active: true,
  1832  		Sequence: []*snap.SideInfo{
  1833  			{RealName: "bar", SnapID: "bar-id", Revision: snap.R(1)},
  1834  		},
  1835  		Current:  snap.R(1),
  1836  		SnapType: "app",
  1837  	})
  1838  
  1839  	develSigning := assertstest.NewSigningDB("devdevdev", develPrivKey)
  1840  
  1841  	develAccKey := assertstest.NewAccountKey(s.storeSigning, s.devAcct, nil, develPrivKey.PublicKey(), "")
  1842  	err = s.storeSigning.Add(develAccKey)
  1843  	c.Assert(err, IsNil)
  1844  
  1845  	ver = "2.0"
  1846  	revno = "50"
  1847  	snapPath, _ = s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno)
  1848  	s.serveSnap(snapPath, revno)
  1849  
  1850  	updated, tss, err := snapstate.UpdateMany(context.TODO(), st, []string{"foo"}, 0, nil)
  1851  	c.Check(updated, IsNil)
  1852  	c.Check(tss, IsNil)
  1853  	// no validation we, get an error
  1854  	c.Check(err, ErrorMatches, `cannot refresh "foo" to revision 50: no validation by "bar"`)
  1855  
  1856  	// setup validation
  1857  	headers = map[string]interface{}{
  1858  		"series":                 "16",
  1859  		"snap-id":                "bar-id",
  1860  		"approved-snap-id":       fooSnapID,
  1861  		"approved-snap-revision": "50",
  1862  		"timestamp":              time.Now().Format(time.RFC3339),
  1863  	}
  1864  	barValidation, err := develSigning.Sign(asserts.ValidationType, headers, nil, "")
  1865  	c.Assert(err, IsNil)
  1866  	err = s.storeSigning.Add(barValidation)
  1867  	c.Assert(err, IsNil)
  1868  
  1869  	// ... and try again
  1870  	updated, tss, err = snapstate.UpdateMany(context.TODO(), st, []string{"foo"}, 0, nil)
  1871  	c.Assert(err, IsNil)
  1872  	c.Assert(updated, DeepEquals, []string{"foo"})
  1873  	c.Assert(tss, HasLen, 2)
  1874  	verifyLastTasksetIsRerefresh(c, tss)
  1875  	chg = st.NewChange("upgrade-snaps", "...")
  1876  	chg.AddAll(tss[0])
  1877  
  1878  	st.Unlock()
  1879  	err = s.o.Settle(settleTimeout)
  1880  	st.Lock()
  1881  	c.Assert(err, IsNil)
  1882  
  1883  	c.Assert(chg.Err(), IsNil)
  1884  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  1885  
  1886  	info, err = snapstate.CurrentInfo(st, "foo")
  1887  	c.Assert(err, IsNil)
  1888  
  1889  	c.Check(info.Revision, Equals, snap.R(50))
  1890  }
  1891  
  1892  // core & kernel
  1893  
  1894  var modelDefaults = map[string]interface{}{
  1895  	"architecture": "amd64",
  1896  	"store":        "my-brand-store-id",
  1897  	"gadget":       "pc",
  1898  	"kernel":       "pc-kernel",
  1899  }
  1900  
  1901  func findKind(chg *state.Change, kind string) *state.Task {
  1902  	for _, t := range chg.Tasks() {
  1903  		if t.Kind() == kind {
  1904  			return t
  1905  		}
  1906  	}
  1907  	return nil
  1908  }
  1909  
  1910  func (s *mgrsSuite) TestInstallCoreSnapUpdatesBootloaderEnvAndSplitsAcrossRestart(c *C) {
  1911  	bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
  1912  	bootloader.Force(bloader)
  1913  	defer bootloader.Force(nil)
  1914  	bloader.SetBootBase("core_99.snap")
  1915  
  1916  	restore := release.MockOnClassic(false)
  1917  	defer restore()
  1918  
  1919  	model := s.brands.Model("my-brand", "my-model", modelDefaults)
  1920  
  1921  	const packageOS = `
  1922  name: core
  1923  version: 16.04-1
  1924  type: os
  1925  `
  1926  	snapPath := makeTestSnap(c, packageOS)
  1927  
  1928  	st := s.o.State()
  1929  	st.Lock()
  1930  	defer st.Unlock()
  1931  
  1932  	// setup model assertion
  1933  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  1934  	devicestatetest.SetDevice(st, &auth.DeviceState{
  1935  		Brand:  "my-brand",
  1936  		Model:  "my-model",
  1937  		Serial: "serialserialserial",
  1938  	})
  1939  	err := assertstate.Add(st, model)
  1940  	c.Assert(err, IsNil)
  1941  
  1942  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "core"}, snapPath, "", "", snapstate.Flags{})
  1943  	c.Assert(err, IsNil)
  1944  	chg := st.NewChange("install-snap", "...")
  1945  	chg.AddAll(ts)
  1946  
  1947  	st.Unlock()
  1948  	err = s.o.Settle(settleTimeout)
  1949  	st.Lock()
  1950  	c.Assert(err, IsNil)
  1951  
  1952  	// final steps will are post poned until we are in the restarted snapd
  1953  	ok, rst := st.Restarting()
  1954  	c.Assert(ok, Equals, true)
  1955  	c.Assert(rst, Equals, state.RestartSystem)
  1956  
  1957  	t := findKind(chg, "auto-connect")
  1958  	c.Assert(t, NotNil)
  1959  	c.Assert(t.Status(), Equals, state.DoingStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1960  
  1961  	// this is already set
  1962  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  1963  		"snap_core":       "core_99.snap",
  1964  		"snap_try_core":   "core_x1.snap",
  1965  		"snap_try_kernel": "",
  1966  		"snap_mode":       boot.TryStatus,
  1967  	})
  1968  
  1969  	// simulate successful restart happened
  1970  	state.MockRestarting(st, state.RestartUnset)
  1971  	bloader.BootVars["snap_mode"] = boot.DefaultStatus
  1972  	bloader.SetBootBase("core_x1.snap")
  1973  
  1974  	st.Unlock()
  1975  	err = s.o.Settle(settleTimeout)
  1976  	st.Lock()
  1977  	c.Assert(err, IsNil)
  1978  
  1979  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1980  }
  1981  
  1982  type rebootEnv interface {
  1983  	SetTryingDuringReboot(which []snap.Type) error
  1984  	SetRollbackAcrossReboot(which []snap.Type) error
  1985  }
  1986  
  1987  func (s *baseMgrsSuite) mockSuccessfulReboot(c *C, be rebootEnv, which []snap.Type) {
  1988  	st := s.o.State()
  1989  	restarting, restartType := st.Restarting()
  1990  	c.Assert(restarting, Equals, true, Commentf("mockSuccessfulReboot called when there was no pending restart"))
  1991  	c.Assert(restartType, Equals, state.RestartSystem, Commentf("mockSuccessfulReboot called but restartType is not SystemRestart but %v", restartType))
  1992  	state.MockRestarting(st, state.RestartUnset)
  1993  	err := be.SetTryingDuringReboot(which)
  1994  	c.Assert(err, IsNil)
  1995  	s.o.DeviceManager().ResetToPostBootState()
  1996  	st.Unlock()
  1997  	defer st.Lock()
  1998  	err = s.o.DeviceManager().Ensure()
  1999  	c.Assert(err, IsNil)
  2000  }
  2001  
  2002  func (s *baseMgrsSuite) mockRollbackAcrossReboot(c *C, be rebootEnv, which []snap.Type) {
  2003  	st := s.o.State()
  2004  	restarting, restartType := st.Restarting()
  2005  	c.Assert(restarting, Equals, true, Commentf("mockRollbackAcrossReboot called when there was no pending restart"))
  2006  	c.Assert(restartType, Equals, state.RestartSystem, Commentf("mockRollbackAcrossReboot called but restartType is not SystemRestart but %v", restartType))
  2007  	state.MockRestarting(st, state.RestartUnset)
  2008  	err := be.SetRollbackAcrossReboot(which)
  2009  	c.Assert(err, IsNil)
  2010  	s.o.DeviceManager().ResetToPostBootState()
  2011  	st.Unlock()
  2012  	s.o.Settle(settleTimeout)
  2013  	st.Lock()
  2014  }
  2015  
  2016  func (s *mgrsSuite) TestInstallKernelSnapUpdatesBootloaderEnv(c *C) {
  2017  	bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
  2018  	bootloader.Force(bloader)
  2019  	defer bootloader.Force(nil)
  2020  
  2021  	restore := release.MockOnClassic(false)
  2022  	defer restore()
  2023  
  2024  	model := s.brands.Model("my-brand", "my-model", modelDefaults)
  2025  
  2026  	const packageKernel = `
  2027  name: pc-kernel
  2028  version: 4.0-1
  2029  type: kernel`
  2030  
  2031  	files := [][]string{
  2032  		{"kernel.img", "I'm a kernel"},
  2033  		{"initrd.img", "...and I'm an initrd"},
  2034  		{"meta/kernel.yaml", "version: 4.2"},
  2035  	}
  2036  	snapPath := snaptest.MakeTestSnapWithFiles(c, packageKernel, files)
  2037  
  2038  	st := s.o.State()
  2039  	st.Lock()
  2040  	defer st.Unlock()
  2041  
  2042  	// pretend we have core18/pc-kernel
  2043  	bloader.BootVars = map[string]string{
  2044  		"snap_core":   "core18_2.snap",
  2045  		"snap_kernel": "pc-kernel_123.snap",
  2046  		"snap_mode":   boot.DefaultStatus,
  2047  	}
  2048  	si1 := &snap.SideInfo{RealName: "pc-kernel", Revision: snap.R(123)}
  2049  	snapstate.Set(st, "pc-kernel", &snapstate.SnapState{
  2050  		SnapType: "kernel",
  2051  		Active:   true,
  2052  		Sequence: []*snap.SideInfo{si1},
  2053  		Current:  si1.Revision,
  2054  	})
  2055  	snaptest.MockSnapWithFiles(c, packageKernel, si1, [][]string{
  2056  		{"meta/kernel.yaml", ""},
  2057  	})
  2058  	si2 := &snap.SideInfo{RealName: "core18", Revision: snap.R(2)}
  2059  	snapstate.Set(st, "core18", &snapstate.SnapState{
  2060  		SnapType: "base",
  2061  		Active:   true,
  2062  		Sequence: []*snap.SideInfo{si2},
  2063  		Current:  si2.Revision,
  2064  	})
  2065  
  2066  	// setup model assertion
  2067  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  2068  	devicestatetest.SetDevice(st, &auth.DeviceState{
  2069  		Brand:  "my-brand",
  2070  		Model:  "my-model",
  2071  		Serial: "serialserialserial",
  2072  	})
  2073  	err := assertstate.Add(st, model)
  2074  	c.Assert(err, IsNil)
  2075  
  2076  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "pc-kernel"}, snapPath, "", "", snapstate.Flags{})
  2077  	c.Assert(err, IsNil)
  2078  	chg := st.NewChange("install-snap", "...")
  2079  	chg.AddAll(ts)
  2080  
  2081  	// run, this will trigger a wait for the restart
  2082  	st.Unlock()
  2083  	err = s.o.Settle(settleTimeout)
  2084  	st.Lock()
  2085  	c.Assert(err, IsNil)
  2086  	// we are in restarting state and the change is not done yet
  2087  	restarting, _ := st.Restarting()
  2088  	c.Check(restarting, Equals, true)
  2089  	c.Check(chg.Status(), Equals, state.DoingStatus)
  2090  
  2091  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  2092  		"snap_core":       "core18_2.snap",
  2093  		"snap_try_core":   "",
  2094  		"snap_kernel":     "pc-kernel_123.snap",
  2095  		"snap_try_kernel": "pc-kernel_x1.snap",
  2096  		"snap_mode":       boot.TryStatus,
  2097  	})
  2098  	// pretend we restarted
  2099  	s.mockSuccessfulReboot(c, bloader, []snap.Type{snap.TypeKernel})
  2100  
  2101  	st.Unlock()
  2102  	err = s.o.Settle(settleTimeout)
  2103  	st.Lock()
  2104  	c.Assert(err, IsNil)
  2105  
  2106  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  2107  }
  2108  
  2109  func (s *mgrsSuite) TestInstallKernelSnapUndoUpdatesBootloaderEnv(c *C) {
  2110  	bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
  2111  	bootloader.Force(bloader)
  2112  	defer bootloader.Force(nil)
  2113  
  2114  	restore := release.MockOnClassic(false)
  2115  	defer restore()
  2116  
  2117  	model := s.brands.Model("my-brand", "my-model", modelDefaults)
  2118  
  2119  	const packageKernel = `
  2120  name: pc-kernel
  2121  version: 4.0-1
  2122  type: kernel`
  2123  
  2124  	files := [][]string{
  2125  		{"kernel.img", "I'm a kernel"},
  2126  		{"initrd.img", "...and I'm an initrd"},
  2127  		{"meta/kernel.yaml", "version: 4.2"},
  2128  	}
  2129  	snapPath := snaptest.MakeTestSnapWithFiles(c, packageKernel, files)
  2130  
  2131  	st := s.o.State()
  2132  	st.Lock()
  2133  	defer st.Unlock()
  2134  
  2135  	// pretend we have core18/pc-kernel
  2136  	bloader.BootVars = map[string]string{
  2137  		"snap_core":   "core18_2.snap",
  2138  		"snap_kernel": "pc-kernel_123.snap",
  2139  		"snap_mode":   boot.DefaultStatus,
  2140  	}
  2141  	si1 := &snap.SideInfo{RealName: "pc-kernel", Revision: snap.R(123)}
  2142  	snapstate.Set(st, "pc-kernel", &snapstate.SnapState{
  2143  		SnapType: "kernel",
  2144  		Active:   true,
  2145  		Sequence: []*snap.SideInfo{si1},
  2146  		Current:  si1.Revision,
  2147  	})
  2148  	snaptest.MockSnapWithFiles(c, packageKernel, si1, [][]string{
  2149  		{"meta/kernel.yaml", ""},
  2150  	})
  2151  	si2 := &snap.SideInfo{RealName: "core18", Revision: snap.R(2)}
  2152  	snapstate.Set(st, "core18", &snapstate.SnapState{
  2153  		SnapType: "base",
  2154  		Active:   true,
  2155  		Sequence: []*snap.SideInfo{si2},
  2156  		Current:  si2.Revision,
  2157  	})
  2158  
  2159  	// setup model assertion
  2160  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  2161  	devicestatetest.SetDevice(st, &auth.DeviceState{
  2162  		Brand:  "my-brand",
  2163  		Model:  "my-model",
  2164  		Serial: "serialserialserial",
  2165  	})
  2166  	err := assertstate.Add(st, model)
  2167  	c.Assert(err, IsNil)
  2168  
  2169  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "pc-kernel"}, snapPath, "", "", snapstate.Flags{})
  2170  	c.Assert(err, IsNil)
  2171  
  2172  	terr := st.NewTask("error-trigger", "provoking total undo")
  2173  	terr.WaitFor(ts.Tasks()[len(ts.Tasks())-1])
  2174  	ts.AddTask(terr)
  2175  	chg := st.NewChange("install-snap", "...")
  2176  	chg.AddAll(ts)
  2177  
  2178  	// run, this will trigger a wait for the restart
  2179  	st.Unlock()
  2180  	err = s.o.Settle(settleTimeout)
  2181  	st.Lock()
  2182  	c.Assert(err, IsNil)
  2183  
  2184  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  2185  		"snap_core":       "core18_2.snap",
  2186  		"snap_kernel":     "pc-kernel_123.snap",
  2187  		"snap_try_kernel": "pc-kernel_x1.snap",
  2188  		"snap_mode":       boot.TryStatus,
  2189  		"snap_try_core":   "",
  2190  	})
  2191  
  2192  	// we are in restarting state and the change is not done yet
  2193  	restarting, _ := st.Restarting()
  2194  	c.Check(restarting, Equals, true)
  2195  	c.Check(chg.Status(), Equals, state.DoingStatus)
  2196  	// pretend we restarted
  2197  	s.mockSuccessfulReboot(c, bloader, []snap.Type{snap.TypeKernel})
  2198  
  2199  	st.Unlock()
  2200  	err = s.o.Settle(settleTimeout)
  2201  	st.Lock()
  2202  	c.Assert(err, IsNil)
  2203  
  2204  	c.Assert(chg.Status(), Equals, state.ErrorStatus)
  2205  
  2206  	// and we undo the bootvars and trigger a reboot
  2207  	c.Check(bloader.BootVars, DeepEquals, map[string]string{
  2208  		"snap_core":       "core18_2.snap",
  2209  		"snap_try_core":   "",
  2210  		"snap_try_kernel": "pc-kernel_123.snap",
  2211  		"snap_kernel":     "pc-kernel_x1.snap",
  2212  		"snap_mode":       boot.TryStatus,
  2213  	})
  2214  	restarting, _ = st.Restarting()
  2215  	c.Check(restarting, Equals, true)
  2216  }
  2217  
  2218  func (s *mgrsSuite) TestInstallKernelSnap20UpdatesBootloaderEnv(c *C) {
  2219  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
  2220  	bootloader.Force(bloader)
  2221  	defer bootloader.Force(nil)
  2222  
  2223  	// we have revision 1 installed
  2224  	kernel, err := snap.ParsePlaceInfoFromSnapFileName("pc-kernel_1.snap")
  2225  	c.Assert(err, IsNil)
  2226  	restore := bloader.SetEnabledKernel(kernel)
  2227  	defer restore()
  2228  
  2229  	restore = release.MockOnClassic(false)
  2230  	defer restore()
  2231  
  2232  	uc20ModelDefaults := map[string]interface{}{
  2233  		"architecture": "amd64",
  2234  		"base":         "core20",
  2235  		"store":        "my-brand-store-id",
  2236  		"snaps": []interface{}{
  2237  			map[string]interface{}{
  2238  				"name":            "pc-kernel",
  2239  				"id":              snaptest.AssertedSnapID("pc-kernel"),
  2240  				"type":            "kernel",
  2241  				"default-channel": "20",
  2242  			},
  2243  			map[string]interface{}{
  2244  				"name":            "pc",
  2245  				"id":              snaptest.AssertedSnapID("pc"),
  2246  				"type":            "gadget",
  2247  				"default-channel": "20",
  2248  			}},
  2249  	}
  2250  
  2251  	model := s.brands.Model("my-brand", "my-model", uc20ModelDefaults)
  2252  
  2253  	const packageKernel = `
  2254  name: pc-kernel
  2255  version: 4.0-1
  2256  type: kernel`
  2257  
  2258  	files := [][]string{
  2259  		{"kernel.efi", "I'm a kernel.efi"},
  2260  		{"meta/kernel.yaml", "version: 4.2"},
  2261  	}
  2262  	kernelSnapSideInfo := &snap.SideInfo{RealName: "pc-kernel"}
  2263  	kernelSnapPath, kernelSnapInfo := snaptest.MakeTestSnapInfoWithFiles(c, packageKernel, files, kernelSnapSideInfo)
  2264  
  2265  	// mock the modeenv file
  2266  	m := boot.Modeenv{
  2267  		Mode:           "run",
  2268  		RecoverySystem: "20191127",
  2269  		Base:           "core20_1.snap",
  2270  	}
  2271  	err = m.WriteTo("")
  2272  	c.Assert(err, IsNil)
  2273  	c.Assert(s.o.DeviceManager().ReloadModeenv(), IsNil)
  2274  
  2275  	st := s.o.State()
  2276  	st.Lock()
  2277  	defer st.Unlock()
  2278  
  2279  	si1 := &snap.SideInfo{RealName: "pc-kernel", Revision: snap.R(1)}
  2280  	snapstate.Set(st, "pc-kernel", &snapstate.SnapState{
  2281  		SnapType: "kernel",
  2282  		Active:   true,
  2283  		Sequence: []*snap.SideInfo{si1},
  2284  		Current:  si1.Revision,
  2285  	})
  2286  	snaptest.MockSnapWithFiles(c, packageKernel, si1, [][]string{
  2287  		{"meta/kernel.yaml", ""},
  2288  	})
  2289  	si2 := &snap.SideInfo{RealName: "core20", Revision: snap.R(1)}
  2290  	snapstate.Set(st, "core20", &snapstate.SnapState{
  2291  		SnapType: "base",
  2292  		Active:   true,
  2293  		Sequence: []*snap.SideInfo{si2},
  2294  		Current:  si2.Revision,
  2295  	})
  2296  
  2297  	// setup model assertion
  2298  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  2299  	devicestatetest.SetDevice(st, &auth.DeviceState{
  2300  		Brand:  "my-brand",
  2301  		Model:  "my-model",
  2302  		Serial: "serialserialserial",
  2303  	})
  2304  	err = assertstate.Add(st, model)
  2305  	c.Assert(err, IsNil)
  2306  
  2307  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "pc-kernel"}, kernelSnapPath, "", "", snapstate.Flags{})
  2308  	c.Assert(err, IsNil)
  2309  	chg := st.NewChange("install-snap", "...")
  2310  	chg.AddAll(ts)
  2311  
  2312  	// run, this will trigger a wait for the restart
  2313  	st.Unlock()
  2314  	err = s.o.Settle(settleTimeout)
  2315  	st.Lock()
  2316  	c.Assert(err, IsNil)
  2317  
  2318  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  2319  		"kernel_status": boot.TryStatus,
  2320  	})
  2321  
  2322  	// we are in restarting state and the change is not done yet
  2323  	restarting, _ := st.Restarting()
  2324  	c.Check(restarting, Equals, true)
  2325  	c.Check(chg.Status(), Equals, state.DoingStatus)
  2326  
  2327  	// the kernelSnapInfo we mocked earlier will not have a revision set for the
  2328  	// SideInfo, but since the previous revision was "1", the next revision will
  2329  	// be x1 since it's unasserted, so we can set the Revision on the SideInfo
  2330  	// here to make comparison easier
  2331  	kernelSnapInfo.SideInfo.Revision = snap.R(-1)
  2332  
  2333  	// the current kernel in the bootloader is still the same
  2334  	currentKernel, err := bloader.Kernel()
  2335  	c.Assert(err, IsNil)
  2336  	firstKernel := snap.Info{SideInfo: *si1}
  2337  	c.Assert(currentKernel.Filename(), Equals, firstKernel.Filename())
  2338  
  2339  	// the current try kernel in the bootloader is our new kernel
  2340  	currentTryKernel, err := bloader.TryKernel()
  2341  	c.Assert(err, IsNil)
  2342  	c.Assert(currentTryKernel.Filename(), Equals, kernelSnapInfo.Filename())
  2343  
  2344  	// check that we extracted the kernel snap assets
  2345  	extractedKernels := bloader.ExtractKernelAssetsCalls
  2346  	c.Assert(extractedKernels, HasLen, 1)
  2347  	c.Assert(extractedKernels[0].Filename(), Equals, kernelSnapInfo.Filename())
  2348  
  2349  	// pretend we restarted
  2350  	s.mockSuccessfulReboot(c, bloader, []snap.Type{snap.TypeKernel})
  2351  
  2352  	st.Unlock()
  2353  	err = s.o.Settle(settleTimeout)
  2354  	st.Lock()
  2355  	c.Assert(err, IsNil)
  2356  
  2357  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  2358  
  2359  	// also check that we are active on the second revision
  2360  	var snapst snapstate.SnapState
  2361  	err = snapstate.Get(st, "pc-kernel", &snapst)
  2362  	c.Assert(err, IsNil)
  2363  	c.Check(snapst.Sequence, HasLen, 2)
  2364  	c.Check(snapst.Sequence, DeepEquals, []*snap.SideInfo{si1, &kernelSnapInfo.SideInfo})
  2365  	c.Check(snapst.Active, Equals, true)
  2366  	c.Check(snapst.Current, DeepEquals, snap.R(-1))
  2367  
  2368  	// since we need to do a reboot to go back to the old kernel, we should now
  2369  	// have kernel on the bootloader as the new one, and no try kernel on the
  2370  	// bootloader
  2371  	finalCurrentKernel, err := bloader.Kernel()
  2372  	c.Assert(err, IsNil)
  2373  	c.Assert(finalCurrentKernel.Filename(), Equals, kernelSnapInfo.Filename())
  2374  
  2375  	_, err = bloader.TryKernel()
  2376  	c.Assert(err, Equals, bootloader.ErrNoTryKernelRef)
  2377  
  2378  	// finally check that GetCurrentBoot gives us the new kernel
  2379  	dev, err := devicestate.DeviceCtx(st, nil, nil)
  2380  	c.Assert(err, IsNil)
  2381  	sn, err := boot.GetCurrentBoot(snap.TypeKernel, dev)
  2382  	c.Assert(err, IsNil)
  2383  	c.Assert(sn.Filename(), Equals, kernelSnapInfo.Filename())
  2384  }
  2385  
  2386  func (s *mgrsSuite) TestInstallKernelSnap20UndoUpdatesBootloaderEnv(c *C) {
  2387  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
  2388  	bootloader.Force(bloader)
  2389  	defer bootloader.Force(nil)
  2390  
  2391  	// we have revision 1 installed
  2392  	kernel, err := snap.ParsePlaceInfoFromSnapFileName("pc-kernel_1.snap")
  2393  	c.Assert(err, IsNil)
  2394  	restore := bloader.SetEnabledKernel(kernel)
  2395  	defer restore()
  2396  
  2397  	restore = release.MockOnClassic(false)
  2398  	defer restore()
  2399  
  2400  	uc20ModelDefaults := map[string]interface{}{
  2401  		"architecture": "amd64",
  2402  		"base":         "core20",
  2403  		"store":        "my-brand-store-id",
  2404  		"snaps": []interface{}{
  2405  			map[string]interface{}{
  2406  				"name":            "pc-kernel",
  2407  				"id":              snaptest.AssertedSnapID("pc-kernel"),
  2408  				"type":            "kernel",
  2409  				"default-channel": "20",
  2410  			},
  2411  			map[string]interface{}{
  2412  				"name":            "pc",
  2413  				"id":              snaptest.AssertedSnapID("pc"),
  2414  				"type":            "gadget",
  2415  				"default-channel": "20",
  2416  			}},
  2417  	}
  2418  
  2419  	model := s.brands.Model("my-brand", "my-model", uc20ModelDefaults)
  2420  
  2421  	const packageKernel = `
  2422  name: pc-kernel
  2423  version: 4.0-1
  2424  type: kernel`
  2425  
  2426  	files := [][]string{
  2427  		{"kernel.efi", "I'm a kernel.efi"},
  2428  		{"meta/kernel.yaml", "version: 4.2"},
  2429  	}
  2430  	kernelSnapSideInfo := &snap.SideInfo{RealName: "pc-kernel"}
  2431  	kernelSnapPath, kernelSnapInfo := snaptest.MakeTestSnapInfoWithFiles(c, packageKernel, files, kernelSnapSideInfo)
  2432  
  2433  	// mock the modeenv file
  2434  	m := boot.Modeenv{
  2435  		Mode:           "run",
  2436  		RecoverySystem: "20191127",
  2437  		Base:           "core20_1.snap",
  2438  	}
  2439  	err = m.WriteTo("")
  2440  	c.Assert(err, IsNil)
  2441  	c.Assert(s.o.DeviceManager().ReloadModeenv(), IsNil)
  2442  
  2443  	st := s.o.State()
  2444  	st.Lock()
  2445  	defer st.Unlock()
  2446  
  2447  	si1 := &snap.SideInfo{RealName: "pc-kernel", Revision: snap.R(1)}
  2448  	snapstate.Set(st, "pc-kernel", &snapstate.SnapState{
  2449  		SnapType: "kernel",
  2450  		Active:   true,
  2451  		Sequence: []*snap.SideInfo{si1},
  2452  		Current:  si1.Revision,
  2453  	})
  2454  	snaptest.MockSnapWithFiles(c, packageKernel, si1, [][]string{
  2455  		{"meta/kernel.yaml", ""},
  2456  	})
  2457  	si2 := &snap.SideInfo{RealName: "core20", Revision: snap.R(1)}
  2458  	snapstate.Set(st, "core20", &snapstate.SnapState{
  2459  		SnapType: "base",
  2460  		Active:   true,
  2461  		Sequence: []*snap.SideInfo{si2},
  2462  		Current:  si2.Revision,
  2463  	})
  2464  
  2465  	// setup model assertion
  2466  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  2467  	devicestatetest.SetDevice(st, &auth.DeviceState{
  2468  		Brand:  "my-brand",
  2469  		Model:  "my-model",
  2470  		Serial: "serialserialserial",
  2471  	})
  2472  	err = assertstate.Add(st, model)
  2473  	c.Assert(err, IsNil)
  2474  
  2475  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "pc-kernel"}, kernelSnapPath, "", "", snapstate.Flags{})
  2476  	c.Assert(err, IsNil)
  2477  
  2478  	terr := st.NewTask("error-trigger", "provoking total undo")
  2479  	terr.WaitFor(ts.Tasks()[len(ts.Tasks())-1])
  2480  	ts.AddTask(terr)
  2481  	chg := st.NewChange("install-snap", "...")
  2482  	chg.AddAll(ts)
  2483  
  2484  	// run, this will trigger a wait for the restart
  2485  	st.Unlock()
  2486  	err = s.o.Settle(settleTimeout)
  2487  	st.Lock()
  2488  	c.Assert(err, IsNil)
  2489  
  2490  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  2491  		"kernel_status": boot.TryStatus,
  2492  	})
  2493  
  2494  	// the kernelSnapInfo we mocked earlier will not have a revision set for the
  2495  	// SideInfo, but since the previous revision was "1", the next revision will
  2496  	// be x1 since it's unasserted, so we can set the Revision on the SideInfo
  2497  	// here to make comparison easier
  2498  	kernelSnapInfo.SideInfo.Revision = snap.R(-1)
  2499  
  2500  	// check that we extracted the kernel snap assets
  2501  	extractedKernels := bloader.ExtractKernelAssetsCalls
  2502  	c.Assert(extractedKernels, HasLen, 1)
  2503  	c.Assert(extractedKernels[0].Filename(), Equals, kernelSnapInfo.Filename())
  2504  
  2505  	// the current kernel in the bootloader is still the same
  2506  	currentKernel, err := bloader.Kernel()
  2507  	c.Assert(err, IsNil)
  2508  	firstKernel := snap.Info{SideInfo: *si1}
  2509  	c.Assert(currentKernel.Filename(), Equals, firstKernel.Filename())
  2510  
  2511  	// the current try kernel in the bootloader is our new kernel
  2512  	currentTryKernel, err := bloader.TryKernel()
  2513  	c.Assert(err, IsNil)
  2514  	c.Assert(currentTryKernel.Filename(), Equals, kernelSnapInfo.Filename())
  2515  
  2516  	// we are in restarting state and the change is not done yet
  2517  	restarting, _ := st.Restarting()
  2518  	c.Check(restarting, Equals, true)
  2519  	c.Check(chg.Status(), Equals, state.DoingStatus)
  2520  	// pretend we restarted
  2521  	s.mockSuccessfulReboot(c, bloader, []snap.Type{snap.TypeKernel})
  2522  
  2523  	st.Unlock()
  2524  	err = s.o.Settle(settleTimeout)
  2525  	st.Lock()
  2526  	c.Assert(err, IsNil)
  2527  
  2528  	c.Assert(chg.Status(), Equals, state.ErrorStatus)
  2529  
  2530  	// we should have triggered a reboot to undo the boot changes
  2531  	restarting, _ = st.Restarting()
  2532  	c.Check(restarting, Equals, true)
  2533  
  2534  	// we need to reboot with a "new" try kernel, so kernel_status was set again
  2535  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  2536  		"kernel_status": boot.TryStatus,
  2537  	})
  2538  
  2539  	// we should not have extracted any more kernel assets than before, since
  2540  	// the fallback kernel was already extracted
  2541  	extractedKernels = bloader.ExtractKernelAssetsCalls
  2542  	c.Assert(extractedKernels, HasLen, 1) // same as above check
  2543  
  2544  	// also check that we are active on the first revision again
  2545  	var snapst snapstate.SnapState
  2546  	err = snapstate.Get(st, "pc-kernel", &snapst)
  2547  	c.Assert(err, IsNil)
  2548  	c.Check(snapst.Sequence, HasLen, 1)
  2549  	c.Check(snapst.Sequence, DeepEquals, []*snap.SideInfo{si1})
  2550  	c.Check(snapst.Active, Equals, true)
  2551  	c.Check(snapst.Current, DeepEquals, snap.R(1))
  2552  
  2553  	// since we need to do a reboot to go back to the old kernel, we should now
  2554  	// have kernel on the bootloader as the new one, and the try kernel on the
  2555  	// booloader as the old one
  2556  	finalCurrentKernel, err := bloader.Kernel()
  2557  	c.Assert(err, IsNil)
  2558  	c.Assert(finalCurrentKernel.Filename(), Equals, kernelSnapInfo.Filename())
  2559  
  2560  	finalTryKernel, err := bloader.TryKernel()
  2561  	c.Assert(err, IsNil)
  2562  	c.Assert(finalTryKernel.Filename(), Equals, firstKernel.Filename())
  2563  
  2564  	// TODO:UC20: this test should probably simulate another reboot and confirm
  2565  	// that at the end of everything we have GetCurrentBoot() return the old
  2566  	// kernel we reverted back to again
  2567  }
  2568  
  2569  func (s *mgrsSuite) installLocalTestSnap(c *C, snapYamlContent string) *snap.Info {
  2570  	st := s.o.State()
  2571  
  2572  	snapPath := makeTestSnap(c, snapYamlContent)
  2573  	snapf, err := snapfile.Open(snapPath)
  2574  	c.Assert(err, IsNil)
  2575  	info, err := snap.ReadInfoFromSnapFile(snapf, nil)
  2576  	c.Assert(err, IsNil)
  2577  
  2578  	// store current state
  2579  	snapName := info.InstanceName()
  2580  	var snapst snapstate.SnapState
  2581  	snapstate.Get(st, snapName, &snapst)
  2582  
  2583  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: snapName}, snapPath, "", "", snapstate.Flags{DevMode: true})
  2584  	c.Assert(err, IsNil)
  2585  	chg := st.NewChange("install-snap", "...")
  2586  	chg.AddAll(ts)
  2587  
  2588  	st.Unlock()
  2589  	err = s.o.Settle(settleTimeout)
  2590  	st.Lock()
  2591  	c.Assert(err, IsNil)
  2592  
  2593  	c.Assert(chg.Err(), IsNil)
  2594  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  2595  
  2596  	return info
  2597  }
  2598  
  2599  func (s *mgrsSuite) removeSnap(c *C, name string) {
  2600  	st := s.o.State()
  2601  
  2602  	ts, err := snapstate.Remove(st, name, snap.R(0), &snapstate.RemoveFlags{Purge: true})
  2603  	c.Assert(err, IsNil)
  2604  	chg := st.NewChange("remove-snap", "...")
  2605  	chg.AddAll(ts)
  2606  
  2607  	st.Unlock()
  2608  	err = s.o.Settle(settleTimeout)
  2609  	st.Lock()
  2610  	c.Assert(err, IsNil)
  2611  
  2612  	c.Assert(chg.Err(), IsNil)
  2613  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("remove-snap change failed with: %v", chg.Err()))
  2614  }
  2615  
  2616  func (s *mgrsSuite) TestHappyRevert(c *C) {
  2617  	st := s.o.State()
  2618  	st.Lock()
  2619  	defer st.Unlock()
  2620  
  2621  	x1Yaml := `name: foo
  2622  version: 1.0
  2623  apps:
  2624   x1:
  2625    command: bin/bar
  2626  `
  2627  	x1binary := filepath.Join(dirs.SnapBinariesDir, "foo.x1")
  2628  
  2629  	x2Yaml := `name: foo
  2630  version: 2.0
  2631  apps:
  2632   x2:
  2633    command: bin/bar
  2634  `
  2635  	x2binary := filepath.Join(dirs.SnapBinariesDir, "foo.x2")
  2636  
  2637  	s.installLocalTestSnap(c, x1Yaml)
  2638  	s.installLocalTestSnap(c, x2Yaml)
  2639  
  2640  	// ensure we are on x2
  2641  	_, err := os.Lstat(x2binary)
  2642  	c.Assert(err, IsNil)
  2643  	_, err = os.Lstat(x1binary)
  2644  	c.Assert(err, ErrorMatches, ".*no such file.*")
  2645  
  2646  	// now do the revert
  2647  	ts, err := snapstate.Revert(st, "foo", snapstate.Flags{})
  2648  	c.Assert(err, IsNil)
  2649  	chg := st.NewChange("revert-snap", "...")
  2650  	chg.AddAll(ts)
  2651  
  2652  	st.Unlock()
  2653  	err = s.o.Settle(settleTimeout)
  2654  	st.Lock()
  2655  	c.Assert(err, IsNil)
  2656  
  2657  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("revert-snap change failed with: %v", chg.Err()))
  2658  
  2659  	// ensure that we use x1 now
  2660  	_, err = os.Lstat(x1binary)
  2661  	c.Assert(err, IsNil)
  2662  	_, err = os.Lstat(x2binary)
  2663  	c.Assert(err, ErrorMatches, ".*no such file.*")
  2664  
  2665  	// ensure that x1,x2 is still there, revert just moves the "current"
  2666  	// pointer
  2667  	for _, fn := range []string{"foo_x2.snap", "foo_x1.snap"} {
  2668  		p := filepath.Join(dirs.SnapBlobDir, fn)
  2669  		c.Assert(osutil.FileExists(p), Equals, true)
  2670  	}
  2671  }
  2672  
  2673  func (s *mgrsSuite) TestHappyAlias(c *C) {
  2674  	st := s.o.State()
  2675  	st.Lock()
  2676  	defer st.Unlock()
  2677  
  2678  	fooYaml := `name: foo
  2679  version: 1.0
  2680  apps:
  2681      foo:
  2682          command: bin/foo
  2683  `
  2684  	s.installLocalTestSnap(c, fooYaml)
  2685  
  2686  	ts, err := snapstate.Alias(st, "foo", "foo", "foo_")
  2687  	c.Assert(err, IsNil)
  2688  	chg := st.NewChange("alias", "...")
  2689  	chg.AddAll(ts)
  2690  
  2691  	st.Unlock()
  2692  	err = s.o.Settle(settleTimeout)
  2693  	st.Lock()
  2694  	c.Assert(err, IsNil)
  2695  
  2696  	c.Assert(chg.Err(), IsNil)
  2697  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("alias change failed with: %v", chg.Err()))
  2698  
  2699  	foo_Alias := filepath.Join(dirs.SnapBinariesDir, "foo_")
  2700  	dest, err := os.Readlink(foo_Alias)
  2701  	c.Assert(err, IsNil)
  2702  
  2703  	c.Check(dest, Equals, "foo")
  2704  
  2705  	var snapst snapstate.SnapState
  2706  	err = snapstate.Get(st, "foo", &snapst)
  2707  	c.Assert(err, IsNil)
  2708  	c.Check(snapst.AutoAliasesDisabled, Equals, false)
  2709  	c.Check(snapst.AliasesPending, Equals, false)
  2710  	c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  2711  		"foo_": {Manual: "foo"},
  2712  	})
  2713  
  2714  	s.removeSnap(c, "foo")
  2715  
  2716  	c.Check(osutil.IsSymlink(foo_Alias), Equals, false)
  2717  }
  2718  
  2719  func (s *mgrsSuite) TestHappyUnalias(c *C) {
  2720  	st := s.o.State()
  2721  	st.Lock()
  2722  	defer st.Unlock()
  2723  
  2724  	fooYaml := `name: foo
  2725  version: 1.0
  2726  apps:
  2727      foo:
  2728          command: bin/foo
  2729  `
  2730  	s.installLocalTestSnap(c, fooYaml)
  2731  
  2732  	ts, err := snapstate.Alias(st, "foo", "foo", "foo_")
  2733  	c.Assert(err, IsNil)
  2734  	chg := st.NewChange("alias", "...")
  2735  	chg.AddAll(ts)
  2736  
  2737  	st.Unlock()
  2738  	err = s.o.Settle(settleTimeout)
  2739  	st.Lock()
  2740  	c.Assert(err, IsNil)
  2741  
  2742  	c.Assert(chg.Err(), IsNil)
  2743  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("alias change failed with: %v", chg.Err()))
  2744  
  2745  	foo_Alias := filepath.Join(dirs.SnapBinariesDir, "foo_")
  2746  	dest, err := os.Readlink(foo_Alias)
  2747  	c.Assert(err, IsNil)
  2748  
  2749  	c.Check(dest, Equals, "foo")
  2750  
  2751  	ts, snapName, err := snapstate.RemoveManualAlias(st, "foo_")
  2752  	c.Assert(err, IsNil)
  2753  	c.Check(snapName, Equals, "foo")
  2754  	chg = st.NewChange("unalias", "...")
  2755  	chg.AddAll(ts)
  2756  
  2757  	st.Unlock()
  2758  	err = s.o.Settle(settleTimeout)
  2759  	st.Lock()
  2760  	c.Assert(err, IsNil)
  2761  
  2762  	c.Assert(chg.Err(), IsNil)
  2763  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("unalias change failed with: %v", chg.Err()))
  2764  
  2765  	c.Check(osutil.IsSymlink(foo_Alias), Equals, false)
  2766  
  2767  	var snapst snapstate.SnapState
  2768  	err = snapstate.Get(st, "foo", &snapst)
  2769  	c.Assert(err, IsNil)
  2770  	c.Check(snapst.AutoAliasesDisabled, Equals, false)
  2771  	c.Check(snapst.AliasesPending, Equals, false)
  2772  	c.Check(snapst.Aliases, HasLen, 0)
  2773  }
  2774  
  2775  func (s *mgrsSuite) TestHappyRemoteInstallAutoAliases(c *C) {
  2776  	s.prereqSnapAssertions(c, map[string]interface{}{
  2777  		"snap-name": "foo",
  2778  		"aliases": []interface{}{
  2779  			map[string]interface{}{"name": "app1", "target": "app1"},
  2780  			map[string]interface{}{"name": "app2", "target": "app2"},
  2781  		},
  2782  	})
  2783  
  2784  	snapYamlContent := `name: foo
  2785  version: @VERSION@
  2786  apps:
  2787   app1:
  2788    command: bin/app1
  2789   app2:
  2790    command: bin/app2
  2791  `
  2792  
  2793  	ver := "1.0"
  2794  	revno := "42"
  2795  	snapPath, _ := s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno)
  2796  	s.serveSnap(snapPath, revno)
  2797  
  2798  	mockServer := s.mockStore(c)
  2799  	defer mockServer.Close()
  2800  
  2801  	st := s.o.State()
  2802  	st.Lock()
  2803  	defer st.Unlock()
  2804  
  2805  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  2806  	c.Assert(err, IsNil)
  2807  	chg := st.NewChange("install-snap", "...")
  2808  	chg.AddAll(ts)
  2809  
  2810  	st.Unlock()
  2811  	err = s.o.Settle(settleTimeout)
  2812  	st.Lock()
  2813  	c.Assert(err, IsNil)
  2814  
  2815  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  2816  
  2817  	var snapst snapstate.SnapState
  2818  	err = snapstate.Get(st, "foo", &snapst)
  2819  	c.Assert(err, IsNil)
  2820  	c.Check(snapst.AutoAliasesDisabled, Equals, false)
  2821  	c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  2822  		"app1": {Auto: "app1"},
  2823  		"app2": {Auto: "app2"},
  2824  	})
  2825  
  2826  	// check disk
  2827  	app1Alias := filepath.Join(dirs.SnapBinariesDir, "app1")
  2828  	dest, err := os.Readlink(app1Alias)
  2829  	c.Assert(err, IsNil)
  2830  	c.Check(dest, Equals, "foo.app1")
  2831  
  2832  	app2Alias := filepath.Join(dirs.SnapBinariesDir, "app2")
  2833  	dest, err = os.Readlink(app2Alias)
  2834  	c.Assert(err, IsNil)
  2835  	c.Check(dest, Equals, "foo.app2")
  2836  }
  2837  
  2838  func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateAutoAliases(c *C) {
  2839  	s.prereqSnapAssertions(c, map[string]interface{}{
  2840  		"snap-name": "foo",
  2841  		"aliases": []interface{}{
  2842  			map[string]interface{}{"name": "app1", "target": "app1"},
  2843  		},
  2844  	})
  2845  
  2846  	fooYaml := `name: foo
  2847  version: @VERSION@
  2848  apps:
  2849   app1:
  2850    command: bin/app1
  2851   app2:
  2852    command: bin/app2
  2853  `
  2854  
  2855  	fooPath, _ := s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.0", -1), "10")
  2856  	s.serveSnap(fooPath, "10")
  2857  
  2858  	mockServer := s.mockStore(c)
  2859  	defer mockServer.Close()
  2860  
  2861  	st := s.o.State()
  2862  	st.Lock()
  2863  	defer st.Unlock()
  2864  
  2865  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  2866  	c.Assert(err, IsNil)
  2867  	chg := st.NewChange("install-snap", "...")
  2868  	chg.AddAll(ts)
  2869  
  2870  	st.Unlock()
  2871  	err = s.o.Settle(settleTimeout)
  2872  	st.Lock()
  2873  	c.Assert(err, IsNil)
  2874  
  2875  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  2876  
  2877  	info, err := snapstate.CurrentInfo(st, "foo")
  2878  	c.Assert(err, IsNil)
  2879  	c.Check(info.Revision, Equals, snap.R(10))
  2880  	c.Check(info.Version, Equals, "1.0")
  2881  
  2882  	var snapst snapstate.SnapState
  2883  	err = snapstate.Get(st, "foo", &snapst)
  2884  	c.Assert(err, IsNil)
  2885  	c.Check(snapst.AutoAliasesDisabled, Equals, false)
  2886  	c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  2887  		"app1": {Auto: "app1"},
  2888  	})
  2889  
  2890  	app1Alias := filepath.Join(dirs.SnapBinariesDir, "app1")
  2891  	dest, err := os.Readlink(app1Alias)
  2892  	c.Assert(err, IsNil)
  2893  	c.Check(dest, Equals, "foo.app1")
  2894  
  2895  	s.prereqSnapAssertions(c, map[string]interface{}{
  2896  		"snap-name": "foo",
  2897  		"aliases": []interface{}{
  2898  			map[string]interface{}{"name": "app2", "target": "app2"},
  2899  		},
  2900  		"revision": "1",
  2901  	})
  2902  
  2903  	// new foo version/revision
  2904  	fooPath, _ = s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.5", -1), "15")
  2905  	s.serveSnap(fooPath, "15")
  2906  
  2907  	// refresh all
  2908  	updated, tss, err := snapstate.UpdateMany(context.TODO(), st, nil, 0, nil)
  2909  	c.Assert(err, IsNil)
  2910  	c.Assert(updated, DeepEquals, []string{"foo"})
  2911  	c.Assert(tss, HasLen, 2)
  2912  	verifyLastTasksetIsRerefresh(c, tss)
  2913  	chg = st.NewChange("upgrade-snaps", "...")
  2914  	chg.AddAll(tss[0])
  2915  
  2916  	st.Unlock()
  2917  	err = s.o.Settle(settleTimeout)
  2918  	st.Lock()
  2919  	c.Assert(err, IsNil)
  2920  
  2921  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  2922  
  2923  	info, err = snapstate.CurrentInfo(st, "foo")
  2924  	c.Assert(err, IsNil)
  2925  	c.Check(info.Revision, Equals, snap.R(15))
  2926  	c.Check(info.Version, Equals, "1.5")
  2927  
  2928  	var snapst2 snapstate.SnapState
  2929  	err = snapstate.Get(st, "foo", &snapst2)
  2930  	c.Assert(err, IsNil)
  2931  	c.Check(snapst2.AutoAliasesDisabled, Equals, false)
  2932  	c.Check(snapst2.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  2933  		"app2": {Auto: "app2"},
  2934  	})
  2935  
  2936  	c.Check(osutil.IsSymlink(app1Alias), Equals, false)
  2937  
  2938  	app2Alias := filepath.Join(dirs.SnapBinariesDir, "app2")
  2939  	dest, err = os.Readlink(app2Alias)
  2940  	c.Assert(err, IsNil)
  2941  	c.Check(dest, Equals, "foo.app2")
  2942  }
  2943  
  2944  func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateAutoAliasesUnaliased(c *C) {
  2945  	s.prereqSnapAssertions(c, map[string]interface{}{
  2946  		"snap-name": "foo",
  2947  		"aliases": []interface{}{
  2948  			map[string]interface{}{"name": "app1", "target": "app1"},
  2949  		},
  2950  	})
  2951  
  2952  	fooYaml := `name: foo
  2953  version: @VERSION@
  2954  apps:
  2955   app1:
  2956    command: bin/app1
  2957   app2:
  2958    command: bin/app2
  2959  `
  2960  
  2961  	fooPath, _ := s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.0", -1), "10")
  2962  	s.serveSnap(fooPath, "10")
  2963  
  2964  	mockServer := s.mockStore(c)
  2965  	defer mockServer.Close()
  2966  
  2967  	st := s.o.State()
  2968  	st.Lock()
  2969  	defer st.Unlock()
  2970  
  2971  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{Unaliased: true})
  2972  	c.Assert(err, IsNil)
  2973  	chg := st.NewChange("install-snap", "...")
  2974  	chg.AddAll(ts)
  2975  
  2976  	st.Unlock()
  2977  	err = s.o.Settle(settleTimeout)
  2978  	st.Lock()
  2979  	c.Assert(err, IsNil)
  2980  
  2981  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  2982  
  2983  	info, err := snapstate.CurrentInfo(st, "foo")
  2984  	c.Assert(err, IsNil)
  2985  	c.Check(info.Revision, Equals, snap.R(10))
  2986  	c.Check(info.Version, Equals, "1.0")
  2987  
  2988  	var snapst snapstate.SnapState
  2989  	err = snapstate.Get(st, "foo", &snapst)
  2990  	c.Assert(err, IsNil)
  2991  	c.Check(snapst.AutoAliasesDisabled, Equals, true)
  2992  	c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  2993  		"app1": {Auto: "app1"},
  2994  	})
  2995  
  2996  	app1Alias := filepath.Join(dirs.SnapBinariesDir, "app1")
  2997  	c.Check(osutil.IsSymlink(app1Alias), Equals, false)
  2998  
  2999  	s.prereqSnapAssertions(c, map[string]interface{}{
  3000  		"snap-name": "foo",
  3001  		"aliases": []interface{}{
  3002  			map[string]interface{}{"name": "app2", "target": "app2"},
  3003  		},
  3004  		"revision": "1",
  3005  	})
  3006  
  3007  	// new foo version/revision
  3008  	fooPath, _ = s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.5", -1), "15")
  3009  	s.serveSnap(fooPath, "15")
  3010  
  3011  	// refresh foo
  3012  	ts, err = snapstate.Update(st, "foo", nil, 0, snapstate.Flags{})
  3013  	c.Assert(err, IsNil)
  3014  	chg = st.NewChange("upgrade-snap", "...")
  3015  	chg.AddAll(ts)
  3016  
  3017  	st.Unlock()
  3018  	err = s.o.Settle(settleTimeout)
  3019  	st.Lock()
  3020  	c.Assert(err, IsNil)
  3021  
  3022  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  3023  
  3024  	info, err = snapstate.CurrentInfo(st, "foo")
  3025  	c.Assert(err, IsNil)
  3026  	c.Check(info.Revision, Equals, snap.R(15))
  3027  	c.Check(info.Version, Equals, "1.5")
  3028  
  3029  	var snapst2 snapstate.SnapState
  3030  	err = snapstate.Get(st, "foo", &snapst2)
  3031  	c.Assert(err, IsNil)
  3032  	c.Check(snapst2.AutoAliasesDisabled, Equals, true)
  3033  	c.Check(snapst2.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  3034  		"app2": {Auto: "app2"},
  3035  	})
  3036  
  3037  	c.Check(osutil.IsSymlink(app1Alias), Equals, false)
  3038  
  3039  	app2Alias := filepath.Join(dirs.SnapBinariesDir, "app2")
  3040  	c.Check(osutil.IsSymlink(app2Alias), Equals, false)
  3041  }
  3042  
  3043  func (s *mgrsSuite) TestHappyOrthogonalRefreshAutoAliases(c *C) {
  3044  	s.prereqSnapAssertions(c, map[string]interface{}{
  3045  		"snap-name": "foo",
  3046  		"aliases": []interface{}{
  3047  			map[string]interface{}{"name": "app1", "target": "app1"},
  3048  		},
  3049  	}, map[string]interface{}{
  3050  		"snap-name": "bar",
  3051  	})
  3052  
  3053  	fooYaml := `name: foo
  3054  version: @VERSION@
  3055  apps:
  3056   app1:
  3057    command: bin/app1
  3058   app2:
  3059    command: bin/app2
  3060  `
  3061  
  3062  	barYaml := `name: bar
  3063  version: @VERSION@
  3064  apps:
  3065   app1:
  3066    command: bin/app1
  3067   app3:
  3068    command: bin/app3
  3069  `
  3070  
  3071  	fooPath, _ := s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.0", -1), "10")
  3072  	s.serveSnap(fooPath, "10")
  3073  
  3074  	barPath, _ := s.makeStoreTestSnap(c, strings.Replace(barYaml, "@VERSION@", "2.0", -1), "20")
  3075  	s.serveSnap(barPath, "20")
  3076  
  3077  	mockServer := s.mockStore(c)
  3078  	defer mockServer.Close()
  3079  
  3080  	st := s.o.State()
  3081  	st.Lock()
  3082  	defer st.Unlock()
  3083  
  3084  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  3085  	c.Assert(err, IsNil)
  3086  	chg := st.NewChange("install-snap", "...")
  3087  	chg.AddAll(ts)
  3088  
  3089  	st.Unlock()
  3090  	err = s.o.Settle(settleTimeout)
  3091  	st.Lock()
  3092  	c.Assert(err, IsNil)
  3093  
  3094  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  3095  
  3096  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  3097  
  3098  	ts, err = snapstate.Install(context.TODO(), st, "bar", nil, 0, snapstate.Flags{})
  3099  	c.Assert(err, IsNil)
  3100  	chg = st.NewChange("install-snap", "...")
  3101  	chg.AddAll(ts)
  3102  
  3103  	st.Unlock()
  3104  	err = s.o.Settle(settleTimeout)
  3105  	st.Lock()
  3106  	c.Assert(err, IsNil)
  3107  
  3108  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  3109  
  3110  	info, err := snapstate.CurrentInfo(st, "foo")
  3111  	c.Assert(err, IsNil)
  3112  	c.Check(info.Revision, Equals, snap.R(10))
  3113  	c.Check(info.Version, Equals, "1.0")
  3114  
  3115  	info, err = snapstate.CurrentInfo(st, "bar")
  3116  	c.Assert(err, IsNil)
  3117  	c.Check(info.Revision, Equals, snap.R(20))
  3118  	c.Check(info.Version, Equals, "2.0")
  3119  
  3120  	var snapst snapstate.SnapState
  3121  	err = snapstate.Get(st, "foo", &snapst)
  3122  	c.Assert(err, IsNil)
  3123  	c.Check(snapst.AutoAliasesDisabled, Equals, false)
  3124  	c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  3125  		"app1": {Auto: "app1"},
  3126  	})
  3127  
  3128  	// foo gets a new version/revision and a change of automatic aliases
  3129  	// bar gets only the latter
  3130  	// app1 is transferred from foo to bar
  3131  	// UpdateMany after a snap-declaration refresh handles all of this
  3132  	s.prereqSnapAssertions(c, map[string]interface{}{
  3133  		"snap-name": "foo",
  3134  		"aliases": []interface{}{
  3135  			map[string]interface{}{"name": "app2", "target": "app2"},
  3136  		},
  3137  		"revision": "1",
  3138  	}, map[string]interface{}{
  3139  		"snap-name": "bar",
  3140  		"aliases": []interface{}{
  3141  			map[string]interface{}{"name": "app1", "target": "app1"},
  3142  			map[string]interface{}{"name": "app3", "target": "app3"},
  3143  		},
  3144  		"revision": "1",
  3145  	})
  3146  
  3147  	// new foo version/revision
  3148  	fooPath, _ = s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.5", -1), "15")
  3149  	s.serveSnap(fooPath, "15")
  3150  
  3151  	// refresh all
  3152  	err = assertstate.RefreshSnapDeclarations(st, 0, nil)
  3153  	c.Assert(err, IsNil)
  3154  
  3155  	updated, tss, err := snapstate.UpdateMany(context.TODO(), st, nil, 0, nil)
  3156  	c.Assert(err, IsNil)
  3157  	sort.Strings(updated)
  3158  	c.Assert(updated, DeepEquals, []string{"bar", "foo"})
  3159  	c.Assert(tss, HasLen, 4)
  3160  	verifyLastTasksetIsRerefresh(c, tss)
  3161  	chg = st.NewChange("upgrade-snaps", "...")
  3162  	chg.AddAll(tss[0])
  3163  	chg.AddAll(tss[1])
  3164  	chg.AddAll(tss[2])
  3165  
  3166  	st.Unlock()
  3167  	err = s.o.Settle(settleTimeout)
  3168  	st.Lock()
  3169  	c.Assert(err, IsNil)
  3170  
  3171  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  3172  
  3173  	info, err = snapstate.CurrentInfo(st, "foo")
  3174  	c.Assert(err, IsNil)
  3175  	c.Check(info.Revision, Equals, snap.R(15))
  3176  	c.Check(info.Version, Equals, "1.5")
  3177  
  3178  	var snapst2 snapstate.SnapState
  3179  	err = snapstate.Get(st, "foo", &snapst2)
  3180  	c.Assert(err, IsNil)
  3181  	c.Check(snapst2.AutoAliasesDisabled, Equals, false)
  3182  	c.Check(snapst2.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  3183  		"app2": {Auto: "app2"},
  3184  	})
  3185  	var snapst3 snapstate.SnapState
  3186  	err = snapstate.Get(st, "bar", &snapst3)
  3187  	c.Assert(err, IsNil)
  3188  	c.Check(snapst3.AutoAliasesDisabled, Equals, false)
  3189  	c.Check(snapst3.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  3190  		"app1": {Auto: "app1"},
  3191  		"app3": {Auto: "app3"},
  3192  	})
  3193  
  3194  	app2Alias := filepath.Join(dirs.SnapBinariesDir, "app2")
  3195  	dest, err := os.Readlink(app2Alias)
  3196  	c.Assert(err, IsNil)
  3197  	c.Check(dest, Equals, "foo.app2")
  3198  
  3199  	app1Alias := filepath.Join(dirs.SnapBinariesDir, "app1")
  3200  	dest, err = os.Readlink(app1Alias)
  3201  	c.Assert(err, IsNil)
  3202  	c.Check(dest, Equals, "bar.app1")
  3203  	app3Alias := filepath.Join(dirs.SnapBinariesDir, "app3")
  3204  	dest, err = os.Readlink(app3Alias)
  3205  	c.Assert(err, IsNil)
  3206  	c.Check(dest, Equals, "bar.app3")
  3207  }
  3208  
  3209  func (s *mgrsSuite) TestHappyStopWhileDownloadingHeader(c *C) {
  3210  	s.prereqSnapAssertions(c)
  3211  
  3212  	snapYamlContent := `name: foo
  3213  version: 1.0
  3214  `
  3215  	snapPath, _ := s.makeStoreTestSnap(c, snapYamlContent, "42")
  3216  	s.serveSnap(snapPath, "42")
  3217  
  3218  	stopped := make(chan struct{})
  3219  	s.hijackServeSnap = func(_ http.ResponseWriter) {
  3220  		s.o.Stop()
  3221  		close(stopped)
  3222  	}
  3223  
  3224  	mockServer := s.mockStore(c)
  3225  	defer mockServer.Close()
  3226  
  3227  	st := s.o.State()
  3228  	st.Lock()
  3229  	defer st.Unlock()
  3230  
  3231  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  3232  	c.Assert(err, IsNil)
  3233  	chg := st.NewChange("install-snap", "...")
  3234  	chg.AddAll(ts)
  3235  
  3236  	st.Unlock()
  3237  	s.o.Loop()
  3238  
  3239  	<-stopped
  3240  
  3241  	st.Lock()
  3242  	c.Assert(chg.Status(), Equals, state.DoingStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  3243  }
  3244  
  3245  func (s *mgrsSuite) TestHappyStopWhileDownloadingBody(c *C) {
  3246  	s.prereqSnapAssertions(c)
  3247  
  3248  	snapYamlContent := `name: foo
  3249  version: 1.0
  3250  `
  3251  	snapPath, _ := s.makeStoreTestSnap(c, snapYamlContent, "42")
  3252  	s.serveSnap(snapPath, "42")
  3253  
  3254  	stopped := make(chan struct{})
  3255  	s.hijackServeSnap = func(w http.ResponseWriter) {
  3256  		w.WriteHeader(200)
  3257  		// best effort to reach the body reading part in the client
  3258  		w.Write(make([]byte, 10000))
  3259  		time.Sleep(100 * time.Millisecond)
  3260  		w.Write(make([]byte, 10000))
  3261  		s.o.Stop()
  3262  		close(stopped)
  3263  	}
  3264  
  3265  	mockServer := s.mockStore(c)
  3266  	defer mockServer.Close()
  3267  
  3268  	st := s.o.State()
  3269  	st.Lock()
  3270  	defer st.Unlock()
  3271  
  3272  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  3273  	c.Assert(err, IsNil)
  3274  	chg := st.NewChange("install-snap", "...")
  3275  	chg.AddAll(ts)
  3276  
  3277  	st.Unlock()
  3278  	s.o.Loop()
  3279  
  3280  	<-stopped
  3281  
  3282  	st.Lock()
  3283  	c.Assert(chg.Status(), Equals, state.DoingStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  3284  }
  3285  
  3286  func (s *mgrsSuite) TestInstallWithAssumesIsRefusedEarly(c *C) {
  3287  	s.prereqSnapAssertions(c)
  3288  
  3289  	revno := "1"
  3290  	snapYamlContent := `name: some-snap
  3291  version: 1.0
  3292  assumes: [something-that-is-not-provided]
  3293  `
  3294  	snapPath, _ := s.makeStoreTestSnap(c, snapYamlContent, revno)
  3295  	s.serveSnap(snapPath, revno)
  3296  
  3297  	mockServer := s.mockStore(c)
  3298  	defer mockServer.Close()
  3299  
  3300  	st := s.o.State()
  3301  	st.Lock()
  3302  	defer st.Unlock()
  3303  
  3304  	_, err := snapstate.Install(context.TODO(), st, "some-snap", nil, 0, snapstate.Flags{})
  3305  	c.Assert(err, ErrorMatches, `snap "some-snap" assumes unsupported features: something-that-is-not-provided \(try to refresh snapd\)`)
  3306  }
  3307  
  3308  func (s *mgrsSuite) TestUpdateWithAssumesIsRefusedEarly(c *C) {
  3309  	s.prereqSnapAssertions(c)
  3310  
  3311  	revno := "40"
  3312  	snapYamlContent := `name: some-snap
  3313  version: 1.0
  3314  assumes: [something-that-is-not-provided]
  3315  `
  3316  	snapPath, _ := s.makeStoreTestSnap(c, snapYamlContent, revno)
  3317  	s.serveSnap(snapPath, revno)
  3318  
  3319  	mockServer := s.mockStore(c)
  3320  	defer mockServer.Close()
  3321  
  3322  	st := s.o.State()
  3323  	st.Lock()
  3324  	defer st.Unlock()
  3325  
  3326  	si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)}
  3327  	snapstate.Set(st, "some-snap", &snapstate.SnapState{
  3328  		Active:   true,
  3329  		Sequence: []*snap.SideInfo{si},
  3330  		Current:  snap.R(1),
  3331  		SnapType: "app",
  3332  	})
  3333  
  3334  	_, err := snapstate.Update(st, "some-snap", nil, 0, snapstate.Flags{})
  3335  	c.Assert(err, ErrorMatches, `snap "some-snap" assumes unsupported features: something-that-is-not-provided \(try to refresh snapd\)`)
  3336  }
  3337  
  3338  func (s *mgrsSuite) TestUpdateManyWithAssumesIsRefusedEarly(c *C) {
  3339  	s.prereqSnapAssertions(c)
  3340  
  3341  	revno := "40"
  3342  	snapYamlContent := `name: some-snap
  3343  version: 1.0
  3344  assumes: [something-that-is-not-provided]
  3345  `
  3346  	snapPath, _ := s.makeStoreTestSnap(c, snapYamlContent, revno)
  3347  	s.serveSnap(snapPath, revno)
  3348  
  3349  	mockServer := s.mockStore(c)
  3350  	defer mockServer.Close()
  3351  
  3352  	st := s.o.State()
  3353  	st.Lock()
  3354  	defer st.Unlock()
  3355  
  3356  	si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)}
  3357  	snapstate.Set(st, "some-snap", &snapstate.SnapState{
  3358  		Active:   true,
  3359  		Sequence: []*snap.SideInfo{si},
  3360  		Current:  snap.R(1),
  3361  		SnapType: "app",
  3362  	})
  3363  
  3364  	// updateMany will just skip snaps with assumes but not error
  3365  	affected, tss, err := snapstate.UpdateMany(context.TODO(), st, nil, 0, nil)
  3366  	c.Assert(err, IsNil)
  3367  	c.Check(affected, HasLen, 0)
  3368  	// the skipping is logged though
  3369  	c.Check(s.logbuf.String(), testutil.Contains, `cannot update "some-snap": snap "some-snap" assumes unsupported features: something-that-is-not-provided (try`)
  3370  	// XXX: should we really check for re-refreshes if there is nothing
  3371  	// to update?
  3372  	c.Check(tss, HasLen, 1)
  3373  	c.Check(tss[0].Tasks(), HasLen, 1)
  3374  	c.Check(tss[0].Tasks()[0].Kind(), Equals, "check-rerefresh")
  3375  }
  3376  
  3377  type storeCtxSetupSuite struct {
  3378  	o  *overlord.Overlord
  3379  	sc store.DeviceAndAuthContext
  3380  
  3381  	storeSigning   *assertstest.StoreStack
  3382  	restoreTrusted func()
  3383  
  3384  	brands *assertstest.SigningAccounts
  3385  
  3386  	model  *asserts.Model
  3387  	serial *asserts.Serial
  3388  
  3389  	restoreBackends func()
  3390  }
  3391  
  3392  func (s *storeCtxSetupSuite) SetUpTest(c *C) {
  3393  	tempdir := c.MkDir()
  3394  	dirs.SetRootDir(tempdir)
  3395  	err := os.MkdirAll(filepath.Dir(dirs.SnapStateFile), 0755)
  3396  	c.Assert(err, IsNil)
  3397  
  3398  	captureStoreCtx := func(_ *store.Config, dac store.DeviceAndAuthContext) *store.Store {
  3399  		s.sc = dac
  3400  		return store.New(nil, nil)
  3401  	}
  3402  	r := overlord.MockStoreNew(captureStoreCtx)
  3403  	defer r()
  3404  
  3405  	s.storeSigning = assertstest.NewStoreStack("can0nical", nil)
  3406  	s.restoreTrusted = sysdb.InjectTrusted(s.storeSigning.Trusted)
  3407  
  3408  	s.brands = assertstest.NewSigningAccounts(s.storeSigning)
  3409  	s.brands.Register("my-brand", brandPrivKey, map[string]interface{}{
  3410  		"verification": "verified",
  3411  	})
  3412  	assertstest.AddMany(s.storeSigning, s.brands.AccountsAndKeys("my-brand")...)
  3413  
  3414  	s.model = s.brands.Model("my-brand", "my-model", modelDefaults)
  3415  
  3416  	encDevKey, err := asserts.EncodePublicKey(deviceKey.PublicKey())
  3417  	c.Assert(err, IsNil)
  3418  	serial, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, map[string]interface{}{
  3419  		"authority-id":        "my-brand",
  3420  		"brand-id":            "my-brand",
  3421  		"model":               "my-model",
  3422  		"serial":              "7878",
  3423  		"device-key":          string(encDevKey),
  3424  		"device-key-sha3-384": deviceKey.PublicKey().ID(),
  3425  		"timestamp":           time.Now().Format(time.RFC3339),
  3426  	}, nil, "")
  3427  	c.Assert(err, IsNil)
  3428  	s.serial = serial.(*asserts.Serial)
  3429  
  3430  	s.restoreBackends = ifacestate.MockSecurityBackends(nil)
  3431  
  3432  	o, err := overlord.New(nil)
  3433  	c.Assert(err, IsNil)
  3434  	o.InterfaceManager().DisableUDevMonitor()
  3435  	s.o = o
  3436  
  3437  	st := o.State()
  3438  	st.Lock()
  3439  	defer st.Unlock()
  3440  
  3441  	assertstatetest.AddMany(st, s.storeSigning.StoreAccountKey(""))
  3442  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  3443  }
  3444  
  3445  func (s *storeCtxSetupSuite) TearDownTest(c *C) {
  3446  	dirs.SetRootDir("")
  3447  	s.restoreBackends()
  3448  	s.restoreTrusted()
  3449  }
  3450  
  3451  func (s *storeCtxSetupSuite) TestStoreID(c *C) {
  3452  	st := s.o.State()
  3453  	st.Lock()
  3454  	defer st.Unlock()
  3455  
  3456  	st.Unlock()
  3457  	storeID, err := s.sc.StoreID("fallback")
  3458  	st.Lock()
  3459  	c.Assert(err, IsNil)
  3460  	c.Check(storeID, Equals, "fallback")
  3461  
  3462  	// setup model in system statey
  3463  	devicestatetest.SetDevice(st, &auth.DeviceState{
  3464  		Brand:  s.serial.BrandID(),
  3465  		Model:  s.serial.Model(),
  3466  		Serial: s.serial.Serial(),
  3467  	})
  3468  	err = assertstate.Add(st, s.model)
  3469  	c.Assert(err, IsNil)
  3470  
  3471  	st.Unlock()
  3472  	storeID, err = s.sc.StoreID("fallback")
  3473  	st.Lock()
  3474  	c.Assert(err, IsNil)
  3475  	c.Check(storeID, Equals, "my-brand-store-id")
  3476  }
  3477  
  3478  func (s *storeCtxSetupSuite) TestDeviceSessionRequestParams(c *C) {
  3479  	st := s.o.State()
  3480  	st.Lock()
  3481  	defer st.Unlock()
  3482  
  3483  	st.Unlock()
  3484  	_, err := s.sc.DeviceSessionRequestParams("NONCE")
  3485  	st.Lock()
  3486  	c.Check(err, Equals, store.ErrNoSerial)
  3487  
  3488  	// setup model, serial and key in system state
  3489  	err = assertstate.Add(st, s.model)
  3490  	c.Assert(err, IsNil)
  3491  	err = assertstate.Add(st, s.serial)
  3492  	c.Assert(err, IsNil)
  3493  	kpMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir)
  3494  	c.Assert(err, IsNil)
  3495  	err = kpMgr.Put(deviceKey)
  3496  	c.Assert(err, IsNil)
  3497  	devicestatetest.SetDevice(st, &auth.DeviceState{
  3498  		Brand:  s.serial.BrandID(),
  3499  		Model:  s.serial.Model(),
  3500  		Serial: s.serial.Serial(),
  3501  		KeyID:  deviceKey.PublicKey().ID(),
  3502  	})
  3503  
  3504  	st.Unlock()
  3505  	params, err := s.sc.DeviceSessionRequestParams("NONCE")
  3506  	st.Lock()
  3507  	c.Assert(err, IsNil)
  3508  	c.Check(strings.HasPrefix(params.EncodedRequest(), "type: device-session-request\n"), Equals, true)
  3509  	c.Check(params.EncodedSerial(), DeepEquals, string(asserts.Encode(s.serial)))
  3510  	c.Check(params.EncodedModel(), DeepEquals, string(asserts.Encode(s.model)))
  3511  
  3512  }
  3513  
  3514  func (s *storeCtxSetupSuite) TestProxyStoreParams(c *C) {
  3515  	st := s.o.State()
  3516  	st.Lock()
  3517  	defer st.Unlock()
  3518  
  3519  	defURL, err := url.Parse("http://store")
  3520  	c.Assert(err, IsNil)
  3521  
  3522  	st.Unlock()
  3523  	proxyStoreID, proxyStoreURL, err := s.sc.ProxyStoreParams(defURL)
  3524  	st.Lock()
  3525  	c.Assert(err, IsNil)
  3526  	c.Check(proxyStoreID, Equals, "")
  3527  	c.Check(proxyStoreURL, Equals, defURL)
  3528  
  3529  	// setup proxy store reference and assertion
  3530  	operatorAcct := assertstest.NewAccount(s.storeSigning, "foo-operator", nil, "")
  3531  	err = assertstate.Add(st, operatorAcct)
  3532  	c.Assert(err, IsNil)
  3533  	stoAs, err := s.storeSigning.Sign(asserts.StoreType, map[string]interface{}{
  3534  		"store":       "foo",
  3535  		"operator-id": operatorAcct.AccountID(),
  3536  		"url":         "http://foo.internal",
  3537  		"timestamp":   time.Now().Format(time.RFC3339),
  3538  	}, nil, "")
  3539  	c.Assert(err, IsNil)
  3540  	err = assertstate.Add(st, stoAs)
  3541  	c.Assert(err, IsNil)
  3542  	tr := config.NewTransaction(st)
  3543  	err = tr.Set("core", "proxy.store", "foo")
  3544  	c.Assert(err, IsNil)
  3545  	tr.Commit()
  3546  
  3547  	fooURL, err := url.Parse("http://foo.internal")
  3548  	c.Assert(err, IsNil)
  3549  
  3550  	st.Unlock()
  3551  	proxyStoreID, proxyStoreURL, err = s.sc.ProxyStoreParams(defURL)
  3552  	st.Lock()
  3553  	c.Assert(err, IsNil)
  3554  	c.Check(proxyStoreID, Equals, "foo")
  3555  	c.Check(proxyStoreURL, DeepEquals, fooURL)
  3556  }
  3557  
  3558  const snapYamlContent1 = `name: snap1
  3559  plugs:
  3560   shared-data-plug:
  3561    interface: content
  3562    target: import
  3563    content: mylib
  3564  apps:
  3565   bar:
  3566    command: bin/bar
  3567  `
  3568  const snapYamlContent2 = `name: snap2
  3569  slots:
  3570   shared-data-slot:
  3571    interface: content
  3572    content: mylib
  3573    read:
  3574     - /
  3575  apps:
  3576   bar:
  3577    command: bin/bar
  3578  `
  3579  
  3580  func (s *mgrsSuite) testTwoInstalls(c *C, snapName1, snapYaml1, snapName2, snapYaml2 string) {
  3581  	snapPath1 := makeTestSnap(c, snapYaml1+"version: 1.0")
  3582  	snapPath2 := makeTestSnap(c, snapYaml2+"version: 1.0")
  3583  
  3584  	st := s.o.State()
  3585  	st.Lock()
  3586  	defer st.Unlock()
  3587  
  3588  	ts1, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: snapName1, SnapID: fakeSnapID(snapName1), Revision: snap.R(3)}, snapPath1, "", "", snapstate.Flags{DevMode: true})
  3589  	c.Assert(err, IsNil)
  3590  	chg := st.NewChange("install-snap", "...")
  3591  	chg.AddAll(ts1)
  3592  
  3593  	ts2, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: snapName2, SnapID: fakeSnapID(snapName2), Revision: snap.R(3)}, snapPath2, "", "", snapstate.Flags{DevMode: true})
  3594  	c.Assert(err, IsNil)
  3595  
  3596  	ts2.WaitAll(ts1)
  3597  	chg.AddAll(ts2)
  3598  
  3599  	st.Unlock()
  3600  	err = s.o.Settle(settleTimeout)
  3601  	st.Lock()
  3602  	c.Assert(err, IsNil)
  3603  
  3604  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  3605  
  3606  	tasks := chg.Tasks()
  3607  	connectTask := tasks[len(tasks)-2]
  3608  	c.Assert(connectTask.Kind(), Equals, "connect")
  3609  
  3610  	setupProfilesTask := tasks[len(tasks)-1]
  3611  	c.Assert(setupProfilesTask.Kind(), Equals, "setup-profiles")
  3612  
  3613  	// verify connect task data
  3614  	var plugRef interfaces.PlugRef
  3615  	var slotRef interfaces.SlotRef
  3616  	c.Assert(connectTask.Get("plug", &plugRef), IsNil)
  3617  	c.Assert(connectTask.Get("slot", &slotRef), IsNil)
  3618  	c.Assert(plugRef.Snap, Equals, "snap1")
  3619  	c.Assert(plugRef.Name, Equals, "shared-data-plug")
  3620  	c.Assert(slotRef.Snap, Equals, "snap2")
  3621  	c.Assert(slotRef.Name, Equals, "shared-data-slot")
  3622  
  3623  	// verify that connection was made
  3624  	var conns map[string]interface{}
  3625  	c.Assert(st.Get("conns", &conns), IsNil)
  3626  	c.Assert(conns, HasLen, 1)
  3627  
  3628  	repo := s.o.InterfaceManager().Repository()
  3629  	cn, err := repo.Connected("snap1", "shared-data-plug")
  3630  	c.Assert(err, IsNil)
  3631  	c.Assert(cn, HasLen, 1)
  3632  	c.Assert(cn, DeepEquals, []*interfaces.ConnRef{{
  3633  		PlugRef: interfaces.PlugRef{Snap: "snap1", Name: "shared-data-plug"},
  3634  		SlotRef: interfaces.SlotRef{Snap: "snap2", Name: "shared-data-slot"},
  3635  	}})
  3636  }
  3637  
  3638  func (s *mgrsSuite) TestTwoInstallsWithAutoconnectPlugSnapFirst(c *C) {
  3639  	s.testTwoInstalls(c, "snap1", snapYamlContent1, "snap2", snapYamlContent2)
  3640  }
  3641  
  3642  func (s *mgrsSuite) TestTwoInstallsWithAutoconnectSlotSnapFirst(c *C) {
  3643  	s.testTwoInstalls(c, "snap2", snapYamlContent2, "snap1", snapYamlContent1)
  3644  }
  3645  
  3646  func (s *mgrsSuite) TestRemoveAndInstallWithAutoconnectHappy(c *C) {
  3647  	st := s.o.State()
  3648  	st.Lock()
  3649  	defer st.Unlock()
  3650  
  3651  	_ = s.installLocalTestSnap(c, snapYamlContent1+"version: 1.0")
  3652  
  3653  	ts, err := snapstate.Remove(st, "snap1", snap.R(0), &snapstate.RemoveFlags{Purge: true})
  3654  	c.Assert(err, IsNil)
  3655  	chg := st.NewChange("remove-snap", "...")
  3656  	chg.AddAll(ts)
  3657  
  3658  	snapPath := makeTestSnap(c, snapYamlContent2+"version: 1.0")
  3659  	chg2 := st.NewChange("install-snap", "...")
  3660  	ts2, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "snap2", SnapID: fakeSnapID("snap2"), Revision: snap.R(3)}, snapPath, "", "", snapstate.Flags{DevMode: true})
  3661  	chg2.AddAll(ts2)
  3662  	c.Assert(err, IsNil)
  3663  
  3664  	st.Unlock()
  3665  	err = s.o.Settle(settleTimeout)
  3666  	st.Lock()
  3667  	c.Assert(err, IsNil)
  3668  
  3669  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("remove-snap change failed with: %v", chg.Err()))
  3670  	c.Assert(chg2.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  3671  }
  3672  
  3673  const otherSnapYaml = `name: other-snap
  3674  version: 1.0
  3675  apps:
  3676     baz:
  3677          command: bin/bar
  3678          plugs: [media-hub]
  3679  `
  3680  
  3681  func (s *mgrsSuite) TestUpdateManyWithAutoconnect(c *C) {
  3682  	const someSnapYaml = `name: some-snap
  3683  version: 1.0
  3684  apps:
  3685     foo:
  3686          command: bin/bar
  3687          plugs: [network,home]
  3688          slots: [media-hub]
  3689  `
  3690  
  3691  	const coreSnapYaml = `name: core
  3692  type: os
  3693  version: @VERSION@`
  3694  
  3695  	snapPath, _ := s.makeStoreTestSnap(c, someSnapYaml, "40")
  3696  	s.serveSnap(snapPath, "40")
  3697  
  3698  	snapPath, _ = s.makeStoreTestSnap(c, otherSnapYaml, "50")
  3699  	s.serveSnap(snapPath, "50")
  3700  
  3701  	corePath, _ := s.makeStoreTestSnap(c, strings.Replace(coreSnapYaml, "@VERSION@", "30", -1), "30")
  3702  	s.serveSnap(corePath, "30")
  3703  
  3704  	mockServer := s.mockStore(c)
  3705  	defer mockServer.Close()
  3706  
  3707  	st := s.o.State()
  3708  	st.Lock()
  3709  	defer st.Unlock()
  3710  
  3711  	st.Set("conns", map[string]interface{}{})
  3712  
  3713  	si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)}
  3714  	snapInfo := snaptest.MockSnap(c, someSnapYaml, si)
  3715  	c.Assert(snapInfo.Plugs, HasLen, 2)
  3716  
  3717  	oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)}
  3718  	otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi)
  3719  	c.Assert(otherInfo.Plugs, HasLen, 1)
  3720  
  3721  	csi := &snap.SideInfo{RealName: "core", SnapID: fakeSnapID("core"), Revision: snap.R(1)}
  3722  	coreInfo := snaptest.MockSnap(c, strings.Replace(coreSnapYaml, "@VERSION@", "1", -1), csi)
  3723  
  3724  	// add implicit slots
  3725  	coreInfo.Slots["network"] = &snap.SlotInfo{
  3726  		Name:      "network",
  3727  		Snap:      coreInfo,
  3728  		Interface: "network",
  3729  	}
  3730  	coreInfo.Slots["home"] = &snap.SlotInfo{
  3731  		Name:      "home",
  3732  		Snap:      coreInfo,
  3733  		Interface: "home",
  3734  	}
  3735  
  3736  	snapstate.Set(st, "some-snap", &snapstate.SnapState{
  3737  		Active:   true,
  3738  		Sequence: []*snap.SideInfo{si},
  3739  		Current:  snap.R(1),
  3740  		SnapType: "app",
  3741  	})
  3742  	snapstate.Set(st, "other-snap", &snapstate.SnapState{
  3743  		Active:   true,
  3744  		Sequence: []*snap.SideInfo{oi},
  3745  		Current:  snap.R(1),
  3746  		SnapType: "app",
  3747  	})
  3748  
  3749  	repo := s.o.InterfaceManager().Repository()
  3750  
  3751  	// add snaps to the repo to have plugs/slots
  3752  	c.Assert(repo.AddSnap(snapInfo), IsNil)
  3753  	c.Assert(repo.AddSnap(otherInfo), IsNil)
  3754  	c.Assert(repo.AddSnap(coreInfo), IsNil)
  3755  
  3756  	// refresh all
  3757  	err := assertstate.RefreshSnapDeclarations(st, 0, nil)
  3758  	c.Assert(err, IsNil)
  3759  
  3760  	updates, tts, err := snapstate.UpdateMany(context.TODO(), st, []string{"core", "some-snap", "other-snap"}, 0, nil)
  3761  	c.Assert(err, IsNil)
  3762  	c.Check(updates, HasLen, 3)
  3763  	c.Assert(tts, HasLen, 4)
  3764  	verifyLastTasksetIsRerefresh(c, tts)
  3765  
  3766  	// to make TaskSnapSetup work
  3767  	chg := st.NewChange("refresh", "...")
  3768  	for _, ts := range tts[:len(tts)-1] {
  3769  		chg.AddAll(ts)
  3770  	}
  3771  
  3772  	// force hold state to hit ignore status of findSymmetricAutoconnect
  3773  	tts[2].Tasks()[0].SetStatus(state.HoldStatus)
  3774  
  3775  	st.Unlock()
  3776  	err = s.o.Settle(settleTimeout)
  3777  	st.Lock()
  3778  	c.Assert(err, IsNil)
  3779  
  3780  	// simulate successful restart happened
  3781  	state.MockRestarting(st, state.RestartUnset)
  3782  	tts[2].Tasks()[0].SetStatus(state.DefaultStatus)
  3783  	st.Unlock()
  3784  
  3785  	err = s.o.Settle(settleTimeout)
  3786  	st.Lock()
  3787  
  3788  	c.Assert(err, IsNil)
  3789  
  3790  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  3791  
  3792  	// check connections
  3793  	var conns map[string]interface{}
  3794  	st.Get("conns", &conns)
  3795  	c.Assert(conns, DeepEquals, map[string]interface{}{
  3796  		"some-snap:home core:home":                 map[string]interface{}{"interface": "home", "auto": true},
  3797  		"some-snap:network core:network":           map[string]interface{}{"interface": "network", "auto": true},
  3798  		"other-snap:media-hub some-snap:media-hub": map[string]interface{}{"interface": "media-hub", "auto": true},
  3799  	})
  3800  
  3801  	connections, err := repo.Connections("some-snap")
  3802  	c.Assert(err, IsNil)
  3803  	c.Assert(connections, HasLen, 3)
  3804  }
  3805  
  3806  func (s *mgrsSuite) TestUpdateWithAutoconnectAndInactiveRevisions(c *C) {
  3807  	const someSnapYaml = `name: some-snap
  3808  version: 1.0
  3809  apps:
  3810     foo:
  3811          command: bin/bar
  3812          plugs: [network]
  3813  `
  3814  	const coreSnapYaml = `name: core
  3815  type: os
  3816  version: 1`
  3817  
  3818  	snapPath, _ := s.makeStoreTestSnap(c, someSnapYaml, "40")
  3819  	s.serveSnap(snapPath, "40")
  3820  
  3821  	mockServer := s.mockStore(c)
  3822  	defer mockServer.Close()
  3823  
  3824  	st := s.o.State()
  3825  	st.Lock()
  3826  	defer st.Unlock()
  3827  
  3828  	si1 := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)}
  3829  	snapInfo := snaptest.MockSnap(c, someSnapYaml, si1)
  3830  	c.Assert(snapInfo.Plugs, HasLen, 1)
  3831  
  3832  	csi := &snap.SideInfo{RealName: "core", SnapID: fakeSnapID("core"), Revision: snap.R(1)}
  3833  	coreInfo := snaptest.MockSnap(c, coreSnapYaml, csi)
  3834  
  3835  	// add implicit slots
  3836  	coreInfo.Slots["network"] = &snap.SlotInfo{
  3837  		Name:      "network",
  3838  		Snap:      coreInfo,
  3839  		Interface: "network",
  3840  	}
  3841  
  3842  	// some-snap has inactive revisions
  3843  	si0 := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(0)}
  3844  	si2 := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(2)}
  3845  	snapstate.Set(st, "some-snap", &snapstate.SnapState{
  3846  		Active:   true,
  3847  		Sequence: []*snap.SideInfo{si0, si1, si2},
  3848  		Current:  snap.R(1),
  3849  		SnapType: "app",
  3850  	})
  3851  
  3852  	repo := s.o.InterfaceManager().Repository()
  3853  
  3854  	// add snaps to the repo to have plugs/slots
  3855  	c.Assert(repo.AddSnap(snapInfo), IsNil)
  3856  	c.Assert(repo.AddSnap(coreInfo), IsNil)
  3857  
  3858  	// refresh all
  3859  	err := assertstate.RefreshSnapDeclarations(st, 0, nil)
  3860  	c.Assert(err, IsNil)
  3861  
  3862  	updates, tts, err := snapstate.UpdateMany(context.TODO(), st, []string{"some-snap"}, 0, nil)
  3863  	c.Assert(err, IsNil)
  3864  	c.Check(updates, HasLen, 1)
  3865  	c.Assert(tts, HasLen, 2)
  3866  	verifyLastTasksetIsRerefresh(c, tts)
  3867  
  3868  	// to make TaskSnapSetup work
  3869  	chg := st.NewChange("refresh", "...")
  3870  	chg.AddAll(tts[0])
  3871  
  3872  	st.Unlock()
  3873  	err = s.o.Settle(settleTimeout)
  3874  	st.Lock()
  3875  
  3876  	c.Assert(err, IsNil)
  3877  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  3878  
  3879  	// check connections
  3880  	var conns map[string]interface{}
  3881  	st.Get("conns", &conns)
  3882  	c.Assert(conns, DeepEquals, map[string]interface{}{
  3883  		"some-snap:network core:network": map[string]interface{}{"interface": "network", "auto": true},
  3884  	})
  3885  }
  3886  
  3887  const someSnapYaml = `name: some-snap
  3888  version: 1.0
  3889  apps:
  3890     foo:
  3891          command: bin/bar
  3892          slots: [media-hub]
  3893  `
  3894  
  3895  func (s *mgrsSuite) testUpdateWithAutoconnectRetry(c *C, updateSnapName, removeSnapName string) {
  3896  	snapPath, _ := s.makeStoreTestSnap(c, someSnapYaml, "40")
  3897  	s.serveSnap(snapPath, "40")
  3898  
  3899  	snapPath, _ = s.makeStoreTestSnap(c, otherSnapYaml, "50")
  3900  	s.serveSnap(snapPath, "50")
  3901  
  3902  	mockServer := s.mockStore(c)
  3903  	defer mockServer.Close()
  3904  
  3905  	st := s.o.State()
  3906  	st.Lock()
  3907  	defer st.Unlock()
  3908  
  3909  	st.Set("conns", map[string]interface{}{})
  3910  
  3911  	si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)}
  3912  	snapInfo := snaptest.MockSnap(c, someSnapYaml, si)
  3913  	c.Assert(snapInfo.Slots, HasLen, 1)
  3914  
  3915  	oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)}
  3916  	otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi)
  3917  	c.Assert(otherInfo.Plugs, HasLen, 1)
  3918  
  3919  	snapstate.Set(st, "some-snap", &snapstate.SnapState{
  3920  		Active:   true,
  3921  		Sequence: []*snap.SideInfo{si},
  3922  		Current:  snap.R(1),
  3923  		SnapType: "app",
  3924  	})
  3925  	snapstate.Set(st, "other-snap", &snapstate.SnapState{
  3926  		Active:   true,
  3927  		Sequence: []*snap.SideInfo{oi},
  3928  		Current:  snap.R(1),
  3929  		SnapType: "app",
  3930  	})
  3931  
  3932  	repo := s.o.InterfaceManager().Repository()
  3933  
  3934  	// add snaps to the repo to have plugs/slots
  3935  	c.Assert(repo.AddSnap(snapInfo), IsNil)
  3936  	c.Assert(repo.AddSnap(otherInfo), IsNil)
  3937  
  3938  	// refresh all
  3939  	err := assertstate.RefreshSnapDeclarations(st, 0, nil)
  3940  	c.Assert(err, IsNil)
  3941  
  3942  	ts, err := snapstate.Update(st, updateSnapName, nil, 0, snapstate.Flags{})
  3943  	c.Assert(err, IsNil)
  3944  
  3945  	// to make TaskSnapSetup work
  3946  	chg := st.NewChange("refresh", "...")
  3947  	chg.AddAll(ts)
  3948  
  3949  	// remove other-snap
  3950  	ts2, err := snapstate.Remove(st, removeSnapName, snap.R(0), &snapstate.RemoveFlags{Purge: true})
  3951  	c.Assert(err, IsNil)
  3952  	chg2 := st.NewChange("remove-snap", "...")
  3953  	chg2.AddAll(ts2)
  3954  
  3955  	// force hold state on first removal task to hit Retry error
  3956  	ts2.Tasks()[0].SetStatus(state.HoldStatus)
  3957  
  3958  	// Settle is not converging here because of the task in Hold status, therefore
  3959  	// it always hits given timeout before we carry on with the test. We're
  3960  	// interested in hitting the retry condition on auto-connect task, so
  3961  	// instead of passing a generous timeout to Settle(), repeat Settle() a number
  3962  	// of times with an aggressive timeout and break as soon as we reach the desired
  3963  	// state of auto-connect task.
  3964  	var retryCheck bool
  3965  	var autoconnectLog string
  3966  	for i := 0; i < 50 && !retryCheck; i++ {
  3967  		st.Unlock()
  3968  		s.o.Settle(aggressiveSettleTimeout)
  3969  		st.Lock()
  3970  
  3971  		for _, t := range st.Tasks() {
  3972  			if t.Kind() == "auto-connect" && t.Status() == state.DoingStatus && strings.Contains(strings.Join(t.Log(), ""), "Waiting") {
  3973  				autoconnectLog = strings.Join(t.Log(), "")
  3974  				retryCheck = true
  3975  				break
  3976  			}
  3977  		}
  3978  	}
  3979  
  3980  	c.Check(retryCheck, Equals, true)
  3981  	c.Assert(autoconnectLog, Matches, `.*Waiting for conflicting change in progress: conflicting snap.*`)
  3982  
  3983  	// back to default state, that will unblock autoconnect
  3984  	ts2.Tasks()[0].SetStatus(state.DefaultStatus)
  3985  	st.Unlock()
  3986  	err = s.o.Settle(settleTimeout)
  3987  	st.Lock()
  3988  	c.Assert(err, IsNil)
  3989  
  3990  	c.Check(chg.Err(), IsNil)
  3991  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  3992  
  3993  	// check connections
  3994  	var conns map[string]interface{}
  3995  	st.Get("conns", &conns)
  3996  	c.Assert(conns, HasLen, 0)
  3997  }
  3998  
  3999  func (s *mgrsSuite) TestUpdateWithAutoconnectRetrySlotSide(c *C) {
  4000  	s.testUpdateWithAutoconnectRetry(c, "some-snap", "other-snap")
  4001  }
  4002  
  4003  func (s *mgrsSuite) TestUpdateWithAutoconnectRetryPlugSide(c *C) {
  4004  	s.testUpdateWithAutoconnectRetry(c, "other-snap", "some-snap")
  4005  }
  4006  
  4007  func (s *mgrsSuite) TestDisconnectIgnoredOnSymmetricRemove(c *C) {
  4008  	const someSnapYaml = `name: some-snap
  4009  version: 1.0
  4010  apps:
  4011     foo:
  4012          command: bin/bar
  4013          slots: [media-hub]
  4014  hooks:
  4015     disconnect-slot-media-hub:
  4016  `
  4017  	const otherSnapYaml = `name: other-snap
  4018  version: 1.0
  4019  apps:
  4020     baz:
  4021          command: bin/bar
  4022          plugs: [media-hub]
  4023  hooks:
  4024     disconnect-plug-media-hub:
  4025  `
  4026  	st := s.o.State()
  4027  	st.Lock()
  4028  	defer st.Unlock()
  4029  
  4030  	st.Set("conns", map[string]interface{}{
  4031  		"other-snap:media-hub some-snap:media-hub": map[string]interface{}{"interface": "media-hub", "auto": false},
  4032  	})
  4033  
  4034  	si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)}
  4035  	snapInfo := snaptest.MockSnap(c, someSnapYaml, si)
  4036  	c.Assert(snapInfo.Slots, HasLen, 1)
  4037  
  4038  	oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)}
  4039  	otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi)
  4040  	c.Assert(otherInfo.Plugs, HasLen, 1)
  4041  
  4042  	snapstate.Set(st, "some-snap", &snapstate.SnapState{
  4043  		Active:   true,
  4044  		Sequence: []*snap.SideInfo{si},
  4045  		Current:  snap.R(1),
  4046  		SnapType: "app",
  4047  	})
  4048  	snapstate.Set(st, "other-snap", &snapstate.SnapState{
  4049  		Active:   true,
  4050  		Sequence: []*snap.SideInfo{oi},
  4051  		Current:  snap.R(1),
  4052  		SnapType: "app",
  4053  	})
  4054  
  4055  	repo := s.o.InterfaceManager().Repository()
  4056  
  4057  	// add snaps to the repo to have plugs/slots
  4058  	c.Assert(repo.AddSnap(snapInfo), IsNil)
  4059  	c.Assert(repo.AddSnap(otherInfo), IsNil)
  4060  	repo.Connect(&interfaces.ConnRef{
  4061  		PlugRef: interfaces.PlugRef{Snap: "other-snap", Name: "media-hub"},
  4062  		SlotRef: interfaces.SlotRef{Snap: "some-snap", Name: "media-hub"},
  4063  	}, nil, nil, nil, nil, nil)
  4064  
  4065  	flags := &snapstate.RemoveFlags{Purge: true}
  4066  	ts, err := snapstate.Remove(st, "some-snap", snap.R(0), flags)
  4067  	c.Assert(err, IsNil)
  4068  	chg := st.NewChange("uninstall", "...")
  4069  	chg.AddAll(ts)
  4070  
  4071  	// remove other-snap
  4072  	ts2, err := snapstate.Remove(st, "other-snap", snap.R(0), flags)
  4073  	c.Assert(err, IsNil)
  4074  	chg2 := st.NewChange("uninstall", "...")
  4075  	chg2.AddAll(ts2)
  4076  
  4077  	st.Unlock()
  4078  	err = s.o.Settle(settleTimeout)
  4079  	st.Lock()
  4080  	c.Assert(err, IsNil)
  4081  
  4082  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  4083  
  4084  	// check connections
  4085  	var conns map[string]interface{}
  4086  	st.Get("conns", &conns)
  4087  	c.Assert(conns, HasLen, 0)
  4088  
  4089  	var disconnectInterfacesCount, slotHookCount, plugHookCount int
  4090  	for _, t := range st.Tasks() {
  4091  		if t.Kind() == "auto-disconnect" {
  4092  			disconnectInterfacesCount++
  4093  		}
  4094  		if t.Kind() == "run-hook" {
  4095  			var hsup hookstate.HookSetup
  4096  			c.Assert(t.Get("hook-setup", &hsup), IsNil)
  4097  			if hsup.Hook == "disconnect-plug-media-hub" {
  4098  				plugHookCount++
  4099  			}
  4100  			if hsup.Hook == "disconnect-slot-media-hub" {
  4101  				slotHookCount++
  4102  			}
  4103  		}
  4104  	}
  4105  	c.Assert(plugHookCount, Equals, 1)
  4106  	c.Assert(slotHookCount, Equals, 1)
  4107  	c.Assert(disconnectInterfacesCount, Equals, 2)
  4108  
  4109  	var snst snapstate.SnapState
  4110  	err = snapstate.Get(st, "other-snap", &snst)
  4111  	c.Assert(err, Equals, state.ErrNoState)
  4112  	_, err = repo.Connected("other-snap", "media-hub")
  4113  	c.Assert(err, ErrorMatches, `snap "other-snap" has no plug or slot named "media-hub"`)
  4114  }
  4115  
  4116  func (s *mgrsSuite) TestDisconnectOnUninstallRemovesAutoconnection(c *C) {
  4117  	st := s.o.State()
  4118  	st.Lock()
  4119  	defer st.Unlock()
  4120  
  4121  	st.Set("conns", map[string]interface{}{
  4122  		"other-snap:media-hub some-snap:media-hub": map[string]interface{}{"interface": "media-hub", "auto": true},
  4123  	})
  4124  
  4125  	si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)}
  4126  	snapInfo := snaptest.MockSnap(c, someSnapYaml, si)
  4127  
  4128  	oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)}
  4129  	otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi)
  4130  
  4131  	snapstate.Set(st, "some-snap", &snapstate.SnapState{
  4132  		Active:   true,
  4133  		Sequence: []*snap.SideInfo{si},
  4134  		Current:  snap.R(1),
  4135  		SnapType: "app",
  4136  	})
  4137  	snapstate.Set(st, "other-snap", &snapstate.SnapState{
  4138  		Active:   true,
  4139  		Sequence: []*snap.SideInfo{oi},
  4140  		Current:  snap.R(1),
  4141  		SnapType: "app",
  4142  	})
  4143  
  4144  	repo := s.o.InterfaceManager().Repository()
  4145  
  4146  	// add snaps to the repo to have plugs/slots
  4147  	c.Assert(repo.AddSnap(snapInfo), IsNil)
  4148  	c.Assert(repo.AddSnap(otherInfo), IsNil)
  4149  	repo.Connect(&interfaces.ConnRef{
  4150  		PlugRef: interfaces.PlugRef{Snap: "other-snap", Name: "media-hub"},
  4151  		SlotRef: interfaces.SlotRef{Snap: "some-snap", Name: "media-hub"},
  4152  	}, nil, nil, nil, nil, nil)
  4153  
  4154  	ts, err := snapstate.Remove(st, "some-snap", snap.R(0), &snapstate.RemoveFlags{Purge: true})
  4155  	c.Assert(err, IsNil)
  4156  	chg := st.NewChange("uninstall", "...")
  4157  	chg.AddAll(ts)
  4158  
  4159  	st.Unlock()
  4160  	err = s.o.Settle(settleTimeout)
  4161  	st.Lock()
  4162  	c.Assert(err, IsNil)
  4163  
  4164  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  4165  
  4166  	// check connections; auto-connection should be removed completely from conns on uninstall.
  4167  	var conns map[string]interface{}
  4168  	st.Get("conns", &conns)
  4169  	c.Assert(conns, HasLen, 0)
  4170  }
  4171  
  4172  // TODO: add a custom checker in testutils for this and similar
  4173  func validateDownloadCheckTasks(c *C, tasks []*state.Task, name, revno, channel string) int {
  4174  	var i int
  4175  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Ensure prerequisites for "%s" are available`, name))
  4176  	i++
  4177  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Download snap "%s" (%s) from channel "%s"`, name, revno, channel))
  4178  	i++
  4179  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Fetch and check assertions for snap "%s" (%s)`, name, revno))
  4180  	i++
  4181  	return i
  4182  }
  4183  
  4184  const (
  4185  	noConfigure = 1 << iota
  4186  	isGadget
  4187  	isKernel
  4188  )
  4189  
  4190  func validateInstallTasks(c *C, tasks []*state.Task, name, revno string, flags int) int {
  4191  	var i int
  4192  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Mount snap "%s" (%s)`, name, revno))
  4193  	i++
  4194  	if flags&isGadget != 0 || flags&isKernel != 0 {
  4195  		what := "gadget"
  4196  		if flags&isKernel != 0 {
  4197  			what = "kernel"
  4198  		}
  4199  		c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Update assets from %s "%s" (%s)`, what, name, revno))
  4200  		i++
  4201  	}
  4202  	if flags&isGadget != 0 {
  4203  		c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Update kernel command line from gadget %q (%s)`, name, revno))
  4204  		i++
  4205  	}
  4206  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Copy snap "%s" data`, name))
  4207  	i++
  4208  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Setup snap "%s" (%s) security profiles`, name, revno))
  4209  	i++
  4210  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Make snap "%s" (%s) available to the system`, name, revno))
  4211  	i++
  4212  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Automatically connect eligible plugs and slots of snap "%s"`, name))
  4213  	i++
  4214  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Set automatic aliases for snap "%s"`, name))
  4215  	i++
  4216  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Setup snap "%s" aliases`, name))
  4217  	i++
  4218  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run install hook of "%s" snap if present`, name))
  4219  	i++
  4220  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Start snap "%s" (%s) services`, name, revno))
  4221  	i++
  4222  	if flags&noConfigure == 0 {
  4223  		c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run configure hook of "%s" snap if present`, name))
  4224  		i++
  4225  	}
  4226  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run health check of "%s" snap`, name))
  4227  	i++
  4228  	return i
  4229  }
  4230  
  4231  func validateRefreshTasks(c *C, tasks []*state.Task, name, revno string, flags int) int {
  4232  	var i int
  4233  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Mount snap "%s" (%s)`, name, revno))
  4234  	i++
  4235  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run pre-refresh hook of "%s" snap if present`, name))
  4236  	i++
  4237  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Stop snap "%s" services`, name))
  4238  	i++
  4239  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Remove aliases for snap "%s"`, name))
  4240  	i++
  4241  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Make current revision for snap "%s" unavailable`, name))
  4242  	i++
  4243  	if flags&isGadget != 0 || flags&isKernel != 0 {
  4244  		what := "gadget"
  4245  		if flags&isKernel != 0 {
  4246  			what = "kernel"
  4247  		}
  4248  		c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Update assets from %s %q (%s)`, what, name, revno))
  4249  		i++
  4250  	}
  4251  	if flags&isGadget != 0 {
  4252  		c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Update kernel command line from gadget %q (%s)`, name, revno))
  4253  		i++
  4254  	}
  4255  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Copy snap "%s" data`, name))
  4256  	i++
  4257  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Setup snap "%s" (%s) security profiles`, name, revno))
  4258  	i++
  4259  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Make snap "%s" (%s) available to the system`, name, revno))
  4260  	i++
  4261  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Automatically connect eligible plugs and slots of snap "%s"`, name))
  4262  	i++
  4263  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Set automatic aliases for snap "%s"`, name))
  4264  	i++
  4265  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Setup snap "%s" aliases`, name))
  4266  	i++
  4267  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run post-refresh hook of "%s" snap if present`, name))
  4268  	i++
  4269  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Start snap "%s" (%s) services`, name, revno))
  4270  	i++
  4271  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Clean up "%s" (%s) install`, name, revno))
  4272  	i++
  4273  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run configure hook of "%s" snap if present`, name))
  4274  	i++
  4275  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run health check of "%s" snap`, name))
  4276  	i++
  4277  	return i
  4278  }
  4279  
  4280  func validateRecoverySystemTasks(c *C, tasks []*state.Task, label string) int {
  4281  	var i int
  4282  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Create recovery system with label %q`, label))
  4283  	i++
  4284  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Finalize recovery system with label %q`, label))
  4285  	i++
  4286  	return i
  4287  }
  4288  
  4289  // byReadyTime sorts a list of tasks by their "ready" time
  4290  type byReadyTime []*state.Task
  4291  
  4292  func (a byReadyTime) Len() int           { return len(a) }
  4293  func (a byReadyTime) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
  4294  func (a byReadyTime) Less(i, j int) bool { return a[i].ReadyTime().Before(a[j].ReadyTime()) }
  4295  
  4296  func (s *mgrsSuite) TestRemodelRequiredSnapsAdded(c *C) {
  4297  	for _, name := range []string{"foo", "bar", "baz"} {
  4298  		s.prereqSnapAssertions(c, map[string]interface{}{
  4299  			"snap-name": name,
  4300  		})
  4301  		snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 1.0}", name), "1")
  4302  		s.serveSnap(snapPath, "1")
  4303  	}
  4304  
  4305  	mockServer := s.mockStore(c)
  4306  	defer mockServer.Close()
  4307  
  4308  	st := s.o.State()
  4309  	st.Lock()
  4310  	defer st.Unlock()
  4311  
  4312  	// pretend we have an old required snap installed
  4313  	si1 := &snap.SideInfo{RealName: "old-required-snap-1", Revision: snap.R(1)}
  4314  	snapstate.Set(st, "old-required-snap-1", &snapstate.SnapState{
  4315  		SnapType: "app",
  4316  		Active:   true,
  4317  		Sequence: []*snap.SideInfo{si1},
  4318  		Current:  si1.Revision,
  4319  		Flags:    snapstate.Flags{Required: true},
  4320  	})
  4321  
  4322  	// create/set custom model assertion
  4323  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  4324  
  4325  	model := s.brands.Model("my-brand", "my-model", modelDefaults)
  4326  
  4327  	// setup model assertion
  4328  	devicestatetest.SetDevice(st, &auth.DeviceState{
  4329  		Brand:  "my-brand",
  4330  		Model:  "my-model",
  4331  		Serial: "serialserialserial",
  4332  	})
  4333  	err := assertstate.Add(st, model)
  4334  	c.Assert(err, IsNil)
  4335  	s.makeSerialAssertionInState(c, st, "my-brand", "my-model", "serialserialserial")
  4336  
  4337  	// create a new model
  4338  	newModel := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{
  4339  		"required-snaps": []interface{}{"foo", "bar", "baz"},
  4340  		"revision":       "1",
  4341  	})
  4342  
  4343  	chg, err := devicestate.Remodel(st, newModel)
  4344  	c.Assert(err, IsNil)
  4345  
  4346  	c.Check(devicestate.Remodeling(st), Equals, true)
  4347  
  4348  	st.Unlock()
  4349  	err = s.o.Settle(settleTimeout)
  4350  	st.Lock()
  4351  	c.Assert(err, IsNil)
  4352  
  4353  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  4354  
  4355  	c.Check(devicestate.Remodeling(st), Equals, false)
  4356  
  4357  	// the new required-snap "foo" is installed
  4358  	var snapst snapstate.SnapState
  4359  	err = snapstate.Get(st, "foo", &snapst)
  4360  	c.Assert(err, IsNil)
  4361  	info, err := snapst.CurrentInfo()
  4362  	c.Assert(err, IsNil)
  4363  	c.Check(info.Revision, Equals, snap.R(1))
  4364  	c.Check(info.Version, Equals, "1.0")
  4365  
  4366  	// and marked required
  4367  	c.Check(snapst.Required, Equals, true)
  4368  
  4369  	// and core is still marked required
  4370  	err = snapstate.Get(st, "core", &snapst)
  4371  	c.Assert(err, IsNil)
  4372  	c.Check(snapst.Required, Equals, true)
  4373  
  4374  	// but old-required-snap-1 is no longer marked required
  4375  	err = snapstate.Get(st, "old-required-snap-1", &snapst)
  4376  	c.Assert(err, IsNil)
  4377  	c.Check(snapst.Required, Equals, false)
  4378  
  4379  	// ensure sorting is correct
  4380  	tasks := chg.Tasks()
  4381  	sort.Sort(byReadyTime(tasks))
  4382  
  4383  	var i int
  4384  	// first all downloads/checks in sequential order
  4385  	for _, name := range []string{"foo", "bar", "baz"} {
  4386  		i += validateDownloadCheckTasks(c, tasks[i:], name, "1", "stable")
  4387  	}
  4388  	// then all installs in sequential order
  4389  	for _, name := range []string{"foo", "bar", "baz"} {
  4390  		i += validateInstallTasks(c, tasks[i:], name, "1", 0)
  4391  	}
  4392  	// ensure that we only have the tasks we checked (plus the one
  4393  	// extra "set-model" task)
  4394  	c.Assert(tasks, HasLen, i+1)
  4395  }
  4396  
  4397  func (s *mgrsSuite) TestRemodelRequiredSnapsAddedUndo(c *C) {
  4398  	for _, name := range []string{"foo", "bar", "baz"} {
  4399  		s.prereqSnapAssertions(c, map[string]interface{}{
  4400  			"snap-name": name,
  4401  		})
  4402  		snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 1.0}", name), "1")
  4403  		s.serveSnap(snapPath, "1")
  4404  	}
  4405  
  4406  	mockServer := s.mockStore(c)
  4407  	defer mockServer.Close()
  4408  
  4409  	st := s.o.State()
  4410  	st.Lock()
  4411  	defer st.Unlock()
  4412  
  4413  	// pretend we have an old required snap installed
  4414  	si1 := &snap.SideInfo{RealName: "old-required-snap-1", Revision: snap.R(1)}
  4415  	snapstate.Set(st, "old-required-snap-1", &snapstate.SnapState{
  4416  		SnapType: "app",
  4417  		Active:   true,
  4418  		Sequence: []*snap.SideInfo{si1},
  4419  		Current:  si1.Revision,
  4420  		Flags:    snapstate.Flags{Required: true},
  4421  	})
  4422  
  4423  	// create/set custom model assertion
  4424  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  4425  	curModel := s.brands.Model("my-brand", "my-model", modelDefaults)
  4426  
  4427  	// setup model assertion
  4428  	devicestatetest.SetDevice(st, &auth.DeviceState{
  4429  		Brand:  "my-brand",
  4430  		Model:  "my-model",
  4431  		Serial: "serialserialserial",
  4432  	})
  4433  	err := assertstate.Add(st, curModel)
  4434  	c.Assert(err, IsNil)
  4435  	s.makeSerialAssertionInState(c, st, "my-brand", "my-model", "serialserialserial")
  4436  
  4437  	// create a new model
  4438  	newModel := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{
  4439  		"required-snaps": []interface{}{"foo", "bar", "baz"},
  4440  		"revision":       "1",
  4441  	})
  4442  
  4443  	devicestate.InjectSetModelError(fmt.Errorf("boom"))
  4444  	defer devicestate.InjectSetModelError(nil)
  4445  
  4446  	chg, err := devicestate.Remodel(st, newModel)
  4447  	c.Assert(err, IsNil)
  4448  
  4449  	st.Unlock()
  4450  	err = s.o.Settle(settleTimeout)
  4451  	st.Lock()
  4452  	c.Assert(err, IsNil)
  4453  
  4454  	c.Assert(chg.Status(), Equals, state.ErrorStatus)
  4455  
  4456  	// None of the new snaps got installed
  4457  	var snapst snapstate.SnapState
  4458  	for _, snapName := range []string{"foo", "bar", "baz"} {
  4459  		err = snapstate.Get(st, snapName, &snapst)
  4460  		c.Assert(err, Equals, state.ErrNoState)
  4461  	}
  4462  
  4463  	// old-required-snap-1 is still marked required
  4464  	err = snapstate.Get(st, "old-required-snap-1", &snapst)
  4465  	c.Assert(err, IsNil)
  4466  	c.Check(snapst.Required, Equals, true)
  4467  
  4468  	// check tasks are in undo state
  4469  	for _, t := range chg.Tasks() {
  4470  		if t.Kind() == "link-snap" {
  4471  			c.Assert(t.Status(), Equals, state.UndoneStatus)
  4472  		}
  4473  	}
  4474  
  4475  	model, err := s.o.DeviceManager().Model()
  4476  	c.Assert(err, IsNil)
  4477  	c.Assert(model, DeepEquals, curModel)
  4478  }
  4479  
  4480  func (s *mgrsSuite) TestRemodelDifferentBase(c *C) {
  4481  	// make "core18" snap available in the store
  4482  	snapYamlContent := `name: core18
  4483  version: 18.04
  4484  type: base`
  4485  	snapPath, _ := s.makeStoreTestSnap(c, snapYamlContent, "18")
  4486  	s.serveSnap(snapPath, "18")
  4487  
  4488  	mockServer := s.mockStore(c)
  4489  	defer mockServer.Close()
  4490  
  4491  	st := s.o.State()
  4492  	st.Lock()
  4493  	defer st.Unlock()
  4494  
  4495  	// create/set custom model assertion
  4496  	model := s.brands.Model("can0nical", "my-model", modelDefaults)
  4497  	// setup model assertion
  4498  	devicestatetest.SetDevice(st, &auth.DeviceState{
  4499  		Brand:  "can0nical",
  4500  		Model:  "my-model",
  4501  		Serial: "serialserialserial",
  4502  	})
  4503  	err := assertstate.Add(st, model)
  4504  	c.Assert(err, IsNil)
  4505  	s.makeSerialAssertionInState(c, st, "can0nical", "my-model", "serialserialserial")
  4506  
  4507  	// create a new model
  4508  	newModel := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  4509  		"base":     "core18",
  4510  		"revision": "1",
  4511  	})
  4512  
  4513  	chg, err := devicestate.Remodel(st, newModel)
  4514  	c.Assert(err, ErrorMatches, "cannot remodel from core to bases yet")
  4515  	c.Assert(chg, IsNil)
  4516  }
  4517  
  4518  func (ms *mgrsSuite) TestRemodelSwitchToDifferentBase(c *C) {
  4519  	bloader := bootloadertest.Mock("mock", c.MkDir())
  4520  	bootloader.Force(bloader)
  4521  	defer bootloader.Force(nil)
  4522  	bloader.SetBootVars(map[string]string{
  4523  		"snap_mode":   boot.DefaultStatus,
  4524  		"snap_core":   "core18_1.snap",
  4525  		"snap_kernel": "pc-kernel_1.snap",
  4526  	})
  4527  
  4528  	restore := release.MockOnClassic(false)
  4529  	defer restore()
  4530  
  4531  	mockServer := ms.mockStore(c)
  4532  	defer mockServer.Close()
  4533  
  4534  	st := ms.o.State()
  4535  	st.Lock()
  4536  	defer st.Unlock()
  4537  
  4538  	si := &snap.SideInfo{RealName: "core18", SnapID: fakeSnapID("core18"), Revision: snap.R(1)}
  4539  	snapstate.Set(st, "core18", &snapstate.SnapState{
  4540  		Active:   true,
  4541  		Sequence: []*snap.SideInfo{si},
  4542  		Current:  snap.R(1),
  4543  		SnapType: "base",
  4544  	})
  4545  	si2 := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)}
  4546  	gadgetSnapYaml := "name: pc\nversion: 1.0\ntype: gadget"
  4547  	snapstate.Set(st, "pc", &snapstate.SnapState{
  4548  		Active:   true,
  4549  		Sequence: []*snap.SideInfo{si2},
  4550  		Current:  snap.R(1),
  4551  		SnapType: "gadget",
  4552  	})
  4553  	gadgetYaml := `
  4554  volumes:
  4555      volume-id:
  4556          bootloader: grub
  4557  `
  4558  	snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si2, [][]string{
  4559  		{"meta/gadget.yaml", gadgetYaml},
  4560  	})
  4561  
  4562  	// add "core20" snap to fake store
  4563  	const core20Yaml = `name: core20
  4564  type: base
  4565  version: 20.04`
  4566  	snapPath, _ := ms.makeStoreTestSnap(c, core20Yaml, "2")
  4567  	ms.serveSnap(snapPath, "2")
  4568  
  4569  	// add "foo" snap to fake store
  4570  	ms.prereqSnapAssertions(c, map[string]interface{}{
  4571  		"snap-name": "foo",
  4572  	})
  4573  	snapPath, _ = ms.makeStoreTestSnap(c, `{name: "foo", version: 1.0}`, "1")
  4574  	ms.serveSnap(snapPath, "1")
  4575  
  4576  	// create/set custom model assertion
  4577  	model := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  4578  		"base": "core18",
  4579  	})
  4580  
  4581  	// setup model assertion
  4582  	devicestatetest.SetDevice(st, &auth.DeviceState{
  4583  		Brand:  "can0nical",
  4584  		Model:  "my-model",
  4585  		Serial: "serialserialserial",
  4586  	})
  4587  	err := assertstate.Add(st, model)
  4588  	c.Assert(err, IsNil)
  4589  	ms.makeSerialAssertionInState(c, st, "can0nical", "my-model", "serialserialserial")
  4590  
  4591  	// create a new model
  4592  	newModel := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  4593  		"base":           "core20",
  4594  		"revision":       "1",
  4595  		"required-snaps": []interface{}{"foo"},
  4596  	})
  4597  
  4598  	chg, err := devicestate.Remodel(st, newModel)
  4599  	c.Assert(err, IsNil)
  4600  
  4601  	st.Unlock()
  4602  	err = ms.o.Settle(settleTimeout)
  4603  	st.Lock()
  4604  	c.Assert(err, IsNil)
  4605  	c.Assert(chg.Err(), IsNil)
  4606  
  4607  	// system waits for a restart because of the new base
  4608  	t := findKind(chg, "auto-connect")
  4609  	c.Assert(t, NotNil)
  4610  	c.Assert(t.Status(), Equals, state.DoingStatus)
  4611  
  4612  	// check that the boot vars got updated as expected
  4613  	bvars, err := bloader.GetBootVars("snap_mode", "snap_core", "snap_try_core", "snap_kernel", "snap_try_kernel")
  4614  	c.Assert(err, IsNil)
  4615  	c.Assert(bvars, DeepEquals, map[string]string{
  4616  		"snap_mode":       boot.TryStatus,
  4617  		"snap_core":       "core18_1.snap",
  4618  		"snap_try_core":   "core20_2.snap",
  4619  		"snap_kernel":     "pc-kernel_1.snap",
  4620  		"snap_try_kernel": "",
  4621  	})
  4622  
  4623  	// simulate successful restart happened and that the bootvars
  4624  	// got updated
  4625  	state.MockRestarting(st, state.RestartUnset)
  4626  	bloader.SetBootVars(map[string]string{
  4627  		"snap_mode":   boot.DefaultStatus,
  4628  		"snap_core":   "core20_2.snap",
  4629  		"snap_kernel": "pc-kernel_1.snap",
  4630  	})
  4631  
  4632  	// continue
  4633  	st.Unlock()
  4634  	err = ms.o.Settle(settleTimeout)
  4635  	st.Lock()
  4636  	c.Assert(err, IsNil)
  4637  
  4638  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  4639  
  4640  	// ensure tasks were run in the right order
  4641  	tasks := chg.Tasks()
  4642  	sort.Sort(byReadyTime(tasks))
  4643  
  4644  	// first all downloads/checks in sequential order
  4645  	var i int
  4646  	i += validateDownloadCheckTasks(c, tasks[i:], "core20", "2", "stable")
  4647  	i += validateDownloadCheckTasks(c, tasks[i:], "foo", "1", "stable")
  4648  
  4649  	// then all installs in sequential order
  4650  	i += validateInstallTasks(c, tasks[i:], "core20", "2", noConfigure)
  4651  	i += validateInstallTasks(c, tasks[i:], "foo", "1", 0)
  4652  
  4653  	// ensure that we only have the tasks we checked (plus the one
  4654  	// extra "set-model" task)
  4655  	c.Assert(tasks, HasLen, i+1)
  4656  }
  4657  
  4658  func (ms *mgrsSuite) TestRemodelSwitchToDifferentBaseUndo(c *C) {
  4659  	bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
  4660  	bootloader.Force(bloader)
  4661  	defer bootloader.Force(nil)
  4662  	bloader.SetBootVars(map[string]string{
  4663  		"snap_mode":   boot.DefaultStatus,
  4664  		"snap_core":   "core18_1.snap",
  4665  		"snap_kernel": "pc-kernel_1.snap",
  4666  	})
  4667  
  4668  	restore := release.MockOnClassic(false)
  4669  	defer restore()
  4670  
  4671  	mockServer := ms.mockStore(c)
  4672  	defer mockServer.Close()
  4673  
  4674  	st := ms.o.State()
  4675  	st.Lock()
  4676  	defer st.Unlock()
  4677  
  4678  	si := &snap.SideInfo{RealName: "core18", SnapID: fakeSnapID("core18"), Revision: snap.R(1)}
  4679  	snapstate.Set(st, "core18", &snapstate.SnapState{
  4680  		Active:   true,
  4681  		Sequence: []*snap.SideInfo{si},
  4682  		Current:  snap.R(1),
  4683  		SnapType: "base",
  4684  	})
  4685  	snaptest.MockSnapWithFiles(c, "name: core18\ntype: base\nversion: 1.0", si, nil)
  4686  
  4687  	si2 := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)}
  4688  	gadgetSnapYaml := "name: pc\nversion: 1.0\ntype: gadget"
  4689  	snapstate.Set(st, "pc", &snapstate.SnapState{
  4690  		Active:   true,
  4691  		Sequence: []*snap.SideInfo{si2},
  4692  		Current:  snap.R(1),
  4693  		SnapType: "gadget",
  4694  	})
  4695  	gadgetYaml := `
  4696  volumes:
  4697      volume-id:
  4698          bootloader: grub
  4699  `
  4700  	snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si2, [][]string{
  4701  		{"meta/gadget.yaml", gadgetYaml},
  4702  	})
  4703  
  4704  	// add "core20" snap to fake store
  4705  	const core20Yaml = `name: core20
  4706  type: base
  4707  version: 20.04`
  4708  	snapPath, _ := ms.makeStoreTestSnap(c, core20Yaml, "2")
  4709  	ms.serveSnap(snapPath, "2")
  4710  
  4711  	// add "foo" snap to fake store
  4712  	ms.prereqSnapAssertions(c, map[string]interface{}{
  4713  		"snap-name": "foo",
  4714  	})
  4715  	snapPath, _ = ms.makeStoreTestSnap(c, `{name: "foo", version: 1.0}`, "1")
  4716  	ms.serveSnap(snapPath, "1")
  4717  
  4718  	// create/set custom model assertion
  4719  	model := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  4720  		"base": "core18",
  4721  	})
  4722  
  4723  	// setup model assertion
  4724  	devicestatetest.SetDevice(st, &auth.DeviceState{
  4725  		Brand:  "can0nical",
  4726  		Model:  "my-model",
  4727  		Serial: "serialserialserial",
  4728  	})
  4729  	err := assertstate.Add(st, model)
  4730  	c.Assert(err, IsNil)
  4731  	ms.makeSerialAssertionInState(c, st, "can0nical", "my-model", "serialserialserial")
  4732  
  4733  	// create a new model
  4734  	newModel := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  4735  		"base":           "core20",
  4736  		"revision":       "1",
  4737  		"required-snaps": []interface{}{"foo"},
  4738  	})
  4739  
  4740  	devicestate.InjectSetModelError(fmt.Errorf("boom"))
  4741  	defer devicestate.InjectSetModelError(nil)
  4742  
  4743  	chg, err := devicestate.Remodel(st, newModel)
  4744  	c.Assert(err, IsNil)
  4745  
  4746  	st.Unlock()
  4747  	err = ms.o.Settle(settleTimeout)
  4748  	st.Lock()
  4749  	c.Assert(err, IsNil)
  4750  	c.Assert(chg.Err(), IsNil)
  4751  
  4752  	// system waits for a restart because of the new base
  4753  	t := findKind(chg, "auto-connect")
  4754  	c.Assert(t, NotNil)
  4755  	c.Assert(t.Status(), Equals, state.DoingStatus)
  4756  
  4757  	// check that the boot vars got updated as expected
  4758  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  4759  		"snap_mode":       boot.TryStatus,
  4760  		"snap_core":       "core18_1.snap",
  4761  		"snap_try_core":   "core20_2.snap",
  4762  		"snap_kernel":     "pc-kernel_1.snap",
  4763  		"snap_try_kernel": "",
  4764  	})
  4765  	// simulate successful restart happened
  4766  	ms.mockSuccessfulReboot(c, bloader, []snap.Type{snap.TypeBase})
  4767  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  4768  		"snap_mode":       boot.DefaultStatus,
  4769  		"snap_core":       "core20_2.snap",
  4770  		"snap_try_core":   "",
  4771  		"snap_kernel":     "pc-kernel_1.snap",
  4772  		"snap_try_kernel": "",
  4773  	})
  4774  
  4775  	// continue
  4776  	st.Unlock()
  4777  	err = ms.o.Settle(settleTimeout)
  4778  	st.Lock()
  4779  	c.Assert(err, IsNil)
  4780  
  4781  	c.Assert(chg.Status(), Equals, state.ErrorStatus)
  4782  
  4783  	// and we are in restarting state
  4784  	restarting, restartType := st.Restarting()
  4785  	c.Check(restarting, Equals, true)
  4786  	c.Check(restartType, Equals, state.RestartSystem)
  4787  
  4788  	// and the undo gave us our old kernel back
  4789  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  4790  		"snap_core":       "core20_2.snap",
  4791  		"snap_try_core":   "core18_1.snap",
  4792  		"snap_kernel":     "pc-kernel_1.snap",
  4793  		"snap_try_kernel": "",
  4794  		"snap_mode":       boot.TryStatus,
  4795  	})
  4796  }
  4797  
  4798  func (ms *mgrsSuite) TestRemodelSwitchToDifferentBaseUndoOnRollback(c *C) {
  4799  	bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
  4800  	bootloader.Force(bloader)
  4801  	defer bootloader.Force(nil)
  4802  	bloader.SetBootVars(map[string]string{
  4803  		"snap_mode":   boot.DefaultStatus,
  4804  		"snap_core":   "core18_1.snap",
  4805  		"snap_kernel": "pc-kernel_1.snap",
  4806  	})
  4807  
  4808  	restore := release.MockOnClassic(false)
  4809  	defer restore()
  4810  
  4811  	mockServer := ms.mockStore(c)
  4812  	defer mockServer.Close()
  4813  
  4814  	st := ms.o.State()
  4815  	st.Lock()
  4816  	defer st.Unlock()
  4817  
  4818  	si := &snap.SideInfo{RealName: "core18", SnapID: fakeSnapID("core18"), Revision: snap.R(1)}
  4819  	snapstate.Set(st, "core18", &snapstate.SnapState{
  4820  		Active:   true,
  4821  		Sequence: []*snap.SideInfo{si},
  4822  		Current:  snap.R(1),
  4823  		SnapType: "base",
  4824  	})
  4825  	snaptest.MockSnapWithFiles(c, "name: core18\ntype: base\nversion: 1.0", si, nil)
  4826  
  4827  	si2 := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)}
  4828  	gadgetSnapYaml := "name: pc\nversion: 1.0\ntype: gadget"
  4829  	snapstate.Set(st, "pc", &snapstate.SnapState{
  4830  		Active:   true,
  4831  		Sequence: []*snap.SideInfo{si2},
  4832  		Current:  snap.R(1),
  4833  		SnapType: "gadget",
  4834  	})
  4835  	gadgetYaml := `
  4836  volumes:
  4837      volume-id:
  4838          bootloader: grub
  4839  `
  4840  	snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si2, [][]string{
  4841  		{"meta/gadget.yaml", gadgetYaml},
  4842  	})
  4843  
  4844  	// add "core20" snap to fake store
  4845  	const core20Yaml = `name: core20
  4846  type: base
  4847  version: 20.04`
  4848  	snapPath, _ := ms.makeStoreTestSnap(c, core20Yaml, "2")
  4849  	ms.serveSnap(snapPath, "2")
  4850  
  4851  	// add "foo" snap to fake store
  4852  	ms.prereqSnapAssertions(c, map[string]interface{}{
  4853  		"snap-name": "foo",
  4854  	})
  4855  	snapPath, _ = ms.makeStoreTestSnap(c, `{name: "foo", version: 1.0}`, "1")
  4856  	ms.serveSnap(snapPath, "1")
  4857  
  4858  	// create/set custom model assertion
  4859  	model := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  4860  		"base": "core18",
  4861  	})
  4862  
  4863  	// setup model assertion
  4864  	devicestatetest.SetDevice(st, &auth.DeviceState{
  4865  		Brand:  "can0nical",
  4866  		Model:  "my-model",
  4867  		Serial: "serialserialserial",
  4868  	})
  4869  	err := assertstate.Add(st, model)
  4870  	c.Assert(err, IsNil)
  4871  	ms.makeSerialAssertionInState(c, st, "can0nical", "my-model", "serialserialserial")
  4872  
  4873  	// create a new model
  4874  	newModel := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  4875  		"base":           "core20",
  4876  		"revision":       "1",
  4877  		"required-snaps": []interface{}{"foo"},
  4878  	})
  4879  
  4880  	chg, err := devicestate.Remodel(st, newModel)
  4881  	c.Assert(err, IsNil)
  4882  
  4883  	st.Unlock()
  4884  	err = ms.o.Settle(settleTimeout)
  4885  	st.Lock()
  4886  	c.Assert(err, IsNil)
  4887  	c.Assert(chg.Err(), IsNil)
  4888  
  4889  	// system waits for a restart because of the new base
  4890  	t := findKind(chg, "auto-connect")
  4891  	c.Assert(t, NotNil)
  4892  	c.Assert(t.Status(), Equals, state.DoingStatus)
  4893  
  4894  	// check that the boot vars got updated as expected
  4895  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  4896  		"snap_mode":       boot.TryStatus,
  4897  		"snap_core":       "core18_1.snap",
  4898  		"snap_try_core":   "core20_2.snap",
  4899  		"snap_kernel":     "pc-kernel_1.snap",
  4900  		"snap_try_kernel": "",
  4901  	})
  4902  	// simulate successful restart happened
  4903  	ms.mockRollbackAcrossReboot(c, bloader, []snap.Type{snap.TypeBase})
  4904  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  4905  		"snap_mode":       boot.DefaultStatus,
  4906  		"snap_core":       "core18_1.snap",
  4907  		"snap_try_core":   "",
  4908  		"snap_kernel":     "pc-kernel_1.snap",
  4909  		"snap_try_kernel": "",
  4910  	})
  4911  
  4912  	// continue
  4913  	st.Unlock()
  4914  	err = ms.o.Settle(settleTimeout)
  4915  	st.Lock()
  4916  	c.Assert(err, IsNil)
  4917  
  4918  	c.Assert(chg.Status(), Equals, state.ErrorStatus)
  4919  
  4920  	// and we are *not* in restarting state
  4921  	restarting, _ := st.Restarting()
  4922  	c.Check(restarting, Equals, false)
  4923  	// bootvars unchanged
  4924  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  4925  		"snap_mode":       boot.DefaultStatus,
  4926  		"snap_core":       "core18_1.snap",
  4927  		"snap_try_core":   "",
  4928  		"snap_kernel":     "pc-kernel_1.snap",
  4929  		"snap_try_kernel": "",
  4930  	})
  4931  }
  4932  
  4933  func (ms *mgrsSuite) TestRefreshSimpleSameRev(c *C) {
  4934  	// the "some-snap" in rev1
  4935  	snapYaml := "name: some-snap\nversion: 1.0"
  4936  	revStr := "1"
  4937  	// is available in the store
  4938  	snapPath, _ := ms.makeStoreTestSnap(c, snapYaml, revStr)
  4939  	ms.serveSnap(snapPath, revStr)
  4940  
  4941  	mockServer := ms.mockStore(c)
  4942  	ms.AddCleanup(mockServer.Close)
  4943  
  4944  	st := ms.o.State()
  4945  	st.Lock()
  4946  	defer st.Unlock()
  4947  
  4948  	// and some-snap:rev1 is also installed
  4949  	info := ms.mockInstalledSnapWithRevAndFiles(c, snapYaml, snap.R(revStr), nil)
  4950  
  4951  	// now refresh from rev1 to rev1
  4952  	revOpts := &snapstate.RevisionOptions{Revision: snap.R(revStr)}
  4953  	ts, err := snapstate.Update(st, "some-snap", revOpts, 0, snapstate.Flags{})
  4954  	c.Assert(err, IsNil)
  4955  
  4956  	chg := st.NewChange("refresh", "...")
  4957  	chg.AddAll(ts)
  4958  
  4959  	st.Unlock()
  4960  	err = ms.o.Settle(settleTimeout)
  4961  	st.Lock()
  4962  	c.Assert(err, IsNil)
  4963  	c.Check(chg.Err(), IsNil)
  4964  	c.Check(chg.Status(), Equals, state.DoneStatus)
  4965  
  4966  	// the snap file is in the right place
  4967  	c.Check(info.MountFile(), testutil.FilePresent)
  4968  
  4969  	// rev1 is installed
  4970  	var snapst snapstate.SnapState
  4971  	snapstate.Get(st, "some-snap", &snapst)
  4972  	info, err = snapst.CurrentInfo()
  4973  	c.Assert(err, IsNil)
  4974  	c.Assert(info.Revision, Equals, snap.R(1))
  4975  }
  4976  
  4977  func (ms *mgrsSuite) TestRefreshSimplePrevRev(c *C) {
  4978  	// the "some-snap" in rev1
  4979  	snapYaml := "name: some-snap\nversion: 1.0"
  4980  	revStr := "1"
  4981  	// is available in the store
  4982  	snapPath, _ := ms.makeStoreTestSnap(c, snapYaml, revStr)
  4983  	ms.serveSnap(snapPath, revStr)
  4984  
  4985  	mockServer := ms.mockStore(c)
  4986  	ms.AddCleanup(mockServer.Close)
  4987  
  4988  	st := ms.o.State()
  4989  	st.Lock()
  4990  	defer st.Unlock()
  4991  
  4992  	// and some-snap at both rev1, rev2 are installed
  4993  	info := snaptest.MockSnapWithFiles(c, snapYaml, &snap.SideInfo{Revision: snap.R(1)}, nil)
  4994  	snaptest.MockSnapWithFiles(c, snapYaml, &snap.SideInfo{Revision: snap.R(2)}, nil)
  4995  	si1 := &snap.SideInfo{
  4996  		RealName: info.SnapName(),
  4997  		SnapID:   fakeSnapID(info.SnapName()),
  4998  		Revision: snap.R(1),
  4999  	}
  5000  	si2 := &snap.SideInfo{
  5001  		RealName: info.SnapName(),
  5002  		SnapID:   fakeSnapID(info.SnapName()),
  5003  		Revision: snap.R(2),
  5004  	}
  5005  	snapstate.Set(st, info.InstanceName(), &snapstate.SnapState{
  5006  		Active:   true,
  5007  		Sequence: []*snap.SideInfo{si1, si2},
  5008  		Current:  snap.R(2),
  5009  		SnapType: string(info.Type()),
  5010  	})
  5011  
  5012  	// now refresh from rev2 to the local rev1
  5013  	revOpts := &snapstate.RevisionOptions{Revision: snap.R(revStr)}
  5014  	ts, err := snapstate.Update(st, "some-snap", revOpts, 0, snapstate.Flags{})
  5015  	c.Assert(err, IsNil)
  5016  
  5017  	chg := st.NewChange("refresh", "...")
  5018  	chg.AddAll(ts)
  5019  
  5020  	st.Unlock()
  5021  	err = ms.o.Settle(settleTimeout)
  5022  	st.Lock()
  5023  	c.Assert(err, IsNil)
  5024  	c.Check(chg.Err(), IsNil)
  5025  	c.Check(chg.Status(), Equals, state.DoneStatus)
  5026  
  5027  	// the snap file is in the right place
  5028  	c.Check(info.MountFile(), testutil.FilePresent)
  5029  
  5030  	var snapst snapstate.SnapState
  5031  	snapstate.Get(st, "some-snap", &snapst)
  5032  	info, err = snapst.CurrentInfo()
  5033  	c.Assert(err, IsNil)
  5034  	c.Assert(info.Revision, Equals, snap.R(1))
  5035  }
  5036  
  5037  func (ms *mgrsSuite) TestRefreshSimpleSameRevFromLocalFile(c *C) {
  5038  	// the "some-snap" in rev1
  5039  	snapYaml := "name: some-snap\nversion: 1.0"
  5040  	revStr := "1"
  5041  
  5042  	// pretend we got a temp snap file from e.g. the snapd daemon
  5043  	tmpSnapFile := makeTestSnap(c, snapYaml)
  5044  
  5045  	st := ms.o.State()
  5046  	st.Lock()
  5047  	defer st.Unlock()
  5048  
  5049  	// and some-snap:rev1 is also installed
  5050  	info := ms.mockInstalledSnapWithRevAndFiles(c, snapYaml, snap.R(revStr), nil)
  5051  
  5052  	// now refresh from rev1 to rev1
  5053  	flags := snapstate.Flags{RemoveSnapPath: true}
  5054  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "some-snap", Revision: snap.R(revStr)}, tmpSnapFile, "", "", flags)
  5055  	c.Assert(err, IsNil)
  5056  
  5057  	chg := st.NewChange("refresh", "...")
  5058  	chg.AddAll(ts)
  5059  
  5060  	st.Unlock()
  5061  	err = ms.o.Settle(settleTimeout)
  5062  	st.Lock()
  5063  	c.Assert(err, IsNil)
  5064  	c.Check(chg.Err(), IsNil)
  5065  	c.Check(chg.Status(), Equals, state.DoneStatus)
  5066  
  5067  	// the temp file got cleaned up
  5068  	snapsup, err := snapstate.TaskSnapSetup(chg.Tasks()[0])
  5069  	c.Assert(err, IsNil)
  5070  	c.Check(snapsup.Flags.RemoveSnapPath, Equals, true)
  5071  	c.Check(snapsup.SnapPath, testutil.FileAbsent)
  5072  
  5073  	// the snap file is in the right place
  5074  	c.Check(info.MountFile(), testutil.FilePresent)
  5075  
  5076  	var snapst snapstate.SnapState
  5077  	snapstate.Get(st, "some-snap", &snapst)
  5078  	info, err = snapst.CurrentInfo()
  5079  	c.Assert(err, IsNil)
  5080  	c.Assert(info.Revision, Equals, snap.R(1))
  5081  }
  5082  
  5083  func (ms *mgrsSuite) TestRefreshSimpleRevertToLocalFromLocalFile(c *C) {
  5084  	// the "some-snap" in rev1
  5085  	snapYaml := "name: some-snap\nversion: 1.0"
  5086  	revStr := "1"
  5087  
  5088  	// pretend we got a temp snap file from e.g. the snapd daemon
  5089  	tmpSnapFile := makeTestSnap(c, snapYaml)
  5090  
  5091  	st := ms.o.State()
  5092  	st.Lock()
  5093  	defer st.Unlock()
  5094  
  5095  	// and some-snap at both rev1, rev2 are installed
  5096  	info := snaptest.MockSnapWithFiles(c, snapYaml, &snap.SideInfo{Revision: snap.R(1)}, nil)
  5097  	snaptest.MockSnapWithFiles(c, snapYaml, &snap.SideInfo{Revision: snap.R(2)}, nil)
  5098  	si1 := &snap.SideInfo{
  5099  		RealName: info.SnapName(),
  5100  		SnapID:   fakeSnapID(info.SnapName()),
  5101  		Revision: snap.R(1),
  5102  	}
  5103  	si2 := &snap.SideInfo{
  5104  		RealName: info.SnapName(),
  5105  		SnapID:   fakeSnapID(info.SnapName()),
  5106  		Revision: snap.R(2),
  5107  	}
  5108  	snapstate.Set(st, info.InstanceName(), &snapstate.SnapState{
  5109  		Active:   true,
  5110  		Sequence: []*snap.SideInfo{si1, si2},
  5111  		Current:  snap.R(2),
  5112  		SnapType: string(info.Type()),
  5113  	})
  5114  
  5115  	// now refresh from rev2 to rev1
  5116  	flags := snapstate.Flags{RemoveSnapPath: true}
  5117  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "some-snap", Revision: snap.R(revStr)}, tmpSnapFile, "", "", flags)
  5118  	c.Assert(err, IsNil)
  5119  
  5120  	chg := st.NewChange("refresh", "...")
  5121  	chg.AddAll(ts)
  5122  
  5123  	st.Unlock()
  5124  	err = ms.o.Settle(settleTimeout)
  5125  	st.Lock()
  5126  	c.Assert(err, IsNil)
  5127  	c.Check(chg.Err(), IsNil)
  5128  	c.Check(chg.Status(), Equals, state.DoneStatus)
  5129  
  5130  	// the temp file got cleaned up
  5131  	snapsup, err := snapstate.TaskSnapSetup(chg.Tasks()[0])
  5132  	c.Assert(err, IsNil)
  5133  	c.Check(snapsup.Flags.RemoveSnapPath, Equals, true)
  5134  	c.Check(snapsup.SnapPath, testutil.FileAbsent)
  5135  
  5136  	// the snap file is in the right place
  5137  	c.Check(info.MountFile(), testutil.FilePresent)
  5138  
  5139  	var snapst snapstate.SnapState
  5140  	snapstate.Get(st, "some-snap", &snapst)
  5141  	info, err = snapst.CurrentInfo()
  5142  	c.Assert(err, IsNil)
  5143  	c.Assert(info.Revision, Equals, snap.R(1))
  5144  }
  5145  
  5146  type kernelSuite struct {
  5147  	baseMgrsSuite
  5148  
  5149  	bloader *boottest.Bootenv16
  5150  }
  5151  
  5152  var _ = Suite(&kernelSuite{})
  5153  
  5154  func (s *kernelSuite) SetUpTest(c *C) {
  5155  	s.baseMgrsSuite.SetUpTest(c)
  5156  
  5157  	s.bloader = boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
  5158  	s.bloader.SetBootKernel("pc-kernel_1.snap")
  5159  	s.bloader.SetBootBase("core_1.snap")
  5160  	bootloader.Force(s.bloader)
  5161  	s.AddCleanup(func() { bootloader.Force(nil) })
  5162  
  5163  	restore := release.MockOnClassic(false)
  5164  	s.AddCleanup(restore)
  5165  	mockServer := s.mockStore(c)
  5166  	s.AddCleanup(mockServer.Close)
  5167  
  5168  	st := s.o.State()
  5169  	st.Lock()
  5170  	defer st.Unlock()
  5171  
  5172  	// create/set custom model assertion
  5173  	model := s.brands.Model("can0nical", "my-model", modelDefaults)
  5174  	devicestatetest.SetDevice(st, &auth.DeviceState{
  5175  		Brand:  "can0nical",
  5176  		Model:  "my-model",
  5177  		Serial: "serialserialserial",
  5178  	})
  5179  	err := assertstate.Add(st, model)
  5180  	c.Assert(err, IsNil)
  5181  	s.makeSerialAssertionInState(c, st, "can0nical", "my-model", "serialserialserial")
  5182  
  5183  	// make a mock "pc-kernel" kernel
  5184  	si := &snap.SideInfo{RealName: "pc-kernel", SnapID: fakeSnapID("pc-kernel"), Revision: snap.R(1)}
  5185  	snapstate.Set(st, "pc-kernel", &snapstate.SnapState{
  5186  		Active:   true,
  5187  		Sequence: []*snap.SideInfo{si},
  5188  		Current:  snap.R(1),
  5189  		SnapType: "kernel",
  5190  	})
  5191  	snaptest.MockSnapWithFiles(c, "name: pc-kernel\ntype: kernel\nversion: 1.0", si, nil)
  5192  
  5193  	// make a mock "pc" gadget
  5194  	si2 := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)}
  5195  	gadgetSnapYaml := "name: pc\nversion: 1.0\ntype: gadget"
  5196  	snapstate.Set(st, "pc", &snapstate.SnapState{
  5197  		Active:   true,
  5198  		Sequence: []*snap.SideInfo{si2},
  5199  		Current:  snap.R(1),
  5200  		SnapType: "gadget",
  5201  	})
  5202  	gadgetYaml := `
  5203  volumes:
  5204      volume-id:
  5205          bootloader: grub
  5206  `
  5207  	snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si2, [][]string{
  5208  		{"meta/gadget.yaml", gadgetYaml},
  5209  	})
  5210  
  5211  	// add some store snaps
  5212  	const kernelYaml = `name: pc-kernel
  5213  type: kernel
  5214  version: 2.0`
  5215  	snapPath, _ := s.makeStoreTestSnap(c, kernelYaml, "2")
  5216  	s.serveSnap(snapPath, "2")
  5217  
  5218  	const brandKernelYaml = `name: brand-kernel
  5219  type: kernel
  5220  version: 1.0`
  5221  	s.prereqSnapAssertions(c, map[string]interface{}{
  5222  		"snap-name":    "brand-kernel",
  5223  		"publisher-id": "can0nical",
  5224  	})
  5225  	snapPath, _ = s.makeStoreTestSnap(c, brandKernelYaml, "2")
  5226  	s.serveSnap(snapPath, "2")
  5227  
  5228  	s.prereqSnapAssertions(c, map[string]interface{}{
  5229  		"snap-name": "foo",
  5230  	})
  5231  	snapPath, _ = s.makeStoreTestSnap(c, `{name: "foo", version: 1.0}`, "1")
  5232  	s.serveSnap(snapPath, "1")
  5233  }
  5234  
  5235  func (s *kernelSuite) TestRemodelSwitchKernelTrack(c *C) {
  5236  	st := s.o.State()
  5237  	st.Lock()
  5238  	defer st.Unlock()
  5239  
  5240  	// create a new model
  5241  	newModel := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  5242  		"kernel":         "pc-kernel=18",
  5243  		"revision":       "1",
  5244  		"required-snaps": []interface{}{"foo"},
  5245  	})
  5246  
  5247  	chg, err := devicestate.Remodel(st, newModel)
  5248  	c.Assert(err, IsNil)
  5249  
  5250  	st.Unlock()
  5251  	err = s.o.Settle(settleTimeout)
  5252  	st.Lock()
  5253  	c.Assert(err, IsNil)
  5254  
  5255  	// system waits for a restart because of the new kernel
  5256  	t := findKind(chg, "auto-connect")
  5257  	c.Assert(t, NotNil)
  5258  	c.Assert(t.Status(), Equals, state.DoingStatus)
  5259  
  5260  	// simulate successful restart happened
  5261  	s.mockSuccessfulReboot(c, s.bloader, []snap.Type{snap.TypeKernel})
  5262  
  5263  	// continue
  5264  	st.Unlock()
  5265  	err = s.o.Settle(settleTimeout)
  5266  	st.Lock()
  5267  	c.Assert(err, IsNil)
  5268  
  5269  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  5270  
  5271  	// ensure tasks were run in the right order
  5272  	tasks := chg.Tasks()
  5273  	sort.Sort(byReadyTime(tasks))
  5274  
  5275  	// first all downloads/checks in sequential order
  5276  	var i int
  5277  	i += validateDownloadCheckTasks(c, tasks[i:], "pc-kernel", "2", "18")
  5278  	i += validateDownloadCheckTasks(c, tasks[i:], "foo", "1", "stable")
  5279  
  5280  	// then all installs in sequential order
  5281  	i += validateRefreshTasks(c, tasks[i:], "pc-kernel", "2", isKernel)
  5282  	i += validateInstallTasks(c, tasks[i:], "foo", "1", 0)
  5283  
  5284  	// ensure that we only have the tasks we checked (plus the one
  5285  	// extra "set-model" task)
  5286  	c.Assert(tasks, HasLen, i+1)
  5287  }
  5288  
  5289  func (ms *kernelSuite) TestRemodelSwitchToDifferentKernel(c *C) {
  5290  	st := ms.o.State()
  5291  	st.Lock()
  5292  	defer st.Unlock()
  5293  
  5294  	// create a new model
  5295  	newModel := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  5296  		"kernel":         "brand-kernel",
  5297  		"revision":       "1",
  5298  		"required-snaps": []interface{}{"foo"},
  5299  	})
  5300  
  5301  	chg, err := devicestate.Remodel(st, newModel)
  5302  	c.Assert(err, IsNil)
  5303  
  5304  	st.Unlock()
  5305  	err = ms.o.Settle(settleTimeout)
  5306  	st.Lock()
  5307  	c.Assert(err, IsNil)
  5308  	c.Assert(chg.Err(), IsNil)
  5309  
  5310  	// system waits for a restart because of the new kernel
  5311  	t := findKind(chg, "auto-connect")
  5312  	c.Assert(t, NotNil)
  5313  	c.Assert(t.Status(), Equals, state.DoingStatus)
  5314  
  5315  	// check that the system tries to boot the new brand kernel
  5316  	c.Assert(ms.bloader.BootVars, DeepEquals, map[string]string{
  5317  		"snap_core":       "core_1.snap",
  5318  		"snap_kernel":     "pc-kernel_1.snap",
  5319  		"snap_try_kernel": "brand-kernel_2.snap",
  5320  		"snap_mode":       boot.TryStatus,
  5321  		"snap_try_core":   "",
  5322  	})
  5323  	// simulate successful system-restart bootenv updates (those
  5324  	// vars will be cleared by snapd on a restart)
  5325  	ms.mockSuccessfulReboot(c, ms.bloader, []snap.Type{snap.TypeKernel})
  5326  	// bootvars are as expected
  5327  	c.Assert(ms.bloader.BootVars, DeepEquals, map[string]string{
  5328  		"snap_core":       "core_1.snap",
  5329  		"snap_kernel":     "brand-kernel_2.snap",
  5330  		"snap_try_core":   "",
  5331  		"snap_try_kernel": "",
  5332  		"snap_mode":       boot.DefaultStatus,
  5333  	})
  5334  
  5335  	// continue
  5336  	st.Unlock()
  5337  	err = ms.o.Settle(settleTimeout)
  5338  	st.Lock()
  5339  	c.Assert(err, IsNil)
  5340  
  5341  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  5342  
  5343  	// bootvars are as expected (i.e. nothing has changed since this
  5344  	// test simulated that we booted successfully)
  5345  	c.Assert(ms.bloader.BootVars, DeepEquals, map[string]string{
  5346  		"snap_core":       "core_1.snap",
  5347  		"snap_kernel":     "brand-kernel_2.snap",
  5348  		"snap_try_kernel": "",
  5349  		"snap_try_core":   "",
  5350  		"snap_mode":       boot.DefaultStatus,
  5351  	})
  5352  
  5353  	// ensure tasks were run in the right order
  5354  	tasks := chg.Tasks()
  5355  	sort.Sort(byReadyTime(tasks))
  5356  
  5357  	// first all downloads/checks in sequential order
  5358  	var i int
  5359  	i += validateDownloadCheckTasks(c, tasks[i:], "brand-kernel", "2", "stable")
  5360  	i += validateDownloadCheckTasks(c, tasks[i:], "foo", "1", "stable")
  5361  
  5362  	// then all installs in sequential order
  5363  	i += validateInstallTasks(c, tasks[i:], "brand-kernel", "2", isKernel)
  5364  	i += validateInstallTasks(c, tasks[i:], "foo", "1", 0)
  5365  
  5366  	// ensure that we only have the tasks we checked (plus the one
  5367  	// extra "set-model" task)
  5368  	c.Assert(tasks, HasLen, i+1)
  5369  
  5370  	// ensure we did not try device registration
  5371  	for _, t := range st.Tasks() {
  5372  		if t.Kind() == "request-serial" {
  5373  			c.Fatalf("test should not create a request-serial task but did")
  5374  		}
  5375  	}
  5376  }
  5377  
  5378  func (ms *kernelSuite) TestRemodelSwitchToDifferentKernelUndo(c *C) {
  5379  	st := ms.o.State()
  5380  	st.Lock()
  5381  	defer st.Unlock()
  5382  
  5383  	// create a new model
  5384  	newModel := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  5385  		"kernel":         "brand-kernel",
  5386  		"revision":       "1",
  5387  		"required-snaps": []interface{}{"foo"},
  5388  	})
  5389  
  5390  	devicestate.InjectSetModelError(fmt.Errorf("boom"))
  5391  	defer devicestate.InjectSetModelError(nil)
  5392  
  5393  	chg, err := devicestate.Remodel(st, newModel)
  5394  	c.Assert(err, IsNil)
  5395  
  5396  	st.Unlock()
  5397  	err = ms.o.Settle(settleTimeout)
  5398  	st.Lock()
  5399  	c.Assert(err, IsNil)
  5400  	c.Assert(chg.Err(), IsNil)
  5401  
  5402  	// system waits for a restart because of the new kernel
  5403  	t := findKind(chg, "auto-connect")
  5404  	c.Assert(t, NotNil)
  5405  	c.Assert(t.Status(), Equals, state.DoingStatus)
  5406  
  5407  	// simulate successful restart happened
  5408  	ms.mockSuccessfulReboot(c, ms.bloader, []snap.Type{snap.TypeKernel})
  5409  
  5410  	// continue
  5411  	st.Unlock()
  5412  	err = ms.o.Settle(settleTimeout)
  5413  	st.Lock()
  5414  	c.Assert(err, IsNil)
  5415  
  5416  	// the change was not successful
  5417  	c.Assert(chg.Status(), Equals, state.ErrorStatus)
  5418  
  5419  	// and we are in restarting state
  5420  	restarting, restartType := st.Restarting()
  5421  	c.Check(restarting, Equals, true)
  5422  	c.Check(restartType, Equals, state.RestartSystem)
  5423  
  5424  	// and the undo gave us our old kernel back
  5425  	c.Assert(ms.bloader.BootVars, DeepEquals, map[string]string{
  5426  		"snap_core":       "core_1.snap",
  5427  		"snap_try_core":   "",
  5428  		"snap_try_kernel": "pc-kernel_1.snap",
  5429  		"snap_kernel":     "brand-kernel_2.snap",
  5430  		"snap_mode":       boot.TryStatus,
  5431  	})
  5432  }
  5433  
  5434  func (ms *kernelSuite) TestRemodelSwitchToDifferentKernelUndoOnRollback(c *C) {
  5435  	st := ms.o.State()
  5436  	st.Lock()
  5437  	defer st.Unlock()
  5438  
  5439  	// create a new model
  5440  	newModel := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  5441  		"kernel":         "brand-kernel",
  5442  		"revision":       "1",
  5443  		"required-snaps": []interface{}{"foo"},
  5444  	})
  5445  
  5446  	devicestate.InjectSetModelError(fmt.Errorf("boom"))
  5447  	defer devicestate.InjectSetModelError(nil)
  5448  
  5449  	chg, err := devicestate.Remodel(st, newModel)
  5450  	c.Assert(err, IsNil)
  5451  
  5452  	st.Unlock()
  5453  	err = ms.o.Settle(settleTimeout)
  5454  	st.Lock()
  5455  	c.Assert(err, IsNil)
  5456  	c.Assert(chg.Err(), IsNil)
  5457  
  5458  	// system waits for a restart because of the new kernel
  5459  	t := findKind(chg, "auto-connect")
  5460  	c.Assert(t, NotNil)
  5461  	c.Assert(t.Status(), Equals, state.DoingStatus)
  5462  
  5463  	// simulate rollback of the kernel during reboot
  5464  	ms.mockRollbackAcrossReboot(c, ms.bloader, []snap.Type{snap.TypeKernel})
  5465  
  5466  	// continue
  5467  	st.Unlock()
  5468  	err = ms.o.Settle(settleTimeout)
  5469  	st.Lock()
  5470  	c.Assert(err, IsNil)
  5471  
  5472  	// the change was not successful
  5473  	c.Assert(chg.Status(), Equals, state.ErrorStatus)
  5474  
  5475  	// and we are *not* in restarting state
  5476  	restarting, _ := st.Restarting()
  5477  	c.Check(restarting, Equals, false)
  5478  
  5479  	// and the undo gave us our old kernel back
  5480  	c.Assert(ms.bloader.BootVars, DeepEquals, map[string]string{
  5481  		"snap_core":       "core_1.snap",
  5482  		"snap_try_core":   "",
  5483  		"snap_kernel":     "pc-kernel_1.snap",
  5484  		"snap_try_kernel": "",
  5485  		"snap_mode":       boot.DefaultStatus,
  5486  	})
  5487  }
  5488  
  5489  func (s *mgrsSuite) TestRemodelStoreSwitch(c *C) {
  5490  	s.prereqSnapAssertions(c, map[string]interface{}{
  5491  		"snap-name": "foo",
  5492  	})
  5493  	snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 1.0}", "foo"), "1")
  5494  	s.serveSnap(snapPath, "1")
  5495  
  5496  	// track the creation of new DeviceAndAutContext (for new Store)
  5497  	newDAC := false
  5498  
  5499  	mockServer := s.mockStore(c)
  5500  	defer mockServer.Close()
  5501  
  5502  	st := s.o.State()
  5503  	st.Lock()
  5504  	defer st.Unlock()
  5505  
  5506  	s.checkDeviceAndAuthContext = func(dac store.DeviceAndAuthContext) {
  5507  		// the DeviceAndAuthContext assumes state is unlocked
  5508  		st.Unlock()
  5509  		defer st.Lock()
  5510  		c.Check(dac, NotNil)
  5511  		stoID, err := dac.StoreID("")
  5512  		c.Assert(err, IsNil)
  5513  		c.Check(stoID, Equals, "switched-store")
  5514  		newDAC = true
  5515  	}
  5516  
  5517  	// create/set custom model assertion
  5518  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  5519  
  5520  	model := s.brands.Model("my-brand", "my-model", modelDefaults)
  5521  
  5522  	// setup model assertion
  5523  	err := assertstate.Add(st, model)
  5524  	c.Assert(err, IsNil)
  5525  
  5526  	// have a serial as well
  5527  	kpMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir)
  5528  	c.Assert(err, IsNil)
  5529  	err = kpMgr.Put(deviceKey)
  5530  	c.Assert(err, IsNil)
  5531  
  5532  	encDevKey, err := asserts.EncodePublicKey(deviceKey.PublicKey())
  5533  	c.Assert(err, IsNil)
  5534  	serial, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, map[string]interface{}{
  5535  		"authority-id":        "my-brand",
  5536  		"brand-id":            "my-brand",
  5537  		"model":               "my-model",
  5538  		"serial":              "store-switch-serial",
  5539  		"device-key":          string(encDevKey),
  5540  		"device-key-sha3-384": deviceKey.PublicKey().ID(),
  5541  		"timestamp":           time.Now().Format(time.RFC3339),
  5542  	}, nil, "")
  5543  	c.Assert(err, IsNil)
  5544  	err = assertstate.Add(st, serial)
  5545  	c.Assert(err, IsNil)
  5546  
  5547  	devicestatetest.SetDevice(st, &auth.DeviceState{
  5548  		Brand:  "my-brand",
  5549  		Model:  "my-model",
  5550  		KeyID:  deviceKey.PublicKey().ID(),
  5551  		Serial: "store-switch-serial",
  5552  	})
  5553  
  5554  	// create a new model
  5555  	newModel := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{
  5556  		"store":          "switched-store",
  5557  		"required-snaps": []interface{}{"foo"},
  5558  		"revision":       "1",
  5559  	})
  5560  
  5561  	s.expectedSerial = "store-switch-serial"
  5562  	s.expectedStore = "switched-store"
  5563  	s.sessionMacaroon = "switched-store-session"
  5564  
  5565  	chg, err := devicestate.Remodel(st, newModel)
  5566  	c.Assert(err, IsNil)
  5567  
  5568  	st.Unlock()
  5569  	err = s.o.Settle(settleTimeout)
  5570  	st.Lock()
  5571  	c.Assert(err, IsNil)
  5572  
  5573  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  5574  
  5575  	// the new required-snap "foo" is installed
  5576  	var snapst snapstate.SnapState
  5577  	err = snapstate.Get(st, "foo", &snapst)
  5578  	c.Assert(err, IsNil)
  5579  
  5580  	// and marked required
  5581  	c.Check(snapst.Required, Equals, true)
  5582  
  5583  	// a new store was made
  5584  	c.Check(newDAC, Equals, true)
  5585  
  5586  	// we have a session with the new store
  5587  	device, err := devicestatetest.Device(st)
  5588  	c.Assert(err, IsNil)
  5589  	c.Check(device.Serial, Equals, "store-switch-serial")
  5590  	c.Check(device.SessionMacaroon, Equals, "switched-store-session")
  5591  }
  5592  
  5593  func (s *mgrsSuite) TestRemodelSwitchGadgetTrack(c *C) {
  5594  	bloader := bootloadertest.Mock("mock", c.MkDir())
  5595  	bootloader.Force(bloader)
  5596  	defer bootloader.Force(nil)
  5597  
  5598  	restore := release.MockOnClassic(false)
  5599  	defer restore()
  5600  
  5601  	mockServer := s.mockStore(c)
  5602  	defer mockServer.Close()
  5603  
  5604  	st := s.o.State()
  5605  	st.Lock()
  5606  	defer st.Unlock()
  5607  
  5608  	si := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)}
  5609  	snapstate.Set(st, "pc", &snapstate.SnapState{
  5610  		Active:   true,
  5611  		Sequence: []*snap.SideInfo{si},
  5612  		Current:  snap.R(1),
  5613  		SnapType: "gadget",
  5614  	})
  5615  	gadgetSnapYaml := "name: pc\nversion: 2.0\ntype: gadget"
  5616  	gadgetYaml := `
  5617  volumes:
  5618      volume-id:
  5619          bootloader: grub
  5620  `
  5621  	snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si, [][]string{
  5622  		{"meta/gadget.yaml", gadgetYaml},
  5623  	})
  5624  	snapPath, _ := s.makeStoreTestSnapWithFiles(c, gadgetSnapYaml, "2", [][]string{
  5625  		{"meta/gadget.yaml", gadgetYaml},
  5626  	})
  5627  	s.serveSnap(snapPath, "2")
  5628  
  5629  	// create/set custom model assertion
  5630  	model := s.brands.Model("can0nical", "my-model", modelDefaults)
  5631  
  5632  	// setup model assertion
  5633  	devicestatetest.SetDevice(st, &auth.DeviceState{
  5634  		Brand:  "can0nical",
  5635  		Model:  "my-model",
  5636  		Serial: "serialserialserial",
  5637  	})
  5638  	err := assertstate.Add(st, model)
  5639  	c.Assert(err, IsNil)
  5640  	s.makeSerialAssertionInState(c, st, "can0nical", "my-model", "serialserialserial")
  5641  
  5642  	// create a new model
  5643  	newModel := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  5644  		"gadget":   "pc=18",
  5645  		"revision": "1",
  5646  	})
  5647  
  5648  	chg, err := devicestate.Remodel(st, newModel)
  5649  	c.Assert(err, IsNil)
  5650  
  5651  	st.Unlock()
  5652  	err = s.o.Settle(settleTimeout)
  5653  	st.Lock()
  5654  	c.Assert(err, IsNil)
  5655  	c.Assert(chg.Err(), IsNil)
  5656  
  5657  	// ensure tasks were run in the right order
  5658  	tasks := chg.Tasks()
  5659  	sort.Sort(byReadyTime(tasks))
  5660  
  5661  	// first all downloads/checks in sequential order
  5662  	var i int
  5663  	i += validateDownloadCheckTasks(c, tasks[i:], "pc", "2", "18")
  5664  
  5665  	// then all installs in sequential order
  5666  	i += validateRefreshTasks(c, tasks[i:], "pc", "2", isGadget)
  5667  
  5668  	// ensure that we only have the tasks we checked (plus the one
  5669  	// extra "set-model" task)
  5670  	c.Assert(tasks, HasLen, i+1)
  5671  }
  5672  
  5673  type mockUpdater struct{}
  5674  
  5675  func (m *mockUpdater) Backup() error { return nil }
  5676  
  5677  func (m *mockUpdater) Rollback() error { return nil }
  5678  
  5679  func (m *mockUpdater) Update() error { return nil }
  5680  
  5681  func (s *mgrsSuite) TestRemodelSwitchToDifferentGadget(c *C) {
  5682  	bloader := bootloadertest.Mock("mock", c.MkDir())
  5683  	bootloader.Force(bloader)
  5684  	defer bootloader.Force(nil)
  5685  	restore := release.MockOnClassic(false)
  5686  	defer restore()
  5687  
  5688  	mockServer := s.mockStore(c)
  5689  	defer mockServer.Close()
  5690  
  5691  	st := s.o.State()
  5692  	st.Lock()
  5693  	defer st.Unlock()
  5694  
  5695  	si := &snap.SideInfo{RealName: "core18", SnapID: fakeSnapID("core18"), Revision: snap.R(1)}
  5696  	snapstate.Set(st, "core18", &snapstate.SnapState{
  5697  		Active:   true,
  5698  		Sequence: []*snap.SideInfo{si},
  5699  		Current:  snap.R(1),
  5700  		SnapType: "base",
  5701  	})
  5702  	si2 := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)}
  5703  	gadgetSnapYaml := "name: pc\nversion: 1.0\ntype: gadget"
  5704  	snapstate.Set(st, "pc", &snapstate.SnapState{
  5705  		Active:   true,
  5706  		Sequence: []*snap.SideInfo{si2},
  5707  		Current:  snap.R(1),
  5708  		SnapType: "gadget",
  5709  	})
  5710  	gadgetYaml := `
  5711  volumes:
  5712      volume-id:
  5713          bootloader: grub
  5714          structure:
  5715            - name: foo
  5716              type: bare
  5717              size: 1M
  5718              content:
  5719                - image: foo.img
  5720  `
  5721  	snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si2, [][]string{
  5722  		{"meta/gadget.yaml", gadgetYaml},
  5723  		{"foo.img", "foo"},
  5724  	})
  5725  
  5726  	// add new gadget "other-pc" snap to fake store
  5727  	const otherPcYaml = `name: other-pc
  5728  type: gadget
  5729  version: 2`
  5730  	s.prereqSnapAssertions(c, map[string]interface{}{
  5731  		"snap-name":    "other-pc",
  5732  		"publisher-id": "can0nical",
  5733  	})
  5734  	otherGadgetYaml := `
  5735  volumes:
  5736      volume-id:
  5737          bootloader: grub
  5738          structure:
  5739            - name: foo
  5740              type: bare
  5741              size: 1M
  5742              content:
  5743                - image: new-foo.img
  5744  `
  5745  	snapPath, _ := s.makeStoreTestSnapWithFiles(c, otherPcYaml, "2", [][]string{
  5746  		// use a compatible gadget YAML
  5747  		{"meta/gadget.yaml", otherGadgetYaml},
  5748  		{"new-foo.img", "new foo"},
  5749  	})
  5750  	s.serveSnap(snapPath, "2")
  5751  
  5752  	updaterForStructureCalls := 0
  5753  	restore = gadget.MockUpdaterForStructure(func(ps *gadget.LaidOutStructure, rootDir, rollbackDir string, observer gadget.ContentUpdateObserver) (gadget.Updater, error) {
  5754  		updaterForStructureCalls++
  5755  		c.Assert(ps.Name, Equals, "foo")
  5756  		return &mockUpdater{}, nil
  5757  	})
  5758  	defer restore()
  5759  
  5760  	// create/set custom model assertion
  5761  	model := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  5762  		"gadget": "pc",
  5763  	})
  5764  
  5765  	// setup model assertion
  5766  	devicestatetest.SetDevice(st, &auth.DeviceState{
  5767  		Brand:  "can0nical",
  5768  		Model:  "my-model",
  5769  		Serial: "serialserialserial",
  5770  	})
  5771  	err := assertstate.Add(st, model)
  5772  	c.Assert(err, IsNil)
  5773  	s.makeSerialAssertionInState(c, st, "can0nical", "my-model", "serialserialserial")
  5774  
  5775  	// create a new model
  5776  	newModel := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  5777  		"gadget":   "other-pc=18",
  5778  		"revision": "1",
  5779  	})
  5780  
  5781  	chg, err := devicestate.Remodel(st, newModel)
  5782  	c.Assert(err, IsNil)
  5783  
  5784  	st.Unlock()
  5785  	err = s.o.Settle(settleTimeout)
  5786  	st.Lock()
  5787  	c.Assert(err, IsNil)
  5788  	c.Assert(chg.Err(), IsNil)
  5789  
  5790  	// gadget updater was set up
  5791  	c.Check(updaterForStructureCalls, Equals, 1)
  5792  
  5793  	// gadget update requests a restart
  5794  	restarting, _ := st.Restarting()
  5795  	c.Check(restarting, Equals, true)
  5796  
  5797  	// simulate successful restart happened
  5798  	state.MockRestarting(st, state.RestartUnset)
  5799  
  5800  	st.Unlock()
  5801  	err = s.o.Settle(settleTimeout)
  5802  	st.Lock()
  5803  	c.Assert(err, IsNil)
  5804  
  5805  	// ensure tasks were run in the right order
  5806  	tasks := chg.Tasks()
  5807  	sort.Sort(byReadyTime(tasks))
  5808  
  5809  	// first all downloads/checks
  5810  	var i int
  5811  	i += validateDownloadCheckTasks(c, tasks[i:], "other-pc", "2", "18")
  5812  
  5813  	// then all installs
  5814  	i += validateInstallTasks(c, tasks[i:], "other-pc", "2", isGadget)
  5815  
  5816  	// ensure that we only have the tasks we checked (plus the one
  5817  	// extra "set-model" task)
  5818  	c.Assert(tasks, HasLen, i+1)
  5819  }
  5820  
  5821  func (s *mgrsSuite) TestRemodelSwitchToIncompatibleGadget(c *C) {
  5822  	bloader := bootloadertest.Mock("mock", c.MkDir())
  5823  	bootloader.Force(bloader)
  5824  	defer bootloader.Force(nil)
  5825  	restore := release.MockOnClassic(false)
  5826  	defer restore()
  5827  
  5828  	mockServer := s.mockStore(c)
  5829  	defer mockServer.Close()
  5830  
  5831  	st := s.o.State()
  5832  	st.Lock()
  5833  	defer st.Unlock()
  5834  
  5835  	si := &snap.SideInfo{RealName: "core18", SnapID: fakeSnapID("core18"), Revision: snap.R(1)}
  5836  	snapstate.Set(st, "core18", &snapstate.SnapState{
  5837  		Active:   true,
  5838  		Sequence: []*snap.SideInfo{si},
  5839  		Current:  snap.R(1),
  5840  		SnapType: "base",
  5841  	})
  5842  	si2 := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)}
  5843  	gadgetSnapYaml := "name: pc\nversion: 1.0\ntype: gadget"
  5844  	snapstate.Set(st, "pc", &snapstate.SnapState{
  5845  		Active:   true,
  5846  		Sequence: []*snap.SideInfo{si2},
  5847  		Current:  snap.R(1),
  5848  		SnapType: "gadget",
  5849  	})
  5850  	gadgetYaml := `
  5851  volumes:
  5852      volume-id:
  5853          bootloader: grub
  5854          structure:
  5855            - name: foo
  5856              type: 00000000-0000-0000-0000-0000deadcafe
  5857              size: 10M
  5858  `
  5859  	snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si2, [][]string{
  5860  		{"meta/gadget.yaml", gadgetYaml},
  5861  	})
  5862  
  5863  	// add new gadget "other-pc" snap to fake store
  5864  	const otherPcYaml = `name: other-pc
  5865  type: gadget
  5866  version: 2`
  5867  	// new gadget layout is incompatible, a structure that exited before has
  5868  	// a different size now
  5869  	otherGadgetYaml := `
  5870  volumes:
  5871      volume-id:
  5872          bootloader: grub
  5873          structure:
  5874            - name: foo
  5875              type: 00000000-0000-0000-0000-0000deadcafe
  5876              size: 20M
  5877  `
  5878  	s.prereqSnapAssertions(c, map[string]interface{}{
  5879  		"snap-name":    "other-pc",
  5880  		"publisher-id": "can0nical",
  5881  	})
  5882  	snapPath, _ := s.makeStoreTestSnapWithFiles(c, otherPcYaml, "2", [][]string{
  5883  		{"meta/gadget.yaml", otherGadgetYaml},
  5884  	})
  5885  	s.serveSnap(snapPath, "2")
  5886  
  5887  	// create/set custom model assertion
  5888  	model := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  5889  		"gadget": "pc",
  5890  	})
  5891  
  5892  	// setup model assertion
  5893  	devicestatetest.SetDevice(st, &auth.DeviceState{
  5894  		Brand:  "can0nical",
  5895  		Model:  "my-model",
  5896  		Serial: "serialserialserial",
  5897  	})
  5898  	err := assertstate.Add(st, model)
  5899  	c.Assert(err, IsNil)
  5900  	s.makeSerialAssertionInState(c, st, "can0nical", "my-model", "serialserialserial")
  5901  
  5902  	// create a new model
  5903  	newModel := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  5904  		"gadget":   "other-pc=18",
  5905  		"revision": "1",
  5906  	})
  5907  
  5908  	chg, err := devicestate.Remodel(st, newModel)
  5909  	c.Assert(err, IsNil)
  5910  
  5911  	st.Unlock()
  5912  	err = s.o.Settle(settleTimeout)
  5913  	st.Lock()
  5914  	c.Assert(err, IsNil)
  5915  	c.Assert(chg.Err(), ErrorMatches, `cannot perform the following tasks:\n.*cannot remodel to an incompatible gadget: .*cannot change structure size.*`)
  5916  }
  5917  
  5918  func (s *mgrsSuite) TestHappyDeviceRegistrationWithPrepareDeviceHook(c *C) {
  5919  	// just to 404 locally eager account-key requests
  5920  	mockStoreServer := s.mockStore(c)
  5921  	defer mockStoreServer.Close()
  5922  
  5923  	model := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{
  5924  		"gadget": "gadget",
  5925  	})
  5926  
  5927  	// reset as seeded but not registered
  5928  	// shortcut: have already device key
  5929  	kpMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir)
  5930  	c.Assert(err, IsNil)
  5931  	err = kpMgr.Put(deviceKey)
  5932  	c.Assert(err, IsNil)
  5933  
  5934  	st := s.o.State()
  5935  	st.Lock()
  5936  	defer st.Unlock()
  5937  
  5938  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  5939  	devicestatetest.SetDevice(st, &auth.DeviceState{
  5940  		Brand: "my-brand",
  5941  		Model: "my-model",
  5942  		KeyID: deviceKey.PublicKey().ID(),
  5943  	})
  5944  	err = assertstate.Add(st, model)
  5945  	c.Assert(err, IsNil)
  5946  
  5947  	signSerial := func(c *C, bhv *devicestatetest.DeviceServiceBehavior, headers map[string]interface{}, body []byte) (serial asserts.Assertion, ancillary []asserts.Assertion, err error) {
  5948  		brandID := headers["brand-id"].(string)
  5949  		model := headers["model"].(string)
  5950  		c.Check(brandID, Equals, "my-brand")
  5951  		c.Check(model, Equals, "my-model")
  5952  		headers["authority-id"] = brandID
  5953  		a, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, headers, body, "")
  5954  		return a, nil, err
  5955  	}
  5956  
  5957  	bhv := &devicestatetest.DeviceServiceBehavior{
  5958  		ReqID:            "REQID-1",
  5959  		RequestIDURLPath: "/svc/request-id",
  5960  		SerialURLPath:    "/svc/serial",
  5961  		SignSerial:       signSerial,
  5962  	}
  5963  
  5964  	mockServer := devicestatetest.MockDeviceService(c, bhv)
  5965  	defer mockServer.Close()
  5966  
  5967  	pDBhv := &devicestatetest.PrepareDeviceBehavior{
  5968  		DeviceSvcURL: mockServer.URL + "/svc/",
  5969  		Headers: map[string]string{
  5970  			"x-extra-header": "extra",
  5971  		},
  5972  		RegBody: map[string]string{
  5973  			"mac": "00:00:00:00:ff:00",
  5974  		},
  5975  		ProposedSerial: "12000",
  5976  	}
  5977  
  5978  	r := devicestatetest.MockGadget(c, st, "gadget", snap.R(2), pDBhv)
  5979  	defer r()
  5980  
  5981  	// run the whole device registration process
  5982  	st.Unlock()
  5983  	err = s.o.Settle(settleTimeout)
  5984  	st.Lock()
  5985  	c.Assert(err, IsNil)
  5986  
  5987  	var becomeOperational *state.Change
  5988  	for _, chg := range st.Changes() {
  5989  		if chg.Kind() == "become-operational" {
  5990  			becomeOperational = chg
  5991  			break
  5992  		}
  5993  	}
  5994  	c.Assert(becomeOperational, NotNil)
  5995  
  5996  	c.Check(becomeOperational.Status().Ready(), Equals, true)
  5997  	c.Check(becomeOperational.Err(), IsNil)
  5998  
  5999  	device, err := devicestatetest.Device(st)
  6000  	c.Assert(err, IsNil)
  6001  	c.Check(device.Brand, Equals, "my-brand")
  6002  	c.Check(device.Model, Equals, "my-model")
  6003  	c.Check(device.Serial, Equals, "12000")
  6004  
  6005  	a, err := assertstate.DB(st).Find(asserts.SerialType, map[string]string{
  6006  		"brand-id": "my-brand",
  6007  		"model":    "my-model",
  6008  		"serial":   "12000",
  6009  	})
  6010  	c.Assert(err, IsNil)
  6011  	serial := a.(*asserts.Serial)
  6012  
  6013  	var details map[string]interface{}
  6014  	err = yaml.Unmarshal(serial.Body(), &details)
  6015  	c.Assert(err, IsNil)
  6016  
  6017  	c.Check(details, DeepEquals, map[string]interface{}{
  6018  		"mac": "00:00:00:00:ff:00",
  6019  	})
  6020  
  6021  	c.Check(serial.DeviceKey().ID(), Equals, device.KeyID)
  6022  }
  6023  
  6024  func (s *mgrsSuite) TestRemodelReregistration(c *C) {
  6025  	s.prereqSnapAssertions(c, map[string]interface{}{
  6026  		"snap-name": "foo",
  6027  	})
  6028  	snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 1.0}", "foo"), "1")
  6029  	s.serveSnap(snapPath, "1")
  6030  
  6031  	// track the creation of new DeviceAndAutContext (for new Store)
  6032  	newDAC := false
  6033  
  6034  	mockServer := s.mockStore(c)
  6035  	defer mockServer.Close()
  6036  
  6037  	st := s.o.State()
  6038  	st.Lock()
  6039  	defer st.Unlock()
  6040  
  6041  	s.checkDeviceAndAuthContext = func(dac store.DeviceAndAuthContext) {
  6042  		// the DeviceAndAuthContext assumes state is unlocked
  6043  		st.Unlock()
  6044  		defer st.Lock()
  6045  		c.Check(dac, NotNil)
  6046  		stoID, err := dac.StoreID("")
  6047  		c.Assert(err, IsNil)
  6048  		c.Check(stoID, Equals, "my-brand-substore")
  6049  		newDAC = true
  6050  	}
  6051  
  6052  	model := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{
  6053  		"gadget": "gadget",
  6054  	})
  6055  
  6056  	// setup initial device identity
  6057  	kpMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir)
  6058  	c.Assert(err, IsNil)
  6059  	err = kpMgr.Put(deviceKey)
  6060  	c.Assert(err, IsNil)
  6061  
  6062  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  6063  	devicestatetest.SetDevice(st, &auth.DeviceState{
  6064  		Brand:  "my-brand",
  6065  		Model:  "my-model",
  6066  		KeyID:  deviceKey.PublicKey().ID(),
  6067  		Serial: "orig-serial",
  6068  	})
  6069  	err = assertstate.Add(st, model)
  6070  	c.Assert(err, IsNil)
  6071  
  6072  	encDevKey, err := asserts.EncodePublicKey(deviceKey.PublicKey())
  6073  	c.Assert(err, IsNil)
  6074  	serialHeaders := map[string]interface{}{
  6075  		"brand-id":            "my-brand",
  6076  		"model":               "my-model",
  6077  		"serial":              "orig-serial",
  6078  		"device-key":          string(encDevKey),
  6079  		"device-key-sha3-384": deviceKey.PublicKey().ID(),
  6080  		"timestamp":           time.Now().Format(time.RFC3339),
  6081  	}
  6082  	serialA, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, serialHeaders, nil, "")
  6083  	c.Assert(err, IsNil)
  6084  	serial := serialA.(*asserts.Serial)
  6085  	err = assertstate.Add(st, serial)
  6086  	c.Assert(err, IsNil)
  6087  
  6088  	signSerial := func(c *C, bhv *devicestatetest.DeviceServiceBehavior, headers map[string]interface{}, body []byte) (serial asserts.Assertion, ancillary []asserts.Assertion, err error) {
  6089  		brandID := headers["brand-id"].(string)
  6090  		model := headers["model"].(string)
  6091  		c.Check(brandID, Equals, "my-brand")
  6092  		c.Check(model, Equals, "other-model")
  6093  		headers["authority-id"] = brandID
  6094  		a, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, headers, body, "")
  6095  		return a, nil, err
  6096  	}
  6097  
  6098  	bhv := &devicestatetest.DeviceServiceBehavior{
  6099  		ReqID:            "REQID-1",
  6100  		RequestIDURLPath: "/svc/request-id",
  6101  		SerialURLPath:    "/svc/serial",
  6102  		SignSerial:       signSerial,
  6103  	}
  6104  
  6105  	mockDeviceService := devicestatetest.MockDeviceService(c, bhv)
  6106  	defer mockDeviceService.Close()
  6107  
  6108  	r := devicestatetest.MockGadget(c, st, "gadget", snap.R(2), nil)
  6109  	defer r()
  6110  
  6111  	// set registration config on gadget
  6112  	tr := config.NewTransaction(st)
  6113  	c.Assert(tr.Set("gadget", "device-service.url", mockDeviceService.URL+"/svc/"), IsNil)
  6114  	c.Assert(tr.Set("gadget", "registration.proposed-serial", "orig-serial"), IsNil)
  6115  	tr.Commit()
  6116  
  6117  	// run the remodel
  6118  	// create a new model
  6119  	newModel := s.brands.Model("my-brand", "other-model", modelDefaults, map[string]interface{}{
  6120  		"store":          "my-brand-substore",
  6121  		"gadget":         "gadget",
  6122  		"required-snaps": []interface{}{"foo"},
  6123  	})
  6124  
  6125  	s.expectedSerial = "orig-serial"
  6126  	s.expectedStore = "my-brand-substore"
  6127  	s.sessionMacaroon = "other-store-session"
  6128  
  6129  	chg, err := devicestate.Remodel(st, newModel)
  6130  	c.Assert(err, IsNil)
  6131  
  6132  	st.Unlock()
  6133  	err = s.o.Settle(settleTimeout)
  6134  	st.Lock()
  6135  	c.Assert(err, IsNil)
  6136  
  6137  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  6138  
  6139  	device, err := devicestatetest.Device(st)
  6140  	c.Assert(err, IsNil)
  6141  	c.Check(device.Brand, Equals, "my-brand")
  6142  	c.Check(device.Model, Equals, "other-model")
  6143  	c.Check(device.Serial, Equals, "orig-serial")
  6144  
  6145  	a, err := assertstate.DB(st).Find(asserts.SerialType, map[string]string{
  6146  		"brand-id": "my-brand",
  6147  		"model":    "other-model",
  6148  		"serial":   "orig-serial",
  6149  	})
  6150  	c.Assert(err, IsNil)
  6151  	serial = a.(*asserts.Serial)
  6152  
  6153  	c.Check(serial.Body(), HasLen, 0)
  6154  	c.Check(serial.DeviceKey().ID(), Equals, device.KeyID)
  6155  
  6156  	// the new required-snap "foo" is installed
  6157  	var snapst snapstate.SnapState
  6158  	err = snapstate.Get(st, "foo", &snapst)
  6159  	c.Assert(err, IsNil)
  6160  
  6161  	// and marked required
  6162  	c.Check(snapst.Required, Equals, true)
  6163  
  6164  	// a new store was made
  6165  	c.Check(newDAC, Equals, true)
  6166  
  6167  	// we have a session with the new store
  6168  	c.Check(device.SessionMacaroon, Equals, "other-store-session")
  6169  }
  6170  
  6171  const pcGadgetSnapYaml = `
  6172  version: 1.0
  6173  name: pc
  6174  type: gadget
  6175  base: core20
  6176  `
  6177  
  6178  const pcKernelSnapYaml = `
  6179  version: 1.0
  6180  name: pc-kernel
  6181  type: kernel
  6182  `
  6183  
  6184  const core20SnapYaml = `
  6185  version: 1.0
  6186  name: core20
  6187  type: base
  6188  `
  6189  
  6190  const snapdSnapYaml = `
  6191  version: 1.0
  6192  name: snapd
  6193  type: snapd
  6194  `
  6195  
  6196  const grubBootConfig = "# Snapd-Boot-Config-Edition: 1\n"
  6197  
  6198  var (
  6199  	pcGadgetFiles = [][]string{
  6200  		{"meta/gadget.yaml", pcGadgetYaml},
  6201  		{"grub.conf", ""},
  6202  		{"grub.conf", ""},
  6203  		// SHA3-384: 21e42a075b0d7bb6177c0eb3b3a1c8c6de6d4b4f902759eae5555e9cf3bebd21277a27102fd5426da989bde96c0cf848
  6204  		{"bootx64.efi", "content"},
  6205  		{"grubx64.efi", "content"},
  6206  	}
  6207  	pcKernelFiles = [][]string{
  6208  		{"kernel.efi", "kernel-efi"},
  6209  	}
  6210  	snapYamlsForRemodel = map[string]string{
  6211  		"pc":        pcGadgetSnapYaml,
  6212  		"pc-kernel": pcKernelSnapYaml,
  6213  		"core20":    core20SnapYaml,
  6214  		"snapd":     snapdSnapYaml,
  6215  	}
  6216  	snapFilesForRemodel = map[string][][]string{
  6217  		"pc":        pcGadgetFiles,
  6218  		"pc-kernel": pcKernelFiles,
  6219  	}
  6220  )
  6221  
  6222  func (s *mgrsSuite) makeInstalledSnapInStateForRemodel(c *C, name string, rev snap.Revision) *snap.Info {
  6223  	si := &snap.SideInfo{
  6224  		RealName: name,
  6225  		SnapID:   fakeSnapID(name),
  6226  		Revision: rev,
  6227  	}
  6228  	snapInfo := snaptest.MakeSnapFileAndDir(c, snapYamlsForRemodel[name],
  6229  		snapFilesForRemodel[name], si)
  6230  	snapstate.Set(s.o.State(), name, &snapstate.SnapState{
  6231  		SnapType: string(snapInfo.Type()),
  6232  		Active:   true,
  6233  		Sequence: []*snap.SideInfo{si},
  6234  		Current:  si.Revision,
  6235  	})
  6236  	sha3_384, size, err := asserts.SnapFileSHA3_384(snapInfo.MountFile())
  6237  	c.Assert(err, IsNil)
  6238  
  6239  	snapRev := s.makeStoreSnapRevision(c, name, rev.String(), sha3_384, size)
  6240  	err = assertstate.Add(s.o.State(), snapRev)
  6241  	c.Assert(err, IsNil)
  6242  	return snapInfo
  6243  }
  6244  
  6245  func (s *mgrsSuite) testRemodelUC20WithRecoverySystem(c *C, encrypted bool) {
  6246  	restore := release.MockOnClassic(false)
  6247  	defer restore()
  6248  	restore = devicestate.AllowUC20RemodelTesting(true)
  6249  	defer restore()
  6250  
  6251  	// mock directories that need to be tweaked by the test
  6252  	c.Assert(os.MkdirAll(boot.InitramfsUbuntuSeedDir, 0755), IsNil)
  6253  	c.Assert(os.MkdirAll(filepath.Base(dirs.SnapSeedDir), 0755), IsNil)
  6254  	// this is a bind mount in a real system
  6255  	c.Assert(os.Symlink(boot.InitramfsUbuntuSeedDir, dirs.SnapSeedDir), IsNil)
  6256  
  6257  	c.Assert(os.MkdirAll(filepath.Join(dirs.GlobalRootDir, "proc"), 0755), IsNil)
  6258  	restore = osutil.MockProcCmdline(filepath.Join(dirs.GlobalRootDir, "proc/cmdline"))
  6259  	defer restore()
  6260  
  6261  	// mock state related to boot assets
  6262  	for _, env := range []string{
  6263  		filepath.Join(boot.InitramfsUbuntuSeedDir, "EFI/ubuntu/grub.cfg"),
  6264  		filepath.Join(boot.InitramfsUbuntuSeedDir, "EFI/ubuntu/grubenv"),
  6265  		filepath.Join(boot.InitramfsUbuntuSeedDir, "EFI/boot/grubx64.efi"),
  6266  		filepath.Join(boot.InitramfsUbuntuSeedDir, "EFI/boot/bootx64.efi"),
  6267  		filepath.Join(boot.InitramfsUbuntuBootDir, "EFI/ubuntu/grub.cfg"),
  6268  		filepath.Join(boot.InitramfsUbuntuBootDir, "EFI/ubuntu/grubenv"),
  6269  		filepath.Join(boot.InitramfsUbuntuBootDir, "EFI/boot/grubx64.efi"),
  6270  		filepath.Join(dirs.GlobalRootDir, "/boot/grub/grub.cfg"),
  6271  		filepath.Join(dirs.GlobalRootDir, "/boot/grub/grubenv"),
  6272  	} {
  6273  		c.Assert(os.MkdirAll(filepath.Dir(env), 0755), IsNil)
  6274  		switch filepath.Base(env) {
  6275  		case "grubenv":
  6276  			e := grubenv.NewEnv(env)
  6277  			c.Assert(e.Save(), IsNil)
  6278  		case "grub.cfg":
  6279  			c.Assert(ioutil.WriteFile(env, []byte(grubBootConfig), 0644), IsNil)
  6280  		case "grubx64.efi", "bootx64.efi":
  6281  			c.Assert(ioutil.WriteFile(env, []byte("content"), 0644), IsNil)
  6282  		default:
  6283  			c.Assert(ioutil.WriteFile(env, nil, 0644), IsNil)
  6284  		}
  6285  	}
  6286  
  6287  	if encrypted {
  6288  		// boot assets are measured on an encrypted system
  6289  		assetsCacheDir := filepath.Join(dirs.SnapBootAssetsDirUnder(dirs.GlobalRootDir), "grub")
  6290  		c.Assert(os.MkdirAll(assetsCacheDir, 0755), IsNil)
  6291  		for _, cachedAsset := range []string{
  6292  			"grubx64.efi-21e42a075b0d7bb6177c0eb3b3a1c8c6de6d4b4f902759eae5555e9cf3bebd21277a27102fd5426da989bde96c0cf848",
  6293  			"bootx64.efi-21e42a075b0d7bb6177c0eb3b3a1c8c6de6d4b4f902759eae5555e9cf3bebd21277a27102fd5426da989bde96c0cf848",
  6294  		} {
  6295  			err := ioutil.WriteFile(filepath.Join(assetsCacheDir, cachedAsset), []byte("content"), 0644)
  6296  			c.Assert(err, IsNil)
  6297  		}
  6298  	}
  6299  
  6300  	// state of booted kernel
  6301  	c.Assert(os.MkdirAll(filepath.Join(dirs.GlobalRootDir, "boot/grub/pc-kernel_2.snap"), 0755), IsNil)
  6302  	c.Assert(ioutil.WriteFile(filepath.Join(dirs.GlobalRootDir, "/boot/grub/pc-kernel_2.snap/kernel.efi"),
  6303  		[]byte("kernel-efi"), 0755), IsNil)
  6304  	c.Assert(os.Symlink("pc-kernel_2.snap/kernel.efi", filepath.Join(dirs.GlobalRootDir, "boot/grub/kernel.efi")), IsNil)
  6305  
  6306  	if encrypted {
  6307  		stamp := filepath.Join(dirs.SnapFDEDir, "sealed-keys")
  6308  		c.Assert(os.MkdirAll(filepath.Dir(stamp), 0755), IsNil)
  6309  		c.Assert(ioutil.WriteFile(stamp, nil, 0644), IsNil)
  6310  	}
  6311  
  6312  	// new snaps from the store
  6313  	for _, name := range []string{"foo", "bar"} {
  6314  		s.prereqSnapAssertions(c, map[string]interface{}{
  6315  			"snap-name": name,
  6316  		})
  6317  		snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 1.0, base: core20}", name), "1")
  6318  		s.serveSnap(snapPath, "1")
  6319  	}
  6320  
  6321  	mockServer := s.mockStore(c)
  6322  	defer mockServer.Close()
  6323  
  6324  	st := s.o.State()
  6325  	st.Lock()
  6326  	defer st.Unlock()
  6327  
  6328  	err := assertstate.Add(st, s.devAcct)
  6329  	c.Assert(err, IsNil)
  6330  	// snaps in state
  6331  	pcInfo := s.makeInstalledSnapInStateForRemodel(c, "pc", snap.R(1))
  6332  	pcKernelInfo := s.makeInstalledSnapInStateForRemodel(c, "pc-kernel", snap.R(2))
  6333  	coreInfo := s.makeInstalledSnapInStateForRemodel(c, "core20", snap.R(3))
  6334  	snapdInfo := s.makeInstalledSnapInStateForRemodel(c, "snapd", snap.R(4))
  6335  
  6336  	// state of the current model
  6337  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir, "device"), 0755), IsNil)
  6338  
  6339  	// a regular UC20 model assertion
  6340  	uc20ModelDefaults := map[string]interface{}{
  6341  		"architecture": "amd64",
  6342  		"base":         "core20",
  6343  		"grade":        "dangerous",
  6344  		"snaps": []interface{}{
  6345  			map[string]interface{}{
  6346  				"name":            "pc-kernel",
  6347  				"id":              fakeSnapID("pc-kernel"),
  6348  				"type":            "kernel",
  6349  				"default-channel": "20",
  6350  			},
  6351  			map[string]interface{}{
  6352  				"name":            "pc",
  6353  				"id":              fakeSnapID("pc"),
  6354  				"type":            "gadget",
  6355  				"default-channel": "20",
  6356  			}},
  6357  	}
  6358  
  6359  	model := s.brands.Model("can0nical", "my-model", uc20ModelDefaults)
  6360  
  6361  	// setup model assertion
  6362  	devicestatetest.SetDevice(st, &auth.DeviceState{
  6363  		Brand:  "can0nical",
  6364  		Model:  "my-model",
  6365  		Serial: "serialserialserial",
  6366  	})
  6367  	err = assertstate.Add(st, model)
  6368  	c.Assert(err, IsNil)
  6369  	s.makeSerialAssertionInState(c, st, "can0nical", "my-model", "serialserialserial")
  6370  
  6371  	// now create a minimal uc20 seed dir with snaps/assertions
  6372  	seed20 := &seedtest.TestingSeed20{
  6373  		SeedSnaps: seedtest.SeedSnaps{
  6374  			StoreSigning: s.storeSigning,
  6375  			Brands:       s.brands,
  6376  		},
  6377  		SeedDir: dirs.SnapSeedDir,
  6378  	}
  6379  	restore = seed.MockTrusted(s.storeSigning.Trusted)
  6380  	defer restore()
  6381  
  6382  	seed20.MakeSeedWithModel(c, "1234", model, []*seedwriter.OptionsSnap{
  6383  		{Path: pcInfo.MountFile()},
  6384  		{Path: pcKernelInfo.MountFile()},
  6385  		{Path: coreInfo.MountFile()},
  6386  		{Path: snapdInfo.MountFile()},
  6387  	})
  6388  
  6389  	// create a new model
  6390  	newModel := s.brands.Model("can0nical", "my-model", uc20ModelDefaults, map[string]interface{}{
  6391  		"snaps": []interface{}{
  6392  			map[string]interface{}{
  6393  				"name":            "pc-kernel",
  6394  				"id":              fakeSnapID("pc-kernel"),
  6395  				"type":            "kernel",
  6396  				"default-channel": "20",
  6397  			},
  6398  			map[string]interface{}{
  6399  				"name":            "pc",
  6400  				"id":              fakeSnapID("pc"),
  6401  				"type":            "gadget",
  6402  				"default-channel": "20",
  6403  			},
  6404  			map[string]interface{}{
  6405  				"name":     "foo",
  6406  				"presence": "required",
  6407  			},
  6408  			map[string]interface{}{
  6409  				"name":     "bar",
  6410  				"presence": "required",
  6411  			}},
  6412  		"revision": "1",
  6413  	})
  6414  
  6415  	// mock the modeenv file
  6416  	m := &boot.Modeenv{
  6417  		Mode:                             "run",
  6418  		Base:                             "core20_3.snap",
  6419  		CurrentKernels:                   []string{"pc-kernel_2.snap"},
  6420  		CurrentRecoverySystems:           []string{"1234"},
  6421  		GoodRecoverySystems:              []string{"1234"},
  6422  		CurrentKernelCommandLines:        []string{"snapd_recovery_mode=run console=ttyS0 console=tty1 panic=-1"},
  6423  		CurrentTrustedRecoveryBootAssets: nil,
  6424  		CurrentTrustedBootAssets:         nil,
  6425  
  6426  		Model:          model.Model(),
  6427  		BrandID:        model.BrandID(),
  6428  		Grade:          string(model.Grade()),
  6429  		ModelSignKeyID: model.SignKeyID(),
  6430  	}
  6431  	if encrypted {
  6432  		m.CurrentTrustedRecoveryBootAssets = map[string][]string{
  6433  			// see gadget content
  6434  			"grubx64.efi": {"21e42a075b0d7bb6177c0eb3b3a1c8c6de6d4b4f902759eae5555e9cf3bebd21277a27102fd5426da989bde96c0cf848"},
  6435  			"bootx64.efi": {"21e42a075b0d7bb6177c0eb3b3a1c8c6de6d4b4f902759eae5555e9cf3bebd21277a27102fd5426da989bde96c0cf848"},
  6436  		}
  6437  		m.CurrentTrustedBootAssets = map[string][]string{
  6438  			"grubx64.efi": {"21e42a075b0d7bb6177c0eb3b3a1c8c6de6d4b4f902759eae5555e9cf3bebd21277a27102fd5426da989bde96c0cf848"},
  6439  		}
  6440  	}
  6441  
  6442  	// make sure cmdline matches what we expect in the modeenv
  6443  	c.Assert(ioutil.WriteFile(filepath.Join(dirs.GlobalRootDir, "proc/cmdline"),
  6444  		[]byte("snapd_recovery_mode=run console=ttyS0 console=tty1 panic=-1"), 0644),
  6445  		IsNil)
  6446  
  6447  	err = m.WriteTo("")
  6448  	c.Assert(err, IsNil)
  6449  	c.Assert(s.o.DeviceManager().ReloadModeenv(), IsNil)
  6450  
  6451  	bl, err := bootloader.Find(boot.InitramfsUbuntuSeedDir, &bootloader.Options{Role: bootloader.RoleRecovery})
  6452  	c.Assert(err, IsNil)
  6453  
  6454  	err = bl.SetBootVars(map[string]string{
  6455  		"snap_kernel": "pc-kernel_2.snap",
  6456  	})
  6457  	c.Assert(err, IsNil)
  6458  
  6459  	secbootResealCalls := 0
  6460  	restore = boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  6461  		secbootResealCalls++
  6462  		if !encrypted {
  6463  			return fmt.Errorf("unexpected call")
  6464  		}
  6465  		return nil
  6466  	})
  6467  	defer restore()
  6468  
  6469  	chg, err := devicestate.Remodel(st, newModel)
  6470  	c.Assert(err, IsNil)
  6471  
  6472  	c.Check(devicestate.Remodeling(st), Equals, true)
  6473  
  6474  	st.Unlock()
  6475  	err = s.o.Settle(settleTimeout)
  6476  	st.Lock()
  6477  	c.Assert(err, IsNil, Commentf(s.logbuf.String()))
  6478  
  6479  	c.Check(chg.Status(), Equals, state.DoingStatus, Commentf("remodel change failed: %v", chg.Err()))
  6480  	c.Check(devicestate.Remodeling(st), Equals, true)
  6481  	restarting, kind := st.Restarting()
  6482  	c.Check(restarting, Equals, true)
  6483  	c.Assert(kind, Equals, state.RestartSystemNow)
  6484  
  6485  	now := time.Now()
  6486  	expectedLabel := now.Format("20060102")
  6487  
  6488  	m, err = boot.ReadModeenv("")
  6489  	c.Assert(err, IsNil)
  6490  	c.Check(m.CurrentRecoverySystems, DeepEquals, []string{"1234", expectedLabel})
  6491  	c.Check(m.GoodRecoverySystems, DeepEquals, []string{"1234"})
  6492  
  6493  	vars, err := bl.GetBootVars("try_recovery_system", "recovery_system_status")
  6494  	c.Assert(err, IsNil)
  6495  	c.Assert(vars, DeepEquals, map[string]string{
  6496  		"try_recovery_system":    expectedLabel,
  6497  		"recovery_system_status": "try",
  6498  	})
  6499  
  6500  	// the new required-snap "foo" is not installed yet
  6501  	var snapst snapstate.SnapState
  6502  	err = snapstate.Get(st, "foo", &snapst)
  6503  	c.Assert(err, Equals, state.ErrNoState)
  6504  
  6505  	// simulate successful reboot to recovery and back
  6506  	state.MockRestarting(st, state.RestartUnset)
  6507  	// this would be done by snap-bootstrap in initramfs
  6508  	err = bl.SetBootVars(map[string]string{
  6509  		"try_recovery_system":    expectedLabel,
  6510  		"recovery_system_status": "tried",
  6511  	})
  6512  	c.Assert(err, IsNil)
  6513  
  6514  	// reset, so that after-reboot handling of tried system is executed
  6515  	s.o.DeviceManager().ResetToPostBootState()
  6516  	st.Unlock()
  6517  	err = s.o.DeviceManager().Ensure()
  6518  	st.Lock()
  6519  	c.Assert(err, IsNil)
  6520  
  6521  	st.Unlock()
  6522  	err = s.o.Settle(settleTimeout)
  6523  	st.Lock()
  6524  	c.Assert(err, IsNil)
  6525  
  6526  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("remodel change failed: %v", chg.Err()))
  6527  	// boot variables are cleared
  6528  	vars, err = bl.GetBootVars("try_recovery_system", "recovery_system_status")
  6529  	c.Assert(err, IsNil)
  6530  	c.Assert(vars, DeepEquals, map[string]string{
  6531  		"try_recovery_system":    "",
  6532  		"recovery_system_status": "",
  6533  	})
  6534  
  6535  	for _, name := range []string{"core20", "pc-kernel", "pc", "snapd", "foo", "bar"} {
  6536  		var snapst snapstate.SnapState
  6537  		err = snapstate.Get(st, name, &snapst)
  6538  		c.Assert(err, IsNil)
  6539  	}
  6540  
  6541  	// ensure sorting is correct
  6542  	tasks := chg.Tasks()
  6543  	sort.Sort(byReadyTime(tasks))
  6544  
  6545  	var i int
  6546  	// first all downloads/checks in sequential order
  6547  	for _, name := range []string{"foo", "bar"} {
  6548  		i += validateDownloadCheckTasks(c, tasks[i:], name, "1", "stable")
  6549  	}
  6550  	// then create recovery
  6551  	i += validateRecoverySystemTasks(c, tasks[i:], expectedLabel)
  6552  	// then all installs in sequential order
  6553  	for _, name := range []string{"foo", "bar"} {
  6554  		i += validateInstallTasks(c, tasks[i:], name, "1", 0)
  6555  	}
  6556  	// ensure that we only have the tasks we checked (plus the one
  6557  	// extra "set-model" task)
  6558  	c.Assert(tasks, HasLen, i+1)
  6559  
  6560  	const usesSnapd = true
  6561  	sd := seedtest.ValidateSeed(c, boot.InitramfsUbuntuSeedDir, expectedLabel, usesSnapd, s.storeSigning.Trusted)
  6562  	c.Assert(sd.Model(), DeepEquals, newModel)
  6563  
  6564  	m, err = boot.ReadModeenv("")
  6565  	c.Assert(err, IsNil)
  6566  	c.Check(m.CurrentRecoverySystems, DeepEquals, []string{"1234", expectedLabel})
  6567  	c.Check(m.GoodRecoverySystems, DeepEquals, []string{"1234", expectedLabel})
  6568  	if encrypted {
  6569  		// boot assets are measured and tracked in modeenv in an encrypted system
  6570  		c.Check(m, testutil.JsonEquals, &boot.Modeenv{
  6571  			Mode:                      "run",
  6572  			Base:                      "core20_3.snap",
  6573  			CurrentKernels:            []string{"pc-kernel_2.snap"},
  6574  			CurrentRecoverySystems:    []string{"1234", expectedLabel},
  6575  			GoodRecoverySystems:       []string{"1234", expectedLabel},
  6576  			CurrentKernelCommandLines: []string{"snapd_recovery_mode=run console=ttyS0 console=tty1 panic=-1"},
  6577  			CurrentTrustedRecoveryBootAssets: map[string][]string{
  6578  				"grubx64.efi": {"21e42a075b0d7bb6177c0eb3b3a1c8c6de6d4b4f902759eae5555e9cf3bebd21277a27102fd5426da989bde96c0cf848"},
  6579  				"bootx64.efi": {"21e42a075b0d7bb6177c0eb3b3a1c8c6de6d4b4f902759eae5555e9cf3bebd21277a27102fd5426da989bde96c0cf848"},
  6580  			},
  6581  			CurrentTrustedBootAssets: map[string][]string{
  6582  				"grubx64.efi": {"21e42a075b0d7bb6177c0eb3b3a1c8c6de6d4b4f902759eae5555e9cf3bebd21277a27102fd5426da989bde96c0cf848"},
  6583  			},
  6584  
  6585  			Model:          newModel.Model(),
  6586  			BrandID:        newModel.BrandID(),
  6587  			Grade:          string(newModel.Grade()),
  6588  			ModelSignKeyID: newModel.SignKeyID(),
  6589  		})
  6590  	} else {
  6591  		c.Check(m, testutil.JsonEquals, &boot.Modeenv{
  6592  			Mode:                      "run",
  6593  			Base:                      "core20_3.snap",
  6594  			CurrentKernels:            []string{"pc-kernel_2.snap"},
  6595  			CurrentRecoverySystems:    []string{"1234", expectedLabel},
  6596  			GoodRecoverySystems:       []string{"1234", expectedLabel},
  6597  			CurrentKernelCommandLines: []string{"snapd_recovery_mode=run console=ttyS0 console=tty1 panic=-1"},
  6598  
  6599  			Model:          newModel.Model(),
  6600  			BrandID:        newModel.BrandID(),
  6601  			Grade:          string(newModel.Grade()),
  6602  			ModelSignKeyID: newModel.SignKeyID(),
  6603  		})
  6604  	}
  6605  
  6606  	// new model has been written to ubuntu-boot/device/model
  6607  	var modelBytes bytes.Buffer
  6608  	c.Assert(asserts.NewEncoder(&modelBytes).Encode(newModel), IsNil)
  6609  	c.Assert(filepath.Join(boot.InitramfsUbuntuBootDir, "device/model"), testutil.FileEquals, modelBytes.String())
  6610  	if encrypted {
  6611  		// keys were resealed
  6612  		c.Assert(secbootResealCalls, Not(Equals), 0)
  6613  	} else {
  6614  		c.Assert(secbootResealCalls, Equals, 0)
  6615  	}
  6616  
  6617  	var seededSystems []map[string]interface{}
  6618  	err = st.Get("seeded-systems", &seededSystems)
  6619  	c.Assert(err, IsNil)
  6620  	c.Assert(seededSystems, HasLen, 1)
  6621  	// since we can't mock the seed timestamp, make sure it's within a
  6622  	// reasonable range, and then clear it
  6623  	c.Assert(seededSystems[0]["seed-time"], FitsTypeOf, "")
  6624  	ts, err := time.Parse(time.RFC3339Nano, seededSystems[0]["seed-time"].(string))
  6625  	c.Assert(err, IsNil)
  6626  	// should be more than enough for the test to finish
  6627  	c.Check(ts.Before(now.Add(10*time.Minute)), Equals, true, Commentf("seed-time is too late: %v", ts))
  6628  	seededSystems[0]["seed-time"] = ""
  6629  	c.Check(seededSystems, DeepEquals, []map[string]interface{}{
  6630  		{
  6631  			"system":    expectedLabel,
  6632  			"model":     newModel.Model(),
  6633  			"brand-id":  newModel.BrandID(),
  6634  			"revision":  float64(newModel.Revision()),
  6635  			"timestamp": newModel.Timestamp().Format(time.RFC3339Nano),
  6636  			// cleared earlier
  6637  			"seed-time": "",
  6638  		},
  6639  	})
  6640  }
  6641  
  6642  func (s *mgrsSuite) TestRemodelUC20WithRecoverySystemEncrypted(c *C) {
  6643  	const encrypted bool = true
  6644  	s.testRemodelUC20WithRecoverySystem(c, encrypted)
  6645  }
  6646  
  6647  func (s *mgrsSuite) TestRemodelUC20WithRecoverySystemUnencrypted(c *C) {
  6648  	const encrypted bool = false
  6649  	s.testRemodelUC20WithRecoverySystem(c, encrypted)
  6650  }
  6651  
  6652  func (s *mgrsSuite) TestCheckRefreshFailureWithConcurrentRemoveOfConnectedSnap(c *C) {
  6653  	hookMgr := s.o.HookManager()
  6654  	c.Assert(hookMgr, NotNil)
  6655  
  6656  	// force configure hook failure for some-snap.
  6657  	hookMgr.RegisterHijack("configure", "some-snap", func(ctx *hookstate.Context) error {
  6658  		return fmt.Errorf("failing configure hook")
  6659  	})
  6660  
  6661  	snapPath, _ := s.makeStoreTestSnap(c, someSnapYaml, "40")
  6662  	s.serveSnap(snapPath, "40")
  6663  	snapPath, _ = s.makeStoreTestSnap(c, otherSnapYaml, "50")
  6664  	s.serveSnap(snapPath, "50")
  6665  
  6666  	mockServer := s.mockStore(c)
  6667  	defer mockServer.Close()
  6668  
  6669  	st := s.o.State()
  6670  	st.Lock()
  6671  	defer st.Unlock()
  6672  
  6673  	st.Set("conns", map[string]interface{}{
  6674  		"other-snap:media-hub some-snap:media-hub": map[string]interface{}{"interface": "media-hub", "auto": false},
  6675  	})
  6676  
  6677  	si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)}
  6678  	snapInfo := snaptest.MockSnap(c, someSnapYaml, si)
  6679  
  6680  	oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)}
  6681  	otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi)
  6682  
  6683  	snapstate.Set(st, "some-snap", &snapstate.SnapState{
  6684  		Active:   true,
  6685  		Sequence: []*snap.SideInfo{si},
  6686  		Current:  snap.R(1),
  6687  		SnapType: "app",
  6688  	})
  6689  	snapstate.Set(st, "other-snap", &snapstate.SnapState{
  6690  		Active:   true,
  6691  		Sequence: []*snap.SideInfo{oi},
  6692  		Current:  snap.R(1),
  6693  		SnapType: "app",
  6694  	})
  6695  
  6696  	// add snaps to the repo and connect them
  6697  	repo := s.o.InterfaceManager().Repository()
  6698  	c.Assert(repo.AddSnap(snapInfo), IsNil)
  6699  	c.Assert(repo.AddSnap(otherInfo), IsNil)
  6700  	_, err := repo.Connect(&interfaces.ConnRef{
  6701  		PlugRef: interfaces.PlugRef{Snap: "other-snap", Name: "media-hub"},
  6702  		SlotRef: interfaces.SlotRef{Snap: "some-snap", Name: "media-hub"},
  6703  	}, nil, nil, nil, nil, nil)
  6704  	c.Assert(err, IsNil)
  6705  
  6706  	// refresh all
  6707  	c.Assert(assertstate.RefreshSnapDeclarations(st, 0, nil), IsNil)
  6708  
  6709  	ts, err := snapstate.Update(st, "some-snap", nil, 0, snapstate.Flags{})
  6710  	c.Assert(err, IsNil)
  6711  	chg := st.NewChange("refresh", "...")
  6712  	chg.AddAll(ts)
  6713  
  6714  	// remove other-snap
  6715  	ts2, err := snapstate.Remove(st, "other-snap", snap.R(0), &snapstate.RemoveFlags{Purge: true})
  6716  	c.Assert(err, IsNil)
  6717  	chg2 := st.NewChange("remove-snap", "...")
  6718  	chg2.AddAll(ts2)
  6719  
  6720  	st.Unlock()
  6721  	err = s.o.Settle(settleTimeout)
  6722  	st.Lock()
  6723  
  6724  	c.Check(err, IsNil)
  6725  
  6726  	// the refresh change has failed due to configure hook error
  6727  	c.Check(chg.Err(), ErrorMatches, `cannot perform the following tasks:\n.*failing configure hook.*`)
  6728  	c.Check(chg.Status(), Equals, state.ErrorStatus)
  6729  
  6730  	// download-snap is one of the first tasks in the refresh change, check that it was undone
  6731  	var downloadSnapStatus state.Status
  6732  	for _, t := range chg.Tasks() {
  6733  		if t.Kind() == "download-snap" {
  6734  			downloadSnapStatus = t.Status()
  6735  			break
  6736  		}
  6737  	}
  6738  	c.Check(downloadSnapStatus, Equals, state.UndoneStatus)
  6739  
  6740  	// the remove change succeeded
  6741  	c.Check(chg2.Err(), IsNil)
  6742  	c.Check(chg2.Status(), Equals, state.DoneStatus)
  6743  }
  6744  
  6745  func (s *mgrsSuite) TestInstallKernelSnapRollbackUpdatesBootloaderEnv(c *C) {
  6746  	bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
  6747  	bootloader.Force(bloader)
  6748  	defer bootloader.Force(nil)
  6749  
  6750  	restore := release.MockOnClassic(false)
  6751  	defer restore()
  6752  
  6753  	model := s.brands.Model("my-brand", "my-model", modelDefaults)
  6754  
  6755  	const packageKernel = `
  6756  name: pc-kernel
  6757  version: 4.0-1
  6758  type: kernel`
  6759  
  6760  	files := [][]string{
  6761  		{"kernel.img", "I'm a kernel"},
  6762  		{"initrd.img", "...and I'm an initrd"},
  6763  		{"meta/kernel.yaml", "version: 4.2"},
  6764  	}
  6765  	snapPath := snaptest.MakeTestSnapWithFiles(c, packageKernel, files)
  6766  
  6767  	st := s.o.State()
  6768  	st.Lock()
  6769  	defer st.Unlock()
  6770  
  6771  	// pretend we have core18/pc-kernel
  6772  	bloader.BootVars = map[string]string{
  6773  		"snap_core":   "core18_2.snap",
  6774  		"snap_kernel": "pc-kernel_123.snap",
  6775  		"snap_mode":   boot.DefaultStatus,
  6776  	}
  6777  	si1 := &snap.SideInfo{RealName: "pc-kernel", Revision: snap.R(123)}
  6778  	snapstate.Set(st, "pc-kernel", &snapstate.SnapState{
  6779  		SnapType: "kernel",
  6780  		Active:   true,
  6781  		Sequence: []*snap.SideInfo{si1},
  6782  		Current:  si1.Revision,
  6783  	})
  6784  	snaptest.MockSnapWithFiles(c, packageKernel, si1, [][]string{
  6785  		{"meta/kernel.yaml", ""},
  6786  	})
  6787  	si2 := &snap.SideInfo{RealName: "core18", Revision: snap.R(2)}
  6788  	snapstate.Set(st, "core18", &snapstate.SnapState{
  6789  		SnapType: "base",
  6790  		Active:   true,
  6791  		Sequence: []*snap.SideInfo{si2},
  6792  		Current:  si2.Revision,
  6793  	})
  6794  
  6795  	// setup model assertion
  6796  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  6797  	devicestatetest.SetDevice(st, &auth.DeviceState{
  6798  		Brand:  "my-brand",
  6799  		Model:  "my-model",
  6800  		Serial: "serialserialserial",
  6801  	})
  6802  	err := assertstate.Add(st, model)
  6803  	c.Assert(err, IsNil)
  6804  
  6805  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "pc-kernel"}, snapPath, "", "", snapstate.Flags{})
  6806  	c.Assert(err, IsNil)
  6807  
  6808  	chg := st.NewChange("install-snap", "...")
  6809  	chg.AddAll(ts)
  6810  
  6811  	// run, this will trigger a wait for the restart
  6812  	st.Unlock()
  6813  	err = s.o.Settle(settleTimeout)
  6814  	st.Lock()
  6815  	c.Assert(err, IsNil)
  6816  
  6817  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  6818  		"snap_core":       "core18_2.snap",
  6819  		"snap_try_core":   "",
  6820  		"snap_kernel":     "pc-kernel_123.snap",
  6821  		"snap_try_kernel": "pc-kernel_x1.snap",
  6822  		"snap_mode":       boot.TryStatus,
  6823  	})
  6824  
  6825  	// we are in restarting state and the change is not done yet
  6826  	restarting, _ := st.Restarting()
  6827  	c.Check(restarting, Equals, true)
  6828  	c.Check(chg.Status(), Equals, state.DoingStatus)
  6829  	s.mockRollbackAcrossReboot(c, bloader, []snap.Type{snap.TypeKernel})
  6830  
  6831  	// the kernel revision got rolled back
  6832  	var snapst snapstate.SnapState
  6833  	snapstate.Get(st, "pc-kernel", &snapst)
  6834  	info, err := snapst.CurrentInfo()
  6835  	c.Assert(err, IsNil)
  6836  	c.Assert(info.Revision, Equals, snap.R(123))
  6837  
  6838  	st.Unlock()
  6839  	err = s.o.Settle(settleTimeout)
  6840  	st.Lock()
  6841  	c.Assert(err, IsNil)
  6842  
  6843  	c.Assert(chg.Status(), Equals, state.ErrorStatus)
  6844  	c.Assert(chg.Err(), ErrorMatches, `(?ms).*cannot finish pc-kernel installation, there was a rollback across reboot\)`)
  6845  
  6846  	// and the bootvars are reset
  6847  	c.Check(bloader.BootVars, DeepEquals, map[string]string{
  6848  		"snap_core":       "core18_2.snap",
  6849  		"snap_kernel":     "pc-kernel_123.snap",
  6850  		"snap_mode":       boot.DefaultStatus,
  6851  		"snap_try_core":   "",
  6852  		"snap_try_kernel": "",
  6853  	})
  6854  }
  6855  
  6856  func (s *mgrsSuite) TestUC18SnapdRefreshUpdatesSnapServiceUnitsAndRestartsKilledUnits(c *C) {
  6857  	restore := release.MockReleaseInfo(&release.OS{ID: "ubuntu"})
  6858  	defer restore()
  6859  	// reload directories
  6860  	dirs.SetRootDir(dirs.GlobalRootDir)
  6861  	restore = release.MockOnClassic(false)
  6862  	defer restore()
  6863  	bl := bootloadertest.Mock("mock", c.MkDir())
  6864  	bootloader.Force(bl)
  6865  	defer bootloader.Force(nil)
  6866  	const snapdSnap = `
  6867  name: snapd
  6868  version: 1.0
  6869  type: snapd`
  6870  	snapPath := snaptest.MakeTestSnapWithFiles(c, snapdSnap, nil)
  6871  	si := &snap.SideInfo{RealName: "snapd"}
  6872  
  6873  	st := s.o.State()
  6874  	st.Lock()
  6875  
  6876  	// we must be seeded
  6877  	st.Set("seeded", true)
  6878  
  6879  	// add the test snap service
  6880  	testSnapSideInfo := &snap.SideInfo{RealName: "test-snap", Revision: snap.R(42)}
  6881  	snapstate.Set(st, "test-snap", &snapstate.SnapState{
  6882  		Sequence: []*snap.SideInfo{testSnapSideInfo},
  6883  		Current:  snap.R(42),
  6884  		Active:   true,
  6885  		SnapType: "app",
  6886  	})
  6887  	snaptest.MockSnapWithFiles(c, `name: test-snap
  6888  version: v1
  6889  apps:
  6890    svc1:
  6891      command: bin.sh
  6892      daemon: simple
  6893  `, testSnapSideInfo, nil)
  6894  
  6895  	// add the snap service unit with Requires=
  6896  	unitTempl := `[Unit]
  6897  # Auto-generated, DO NOT EDIT
  6898  Description=Service for snap application test-snap.svc1
  6899  Requires=%[1]s
  6900  Wants=network.target
  6901  After=%[1]s network.target snapd.apparmor.service
  6902  %[3]s=usr-lib-snapd.mount
  6903  After=usr-lib-snapd.mount
  6904  X-Snappy=yes
  6905  
  6906  [Service]
  6907  EnvironmentFile=-/etc/environment
  6908  ExecStart=/usr/bin/snap run test-snap.svc1
  6909  SyslogIdentifier=test-snap.svc1
  6910  Restart=on-failure
  6911  WorkingDirectory=%[2]s/var/snap/test-snap/42
  6912  TimeoutStopSec=30
  6913  Type=simple
  6914  
  6915  [Install]
  6916  WantedBy=multi-user.target
  6917  `
  6918  
  6919  	initialUnitFile := fmt.Sprintf(unitTempl,
  6920  		systemd.EscapeUnitNamePath(filepath.Join(dirs.SnapMountDir, "test-snap", "42.mount")),
  6921  		dirs.GlobalRootDir,
  6922  		"Requires",
  6923  	)
  6924  
  6925  	err := os.MkdirAll(dirs.SnapServicesDir, 0755)
  6926  	c.Assert(err, IsNil)
  6927  	err = ioutil.WriteFile(filepath.Join(dirs.SnapServicesDir, "snap.test-snap.svc1.service"), []byte(initialUnitFile), 0644)
  6928  	c.Assert(err, IsNil)
  6929  
  6930  	// we also need to setup the usr-lib-snapd.mount file too
  6931  	usrLibSnapdMountFile := filepath.Join(dirs.SnapServicesDir, wrappers.SnapdToolingMountUnit)
  6932  	err = ioutil.WriteFile(usrLibSnapdMountFile, nil, 0644)
  6933  	c.Assert(err, IsNil)
  6934  
  6935  	// the modification time of the usr-lib-snapd.mount file is the first
  6936  	// timestamp we use, then the stop time of the snap svc, then the stop time
  6937  	// of usr-lib-snapd.mount
  6938  	t0 := time.Now()
  6939  	t1 := t0.Add(1 * time.Hour)
  6940  	t2 := t0.Add(2 * time.Hour)
  6941  
  6942  	err = os.Chtimes(usrLibSnapdMountFile, t0, t0)
  6943  	c.Assert(err, IsNil)
  6944  
  6945  	systemctlCalls := 0
  6946  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
  6947  		systemctlCalls++
  6948  
  6949  		c.Logf("call: %v", systemctlCalls)
  6950  		switch systemctlCalls {
  6951  		// first 3 calls are for the snapd refresh itself
  6952  		case 1:
  6953  			c.Check(cmd, DeepEquals, []string{"daemon-reload"})
  6954  			return nil, nil
  6955  		case 2:
  6956  			c.Check(cmd, DeepEquals, []string{"enable", "snap-snapd-x1.mount"})
  6957  			return nil, nil
  6958  		case 3:
  6959  			c.Check(cmd, DeepEquals, []string{"start", "snap-snapd-x1.mount"})
  6960  			return nil, nil
  6961  			// next we get the calls for the rewritten service files after snapd
  6962  			// restarts
  6963  		case 4:
  6964  			c.Check(cmd, DeepEquals, []string{"daemon-reload"})
  6965  			return nil, nil
  6966  		case 5:
  6967  			c.Check(cmd, DeepEquals, []string{"enable", "usr-lib-snapd.mount"})
  6968  			return nil, nil
  6969  		case 6:
  6970  			c.Check(cmd, DeepEquals, []string{"stop", "usr-lib-snapd.mount"})
  6971  			return nil, nil
  6972  		case 7:
  6973  			c.Check(cmd, DeepEquals, []string{"show", "--property=ActiveState", "usr-lib-snapd.mount"})
  6974  			return []byte("ActiveState=inactive"), nil
  6975  		case 8:
  6976  			c.Check(cmd, DeepEquals, []string{"start", "usr-lib-snapd.mount"})
  6977  			return nil, nil
  6978  		case 9:
  6979  			c.Check(cmd, DeepEquals, []string{"daemon-reload"})
  6980  			return nil, nil
  6981  		case 10:
  6982  			c.Check(cmd, DeepEquals, []string{"show", "--property", "InactiveEnterTimestamp", "usr-lib-snapd.mount"})
  6983  			return []byte("InactiveEnterTimestamp=" + t2.Format("Mon 2006-01-02 15:04:05 MST")), nil
  6984  		case 11:
  6985  			c.Check(cmd, DeepEquals, []string{"show", "--property", "InactiveEnterTimestamp", "snap.test-snap.svc1.service"})
  6986  			return []byte("InactiveEnterTimestamp=" + t1.Format("Mon 2006-01-02 15:04:05 MST")), nil
  6987  		case 12:
  6988  			c.Check(cmd, DeepEquals, []string{"is-enabled", "snap.test-snap.svc1.service"})
  6989  			return []byte("enabled"), nil
  6990  		case 13:
  6991  			c.Check(cmd, DeepEquals, []string{"start", "snap.test-snap.svc1.service"})
  6992  			return nil, nil
  6993  		default:
  6994  			c.Errorf("unexpected call to systemctl: %+v", cmd)
  6995  			return nil, fmt.Errorf("broken test")
  6996  		}
  6997  	})
  6998  	s.AddCleanup(r)
  6999  	// make sure that we get the expected number of systemctl calls
  7000  	s.AddCleanup(func() { c.Assert(systemctlCalls, Equals, 13) })
  7001  
  7002  	// also add the snapd snap to state which we will refresh
  7003  	si1 := &snap.SideInfo{RealName: "snapd", Revision: snap.R(1)}
  7004  	snapstate.Set(st, "snapd", &snapstate.SnapState{
  7005  		SnapType: "snapd",
  7006  		Active:   true,
  7007  		Sequence: []*snap.SideInfo{si1},
  7008  		Current:  si1.Revision,
  7009  	})
  7010  	snaptest.MockSnapWithFiles(c, "name: snapd\ntype: snapd\nversion: 123", si1, nil)
  7011  
  7012  	// setup model assertion
  7013  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  7014  	devicestatetest.SetDevice(st, &auth.DeviceState{
  7015  		Brand:  "my-brand",
  7016  		Model:  "my-model",
  7017  		Serial: "serialserialserial",
  7018  	})
  7019  	// model := s.brands.Model("my-brand", "my-model", modelDefaults)
  7020  	model := s.brands.Model("my-brand", "my-model", map[string]interface{}{
  7021  		"type":         "model",
  7022  		"authority-id": "my-brand",
  7023  		"series":       "16",
  7024  		"brand-id":     "my-brand",
  7025  		"model":        "my-model",
  7026  		"gadget":       "pc",
  7027  		"kernel":       "kernel",
  7028  		"architecture": "amd64",
  7029  		"base":         "core18",
  7030  	})
  7031  	err = assertstate.Add(st, model)
  7032  	c.Assert(err, IsNil)
  7033  
  7034  	ts, _, err := snapstate.InstallPath(st, si, snapPath, "", "", snapstate.Flags{})
  7035  	c.Assert(err, IsNil)
  7036  
  7037  	chg := st.NewChange("install-snap", "...")
  7038  	chg.AddAll(ts)
  7039  
  7040  	// make sure we don't try to ensure snap services before the restart
  7041  	r = servicestate.MockEnsuredSnapServices(s.o.ServiceManager(), true)
  7042  	defer r()
  7043  
  7044  	// run, this will trigger wait for restart
  7045  	st.Unlock()
  7046  	err = s.o.Settle(settleTimeout)
  7047  	st.Lock()
  7048  	c.Assert(err, IsNil)
  7049  
  7050  	// check the snapd task state
  7051  	c.Check(chg.Status(), Equals, state.DoingStatus)
  7052  	restarting, kind := st.Restarting()
  7053  	c.Check(restarting, Equals, true)
  7054  	c.Assert(kind, Equals, state.RestartDaemon)
  7055  
  7056  	// now we do want the ensure loop to run though
  7057  	r = servicestate.MockEnsuredSnapServices(s.o.ServiceManager(), false)
  7058  	defer r()
  7059  
  7060  	// mock a restart of snapd to progress with the change
  7061  	state.MockRestarting(st, state.RestartUnset)
  7062  
  7063  	// let the change run its course
  7064  	st.Unlock()
  7065  	err = s.o.Settle(settleTimeout)
  7066  	st.Lock()
  7067  	defer st.Unlock()
  7068  	c.Assert(err, IsNil)
  7069  
  7070  	c.Check(chg.Status(), Equals, state.DoneStatus, Commentf("change failed: %v", chg.Err()))
  7071  
  7072  	// we don't restart since the unit file was just rewritten, no services were
  7073  	// killed
  7074  	restarting, _ = st.Restarting()
  7075  	c.Check(restarting, Equals, false)
  7076  
  7077  	// the unit file was rewritten to use Wants= now
  7078  	rewrittenUnitFile := fmt.Sprintf(unitTempl,
  7079  		systemd.EscapeUnitNamePath(filepath.Join(dirs.SnapMountDir, "test-snap", "42.mount")),
  7080  		dirs.GlobalRootDir,
  7081  		"Wants",
  7082  	)
  7083  	c.Assert(filepath.Join(dirs.SnapServicesDir, "snap.test-snap.svc1.service"), testutil.FileEquals, rewrittenUnitFile)
  7084  }
  7085  
  7086  func (s *mgrsSuite) TestUC18SnapdRefreshUpdatesSnapServiceUnitsAndAttemptsToRestartsKilledUnitsButFails(c *C) {
  7087  	restore := release.MockReleaseInfo(&release.OS{ID: "ubuntu"})
  7088  	defer restore()
  7089  	// reload directories
  7090  	dirs.SetRootDir(dirs.GlobalRootDir)
  7091  	restore = release.MockOnClassic(false)
  7092  	defer restore()
  7093  	bl := bootloadertest.Mock("mock", c.MkDir())
  7094  	bootloader.Force(bl)
  7095  	defer bootloader.Force(nil)
  7096  
  7097  	const snapdSnap = `
  7098  name: snapd
  7099  version: 1.0
  7100  type: snapd`
  7101  	snapPath := snaptest.MakeTestSnapWithFiles(c, snapdSnap, nil)
  7102  	si := &snap.SideInfo{RealName: "snapd"}
  7103  
  7104  	st := s.o.State()
  7105  	st.Lock()
  7106  
  7107  	// we must be seeded
  7108  	st.Set("seeded", true)
  7109  
  7110  	// add the test snap service
  7111  	testSnapSideInfo := &snap.SideInfo{RealName: "test-snap", Revision: snap.R(42)}
  7112  	snapstate.Set(st, "test-snap", &snapstate.SnapState{
  7113  		Sequence: []*snap.SideInfo{testSnapSideInfo},
  7114  		Current:  snap.R(42),
  7115  		Active:   true,
  7116  		SnapType: "app",
  7117  	})
  7118  	snaptest.MockSnapWithFiles(c, `name: test-snap
  7119  version: v1
  7120  apps:
  7121    svc1:
  7122      command: bin.sh
  7123      daemon: simple
  7124  `, testSnapSideInfo, nil)
  7125  
  7126  	// add the snap service unit with Requires=
  7127  	unitTempl := `[Unit]
  7128  # Auto-generated, DO NOT EDIT
  7129  Description=Service for snap application test-snap.svc1
  7130  Requires=%[1]s
  7131  Wants=network.target
  7132  After=%[1]s network.target snapd.apparmor.service
  7133  %[3]s=usr-lib-snapd.mount
  7134  After=usr-lib-snapd.mount
  7135  X-Snappy=yes
  7136  
  7137  [Service]
  7138  EnvironmentFile=-/etc/environment
  7139  ExecStart=/usr/bin/snap run test-snap.svc1
  7140  SyslogIdentifier=test-snap.svc1
  7141  Restart=on-failure
  7142  WorkingDirectory=%[2]s/var/snap/test-snap/42
  7143  TimeoutStopSec=30
  7144  Type=simple
  7145  
  7146  [Install]
  7147  WantedBy=multi-user.target
  7148  `
  7149  
  7150  	initialUnitFile := fmt.Sprintf(unitTempl,
  7151  		systemd.EscapeUnitNamePath(filepath.Join(dirs.SnapMountDir, "test-snap", "42.mount")),
  7152  		dirs.GlobalRootDir,
  7153  		"Requires",
  7154  	)
  7155  
  7156  	err := os.MkdirAll(dirs.SnapServicesDir, 0755)
  7157  	c.Assert(err, IsNil)
  7158  	err = ioutil.WriteFile(filepath.Join(dirs.SnapServicesDir, "snap.test-snap.svc1.service"), []byte(initialUnitFile), 0644)
  7159  	c.Assert(err, IsNil)
  7160  
  7161  	// we also need to setup the usr-lib-snapd.mount file too
  7162  	usrLibSnapdMountFile := filepath.Join(dirs.SnapServicesDir, wrappers.SnapdToolingMountUnit)
  7163  	err = ioutil.WriteFile(usrLibSnapdMountFile, nil, 0644)
  7164  	c.Assert(err, IsNil)
  7165  
  7166  	// the modification time of the usr-lib-snapd.mount file is the first
  7167  	// timestamp we use, then the stop time of the snap svc, then the stop time
  7168  	// of usr-lib-snapd.mount
  7169  	t0 := time.Now()
  7170  	t1 := t0.Add(1 * time.Hour)
  7171  	t2 := t0.Add(2 * time.Hour)
  7172  
  7173  	err = os.Chtimes(usrLibSnapdMountFile, t0, t0)
  7174  	c.Assert(err, IsNil)
  7175  
  7176  	systemctlCalls := 0
  7177  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
  7178  		systemctlCalls++
  7179  
  7180  		switch systemctlCalls {
  7181  		// first 3 calls are for the snapd refresh itself
  7182  		case 1:
  7183  			c.Check(cmd, DeepEquals, []string{"daemon-reload"})
  7184  			return nil, nil
  7185  		case 2:
  7186  			c.Check(cmd, DeepEquals, []string{"enable", "snap-snapd-x1.mount"})
  7187  			return nil, nil
  7188  		case 3:
  7189  			c.Check(cmd, DeepEquals, []string{"start", "snap-snapd-x1.mount"})
  7190  			return nil, nil
  7191  			// next we get the calls for the rewritten service files after snapd
  7192  			// restarts
  7193  		case 4:
  7194  			c.Check(cmd, DeepEquals, []string{"daemon-reload"})
  7195  			return nil, nil
  7196  		case 5:
  7197  			c.Check(cmd, DeepEquals, []string{"enable", "usr-lib-snapd.mount"})
  7198  			return nil, nil
  7199  		case 6:
  7200  			c.Check(cmd, DeepEquals, []string{"stop", "usr-lib-snapd.mount"})
  7201  			return nil, nil
  7202  		case 7:
  7203  			c.Check(cmd, DeepEquals, []string{"show", "--property=ActiveState", "usr-lib-snapd.mount"})
  7204  			return []byte("ActiveState=inactive"), nil
  7205  		case 8:
  7206  			c.Check(cmd, DeepEquals, []string{"start", "usr-lib-snapd.mount"})
  7207  			return nil, nil
  7208  		case 9:
  7209  			c.Check(cmd, DeepEquals, []string{"daemon-reload"})
  7210  			return nil, nil
  7211  		case 10:
  7212  			c.Check(cmd, DeepEquals, []string{"show", "--property", "InactiveEnterTimestamp", "usr-lib-snapd.mount"})
  7213  			return []byte("InactiveEnterTimestamp=" + t2.Format("Mon 2006-01-02 15:04:05 MST")), nil
  7214  		case 11:
  7215  			c.Check(cmd, DeepEquals, []string{"show", "--property", "InactiveEnterTimestamp", "snap.test-snap.svc1.service"})
  7216  			return []byte("InactiveEnterTimestamp=" + t1.Format("Mon 2006-01-02 15:04:05 MST")), nil
  7217  		case 12:
  7218  			c.Check(cmd, DeepEquals, []string{"is-enabled", "snap.test-snap.svc1.service"})
  7219  			return []byte("enabled"), nil
  7220  		case 13:
  7221  			// starting the snap fails
  7222  			c.Check(cmd, DeepEquals, []string{"start", "snap.test-snap.svc1.service"})
  7223  			return nil, fmt.Errorf("the snap service is having a bad day")
  7224  		case 14:
  7225  			// because starting the snap fails, we will automatically try to
  7226  			// undo the starting of the snap by stopping it, hence the request
  7227  			// to stop it
  7228  			// TODO: is this desirable? in the field, what if stopping the
  7229  			// service also dies?
  7230  			c.Check(cmd, DeepEquals, []string{"stop", "snap.test-snap.svc1.service"})
  7231  			return nil, fmt.Errorf("the snap service is still having a bad day")
  7232  		default:
  7233  			c.Errorf("unexpected call to systemctl: %+v", cmd)
  7234  			return nil, fmt.Errorf("broken test")
  7235  		}
  7236  	})
  7237  	s.AddCleanup(r)
  7238  	// make sure that we get the expected number of systemctl calls
  7239  	s.AddCleanup(func() { c.Assert(systemctlCalls, Equals, 14) })
  7240  
  7241  	// also add the snapd snap to state which we will refresh
  7242  	si1 := &snap.SideInfo{RealName: "snapd", Revision: snap.R(1)}
  7243  	snapstate.Set(st, "snapd", &snapstate.SnapState{
  7244  		SnapType: "snapd",
  7245  		Active:   true,
  7246  		Sequence: []*snap.SideInfo{si1},
  7247  		Current:  si1.Revision,
  7248  	})
  7249  	snaptest.MockSnapWithFiles(c, "name: snapd\ntype: snapd\nversion: 123", si1, nil)
  7250  
  7251  	// setup model assertion
  7252  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  7253  	devicestatetest.SetDevice(st, &auth.DeviceState{
  7254  		Brand:  "my-brand",
  7255  		Model:  "my-model",
  7256  		Serial: "serialserialserial",
  7257  	})
  7258  	// model := s.brands.Model("my-brand", "my-model", modelDefaults)
  7259  	model := s.brands.Model("my-brand", "my-model", map[string]interface{}{
  7260  		"type":         "model",
  7261  		"authority-id": "my-brand",
  7262  		"series":       "16",
  7263  		"brand-id":     "my-brand",
  7264  		"model":        "my-model",
  7265  		"gadget":       "pc",
  7266  		"kernel":       "kernel",
  7267  		"architecture": "amd64",
  7268  		"base":         "core18",
  7269  	})
  7270  	err = assertstate.Add(st, model)
  7271  	c.Assert(err, IsNil)
  7272  
  7273  	ts, _, err := snapstate.InstallPath(st, si, snapPath, "", "", snapstate.Flags{})
  7274  	c.Assert(err, IsNil)
  7275  
  7276  	chg := st.NewChange("install-snap", "...")
  7277  	chg.AddAll(ts)
  7278  
  7279  	// make sure we don't try to ensure snap services before the restart
  7280  	r = servicestate.MockEnsuredSnapServices(s.o.ServiceManager(), true)
  7281  	defer r()
  7282  
  7283  	// run, this will trigger wait for restart
  7284  	st.Unlock()
  7285  	err = s.o.Settle(settleTimeout)
  7286  	st.Lock()
  7287  	c.Assert(err, IsNil)
  7288  
  7289  	// check the snapd task state
  7290  	c.Check(chg.Status(), Equals, state.DoingStatus)
  7291  	restarting, kind := st.Restarting()
  7292  	c.Check(restarting, Equals, true)
  7293  	c.Assert(kind, Equals, state.RestartDaemon)
  7294  
  7295  	// now we do want the ensure loop to run though
  7296  	r = servicestate.MockEnsuredSnapServices(s.o.ServiceManager(), false)
  7297  	defer r()
  7298  
  7299  	// mock a restart of snapd to progress with the change
  7300  	state.MockRestarting(st, state.RestartUnset)
  7301  
  7302  	// let the change try to run its course
  7303  	st.Unlock()
  7304  	err = s.o.Settle(settleTimeout)
  7305  	st.Lock()
  7306  	c.Assert(err, ErrorMatches, `state ensure errors: \[error trying to restart killed services, immediately rebooting: the snap service is having a bad day\]`)
  7307  
  7308  	// the change is still in doing status
  7309  	c.Check(chg.Status(), Equals, state.DoingStatus)
  7310  
  7311  	// we do end up restarting now, since we tried to restart the service but
  7312  	// failed and so to be safe as possible we reboot the system immediately
  7313  	restarting, kind = st.Restarting()
  7314  	c.Check(restarting, Equals, true)
  7315  	c.Assert(kind, Equals, state.RestartSystemNow)
  7316  
  7317  	// the unit file was rewritten to use Wants= now
  7318  	rewrittenUnitFile := fmt.Sprintf(unitTempl,
  7319  		systemd.EscapeUnitNamePath(filepath.Join(dirs.SnapMountDir, "test-snap", "42.mount")),
  7320  		dirs.GlobalRootDir,
  7321  		"Wants",
  7322  	)
  7323  	c.Assert(filepath.Join(dirs.SnapServicesDir, "snap.test-snap.svc1.service"), testutil.FileEquals, rewrittenUnitFile)
  7324  
  7325  	// simulate a final restart to demonstrate that the change still finishes
  7326  	// properly - note that this isn't a fully honest test, since in reality it
  7327  	// should be done with a real overlord.RestartBehavior implemented like what
  7328  	// daemon actually provides and here we are using nil, but for the purposes
  7329  	// of this test it's enough to ensure that 1) a restart is requested and 2)
  7330  	// the manager ensure loop doesn't fail after we restart since the unit
  7331  	// files don't need to be rewritten
  7332  	state.MockRestarting(st, state.RestartUnset)
  7333  
  7334  	// we want the service ensure loop to run again to show it doesn't break
  7335  	// anything
  7336  	r = servicestate.MockEnsuredSnapServices(s.o.ServiceManager(), false)
  7337  	defer r()
  7338  
  7339  	st.Unlock()
  7340  	err = s.o.Settle(settleTimeout)
  7341  	st.Lock()
  7342  	defer st.Unlock()
  7343  	c.Assert(err, IsNil)
  7344  
  7345  	// the change is now fully done
  7346  	c.Check(chg.Status(), Equals, state.DoneStatus)
  7347  }
  7348  
  7349  func (s *mgrsSuite) testUC20RunUpdateManagedBootConfig(c *C, snapPath string, si *snap.SideInfo, bl bootloader.Bootloader, updated bool) {
  7350  	restore := release.MockOnClassic(false)
  7351  	defer restore()
  7352  
  7353  	// pretend we booted with the right kernel
  7354  	bl.SetBootVars(map[string]string{"snap_kernel": "pc-kernel_1.snap"})
  7355  
  7356  	uc20ModelDefaults := map[string]interface{}{
  7357  		"architecture": "amd64",
  7358  		"base":         "core20",
  7359  		"store":        "my-brand-store-id",
  7360  		"snaps": []interface{}{
  7361  			map[string]interface{}{
  7362  				"name":            "pc-kernel",
  7363  				"id":              snaptest.AssertedSnapID("pc-kernel"),
  7364  				"type":            "kernel",
  7365  				"default-channel": "20",
  7366  			},
  7367  			map[string]interface{}{
  7368  				"name":            "pc",
  7369  				"id":              snaptest.AssertedSnapID("pc"),
  7370  				"type":            "gadget",
  7371  				"default-channel": "20",
  7372  			}},
  7373  	}
  7374  
  7375  	model := s.brands.Model("my-brand", "my-model", uc20ModelDefaults)
  7376  
  7377  	// mock the modeenv file
  7378  	m := boot.Modeenv{
  7379  		Mode:           "run",
  7380  		RecoverySystem: "20191127",
  7381  		Base:           "core20_1.snap",
  7382  		CurrentKernelCommandLines: []string{
  7383  			"snapd_recovery_mode=run console=ttyS0 console=tty1 panic=-1",
  7384  		},
  7385  	}
  7386  	err := m.WriteTo("")
  7387  	c.Assert(err, IsNil)
  7388  	c.Assert(s.o.DeviceManager().ReloadModeenv(), IsNil)
  7389  
  7390  	st := s.o.State()
  7391  	st.Lock()
  7392  	// defer st.Unlock()
  7393  	st.Set("seeded", true)
  7394  
  7395  	si1 := &snap.SideInfo{RealName: "snapd", Revision: snap.R(1)}
  7396  	snapstate.Set(st, "snapd", &snapstate.SnapState{
  7397  		SnapType: "snapd",
  7398  		Active:   true,
  7399  		Sequence: []*snap.SideInfo{si1},
  7400  		Current:  si1.Revision,
  7401  	})
  7402  	snaptest.MockSnapWithFiles(c, "name: snapd\ntype: snapd\nversion: 123", si1, nil)
  7403  
  7404  	si2 := &snap.SideInfo{RealName: "core20", Revision: snap.R(1)}
  7405  	snapstate.Set(st, "core20", &snapstate.SnapState{
  7406  		SnapType: "base",
  7407  
  7408  		Active:   true,
  7409  		Sequence: []*snap.SideInfo{si2},
  7410  		Current:  si2.Revision,
  7411  	})
  7412  	si3 := &snap.SideInfo{RealName: "pc-kernel", Revision: snap.R(1)}
  7413  	snapstate.Set(st, "pc-kernel", &snapstate.SnapState{
  7414  		SnapType: "kernel",
  7415  		Active:   true,
  7416  		Sequence: []*snap.SideInfo{si3},
  7417  		Current:  si3.Revision,
  7418  	})
  7419  	si4 := &snap.SideInfo{RealName: "pc", Revision: snap.R(1)}
  7420  	snapstate.Set(st, "pc", &snapstate.SnapState{
  7421  		SnapType: "gadget",
  7422  		Active:   true,
  7423  		Sequence: []*snap.SideInfo{si4},
  7424  		Current:  si4.Revision,
  7425  	})
  7426  	const pcGadget = `
  7427  name: pc
  7428  type: gadget
  7429  `
  7430  	const pcGadgetYaml = `
  7431  volumes:
  7432    pc:
  7433      bootloader: grub
  7434  `
  7435  	snaptest.MockSnapWithFiles(c, pcGadget, si4, [][]string{
  7436  		{"meta/gadget.yaml", pcGadgetYaml},
  7437  	})
  7438  
  7439  	// setup model assertion
  7440  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  7441  	devicestatetest.SetDevice(st, &auth.DeviceState{
  7442  		Brand:  "my-brand",
  7443  		Model:  "my-model",
  7444  		Serial: "serialserialserial",
  7445  	})
  7446  	err = assertstate.Add(st, model)
  7447  	c.Assert(err, IsNil)
  7448  
  7449  	ts, _, err := snapstate.InstallPath(st, si, snapPath, "", "", snapstate.Flags{})
  7450  	c.Assert(err, IsNil)
  7451  
  7452  	chg := st.NewChange("install-snap", "...")
  7453  	chg.AddAll(ts)
  7454  
  7455  	// run, this will trigger wait for restart with snapd snap (or be done
  7456  	// with core)
  7457  	st.Unlock()
  7458  	err = s.o.Settle(settleTimeout)
  7459  	st.Lock()
  7460  	c.Assert(err, IsNil)
  7461  
  7462  	if si.RealName == "core" {
  7463  		// core on UC20 is done at this point
  7464  		c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("failed: %v", chg.Err()))
  7465  		c.Assert(chg.Err(), IsNil)
  7466  	} else {
  7467  		// boot config is updated after link-snap, so first comes the
  7468  		// daemon restart
  7469  		c.Check(chg.Status(), Equals, state.DoingStatus)
  7470  		restarting, kind := st.Restarting()
  7471  		c.Check(restarting, Equals, true)
  7472  		c.Assert(kind, Equals, state.RestartDaemon)
  7473  
  7474  		// simulate successful daemon restart happened
  7475  		state.MockRestarting(st, state.RestartUnset)
  7476  
  7477  		// let the change run its course
  7478  		st.Unlock()
  7479  		err = s.o.Settle(settleTimeout)
  7480  		st.Lock()
  7481  		c.Assert(err, IsNil)
  7482  
  7483  		c.Check(chg.Status(), Equals, state.DoneStatus, Commentf("change failed: %v", chg.Err()))
  7484  		restarting, kind = st.Restarting()
  7485  		if updated {
  7486  			// boot config updated, thus a system restart was
  7487  			// requested
  7488  			c.Check(restarting, Equals, true)
  7489  			c.Assert(kind, Equals, state.RestartSystem)
  7490  		} else {
  7491  			c.Check(restarting, Equals, false)
  7492  		}
  7493  	}
  7494  }
  7495  
  7496  func (s *mgrsSuite) TestUC20SnapdUpdatesManagedBootConfig(c *C) {
  7497  	mabloader := bootloadertest.Mock("mock", c.MkDir()).WithTrustedAssets()
  7498  	bootloader.Force(mabloader)
  7499  	defer bootloader.Force(nil)
  7500  
  7501  	mabloader.Updated = true
  7502  
  7503  	const snapdSnap = `
  7504  name: snapd
  7505  version: 1.0
  7506  type: snapd`
  7507  	snapPath := snaptest.MakeTestSnapWithFiles(c, snapdSnap, nil)
  7508  	si := &snap.SideInfo{RealName: "snapd"}
  7509  
  7510  	const updated = true
  7511  	s.testUC20RunUpdateManagedBootConfig(c, snapPath, si, mabloader, updated)
  7512  
  7513  	c.Check(mabloader.UpdateCalls, Equals, 1)
  7514  }
  7515  
  7516  func (s *mgrsSuite) TestUC20SnapdUpdateManagedBootNotNeededConfig(c *C) {
  7517  	mabloader := bootloadertest.Mock("mock", c.MkDir()).WithTrustedAssets()
  7518  	bootloader.Force(mabloader)
  7519  	defer bootloader.Force(nil)
  7520  
  7521  	// nothing was updated, eg. boot config editions are the same
  7522  	mabloader.Updated = false
  7523  
  7524  	const snapdSnap = `
  7525  name: snapd
  7526  version: 1.0
  7527  type: snapd`
  7528  	snapPath := snaptest.MakeTestSnapWithFiles(c, snapdSnap, nil)
  7529  	si := &snap.SideInfo{RealName: "snapd"}
  7530  
  7531  	const updated = false
  7532  	s.testUC20RunUpdateManagedBootConfig(c, snapPath, si, mabloader, updated)
  7533  
  7534  	c.Check(mabloader.UpdateCalls, Equals, 1)
  7535  }
  7536  
  7537  func (s *mgrsSuite) TestUC20CoreDoesNotUpdateManagedBootConfig(c *C) {
  7538  	mabloader := bootloadertest.Mock("mock", c.MkDir()).WithTrustedAssets()
  7539  	bootloader.Force(mabloader)
  7540  	defer bootloader.Force(nil)
  7541  
  7542  	const coreSnap = `
  7543  name: core
  7544  version: 1.0
  7545  type: base`
  7546  	snapPath := snaptest.MakeTestSnapWithFiles(c, coreSnap, nil)
  7547  	si := &snap.SideInfo{RealName: "core"}
  7548  
  7549  	const updated = false
  7550  	s.testUC20RunUpdateManagedBootConfig(c, snapPath, si, mabloader, updated)
  7551  	c.Check(mabloader.UpdateCalls, Equals, 0)
  7552  }
  7553  
  7554  func (s *mgrsSuite) testNonUC20RunUpdateManagedBootConfig(c *C, snapPath string, si *snap.SideInfo, bl bootloader.Bootloader) {
  7555  	// non UC20 device model
  7556  
  7557  	restore := release.MockOnClassic(false)
  7558  	defer restore()
  7559  
  7560  	// pretend we booted with the right kernel & base
  7561  	bl.SetBootVars(map[string]string{
  7562  		"snap_core":   "core_1.snap",
  7563  		"snap_kernel": "pc-kernel_1.snap",
  7564  	})
  7565  
  7566  	model := s.brands.Model("my-brand", "my-model", modelDefaults)
  7567  
  7568  	st := s.o.State()
  7569  	st.Lock()
  7570  	// defer st.Unlock()
  7571  	st.Set("seeded", true)
  7572  
  7573  	si1 := &snap.SideInfo{RealName: "snapd", Revision: snap.R(1)}
  7574  	snapstate.Set(st, "snapd", &snapstate.SnapState{
  7575  		SnapType: "snapd",
  7576  		Active:   true,
  7577  		Sequence: []*snap.SideInfo{si1},
  7578  		Current:  si1.Revision,
  7579  	})
  7580  	si2 := &snap.SideInfo{RealName: "core", Revision: snap.R(1)}
  7581  	snapstate.Set(st, "core", &snapstate.SnapState{
  7582  		SnapType: "base",
  7583  		Active:   true,
  7584  		Sequence: []*snap.SideInfo{si2},
  7585  		Current:  si2.Revision,
  7586  	})
  7587  	si3 := &snap.SideInfo{RealName: "pc-kernel", Revision: snap.R(1)}
  7588  	snapstate.Set(st, "pc-kernel", &snapstate.SnapState{
  7589  		SnapType: "kernel",
  7590  		Active:   true,
  7591  		Sequence: []*snap.SideInfo{si3},
  7592  		Current:  si3.Revision,
  7593  	})
  7594  
  7595  	// setup model assertion
  7596  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  7597  	devicestatetest.SetDevice(st, &auth.DeviceState{
  7598  		Brand:  "my-brand",
  7599  		Model:  "my-model",
  7600  		Serial: "serialserialserial",
  7601  	})
  7602  	err := assertstate.Add(st, model)
  7603  	c.Assert(err, IsNil)
  7604  
  7605  	ts, _, err := snapstate.InstallPath(st, si, snapPath, "", "", snapstate.Flags{})
  7606  	c.Assert(err, IsNil)
  7607  
  7608  	chg := st.NewChange("install-snap", "...")
  7609  	chg.AddAll(ts)
  7610  
  7611  	// run, this will trigger a wait for the restart
  7612  	st.Unlock()
  7613  	err = s.o.Settle(settleTimeout)
  7614  	st.Lock()
  7615  	c.Assert(err, IsNil)
  7616  
  7617  	c.Check(chg.Status(), Equals, state.DoingStatus)
  7618  	restarting, _ := st.Restarting()
  7619  	c.Check(restarting, Equals, true)
  7620  
  7621  	// simulate successful restart happened
  7622  	state.MockRestarting(st, state.RestartUnset)
  7623  	if si.RealName == "core" {
  7624  		// pretend we switched to a new core
  7625  		bl.SetBootVars(map[string]string{
  7626  			"snap_core":   "core_x1.snap",
  7627  			"snap_kernel": "pc-kernel_1.snap",
  7628  		})
  7629  	}
  7630  
  7631  	st.Unlock()
  7632  	err = s.o.Settle(settleTimeout)
  7633  	st.Lock()
  7634  	c.Assert(err, IsNil)
  7635  
  7636  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  7637  	c.Assert(chg.Err(), IsNil)
  7638  }
  7639  
  7640  func (s *mgrsSuite) TestNonUC20DoesNotUpdateManagedBootConfig(c *C) {
  7641  	mabloader := bootloadertest.Mock("mock", c.MkDir()).WithTrustedAssets()
  7642  	bootloader.Force(mabloader)
  7643  	defer bootloader.Force(nil)
  7644  
  7645  	const coreSnap = `
  7646  name: core
  7647  version: 1.0
  7648  type: base`
  7649  	snapPath := snaptest.MakeTestSnapWithFiles(c, coreSnap, nil)
  7650  	si := &snap.SideInfo{RealName: "core"}
  7651  
  7652  	s.testNonUC20RunUpdateManagedBootConfig(c, snapPath, si, mabloader)
  7653  	c.Check(mabloader.UpdateCalls, Equals, 0)
  7654  }
  7655  
  7656  func (s *mgrsSuite) TestNonUC20SnapdNoUpdateNotManagedBootConfig(c *C) {
  7657  	mabloader := bootloadertest.Mock("mock", c.MkDir()).WithTrustedAssets()
  7658  	bootloader.Force(mabloader)
  7659  	defer bootloader.Force(nil)
  7660  
  7661  	const snapdSnap = `
  7662  name: snapd
  7663  version: 1.0
  7664  type: snapd`
  7665  	snapPath := snaptest.MakeTestSnapWithFiles(c, snapdSnap, nil)
  7666  	si := &snap.SideInfo{RealName: "snapd"}
  7667  
  7668  	s.testNonUC20RunUpdateManagedBootConfig(c, snapPath, si, mabloader)
  7669  	c.Check(mabloader.UpdateCalls, Equals, 0)
  7670  }
  7671  
  7672  const pcGadget = `
  7673  name: pc
  7674  version: 1.0
  7675  type: gadget
  7676  `
  7677  const pcGadgetYaml = `
  7678  volumes:
  7679    pc:
  7680      bootloader: grub
  7681      structure:
  7682        - name: ubuntu-seed
  7683          type: EF,C12A7328-F81F-11D2-BA4B-00A0C93EC93B
  7684          role: system-seed
  7685          filesystem: vfat
  7686          size: 100M
  7687        - name: ubuntu-boot
  7688          type: EF,C12A7328-F81F-11D2-BA4B-00A0C93EC93B
  7689          role: system-boot
  7690          filesystem: ext4
  7691          size: 100M
  7692        - name: ubuntu-data
  7693          role: system-data
  7694          type: EF,C12A7328-F81F-11D2-BA4B-00A0C93EC93B
  7695          filesystem: ext4
  7696          size: 500M
  7697  `
  7698  
  7699  func (s *mgrsSuite) testGadgetKernelCommandLine(c *C, gadgetPath string, gadgetSideInfo *snap.SideInfo,
  7700  	bl bootloader.Bootloader, currentFiles [][]string, currentModeenvCmdline string,
  7701  	commandLineAfterReboot string, update bool) {
  7702  	restore := release.MockOnClassic(false)
  7703  	defer restore()
  7704  
  7705  	cmdlineAfterRebootPath := filepath.Join(c.MkDir(), "mock-cmdline")
  7706  	c.Assert(ioutil.WriteFile(cmdlineAfterRebootPath, []byte(commandLineAfterReboot), 0644), IsNil)
  7707  
  7708  	// pretend we booted with the right kernel
  7709  	bl.SetBootVars(map[string]string{"snap_kernel": "pc-kernel_1.snap"})
  7710  
  7711  	uc20ModelDefaults := map[string]interface{}{
  7712  		"architecture": "amd64",
  7713  		"base":         "core20",
  7714  		"store":        "my-brand-store-id",
  7715  		"snaps": []interface{}{
  7716  			map[string]interface{}{
  7717  				"name":            "pc-kernel",
  7718  				"id":              snaptest.AssertedSnapID("pc-kernel"),
  7719  				"type":            "kernel",
  7720  				"default-channel": "20",
  7721  			},
  7722  			map[string]interface{}{
  7723  				"name":            "pc",
  7724  				"id":              snaptest.AssertedSnapID("pc"),
  7725  				"type":            "gadget",
  7726  				"default-channel": "20",
  7727  			}},
  7728  	}
  7729  
  7730  	model := s.brands.Model("my-brand", "my-model", uc20ModelDefaults)
  7731  
  7732  	// mock the modeenv file
  7733  	m := boot.Modeenv{
  7734  		Mode:           "run",
  7735  		RecoverySystem: "20191127",
  7736  		Base:           "core20_1.snap",
  7737  		// leave this line to keep gofmt 1.10 happy
  7738  		CurrentKernelCommandLines: []string{currentModeenvCmdline},
  7739  	}
  7740  	err := m.WriteTo("")
  7741  	c.Assert(err, IsNil)
  7742  	c.Assert(s.o.DeviceManager().ReloadModeenv(), IsNil)
  7743  
  7744  	st := s.o.State()
  7745  	st.Lock()
  7746  	st.Set("seeded", true)
  7747  
  7748  	si1 := &snap.SideInfo{RealName: "snapd", Revision: snap.R(1)}
  7749  	snapstate.Set(st, "snapd", &snapstate.SnapState{
  7750  		SnapType: "snapd",
  7751  		Active:   true,
  7752  		Sequence: []*snap.SideInfo{si1},
  7753  		Current:  si1.Revision,
  7754  	})
  7755  	snaptest.MockSnapWithFiles(c, "name: snapd\ntype: snapd\nversion: 123", si1, nil)
  7756  
  7757  	si2 := &snap.SideInfo{RealName: "core20", Revision: snap.R(1)}
  7758  	snapstate.Set(st, "core20", &snapstate.SnapState{
  7759  		SnapType: "base",
  7760  		Active:   true,
  7761  		Sequence: []*snap.SideInfo{si2},
  7762  		Current:  si2.Revision,
  7763  	})
  7764  	si3 := &snap.SideInfo{RealName: "pc-kernel", Revision: snap.R(1)}
  7765  	snapstate.Set(st, "pc-kernel", &snapstate.SnapState{
  7766  		SnapType: "kernel",
  7767  		Active:   true,
  7768  		Sequence: []*snap.SideInfo{si3},
  7769  		Current:  si3.Revision,
  7770  	})
  7771  	si4 := &snap.SideInfo{RealName: "pc", Revision: snap.R(1)}
  7772  	snapstate.Set(st, "pc", &snapstate.SnapState{
  7773  		SnapType: "gadget",
  7774  		Active:   true,
  7775  		Sequence: []*snap.SideInfo{si4},
  7776  		Current:  si4.Revision,
  7777  	})
  7778  	snaptest.MockSnapWithFiles(c, pcGadget, si4, currentFiles)
  7779  
  7780  	// setup model assertion
  7781  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  7782  	devicestatetest.SetDevice(st, &auth.DeviceState{
  7783  		Brand:  "my-brand",
  7784  		Model:  "my-model",
  7785  		Serial: "serialserialserial",
  7786  	})
  7787  	err = assertstate.Add(st, model)
  7788  	c.Assert(err, IsNil)
  7789  
  7790  	ts, _, err := snapstate.InstallPath(st, gadgetSideInfo, gadgetPath, "", "", snapstate.Flags{})
  7791  	c.Assert(err, IsNil)
  7792  
  7793  	chg := st.NewChange("install-snap", "...")
  7794  	chg.AddAll(ts)
  7795  
  7796  	st.Unlock()
  7797  	err = s.o.Settle(settleTimeout)
  7798  	st.Lock()
  7799  	c.Assert(err, IsNil)
  7800  
  7801  	if update {
  7802  		// when updated, a system restart will be requested
  7803  		c.Check(chg.Status(), Equals, state.DoingStatus, Commentf("change failed: %v", chg.Err()))
  7804  		restarting, kind := st.Restarting()
  7805  		c.Check(restarting, Equals, true)
  7806  		c.Assert(kind, Equals, state.RestartSystem)
  7807  
  7808  		// simulate successful system restart happened
  7809  		state.MockRestarting(st, state.RestartUnset)
  7810  
  7811  		m, err := boot.ReadModeenv("")
  7812  		c.Assert(err, IsNil)
  7813  		// old and pending command line
  7814  		c.Assert(m.CurrentKernelCommandLines, HasLen, 2)
  7815  
  7816  		restore := osutil.MockProcCmdline(cmdlineAfterRebootPath)
  7817  		defer restore()
  7818  
  7819  		// reset bootstate, so that after-reboot command line is
  7820  		// asserted
  7821  		st.Unlock()
  7822  		s.o.DeviceManager().ResetToPostBootState()
  7823  		err = s.o.DeviceManager().Ensure()
  7824  		st.Lock()
  7825  		c.Assert(err, IsNil)
  7826  
  7827  		// let the change run its course
  7828  		st.Unlock()
  7829  		err = s.o.Settle(settleTimeout)
  7830  		st.Lock()
  7831  		c.Assert(err, IsNil)
  7832  	}
  7833  
  7834  	c.Check(chg.Status(), Equals, state.DoneStatus, Commentf("change failed: %v", chg.Err()))
  7835  }
  7836  
  7837  func (s *mgrsSuite) TestGadgetKernelCommandLineAddCmdline(c *C) {
  7838  	mabloader := bootloadertest.Mock("mock", c.MkDir()).WithTrustedAssets()
  7839  	mabloader.StaticCommandLine = "mock static"
  7840  	bootloader.Force(mabloader)
  7841  	defer bootloader.Force(nil)
  7842  
  7843  	err := mabloader.SetBootVars(map[string]string{
  7844  		"snapd_extra_cmdline_args": "",
  7845  		"snapd_full_cmdline_args":  "",
  7846  	})
  7847  	c.Assert(err, IsNil)
  7848  
  7849  	// add new gadget snap kernel command line drop-in file
  7850  	sf := snaptest.MakeTestSnapWithFiles(c, pcGadget, [][]string{
  7851  		{"meta/gadget.yaml", pcGadgetYaml},
  7852  		{"cmdline.extra", "args from gadget"},
  7853  	})
  7854  
  7855  	const currentCmdline = "snapd_recovery_mode=run mock static"
  7856  	const update = true
  7857  	currentFiles := [][]string{{"meta/gadget.yaml", pcGadgetYaml}}
  7858  	const cmdlineAfterReboot = "snapd_recovery_mode=run mock static args from gadget"
  7859  	s.testGadgetKernelCommandLine(c, sf, &snap.SideInfo{RealName: "pc"}, mabloader,
  7860  		currentFiles, currentCmdline, cmdlineAfterReboot, update)
  7861  
  7862  	m, err := boot.ReadModeenv("")
  7863  	c.Assert(err, IsNil)
  7864  	c.Assert([]string(m.CurrentKernelCommandLines), DeepEquals, []string{
  7865  		"snapd_recovery_mode=run mock static args from gadget",
  7866  	})
  7867  	vars, err := mabloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  7868  	c.Assert(err, IsNil)
  7869  	c.Assert(vars, DeepEquals, map[string]string{
  7870  		"snapd_extra_cmdline_args": "args from gadget",
  7871  		"snapd_full_cmdline_args":  "",
  7872  	})
  7873  }
  7874  
  7875  func (s *mgrsSuite) TestGadgetKernelCommandLineRemoveCmdline(c *C) {
  7876  	mabloader := bootloadertest.Mock("mock", c.MkDir()).WithTrustedAssets()
  7877  	mabloader.StaticCommandLine = "mock static"
  7878  	bootloader.Force(mabloader)
  7879  	defer bootloader.Force(nil)
  7880  
  7881  	err := mabloader.SetBootVars(map[string]string{
  7882  		"snapd_extra_cmdline_args": "args from gadget",
  7883  		"snapd_full_cmdline_args":  "",
  7884  	})
  7885  	c.Assert(err, IsNil)
  7886  
  7887  	// current gadget has the command line
  7888  	currentFiles := [][]string{
  7889  		{"meta/gadget.yaml", pcGadgetYaml},
  7890  		{"cmdline.extra", "args from old gadget"},
  7891  	}
  7892  	// add new gadget snap kernel command line without the file
  7893  	sf := snaptest.MakeTestSnapWithFiles(c, pcGadget, [][]string{
  7894  		{"meta/gadget.yaml", pcGadgetYaml},
  7895  	})
  7896  
  7897  	const currentCmdline = "snapd_recovery_mode=run mock static args from old gadget"
  7898  	const update = true
  7899  	const cmdlineAfterReboot = "snapd_recovery_mode=run mock static"
  7900  	s.testGadgetKernelCommandLine(c, sf, &snap.SideInfo{RealName: "pc"}, mabloader,
  7901  		currentFiles, currentCmdline, cmdlineAfterReboot, update)
  7902  
  7903  	m, err := boot.ReadModeenv("")
  7904  	c.Assert(err, IsNil)
  7905  	c.Assert([]string(m.CurrentKernelCommandLines), DeepEquals, []string{
  7906  		"snapd_recovery_mode=run mock static",
  7907  	})
  7908  	vars, err := mabloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  7909  	c.Assert(err, IsNil)
  7910  	c.Assert(vars, DeepEquals, map[string]string{
  7911  		"snapd_extra_cmdline_args": "",
  7912  		"snapd_full_cmdline_args":  "",
  7913  	})
  7914  }
  7915  
  7916  func (s *mgrsSuite) TestGadgetKernelCommandLineNoChange(c *C) {
  7917  	mabloader := bootloadertest.Mock("mock", c.MkDir()).WithTrustedAssets()
  7918  	mabloader.StaticCommandLine = "mock static"
  7919  	bootloader.Force(mabloader)
  7920  	defer bootloader.Force(nil)
  7921  
  7922  	err := mabloader.SetBootVars(map[string]string{
  7923  		"snapd_extra_cmdline_args": "args from gadget",
  7924  		"snapd_full_cmdline_args":  "",
  7925  	})
  7926  	c.Assert(err, IsNil)
  7927  	// current gadget has the command line
  7928  	currentFiles := [][]string{
  7929  		{"meta/gadget.yaml", pcGadgetYaml},
  7930  		{"cmdline.extra", "args from gadget"},
  7931  	}
  7932  	// add new gadget snap kernel command line drop-in file
  7933  	sf := snaptest.MakeTestSnapWithFiles(c, pcGadget, [][]string{
  7934  		{"meta/gadget.yaml", pcGadgetYaml},
  7935  		{"cmdline.extra", "args from gadget"},
  7936  	})
  7937  
  7938  	const currentCmdline = "snapd_recovery_mode=run mock static args from gadget"
  7939  	const update = false
  7940  	const cmdlineAfterReboot = "snapd_recovery_mode=run mock static args from gadget"
  7941  	s.testGadgetKernelCommandLine(c, sf, &snap.SideInfo{RealName: "pc"}, mabloader,
  7942  		currentFiles, currentCmdline, cmdlineAfterReboot, update)
  7943  
  7944  	m, err := boot.ReadModeenv("")
  7945  	c.Assert(err, IsNil)
  7946  	c.Assert([]string(m.CurrentKernelCommandLines), DeepEquals, []string{
  7947  		"snapd_recovery_mode=run mock static args from gadget",
  7948  	})
  7949  	// bootenv is unchanged
  7950  	vars, err := mabloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  7951  	c.Assert(err, IsNil)
  7952  	c.Assert(vars, DeepEquals, map[string]string{
  7953  		"snapd_extra_cmdline_args": "args from gadget",
  7954  		"snapd_full_cmdline_args":  "",
  7955  	})
  7956  }
  7957  
  7958  func (s *mgrsSuite) TestGadgetKernelCommandLineTransitionExtraToFull(c *C) {
  7959  	mabloader := bootloadertest.Mock("mock", c.MkDir()).WithTrustedAssets()
  7960  	mabloader.StaticCommandLine = "mock static"
  7961  	bootloader.Force(mabloader)
  7962  	defer bootloader.Force(nil)
  7963  
  7964  	err := mabloader.SetBootVars(map[string]string{
  7965  		"snapd_extra_cmdline_args": "extra args",
  7966  		"snapd_full_cmdline_args":  "",
  7967  	})
  7968  	c.Assert(err, IsNil)
  7969  
  7970  	// add new gadget snap kernel command line drop-in file
  7971  	sf := snaptest.MakeTestSnapWithFiles(c, pcGadget, [][]string{
  7972  		{"meta/gadget.yaml", pcGadgetYaml},
  7973  		{"cmdline.full", "full args"},
  7974  	})
  7975  
  7976  	const currentCmdline = "snapd_recovery_mode=run mock static extra args"
  7977  	const update = true
  7978  	currentFiles := [][]string{
  7979  		{"meta/gadget.yaml", pcGadgetYaml},
  7980  		{"cmdline.extra", "extra args"},
  7981  	}
  7982  	const cmdlineAfterReboot = "snapd_recovery_mode=run full args"
  7983  	s.testGadgetKernelCommandLine(c, sf, &snap.SideInfo{RealName: "pc"}, mabloader,
  7984  		currentFiles, currentCmdline, cmdlineAfterReboot, update)
  7985  
  7986  	m, err := boot.ReadModeenv("")
  7987  	c.Assert(err, IsNil)
  7988  	c.Assert([]string(m.CurrentKernelCommandLines), DeepEquals, []string{
  7989  		"snapd_recovery_mode=run full args",
  7990  	})
  7991  	vars, err := mabloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  7992  	c.Assert(err, IsNil)
  7993  	c.Assert(vars, DeepEquals, map[string]string{
  7994  		"snapd_extra_cmdline_args": "",
  7995  		"snapd_full_cmdline_args":  "full args",
  7996  	})
  7997  }
  7998  
  7999  type gadgetUpdatesSuite struct {
  8000  	baseMgrsSuite
  8001  
  8002  	bloader *boottest.Bootenv16
  8003  }
  8004  
  8005  var _ = Suite(&gadgetUpdatesSuite{})
  8006  
  8007  func (ms *gadgetUpdatesSuite) SetUpTest(c *C) {
  8008  	ms.baseMgrsSuite.SetUpTest(c)
  8009  
  8010  	bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
  8011  	bootloader.Force(bloader)
  8012  	ms.AddCleanup(func() { bootloader.Force(nil) })
  8013  	bloader.BootVars = map[string]string{
  8014  		"snap_core":   "core18_2.snap",
  8015  		"snap_kernel": "pc-kernel_1.snap",
  8016  		"snap_mode":   boot.DefaultStatus,
  8017  	}
  8018  	ms.bloader = bloader
  8019  
  8020  	restore := release.MockOnClassic(false)
  8021  	ms.AddCleanup(restore)
  8022  
  8023  	mockServer := ms.mockStore(c)
  8024  	ms.AddCleanup(mockServer.Close)
  8025  
  8026  	st := ms.o.State()
  8027  	st.Lock()
  8028  	defer st.Unlock()
  8029  
  8030  	// setup model assertion
  8031  	model := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  8032  		"gadget": "pi",
  8033  		"kernel": "pi-kernel",
  8034  	})
  8035  	devicestatetest.SetDevice(st, &auth.DeviceState{
  8036  		Brand:  "can0nical",
  8037  		Model:  "my-model",
  8038  		Serial: "serialserial",
  8039  	})
  8040  	err := assertstate.Add(st, model)
  8041  	c.Assert(err, IsNil)
  8042  }
  8043  
  8044  // makeMockDev mocks /dev/disk/by-label/{structureName} and the mount
  8045  // point /run/mnt/{structureName} under the test rootdir and for
  8046  // osutil.LoadMountInfo for use by gadget code for test gadgets using
  8047  // structureName. This is useful for e.g. end-to-end testing of gadget
  8048  // assets installs/updates.
  8049  func (ms *gadgetUpdatesSuite) makeMockedDev(c *C, structureName string) {
  8050  	// mock /dev/disk/by-label/{structureName}
  8051  	byLabelDir := filepath.Join(dirs.GlobalRootDir, "/dev/disk/by-label/")
  8052  	err := os.MkdirAll(byLabelDir, 0755)
  8053  	c.Assert(err, IsNil)
  8054  	// create fakedevice node
  8055  	err = ioutil.WriteFile(filepath.Join(dirs.GlobalRootDir, "/dev/fakedevice0p1"), nil, 0644)
  8056  	c.Assert(err, IsNil)
  8057  	// and point the mocked by-label entry to the fakedevice node
  8058  	err = os.Symlink(filepath.Join(dirs.GlobalRootDir, "/dev/fakedevice0p1"), filepath.Join(byLabelDir, structureName))
  8059  	c.Assert(err, IsNil)
  8060  
  8061  	// mock /proc/self/mountinfo with the above generated paths
  8062  	ms.AddCleanup(osutil.MockMountInfo(fmt.Sprintf("26 27 8:3 / %[1]s/run/mnt/%[2]s rw,relatime shared:7 - vfat %[1]s/dev/fakedevice0p1 rw", dirs.GlobalRootDir, structureName)))
  8063  
  8064  	// and mock the mount point
  8065  	err = os.MkdirAll(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName), 0755)
  8066  	c.Assert(err, IsNil)
  8067  
  8068  }
  8069  
  8070  // tsWithoutReRefresh removes the re-refresh task from the given taskset.
  8071  //
  8072  // It assumes that re-refresh is the last task and will fail if that is
  8073  // not the case.
  8074  //
  8075  // This is needed because settle() will not converge with the re-refresh
  8076  // task because re-refresh will always be in doing state.
  8077  //
  8078  // TODO: have variant of Settle() that ends if ensure next time is
  8079  // stable or in the future by a value larger than some threshold, and
  8080  // then we would mock the rerefresh interval to something large and
  8081  // distinct from practical wait time even on slow systems. Once that
  8082  // is done this function can be removed.
  8083  func tsWithoutReRefresh(c *C, ts *state.TaskSet) *state.TaskSet {
  8084  	refreshIdx := len(ts.Tasks()) - 1
  8085  	c.Assert(ts.Tasks()[refreshIdx].Kind(), Equals, "check-rerefresh")
  8086  	ts = state.NewTaskSet(ts.Tasks()[:refreshIdx-1]...)
  8087  	return ts
  8088  }
  8089  
  8090  // mockSnapUpgradeWithFiles will put a "rev 2" of the given snapYaml/files
  8091  // into the mock snapstore
  8092  func (ms *gadgetUpdatesSuite) mockSnapUpgradeWithFiles(c *C, snapYaml string, files [][]string) {
  8093  	snapPath, _ := ms.makeStoreTestSnapWithFiles(c, snapYaml, "2", files)
  8094  	ms.serveSnap(snapPath, "2")
  8095  }
  8096  
  8097  func (ms *gadgetUpdatesSuite) TestRefreshGadgetUpdates(c *C) {
  8098  	structureName := "ubuntu-seed"
  8099  	gadgetYaml := fmt.Sprintf(`
  8100  volumes:
  8101      volume-id:
  8102          schema: mbr
  8103          bootloader: u-boot
  8104          structure:
  8105            - name: %s
  8106              filesystem: vfat
  8107              type: 0C
  8108              size: 1200M
  8109              content:
  8110                - source: boot-assets/
  8111                  target: /
  8112                - source: foo.img
  8113                  target: /subdir/foo-renamed.img`, structureName)
  8114  	newGadgetYaml := gadgetYaml + `
  8115              update:
  8116                edition: 2
  8117  `
  8118  	ms.makeMockedDev(c, structureName)
  8119  
  8120  	st := ms.o.State()
  8121  	st.Lock()
  8122  	defer st.Unlock()
  8123  
  8124  	// we have an installed gadget
  8125  	gadgetSnapYaml := "name: pi\nversion: 1.0\ntype: gadget"
  8126  	ms.mockInstalledSnapWithFiles(c, gadgetSnapYaml, [][]string{
  8127  		{"meta/gadget.yaml", gadgetYaml},
  8128  	})
  8129  
  8130  	// add new gadget snap to fake store
  8131  	ms.mockSnapUpgradeWithFiles(c, gadgetSnapYaml, [][]string{
  8132  		{"meta/gadget.yaml", newGadgetYaml},
  8133  		{"boot-assets/bcm2710-rpi-2-b.dtb", "bcm2710-rpi-2-b.dtb rev2"},
  8134  		{"boot-assets/bcm2710-rpi-3-b.dtb", "bcm2710-rpi-3-b.dtb rev2"},
  8135  		{"boot-assets/overlays/uart0.dtbo", "uart0.dtbo rev2"},
  8136  		{"foo.img", "foo rev2"},
  8137  	})
  8138  
  8139  	ts, err := snapstate.Update(st, "pi", nil, 0, snapstate.Flags{})
  8140  	c.Assert(err, IsNil)
  8141  	// remove the re-refresh as it will prevent settle from converging
  8142  	ts = tsWithoutReRefresh(c, ts)
  8143  
  8144  	chg := st.NewChange("upgrade-gadget", "...")
  8145  	chg.AddAll(ts)
  8146  
  8147  	st.Unlock()
  8148  	err = ms.o.Settle(settleTimeout)
  8149  	st.Lock()
  8150  	c.Assert(err, IsNil)
  8151  
  8152  	// pretend we restarted
  8153  	t := findKind(chg, "auto-connect")
  8154  	c.Assert(t, NotNil)
  8155  	c.Assert(t.Status(), Equals, state.DoingStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  8156  	// simulate successful restart happened
  8157  	state.MockRestarting(st, state.RestartUnset)
  8158  
  8159  	// settle again
  8160  	st.Unlock()
  8161  	err = ms.o.Settle(settleTimeout)
  8162  	st.Lock()
  8163  	c.Assert(err, IsNil)
  8164  
  8165  	c.Assert(chg.Err(), IsNil)
  8166  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  8167  
  8168  	// check that files/dirs got updated and subdirs are correct
  8169  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "subdir/foo-renamed.img"), testutil.FileContains, "foo rev2")
  8170  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "bcm2710-rpi-2-b.dtb"), testutil.FileContains, "bcm2710-rpi-2-b.dtb rev2")
  8171  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "bcm2710-rpi-3-b.dtb"), testutil.FileContains, "bcm2710-rpi-3-b.dtb rev2")
  8172  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "overlays/uart0.dtbo"), testutil.FileContains, "uart0.dtbo rev2")
  8173  }
  8174  
  8175  func (ms *gadgetUpdatesSuite) TestGadgetWithKernelRefKernelRefresh(c *C) {
  8176  	kernelYaml := `
  8177  assets:
  8178    pidtbs:
  8179      update: true
  8180      content:
  8181      - dtbs/broadcom/
  8182      - dtbs/overlays/`
  8183  
  8184  	structureName := "ubuntu-seed"
  8185  	gadgetYaml := fmt.Sprintf(`
  8186  volumes:
  8187      volume-id:
  8188          schema: mbr
  8189          bootloader: u-boot
  8190          structure:
  8191            - name: %s
  8192              filesystem: vfat
  8193              type: 0C
  8194              size: 1200M
  8195              content:
  8196                - source: boot-assets/
  8197                  target: /
  8198                - source: $kernel:pidtbs/dtbs/broadcom/
  8199                  target: /
  8200                - source: $kernel:pidtbs/dtbs/overlays/
  8201                  target: /overlays`, structureName)
  8202  	ms.makeMockedDev(c, structureName)
  8203  
  8204  	st := ms.o.State()
  8205  	st.Lock()
  8206  	defer st.Unlock()
  8207  
  8208  	// we have an installed gadget with kernel refs
  8209  	gadgetSnapYaml := "name: pi\nversion: 1.0\ntype: gadget"
  8210  	ms.mockInstalledSnapWithFiles(c, gadgetSnapYaml, [][]string{
  8211  		{"meta/gadget.yaml", gadgetYaml},
  8212  		{"boot-assets/start.elf", "start.elf rev1"},
  8213  	})
  8214  	// we have an installed kernel with kernel.yaml
  8215  	kernelSnapYaml := "name: pi-kernel\nversion: 1.0\ntype: kernel"
  8216  	ms.mockInstalledSnapWithFiles(c, kernelSnapYaml, [][]string{
  8217  		{"meta/kernel.yaml", kernelYaml},
  8218  	})
  8219  
  8220  	// add new kernel snap to fake store
  8221  	ms.mockSnapUpgradeWithFiles(c, kernelSnapYaml, [][]string{
  8222  		{"meta/kernel.yaml", kernelYaml},
  8223  		{"dtbs/broadcom/bcm2710-rpi-2-b.dtb", "bcm2710-rpi-2-b.dtb rev2"},
  8224  		{"dtbs/broadcom/bcm2710-rpi-3-b.dtb", "bcm2710-rpi-3-b.dtb rev2"},
  8225  		{"dtbs/overlays/uart0.dtbo", "uart0.dtbo rev2"},
  8226  	})
  8227  
  8228  	ts, err := snapstate.Update(st, "pi-kernel", nil, 0, snapstate.Flags{})
  8229  	c.Assert(err, IsNil)
  8230  	// remove the re-refresh as it will prevent settle from converging
  8231  	ts = tsWithoutReRefresh(c, ts)
  8232  
  8233  	chg := st.NewChange("upgrade-kernel", "...")
  8234  	chg.AddAll(ts)
  8235  
  8236  	st.Unlock()
  8237  	err = ms.o.Settle(settleTimeout)
  8238  	st.Lock()
  8239  	c.Assert(err, IsNil)
  8240  	c.Assert(chg.Err(), IsNil)
  8241  
  8242  	// pretend we restarted
  8243  	t := findKind(chg, "auto-connect")
  8244  	c.Assert(t, NotNil)
  8245  	c.Assert(t.Status(), Equals, state.DoingStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  8246  	// pretend we restarted
  8247  	ms.mockSuccessfulReboot(c, ms.bloader, []snap.Type{snap.TypeKernel})
  8248  
  8249  	// settle again
  8250  	st.Unlock()
  8251  	err = ms.o.Settle(settleTimeout)
  8252  	st.Lock()
  8253  	c.Assert(err, IsNil)
  8254  	c.Assert(chg.Err(), IsNil)
  8255  
  8256  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  8257  
  8258  	// check that files/dirs got updated and subdirs are correct
  8259  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "bcm2710-rpi-2-b.dtb"), testutil.FileContains, "bcm2710-rpi-2-b.dtb rev2")
  8260  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "bcm2710-rpi-3-b.dtb"), testutil.FileContains, "bcm2710-rpi-3-b.dtb rev2")
  8261  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "overlays/uart0.dtbo"), testutil.FileContains, "uart0.dtbo rev2")
  8262  	// BUT the gadget content is ignored and not copied again
  8263  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "start.elf"), testutil.FileAbsent)
  8264  }
  8265  
  8266  func (ms *gadgetUpdatesSuite) TestGadgetWithKernelRefGadgetRefresh(c *C) {
  8267  	kernelYaml := `
  8268  assets:
  8269    pidtbs:
  8270      update: true
  8271      content:
  8272      - dtbs/broadcom/
  8273      - dtbs/overlays/`
  8274  
  8275  	structureName := "ubuntu-seed"
  8276  	gadgetYaml := fmt.Sprintf(`
  8277  volumes:
  8278      volume-id:
  8279          schema: mbr
  8280          bootloader: u-boot
  8281          structure:
  8282            - name: %s
  8283              filesystem: vfat
  8284              type: 0C
  8285              size: 1200M
  8286              content:
  8287                - source: boot-assets/
  8288                  target: /
  8289                - source: $kernel:pidtbs/dtbs/broadcom/
  8290                  target: /
  8291                - source: $kernel:pidtbs/dtbs/overlays/
  8292                  target: /overlays`, structureName)
  8293  	newGadgetYaml := gadgetYaml + `
  8294              update:
  8295                edition: 2
  8296  `
  8297  	ms.makeMockedDev(c, structureName)
  8298  
  8299  	st := ms.o.State()
  8300  	st.Lock()
  8301  	defer st.Unlock()
  8302  
  8303  	// we have an installed gadget with kernel refs
  8304  	gadgetSnapYaml := "name: pi\nversion: 1.0\ntype: gadget"
  8305  	ms.mockInstalledSnapWithFiles(c, gadgetSnapYaml, [][]string{
  8306  		{"meta/gadget.yaml", gadgetYaml},
  8307  	})
  8308  	// we have an installed kernel with kernel.yaml
  8309  	kernelSnapYaml := "name: pi-kernel\nversion: 1.0\ntype: kernel"
  8310  	ms.mockInstalledSnapWithFiles(c, kernelSnapYaml, [][]string{
  8311  		{"meta/kernel.yaml", kernelYaml},
  8312  		{"dtbs/broadcom/bcm2710-rpi-2-b.dtb", "bcm2710-rpi-2-b.dtb rev2"},
  8313  		{"dtbs/broadcom/bcm2710-rpi-3-b.dtb", "bcm2710-rpi-3-b.dtb rev2"},
  8314  		{"dtbs/overlays/uart0.dtbo", "uart0.dtbo rev2"},
  8315  	})
  8316  
  8317  	// add new gadget snap to fake store that has an "update: true"
  8318  	// for the kernel ref structure
  8319  	ms.mockSnapUpgradeWithFiles(c, gadgetSnapYaml, [][]string{
  8320  		{"meta/gadget.yaml", newGadgetYaml},
  8321  		{"boot-assets/start.elf", "start.elf rev2"},
  8322  	})
  8323  
  8324  	ts, err := snapstate.Update(st, "pi", nil, 0, snapstate.Flags{})
  8325  	c.Assert(err, IsNil)
  8326  	// remove the re-refresh as it will prevent settle from converging
  8327  	ts = tsWithoutReRefresh(c, ts)
  8328  
  8329  	chg := st.NewChange("upgrade-gadget", "...")
  8330  	chg.AddAll(ts)
  8331  
  8332  	st.Unlock()
  8333  	err = ms.o.Settle(settleTimeout)
  8334  	st.Lock()
  8335  	c.Assert(err, IsNil)
  8336  	c.Assert(chg.Err(), IsNil)
  8337  
  8338  	// pretend we restarted
  8339  	t := findKind(chg, "auto-connect")
  8340  	c.Assert(t, NotNil)
  8341  	c.Assert(t.Status(), Equals, state.DoingStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  8342  	// simulate successful restart happened after gadget update
  8343  	state.MockRestarting(st, state.RestartUnset)
  8344  
  8345  	// settle again
  8346  	st.Unlock()
  8347  	err = ms.o.Settle(settleTimeout)
  8348  	st.Lock()
  8349  	c.Assert(err, IsNil)
  8350  	c.Assert(chg.Err(), IsNil)
  8351  
  8352  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  8353  
  8354  	// check that files/dirs got updated and subdirs are correct
  8355  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "bcm2710-rpi-2-b.dtb"), testutil.FileContains, "bcm2710-rpi-2-b.dtb rev2")
  8356  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "bcm2710-rpi-3-b.dtb"), testutil.FileContains, "bcm2710-rpi-3-b.dtb rev2")
  8357  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "overlays/uart0.dtbo"), testutil.FileContains, "uart0.dtbo rev2")
  8358  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "start.elf"), testutil.FileContains, "start.elf rev2")
  8359  }
  8360  
  8361  func (ms *gadgetUpdatesSuite) TestGadgetWithKernelRefUpgradeFromOld(c *C) {
  8362  	kernelYaml := `
  8363  assets:
  8364    pidtbs:
  8365      update: true
  8366      content:
  8367      - dtbs/broadcom/
  8368      - dtbs/overlays/`
  8369  
  8370  	structureName := "ubuntu-seed"
  8371  	oldGadgetYaml := fmt.Sprintf(`
  8372  volumes:
  8373      volume-id:
  8374          schema: mbr
  8375          bootloader: u-boot
  8376          structure:
  8377            - name: %s
  8378              filesystem: vfat
  8379              type: 0C
  8380              size: 1200M
  8381              content:
  8382                - source: boot-assets/
  8383                  target: /`, structureName)
  8384  	// Note that there is no "edition" jump here for the new "$kernel:ref"
  8385  	// content. This is driven by the kernel.yaml "update: true" value.
  8386  	newGadgetYaml := fmt.Sprintf(`
  8387  volumes:
  8388      volume-id:
  8389          schema: mbr
  8390          bootloader: u-boot
  8391          structure:
  8392            - name: %s
  8393              filesystem: vfat
  8394              type: 0C
  8395              size: 1200M
  8396              content:
  8397                - source: boot-assets/
  8398                  target: /
  8399                - source: $kernel:pidtbs/dtbs/broadcom/
  8400                  target: /
  8401                - source: $kernel:pidtbs/dtbs/overlays/
  8402                  target: /overlays`, structureName)
  8403  	ms.makeMockedDev(c, structureName)
  8404  
  8405  	st := ms.o.State()
  8406  	st.Lock()
  8407  	defer st.Unlock()
  8408  
  8409  	// we have an installed old style pi gadget
  8410  	gadgetSnapYaml := "name: pi\nversion: 1.0\ntype: gadget"
  8411  	ms.mockInstalledSnapWithFiles(c, gadgetSnapYaml, [][]string{
  8412  		{"meta/gadget.yaml", oldGadgetYaml},
  8413  		{"boot-assets/start.elf", "start.elf rev1"},
  8414  		{"boot-assets/bcm2710-rpi-2-b.dtb", "bcm2710-rpi-2-b.dtb rev1"},
  8415  		{"boot-assets/bcm2710-rpi-3-b.dtb", "bcm2710-rpi-3-b.dtb rev1"},
  8416  	})
  8417  	// we have old style boot asssets in the bootloader dir
  8418  	snaptest.PopulateDir(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName), [][]string{
  8419  		{"start.elf", "start.elf rev1"},
  8420  		{"bcm2710-rpi-2-b.dtb", "bcm2710-rpi-2-b.dtb rev1"},
  8421  		{"bcm2710-rpi-3-b.dtb", "bcm2710-rpi-3-b.dtb rev1"},
  8422  	})
  8423  
  8424  	// we have an installed old-style kernel snap
  8425  	kernelSnapYaml := "name: pi-kernel\nversion: 1.0\ntype: kernel"
  8426  	ms.mockInstalledSnapWithFiles(c, kernelSnapYaml, nil)
  8427  
  8428  	// add new kernel snap with kernel-refs to fake store
  8429  	ms.mockSnapUpgradeWithFiles(c, kernelSnapYaml, [][]string{
  8430  		{"meta/kernel.yaml", kernelYaml},
  8431  		{"dtbs/broadcom/bcm2710-rpi-2-b.dtb", "bcm2710-rpi-2-b.dtb rev2-from-kernel"},
  8432  		{"dtbs/broadcom/bcm2710-rpi-3-b.dtb", "bcm2710-rpi-3-b.dtb rev2-from-kernel"},
  8433  		{"dtbs/overlays/uart0.dtbo", "uart0.dtbo rev2-from-kernel"},
  8434  	})
  8435  
  8436  	// add new gadget snap with kernel-refs to fake store
  8437  	ms.mockSnapUpgradeWithFiles(c, gadgetSnapYaml, [][]string{
  8438  		{"meta/gadget.yaml", newGadgetYaml},
  8439  		{"boot-assets/start.elf", "start.elf rev1"},
  8440  		// notice: no dtbs anymore in the gadget
  8441  	})
  8442  
  8443  	affected, tasksets, err := snapstate.UpdateMany(context.TODO(), st, nil, 0, &snapstate.Flags{})
  8444  	c.Assert(err, IsNil)
  8445  	sort.Strings(affected)
  8446  	c.Check(affected, DeepEquals, []string{"pi", "pi-kernel"})
  8447  
  8448  	chg := st.NewChange("upgrade-snaps", "...")
  8449  	for _, ts := range tasksets {
  8450  		// skip the taskset of UpdateMany that does the
  8451  		// check-rerefresh, see tsWithoutReRefresh for details
  8452  		if ts.Tasks()[0].Kind() == "check-rerefresh" {
  8453  			continue
  8454  		}
  8455  		chg.AddAll(ts)
  8456  	}
  8457  
  8458  	st.Unlock()
  8459  	err = ms.o.Settle(settleTimeout)
  8460  	st.Lock()
  8461  	c.Assert(err, IsNil)
  8462  	c.Assert(chg.Err(), IsNil)
  8463  
  8464  	// At this point the gadget and kernel are updated and the kernel
  8465  	// required a restart. Check that *before* this restart the DTB
  8466  	// files from the kernel are in place.
  8467  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "bcm2710-rpi-2-b.dtb"), testutil.FileContains, "bcm2710-rpi-2-b.dtb rev2-from-kernel")
  8468  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "bcm2710-rpi-3-b.dtb"), testutil.FileContains, "bcm2710-rpi-3-b.dtb rev2-from-kernel")
  8469  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "overlays/uart0.dtbo"), testutil.FileContains, "uart0.dtbo rev2-from-kernel")
  8470  	//  gadget content is not updated because there is no edition update
  8471  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "start.elf"), testutil.FileContains, "start.elf rev1")
  8472  
  8473  	// pretend we restarted
  8474  	ms.mockSuccessfulReboot(c, ms.bloader, []snap.Type{snap.TypeKernel})
  8475  
  8476  	// settle again
  8477  	st.Unlock()
  8478  	err = ms.o.Settle(settleTimeout)
  8479  	st.Lock()
  8480  	c.Assert(err, IsNil)
  8481  	c.Assert(chg.Err(), IsNil)
  8482  }
  8483  
  8484  func snapTaskStatusForChange(chg *state.Change) map[string]state.Status {
  8485  	taskStates := make(map[string]state.Status)
  8486  	for _, t := range chg.Tasks() {
  8487  		if snapsup, err := snapstate.TaskSnapSetup(t); err == nil {
  8488  			taskStates[snapsup.SnapName()+":"+t.Kind()] = t.Status()
  8489  		}
  8490  	}
  8491  	return taskStates
  8492  }
  8493  
  8494  func (ms *gadgetUpdatesSuite) TestGadgetWithKernelRefUpgradeFromOldErrorGadget(c *C) {
  8495  	kernelYaml := `
  8496  assets:
  8497    pidtbs:
  8498      update: true
  8499      content:
  8500      - dtbs/broadcom/
  8501      - dtbs/overlays/`
  8502  
  8503  	structureName := "ubuntu-seed"
  8504  	oldGadgetYaml := fmt.Sprintf(`
  8505  volumes:
  8506      volume-id:
  8507          schema: mbr
  8508          bootloader: u-boot
  8509          structure:
  8510            - name: %s
  8511              filesystem: vfat
  8512              type: 0C
  8513              size: 1200M
  8514              content:
  8515                - source: boot-assets/
  8516                  target: /`, structureName)
  8517  	// Note that there is no "edition" jump here for the new "$kernel:ref"
  8518  	// content. This is driven by the kernel.yaml "update: true" value.
  8519  	newGadgetYaml := fmt.Sprintf(`
  8520  volumes:
  8521      volume-id:
  8522          schema: mbr
  8523          bootloader: u-boot
  8524          structure:
  8525            - name: %s
  8526              filesystem: vfat
  8527              type: 0C
  8528              size: 1200M
  8529              content:
  8530                - source: boot-assets/
  8531                  target: /
  8532                - source: $kernel:pidtbs/dtbs/broadcom/
  8533                  target: /
  8534                - source: $kernel:pidtbs/dtbs/overlays/
  8535                  target: /overlays`, structureName)
  8536  	ms.makeMockedDev(c, structureName)
  8537  
  8538  	st := ms.o.State()
  8539  	st.Lock()
  8540  	defer st.Unlock()
  8541  
  8542  	// we have an installed old style pi gadget
  8543  	gadgetSnapYaml := "name: pi\nversion: 1.0\ntype: gadget"
  8544  	ms.mockInstalledSnapWithFiles(c, gadgetSnapYaml, [][]string{
  8545  		{"meta/gadget.yaml", oldGadgetYaml},
  8546  		{"boot-assets/start.elf", "start.elf rev1"},
  8547  		{"boot-assets/bcm2710-rpi-2-b.dtb", "bcm2710-rpi-2-b.dtb rev1"},
  8548  		{"boot-assets/bcm2710-rpi-3-b.dtb", "bcm2710-rpi-3-b.dtb rev1"},
  8549  	})
  8550  	// we have old style boot asssets in the bootloader dir
  8551  	snaptest.PopulateDir(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName), [][]string{
  8552  		{"start.elf", "start.elf rev1"},
  8553  		{"bcm2710-rpi-2-b.dtb", "bcm2710-rpi-2-b.dtb rev1"},
  8554  		{"bcm2710-rpi-3-b.dtb", "bcm2710-rpi-3-b.dtb rev1"},
  8555  	})
  8556  
  8557  	// we have an installed old-style kernel snap
  8558  	kernelSnapYaml := "name: pi-kernel\nversion: 1.0\ntype: kernel"
  8559  	ms.mockInstalledSnapWithFiles(c, kernelSnapYaml, nil)
  8560  
  8561  	// add new kernel snap with kernel-refs to fake store
  8562  	ms.mockSnapUpgradeWithFiles(c, kernelSnapYaml, [][]string{
  8563  		{"meta/kernel.yaml", kernelYaml},
  8564  		{"dtbs/broadcom/bcm2710-rpi-2-b.dtb", "bcm2710-rpi-2-b.dtb rev2-from-kernel"},
  8565  		{"dtbs/broadcom/bcm2710-rpi-3-b.dtb", "bcm2710-rpi-3-b.dtb rev2-from-kernel"},
  8566  		{"dtbs/overlays/uart0.dtbo", "uart0.dtbo rev2-from-kernel"},
  8567  	})
  8568  
  8569  	// add new gadget snap with kernel-refs to fake store
  8570  	ms.mockSnapUpgradeWithFiles(c, gadgetSnapYaml, [][]string{
  8571  		{"meta/gadget.yaml", newGadgetYaml},
  8572  		{"boot-assets/start.elf", "start.elf rev1"},
  8573  		// notice: no dtbs anymore in the gadget
  8574  	})
  8575  
  8576  	affected, tasksets, err := snapstate.UpdateMany(context.TODO(), st, nil, 0, &snapstate.Flags{})
  8577  	c.Assert(err, IsNil)
  8578  	sort.Strings(affected)
  8579  	c.Check(affected, DeepEquals, []string{"pi", "pi-kernel"})
  8580  
  8581  	chg := st.NewChange("upgrade-snaps", "...")
  8582  	tError := st.NewTask("error-trigger", "gadget failed")
  8583  	for _, ts := range tasksets {
  8584  		// skip the taskset of UpdateMany that does the
  8585  		// check-rerefresh, see tsWithoutReRefresh for details
  8586  		tasks := ts.Tasks()
  8587  		if tasks[0].Kind() == "check-rerefresh" {
  8588  			continue
  8589  		}
  8590  
  8591  		snapsup, err := snapstate.TaskSnapSetup(tasks[0])
  8592  		c.Assert(err, IsNil)
  8593  		// trigger an error as last operation of gadget refresh
  8594  		if snapsup.SnapName() == "pi" {
  8595  			last := tasks[len(tasks)-1]
  8596  			tError.WaitFor(last)
  8597  			// XXX: or just use "snap-setup" here?
  8598  			tError.Set("snap-setup-task", tasks[0].ID())
  8599  			ts.AddTask(tError)
  8600  			// must be in the same lane as the gadget update
  8601  			lanes := last.Lanes()
  8602  			c.Assert(lanes, HasLen, 1)
  8603  			tError.JoinLane(lanes[0])
  8604  		}
  8605  
  8606  		chg.AddAll(ts)
  8607  	}
  8608  
  8609  	st.Unlock()
  8610  	err = ms.o.Settle(settleTimeout)
  8611  	st.Lock()
  8612  	c.Assert(err, IsNil)
  8613  	c.Check(chg.Err(), ErrorMatches, `cannot perform the following tasks:\n- gadget failed.*`)
  8614  
  8615  	// check that files/dirs from the kernel did  *not* get updated or installed
  8616  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "bcm2710-rpi-2-b.dtb"), testutil.FileContains, "bcm2710-rpi-2-b.dtb rev1")
  8617  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "bcm2710-rpi-3-b.dtb"), testutil.FileContains, "bcm2710-rpi-3-b.dtb rev1")
  8618  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "overlays/uart0.dtbo"), testutil.FileAbsent)
  8619  
  8620  	// Ensure that tasks states are valid
  8621  	taskStates := snapTaskStatusForChange(chg)
  8622  	// The pi gadget failed in error-trigger and got rolled back
  8623  	c.Check(taskStates["pi:error-trigger"], Equals, state.ErrorStatus)
  8624  	c.Check(taskStates["pi:mount-snap"], Equals, state.UndoneStatus)
  8625  	// And the pi-kernel did not even get started
  8626  	c.Check(taskStates["pi-kernel:download-snap"], Equals, state.HoldStatus)
  8627  }
  8628  
  8629  func (ms *gadgetUpdatesSuite) TestGadgetWithKernelRefUpgradeFromOldErrorKernel(c *C) {
  8630  	kernelYaml := `
  8631  assets:
  8632    pidtbs:
  8633      update: true
  8634      content:
  8635      - dtbs/broadcom/
  8636      - dtbs/overlays/`
  8637  
  8638  	structureName := "ubuntu-seed"
  8639  	oldGadgetYaml := fmt.Sprintf(`
  8640  volumes:
  8641      volume-id:
  8642          schema: mbr
  8643          bootloader: u-boot
  8644          structure:
  8645            - name: %s
  8646              filesystem: vfat
  8647              type: 0C
  8648              size: 1200M
  8649              content:
  8650                - source: boot-assets/
  8651                  target: /`, structureName)
  8652  	// Note that there is no "edition" jump here for the new "$kernel:ref"
  8653  	// content. This is driven by the kernel.yaml "update: true" value.
  8654  	newGadgetYaml := fmt.Sprintf(`
  8655  volumes:
  8656      volume-id:
  8657          schema: mbr
  8658          bootloader: u-boot
  8659          structure:
  8660            - name: %s
  8661              filesystem: vfat
  8662              type: 0C
  8663              size: 1200M
  8664              content:
  8665                - source: boot-assets/
  8666                  target: /
  8667                - source: $kernel:pidtbs/dtbs/broadcom/
  8668                  target: /
  8669                - source: $kernel:pidtbs/dtbs/overlays/
  8670                  target: /overlays`, structureName)
  8671  	ms.makeMockedDev(c, structureName)
  8672  
  8673  	st := ms.o.State()
  8674  	st.Lock()
  8675  	defer st.Unlock()
  8676  
  8677  	// we have an installed old style pi gadget
  8678  	gadgetSnapYaml := "name: pi\nversion: 1.0\ntype: gadget"
  8679  	ms.mockInstalledSnapWithFiles(c, gadgetSnapYaml, [][]string{
  8680  		{"meta/gadget.yaml", oldGadgetYaml},
  8681  		{"boot-assets/start.elf", "start.elf rev1"},
  8682  		{"boot-assets/bcm2710-rpi-2-b.dtb", "bcm2710-rpi-2-b.dtb rev1"},
  8683  		{"boot-assets/bcm2710-rpi-3-b.dtb", "bcm2710-rpi-3-b.dtb rev1"},
  8684  	})
  8685  	// we have old style boot asssets in the bootloader dir
  8686  	snaptest.PopulateDir(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName), [][]string{
  8687  		{"start.elf", "start.elf rev1"},
  8688  		{"bcm2710-rpi-2-b.dtb", "bcm2710-rpi-2-b.dtb rev1"},
  8689  		{"bcm2710-rpi-3-b.dtb", "bcm2710-rpi-3-b.dtb rev1"},
  8690  	})
  8691  
  8692  	// we have an installed old-style kernel snap
  8693  	kernelSnapYaml := "name: pi-kernel\nversion: 1.0\ntype: kernel"
  8694  	ms.mockInstalledSnapWithFiles(c, kernelSnapYaml, nil)
  8695  
  8696  	// add new kernel snap with kernel-refs to fake store
  8697  	ms.mockSnapUpgradeWithFiles(c, kernelSnapYaml, [][]string{
  8698  		{"meta/kernel.yaml", kernelYaml},
  8699  		{"dtbs/broadcom/bcm2710-rpi-2-b.dtb", "bcm2710-rpi-2-b.dtb rev2-from-kernel"},
  8700  		{"dtbs/broadcom/bcm2710-rpi-3-b.dtb", "bcm2710-rpi-3-b.dtb rev2-from-kernel"},
  8701  		{"dtbs/overlays/uart0.dtbo", "uart0.dtbo rev2-from-kernel"},
  8702  	})
  8703  
  8704  	// add new gadget snap with kernel-refs to fake store
  8705  	ms.mockSnapUpgradeWithFiles(c, gadgetSnapYaml, [][]string{
  8706  		{"meta/gadget.yaml", newGadgetYaml},
  8707  		{"boot-assets/start.elf", "start.elf rev1"},
  8708  		// notice: no dtbs anymore in the gadget
  8709  	})
  8710  
  8711  	affected, tasksets, err := snapstate.UpdateMany(context.TODO(), st, nil, 0, &snapstate.Flags{})
  8712  	c.Assert(err, IsNil)
  8713  	sort.Strings(affected)
  8714  	c.Check(affected, DeepEquals, []string{"pi", "pi-kernel"})
  8715  
  8716  	chg := st.NewChange("upgrade-snaps", "...")
  8717  	tError := st.NewTask("error-trigger", "kernel failed")
  8718  	for _, ts := range tasksets {
  8719  		// skip the taskset of UpdateMany that does the
  8720  		// check-rerefresh, see tsWithoutReRefresh for details
  8721  		tasks := ts.Tasks()
  8722  		if tasks[0].Kind() == "check-rerefresh" {
  8723  			continue
  8724  		}
  8725  
  8726  		snapsup, err := snapstate.TaskSnapSetup(tasks[0])
  8727  		c.Assert(err, IsNil)
  8728  		// trigger an error as last operation of gadget refresh
  8729  		if snapsup.SnapName() == "pi-kernel" {
  8730  			last := tasks[len(tasks)-1]
  8731  			tError.WaitFor(last)
  8732  			// XXX: or just use "snap-setup" here?
  8733  			tError.Set("snap-setup-task", tasks[0].ID())
  8734  			ts.AddTask(tError)
  8735  			// must be in the same lane as the kernel update
  8736  			lanes := last.Lanes()
  8737  			c.Assert(lanes, HasLen, 1)
  8738  			tError.JoinLane(lanes[0])
  8739  		}
  8740  
  8741  		chg.AddAll(ts)
  8742  	}
  8743  
  8744  	st.Unlock()
  8745  	err = ms.o.Settle(settleTimeout)
  8746  	st.Lock()
  8747  	c.Assert(err, IsNil)
  8748  	c.Check(chg.Err(), IsNil)
  8749  
  8750  	// At this point the gadget and kernel are updated and the kernel
  8751  	// required a restart. Check that *before* this restart the DTB
  8752  	// files from the kernel are in place.
  8753  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "bcm2710-rpi-2-b.dtb"), testutil.FileContains, "bcm2710-rpi-2-b.dtb rev2-from-kernel")
  8754  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "bcm2710-rpi-3-b.dtb"), testutil.FileContains, "bcm2710-rpi-3-b.dtb rev2-from-kernel")
  8755  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "overlays/uart0.dtbo"), testutil.FileContains, "uart0.dtbo rev2-from-kernel")
  8756  	//  gadget content is not updated because there is no edition update
  8757  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "start.elf"), testutil.FileContains, "start.elf rev1")
  8758  
  8759  	// pretend we restarted
  8760  	ms.mockSuccessfulReboot(c, ms.bloader, []snap.Type{snap.TypeKernel})
  8761  
  8762  	st.Unlock()
  8763  	err = ms.o.Settle(settleTimeout)
  8764  	st.Lock()
  8765  	c.Assert(err, IsNil)
  8766  	c.Check(chg.Err(), ErrorMatches, `cannot perform the following tasks:\n- kernel failed.*`)
  8767  
  8768  	// Ensure that tasks states are what we expect
  8769  	taskStates := snapTaskStatusForChange(chg)
  8770  	// The pi-kernel failed in error-trigger and got rolled back
  8771  	c.Check(taskStates["pi-kernel:error-trigger"], Equals, state.ErrorStatus)
  8772  	c.Check(taskStates["pi-kernel:mount-snap"], Equals, state.UndoneStatus)
  8773  	// But the pi gadget was installed just fine
  8774  	c.Check(taskStates["pi:download-snap"], Equals, state.DoneStatus)
  8775  	c.Check(taskStates["pi:link-snap"], Equals, state.DoneStatus)
  8776  
  8777  	// Note that the undo of the kernel did *not* revert the DTBs on
  8778  	// disk. The reason is that we never undo asset updates on the
  8779  	// basis that if the system booted they are probably good enough.
  8780  	// A really broken DTB can brick the device if the new DTB is written
  8781  	// to disk, the system reboots and neither new kernel nor fallback
  8782  	// kernel will boot because there is no A/B DTB. This is a flaw
  8783  	// of the Pi and u-boot.
  8784  	//
  8785  	// In the future we will integrate with the "pi-boot" mechanism that
  8786  	// allows doing a A/B boot using the config.txt "os-prefix" dir. This
  8787  	// will allow us to write the DTBs to A/B locations.
  8788  	//
  8789  	// TODO:UC20: port this so that it integrates with pi-boot and the
  8790  	//            A/B os-prefix mechanism there so that we can have
  8791  	//            robust DTB updates.
  8792  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "bcm2710-rpi-2-b.dtb"), testutil.FileContains, "bcm2710-rpi-2-b.dtb rev2-from-kernel")
  8793  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "bcm2710-rpi-3-b.dtb"), testutil.FileContains, "bcm2710-rpi-3-b.dtb rev2-from-kernel")
  8794  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "overlays/uart0.dtbo"), testutil.FileContains, "uart0.dtbo rev2-from-kernel")
  8795  	//  gadget content is not updated because there is no edition update
  8796  	c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "start.elf"), testutil.FileContains, "start.elf rev1")
  8797  }
  8798  
  8799  // deal with the missing "update-gadget-assets" tasks, see LP:#1940553
  8800  func (ms *gadgetUpdatesSuite) TestGadgetKernelRefreshFromOldBrokenSnap(c *C) {
  8801  	st := ms.o.State()
  8802  	st.Lock()
  8803  	defer st.Unlock()
  8804  
  8805  	// we have an install kernel/gadget
  8806  	gadgetSnapYaml := "name: pi\nversion: 1.0\ntype: gadget"
  8807  	ms.mockInstalledSnapWithFiles(c, gadgetSnapYaml, [][]string{
  8808  		{"meta/gadget.yaml", "volumes:\n volume-id:\n  bootloader: grub"},
  8809  	})
  8810  	kernelSnapYaml := "name: pi-kernel\nversion: 1.0\ntype: kernel"
  8811  	ms.mockInstalledSnapWithFiles(c, kernelSnapYaml, nil)
  8812  
  8813  	// add new kernel/gadget snap
  8814  	newKernelSnapYaml := kernelSnapYaml
  8815  	ms.mockSnapUpgradeWithFiles(c, newKernelSnapYaml, nil)
  8816  	ms.mockSnapUpgradeWithFiles(c, gadgetSnapYaml, [][]string{
  8817  		{"meta/gadget.yaml", "volumes:\n volume-id:\n  bootloader: grub"},
  8818  	})
  8819  
  8820  	// now a refresh is simulated that does *not* contain an
  8821  	// "update-gadget-assets" task, see LP:#1940553
  8822  	snapstate.TestingLeaveOutKernelUpdateGadgetAssets = true
  8823  	defer func() { snapstate.TestingLeaveOutKernelUpdateGadgetAssets = false }()
  8824  	affected, tasksets, err := snapstate.UpdateMany(context.TODO(), st, nil, 0, &snapstate.Flags{})
  8825  	c.Assert(err, IsNil)
  8826  	sort.Strings(affected)
  8827  	c.Check(affected, DeepEquals, []string{"pi", "pi-kernel"})
  8828  
  8829  	// here we need to manipulate the change to simulate that there
  8830  	// is no "update-gadget-assets" task for the kernel, unfortunately
  8831  	// there is no "state.TaskSet.RemoveTask" nor a "state.Task.Unwait()"
  8832  	chg := st.NewChange("upgrade-snaps", "...")
  8833  	for _, ts := range tasksets {
  8834  		// skip the taskset of UpdateMany that does the
  8835  		// check-rerefresh, see tsWithoutReRefresh for details
  8836  		if ts.Tasks()[0].Kind() == "check-rerefresh" {
  8837  			continue
  8838  		}
  8839  
  8840  		chg.AddAll(ts)
  8841  	}
  8842  
  8843  	st.Unlock()
  8844  	err = ms.o.Settle(settleTimeout)
  8845  	st.Lock()
  8846  	c.Assert(err, IsNil)
  8847  	c.Check(chg.Err(), ErrorMatches, "cannot perform the following tasks:\n.*Mount snap \"pi-kernel\" \\(2\\) \\(cannot refresh kernel with change created by old snapd that is missing gadget update task\\)")
  8848  }