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

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2019 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package devicestate_test
    21  
    22  import (
    23  	"context"
    24  	"errors"
    25  	"fmt"
    26  	"io/ioutil"
    27  	"net"
    28  	"net/http"
    29  	"net/http/httptest"
    30  	"net/url"
    31  	"os"
    32  	"path/filepath"
    33  	"strings"
    34  	"testing"
    35  	"time"
    36  
    37  	. "gopkg.in/check.v1"
    38  	"gopkg.in/tomb.v2"
    39  	"gopkg.in/yaml.v2"
    40  
    41  	"github.com/snapcore/snapd/asserts"
    42  	"github.com/snapcore/snapd/asserts/assertstest"
    43  	"github.com/snapcore/snapd/asserts/sysdb"
    44  	"github.com/snapcore/snapd/bootloader"
    45  	"github.com/snapcore/snapd/bootloader/bootloadertest"
    46  	"github.com/snapcore/snapd/dirs"
    47  	"github.com/snapcore/snapd/gadget"
    48  	"github.com/snapcore/snapd/httputil"
    49  	"github.com/snapcore/snapd/interfaces"
    50  	"github.com/snapcore/snapd/interfaces/builtin"
    51  	"github.com/snapcore/snapd/logger"
    52  	"github.com/snapcore/snapd/osutil"
    53  	"github.com/snapcore/snapd/overlord"
    54  	"github.com/snapcore/snapd/overlord/assertstate"
    55  	"github.com/snapcore/snapd/overlord/assertstate/assertstatetest"
    56  	"github.com/snapcore/snapd/overlord/auth"
    57  	"github.com/snapcore/snapd/overlord/configstate/config"
    58  	"github.com/snapcore/snapd/overlord/devicestate"
    59  	"github.com/snapcore/snapd/overlord/devicestate/devicestatetest"
    60  	"github.com/snapcore/snapd/overlord/hookstate"
    61  	"github.com/snapcore/snapd/overlord/ifacestate/ifacerepo"
    62  	"github.com/snapcore/snapd/overlord/snapstate"
    63  	"github.com/snapcore/snapd/overlord/snapstate/snapstatetest"
    64  	"github.com/snapcore/snapd/overlord/state"
    65  	"github.com/snapcore/snapd/overlord/storecontext"
    66  	"github.com/snapcore/snapd/release"
    67  	"github.com/snapcore/snapd/snap"
    68  	"github.com/snapcore/snapd/snap/snaptest"
    69  	"github.com/snapcore/snapd/store/storetest"
    70  	"github.com/snapcore/snapd/strutil"
    71  	"github.com/snapcore/snapd/testutil"
    72  	"github.com/snapcore/snapd/timings"
    73  )
    74  
    75  func TestDeviceManager(t *testing.T) { TestingT(t) }
    76  
    77  type deviceMgrSuite struct {
    78  	o       *overlord.Overlord
    79  	state   *state.State
    80  	se      *overlord.StateEngine
    81  	hookMgr *hookstate.HookManager
    82  	mgr     *devicestate.DeviceManager
    83  	db      *asserts.Database
    84  
    85  	bootloader *bootloadertest.MockBootloader
    86  
    87  	storeSigning *assertstest.StoreStack
    88  	brands       *assertstest.SigningAccounts
    89  
    90  	reqID string
    91  
    92  	ancillary []asserts.Assertion
    93  
    94  	restartRequests []state.RestartType
    95  
    96  	restoreOnClassic         func()
    97  	restoreGenericClassicMod func()
    98  	restoreSanitize          func()
    99  
   100  	newFakeStore func(storecontext.DeviceBackend) snapstate.StoreService
   101  }
   102  
   103  var _ = Suite(&deviceMgrSuite{})
   104  var testKeyLength = 1024
   105  
   106  type fakeStore struct {
   107  	storetest.Store
   108  
   109  	state *state.State
   110  	db    asserts.RODatabase
   111  }
   112  
   113  func (sto *fakeStore) pokeStateLock() {
   114  	// the store should be called without the state lock held. Try
   115  	// to acquire it.
   116  	sto.state.Lock()
   117  	sto.state.Unlock()
   118  }
   119  
   120  func (sto *fakeStore) Assertion(assertType *asserts.AssertionType, key []string, _ *auth.UserState) (asserts.Assertion, error) {
   121  	sto.pokeStateLock()
   122  	ref := &asserts.Ref{Type: assertType, PrimaryKey: key}
   123  	return ref.Resolve(sto.db.Find)
   124  }
   125  
   126  var (
   127  	brandPrivKey, _  = assertstest.GenerateKey(752)
   128  	brandPrivKey2, _ = assertstest.GenerateKey(752)
   129  )
   130  
   131  func (s *deviceMgrSuite) SetUpTest(c *C) {
   132  	dirs.SetRootDir(c.MkDir())
   133  	os.MkdirAll(dirs.SnapRunDir, 0755)
   134  
   135  	s.restartRequests = nil
   136  
   137  	s.restoreSanitize = snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})
   138  
   139  	s.bootloader = bootloadertest.Mock("mock", c.MkDir())
   140  	bootloader.Force(s.bootloader)
   141  
   142  	s.restoreOnClassic = release.MockOnClassic(false)
   143  
   144  	s.storeSigning = assertstest.NewStoreStack("canonical", nil)
   145  	s.o = overlord.MockWithRestartHandler(func(req state.RestartType) {
   146  		s.restartRequests = append(s.restartRequests, req)
   147  	})
   148  	s.state = s.o.State()
   149  	s.state.Lock()
   150  	s.state.VerifyReboot("boot-id-0")
   151  	s.state.Unlock()
   152  	s.se = s.o.StateEngine()
   153  
   154  	s.restoreGenericClassicMod = sysdb.MockGenericClassicModel(s.storeSigning.GenericClassicModel)
   155  
   156  	s.brands = assertstest.NewSigningAccounts(s.storeSigning)
   157  	s.brands.Register("my-brand", brandPrivKey, nil)
   158  	s.brands.Register("rereg-brand", brandPrivKey2, nil)
   159  
   160  	db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{
   161  		Backstore:       asserts.NewMemoryBackstore(),
   162  		Trusted:         s.storeSigning.Trusted,
   163  		OtherPredefined: s.storeSigning.Generic,
   164  	})
   165  	c.Assert(err, IsNil)
   166  
   167  	s.state.Lock()
   168  	assertstate.ReplaceDB(s.state, db)
   169  	s.state.Unlock()
   170  
   171  	err = db.Add(s.storeSigning.StoreAccountKey(""))
   172  	c.Assert(err, IsNil)
   173  
   174  	hookMgr, err := hookstate.Manager(s.state, s.o.TaskRunner())
   175  	c.Assert(err, IsNil)
   176  	mgr, err := devicestate.Manager(s.state, hookMgr, s.o.TaskRunner(), s.newStore)
   177  	c.Assert(err, IsNil)
   178  
   179  	s.db = db
   180  	s.hookMgr = hookMgr
   181  	s.o.AddManager(s.hookMgr)
   182  	s.mgr = mgr
   183  	s.o.AddManager(s.mgr)
   184  	s.o.AddManager(s.o.TaskRunner())
   185  
   186  	// For triggering errors
   187  	erroringHandler := func(task *state.Task, _ *tomb.Tomb) error {
   188  		return errors.New("error out")
   189  	}
   190  	s.o.TaskRunner().AddHandler("error-trigger", erroringHandler, nil)
   191  
   192  	c.Assert(s.o.StartUp(), IsNil)
   193  
   194  	s.state.Lock()
   195  	snapstate.ReplaceStore(s.state, &fakeStore{
   196  		state: s.state,
   197  		db:    s.storeSigning,
   198  	})
   199  	s.state.Unlock()
   200  }
   201  
   202  func (s *deviceMgrSuite) newStore(devBE storecontext.DeviceBackend) snapstate.StoreService {
   203  	return s.newFakeStore(devBE)
   204  }
   205  
   206  func (s *deviceMgrSuite) TearDownTest(c *C) {
   207  	s.ancillary = nil
   208  	s.state.Lock()
   209  	assertstate.ReplaceDB(s.state, nil)
   210  	s.state.Unlock()
   211  	bootloader.Force(nil)
   212  	dirs.SetRootDir("")
   213  	s.restoreGenericClassicMod()
   214  	s.restoreOnClassic()
   215  	s.restoreSanitize()
   216  }
   217  
   218  var settleTimeout = 15 * time.Second
   219  
   220  func (s *deviceMgrSuite) settle(c *C) {
   221  	err := s.o.Settle(settleTimeout)
   222  	c.Assert(err, IsNil)
   223  }
   224  
   225  // seeding avoids triggering a real full seeding, it simulates having it in process instead
   226  func (s *deviceMgrSuite) seeding() {
   227  	chg := s.state.NewChange("seed", "Seed system")
   228  	chg.SetStatus(state.DoingStatus)
   229  }
   230  
   231  func (s *deviceMgrSuite) signSerial(c *C, bhv *devicestatetest.DeviceServiceBehavior, headers map[string]interface{}, body []byte) (serial asserts.Assertion, ancillary []asserts.Assertion, err error) {
   232  	brandID := headers["brand-id"].(string)
   233  	model := headers["model"].(string)
   234  	keyID := ""
   235  
   236  	var signing assertstest.SignerDB = s.storeSigning
   237  
   238  	switch model {
   239  	case "pc", "pc2":
   240  	case "classic-alt-store":
   241  		c.Check(brandID, Equals, "canonical")
   242  	case "generic-classic":
   243  		c.Check(brandID, Equals, "generic")
   244  		headers["authority-id"] = "generic"
   245  		keyID = s.storeSigning.GenericKey.PublicKeyID()
   246  	case "rereg-model":
   247  		headers["authority-id"] = "rereg-brand"
   248  		signing = s.brands.Signing("rereg-brand")
   249  	default:
   250  		c.Fatalf("unknown model: %s", model)
   251  	}
   252  	a, err := signing.Sign(asserts.SerialType, headers, body, keyID)
   253  	return a, s.ancillary, err
   254  }
   255  
   256  func (s *deviceMgrSuite) mockServer(c *C, reqID string, bhv *devicestatetest.DeviceServiceBehavior) *httptest.Server {
   257  	if bhv == nil {
   258  		bhv = &devicestatetest.DeviceServiceBehavior{}
   259  	}
   260  
   261  	bhv.ReqID = reqID
   262  	bhv.SignSerial = s.signSerial
   263  	bhv.ExpectedCapabilities = "serial-stream"
   264  
   265  	return devicestatetest.MockDeviceService(c, bhv)
   266  }
   267  
   268  func (s *deviceMgrSuite) setupCore(c *C, name, snapYaml string, snapContents string) {
   269  	sideInfoCore := &snap.SideInfo{
   270  		RealName: name,
   271  		Revision: snap.R(3),
   272  	}
   273  	snaptest.MockSnap(c, snapYaml, sideInfoCore)
   274  	snapstate.Set(s.state, name, &snapstate.SnapState{
   275  		SnapType: "os",
   276  		Active:   true,
   277  		Sequence: []*snap.SideInfo{sideInfoCore},
   278  		Current:  sideInfoCore.Revision,
   279  	})
   280  }
   281  
   282  func (s *deviceMgrSuite) findBecomeOperationalChange(skipIDs ...string) *state.Change {
   283  	for _, chg := range s.state.Changes() {
   284  		if chg.Kind() == "become-operational" && !strutil.ListContains(skipIDs, chg.ID()) {
   285  			return chg
   286  		}
   287  	}
   288  	return nil
   289  }
   290  
   291  func (s *deviceMgrSuite) TestFullDeviceRegistrationHappy(c *C) {
   292  	r1 := devicestate.MockKeyLength(testKeyLength)
   293  	defer r1()
   294  
   295  	mockServer := s.mockServer(c, "REQID-1", nil)
   296  	defer mockServer.Close()
   297  
   298  	r2 := devicestate.MockBaseStoreURL(mockServer.URL)
   299  	defer r2()
   300  
   301  	// setup state as will be done by first-boot
   302  	s.state.Lock()
   303  	defer s.state.Unlock()
   304  
   305  	s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
   306  		"architecture": "amd64",
   307  		"kernel":       "pc-kernel",
   308  		"gadget":       "pc",
   309  	})
   310  
   311  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   312  		Brand: "canonical",
   313  		Model: "pc",
   314  	})
   315  
   316  	// avoid full seeding
   317  	s.seeding()
   318  
   319  	// not started if not seeded
   320  	s.state.Unlock()
   321  	s.se.Ensure()
   322  	s.state.Lock()
   323  
   324  	becomeOperational := s.findBecomeOperationalChange()
   325  	c.Check(becomeOperational, IsNil)
   326  
   327  	devicestatetest.MockGadget(c, s.state, "pc", snap.R(2), nil)
   328  	// mark it as seeded
   329  	s.state.Set("seeded", true)
   330  
   331  	// runs the whole device registration process
   332  	s.state.Unlock()
   333  	s.settle(c)
   334  	s.state.Lock()
   335  
   336  	becomeOperational = s.findBecomeOperationalChange()
   337  	c.Assert(becomeOperational, NotNil)
   338  
   339  	c.Check(becomeOperational.Status().Ready(), Equals, true)
   340  	c.Check(becomeOperational.Err(), IsNil)
   341  
   342  	device, err := devicestatetest.Device(s.state)
   343  	c.Assert(err, IsNil)
   344  	c.Check(device.Brand, Equals, "canonical")
   345  	c.Check(device.Model, Equals, "pc")
   346  	c.Check(device.Serial, Equals, "9999")
   347  
   348  	ok := false
   349  	select {
   350  	case <-s.mgr.Registered():
   351  		ok = true
   352  	case <-time.After(5 * time.Second):
   353  		c.Fatal("should have been marked registered")
   354  	}
   355  	c.Check(ok, Equals, true)
   356  
   357  	a, err := s.db.Find(asserts.SerialType, map[string]string{
   358  		"brand-id": "canonical",
   359  		"model":    "pc",
   360  		"serial":   "9999",
   361  	})
   362  	c.Assert(err, IsNil)
   363  	serial := a.(*asserts.Serial)
   364  
   365  	privKey, err := devicestate.KeypairManager(s.mgr).Get(serial.DeviceKey().ID())
   366  	c.Assert(err, IsNil)
   367  	c.Check(privKey, NotNil)
   368  
   369  	c.Check(device.KeyID, Equals, privKey.PublicKey().ID())
   370  }
   371  
   372  func (s *deviceMgrSuite) TestFullDeviceRegistrationHappyWithProxy(c *C) {
   373  	r1 := devicestate.MockKeyLength(testKeyLength)
   374  	defer r1()
   375  
   376  	mockServer := s.mockServer(c, "REQID-1", nil)
   377  	defer mockServer.Close()
   378  
   379  	// as core.proxy.store is set, should not need to do this but just in case
   380  	r2 := devicestate.MockBaseStoreURL(mockServer.URL + "/direct/baaad/")
   381  	defer r2()
   382  
   383  	// setup state as will be done by first-boot
   384  	s.state.Lock()
   385  	defer s.state.Unlock()
   386  
   387  	tr := config.NewTransaction(s.state)
   388  	c.Assert(tr.Set("core", "proxy.store", "foo"), IsNil)
   389  	tr.Commit()
   390  	operatorAcct := assertstest.NewAccount(s.storeSigning, "foo-operator", nil, "")
   391  
   392  	// have a store assertion.
   393  	stoAs, err := s.storeSigning.Sign(asserts.StoreType, map[string]interface{}{
   394  		"store":       "foo",
   395  		"url":         mockServer.URL,
   396  		"operator-id": operatorAcct.AccountID(),
   397  		"timestamp":   time.Now().Format(time.RFC3339),
   398  	}, nil, "")
   399  	c.Assert(err, IsNil)
   400  
   401  	assertstatetest.AddMany(s.state, operatorAcct, stoAs)
   402  
   403  	s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
   404  		"architecture": "amd64",
   405  		"kernel":       "pc-kernel",
   406  		"gadget":       "pc",
   407  	})
   408  
   409  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   410  		Brand: "canonical",
   411  		Model: "pc",
   412  	})
   413  
   414  	devicestatetest.MockGadget(c, s.state, "pc", snap.R(2), nil)
   415  	// mark as seeded
   416  	s.state.Set("seeded", true)
   417  
   418  	// runs the whole device registration process
   419  	s.state.Unlock()
   420  	s.settle(c)
   421  	s.state.Lock()
   422  
   423  	becomeOperational := s.findBecomeOperationalChange()
   424  	c.Assert(becomeOperational, NotNil)
   425  
   426  	c.Check(becomeOperational.Status().Ready(), Equals, true)
   427  	c.Check(becomeOperational.Err(), IsNil)
   428  
   429  	device, err := devicestatetest.Device(s.state)
   430  	c.Assert(err, IsNil)
   431  	c.Check(device.Brand, Equals, "canonical")
   432  	c.Check(device.Model, Equals, "pc")
   433  	c.Check(device.Serial, Equals, "9999")
   434  
   435  	ok := false
   436  	select {
   437  	case <-s.mgr.Registered():
   438  		ok = true
   439  	case <-time.After(5 * time.Second):
   440  		c.Fatal("should have been marked registered")
   441  	}
   442  	c.Check(ok, Equals, true)
   443  
   444  	a, err := s.db.Find(asserts.SerialType, map[string]string{
   445  		"brand-id": "canonical",
   446  		"model":    "pc",
   447  		"serial":   "9999",
   448  	})
   449  	c.Assert(err, IsNil)
   450  	serial := a.(*asserts.Serial)
   451  
   452  	privKey, err := devicestate.KeypairManager(s.mgr).Get(serial.DeviceKey().ID())
   453  	c.Assert(err, IsNil)
   454  	c.Check(privKey, NotNil)
   455  
   456  	c.Check(device.KeyID, Equals, privKey.PublicKey().ID())
   457  }
   458  
   459  func (s *deviceMgrSuite) TestFullDeviceRegistrationHappyClassicNoGadget(c *C) {
   460  	restore := release.MockOnClassic(true)
   461  	defer restore()
   462  
   463  	r1 := devicestate.MockKeyLength(testKeyLength)
   464  	defer r1()
   465  
   466  	mockServer := s.mockServer(c, "REQID-1", nil)
   467  	defer mockServer.Close()
   468  
   469  	r2 := devicestate.MockBaseStoreURL(mockServer.URL)
   470  	defer r2()
   471  
   472  	// setup state as will be done by first-boot
   473  	s.state.Lock()
   474  	defer s.state.Unlock()
   475  
   476  	s.makeModelAssertionInState(c, "canonical", "classic-alt-store", map[string]interface{}{
   477  		"classic": "true",
   478  		"store":   "alt-store",
   479  	})
   480  
   481  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   482  		Brand: "canonical",
   483  		Model: "classic-alt-store",
   484  	})
   485  
   486  	// avoid full seeding
   487  	s.seeding()
   488  
   489  	// runs the whole device registration process
   490  	s.state.Unlock()
   491  	s.settle(c)
   492  	s.state.Lock()
   493  
   494  	becomeOperational := s.findBecomeOperationalChange()
   495  	c.Assert(becomeOperational, NotNil)
   496  
   497  	c.Check(becomeOperational.Status().Ready(), Equals, true)
   498  	c.Check(becomeOperational.Err(), IsNil)
   499  
   500  	device, err := devicestatetest.Device(s.state)
   501  	c.Assert(err, IsNil)
   502  	c.Check(device.Brand, Equals, "canonical")
   503  	c.Check(device.Model, Equals, "classic-alt-store")
   504  	c.Check(device.Serial, Equals, "9999")
   505  
   506  	a, err := s.db.Find(asserts.SerialType, map[string]string{
   507  		"brand-id": "canonical",
   508  		"model":    "classic-alt-store",
   509  		"serial":   "9999",
   510  	})
   511  	c.Assert(err, IsNil)
   512  	serial := a.(*asserts.Serial)
   513  
   514  	privKey, err := devicestate.KeypairManager(s.mgr).Get(serial.DeviceKey().ID())
   515  	c.Assert(err, IsNil)
   516  	c.Check(privKey, NotNil)
   517  
   518  	c.Check(device.KeyID, Equals, privKey.PublicKey().ID())
   519  }
   520  
   521  func (s *deviceMgrSuite) TestFullDeviceRegistrationHappyClassicFallback(c *C) {
   522  	restore := release.MockOnClassic(true)
   523  	defer restore()
   524  
   525  	r1 := devicestate.MockKeyLength(testKeyLength)
   526  	defer r1()
   527  
   528  	mockServer := s.mockServer(c, "REQID-1", nil)
   529  	defer mockServer.Close()
   530  
   531  	r2 := devicestate.MockBaseStoreURL(mockServer.URL)
   532  	defer r2()
   533  
   534  	// setup state as will be done by first-boot
   535  	s.state.Lock()
   536  	defer s.state.Unlock()
   537  
   538  	// in this case is just marked seeded without snaps
   539  	s.state.Set("seeded", true)
   540  
   541  	// not started without some installation happening or happened
   542  	s.state.Unlock()
   543  	s.se.Ensure()
   544  	s.state.Lock()
   545  
   546  	becomeOperational := s.findBecomeOperationalChange()
   547  	c.Check(becomeOperational, IsNil)
   548  
   549  	// have a in-progress installation
   550  	inst := s.state.NewChange("install", "...")
   551  	task := s.state.NewTask("mount-snap", "...")
   552  	inst.AddTask(task)
   553  
   554  	// runs the whole device registration process
   555  	s.state.Unlock()
   556  	s.settle(c)
   557  	s.state.Lock()
   558  
   559  	becomeOperational = s.findBecomeOperationalChange()
   560  	c.Assert(becomeOperational, NotNil)
   561  
   562  	c.Check(becomeOperational.Status().Ready(), Equals, true)
   563  	c.Check(becomeOperational.Err(), IsNil)
   564  
   565  	device, err := devicestatetest.Device(s.state)
   566  	c.Assert(err, IsNil)
   567  	c.Check(device.Brand, Equals, "generic")
   568  	c.Check(device.Model, Equals, "generic-classic")
   569  	c.Check(device.Serial, Equals, "9999")
   570  
   571  	// model was installed
   572  	_, err = s.db.Find(asserts.ModelType, map[string]string{
   573  		"series":   "16",
   574  		"brand-id": "generic",
   575  		"model":    "generic-classic",
   576  		"classic":  "true",
   577  	})
   578  	c.Assert(err, IsNil)
   579  
   580  	a, err := s.db.Find(asserts.SerialType, map[string]string{
   581  		"brand-id": "generic",
   582  		"model":    "generic-classic",
   583  		"serial":   "9999",
   584  	})
   585  	c.Assert(err, IsNil)
   586  	serial := a.(*asserts.Serial)
   587  
   588  	privKey, err := devicestate.KeypairManager(s.mgr).Get(serial.DeviceKey().ID())
   589  	c.Assert(err, IsNil)
   590  	c.Check(privKey, NotNil)
   591  
   592  	c.Check(device.KeyID, Equals, privKey.PublicKey().ID())
   593  
   594  	// auto-refreshes are possible
   595  	ok, err := devicestate.CanAutoRefresh(s.state)
   596  	c.Assert(err, IsNil)
   597  	c.Check(ok, Equals, true)
   598  }
   599  
   600  func (s *deviceMgrSuite) TestFullDeviceRegistrationAltBrandHappy(c *C) {
   601  	c.Skip("not yet supported")
   602  	r1 := devicestate.MockKeyLength(testKeyLength)
   603  	defer r1()
   604  
   605  	mockServer := s.mockServer(c, "REQID-1", nil)
   606  	defer mockServer.Close()
   607  
   608  	r2 := devicestate.MockBaseStoreURL(mockServer.URL)
   609  	defer r2()
   610  
   611  	// setup state as will be done by first-boot
   612  	s.state.Lock()
   613  	defer s.state.Unlock()
   614  
   615  	s.makeModelAssertionInState(c, "my-brand", "my-model", map[string]interface{}{
   616  		"classic": "true",
   617  		"store":   "alt-store",
   618  	})
   619  
   620  	devicestatetest.MockGadget(c, s.state, "gadget", snap.R(2), nil)
   621  
   622  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   623  		Brand: "my-brand",
   624  		Model: "my-model",
   625  	})
   626  
   627  	// avoid full seeding
   628  	s.seeding()
   629  
   630  	// runs the whole device registration process
   631  	s.state.Unlock()
   632  	s.settle(c)
   633  	s.state.Lock()
   634  
   635  	becomeOperational := s.findBecomeOperationalChange()
   636  	c.Assert(becomeOperational, NotNil)
   637  
   638  	c.Check(becomeOperational.Status().Ready(), Equals, true)
   639  	c.Check(becomeOperational.Err(), IsNil)
   640  
   641  	device, err := devicestatetest.Device(s.state)
   642  	c.Assert(err, IsNil)
   643  	c.Check(device.Brand, Equals, "my-brand")
   644  	c.Check(device.Model, Equals, "my-model")
   645  	c.Check(device.Serial, Equals, "9999")
   646  
   647  	a, err := s.db.Find(asserts.SerialType, map[string]string{
   648  		"brand-id": "my-brand",
   649  		"model":    "my-model",
   650  		"serial":   "9999",
   651  	})
   652  	c.Assert(err, IsNil)
   653  	serial := a.(*asserts.Serial)
   654  
   655  	privKey, err := devicestate.KeypairManager(s.mgr).Get(serial.DeviceKey().ID())
   656  	c.Assert(err, IsNil)
   657  	c.Check(privKey, NotNil)
   658  
   659  	c.Check(device.KeyID, Equals, privKey.PublicKey().ID())
   660  }
   661  
   662  func (s *deviceMgrSuite) TestDoRequestSerialIdempotentAfterAddSerial(c *C) {
   663  	privKey, _ := assertstest.GenerateKey(testKeyLength)
   664  
   665  	mockServer := s.mockServer(c, "REQID-1", nil)
   666  	defer mockServer.Close()
   667  
   668  	restore := devicestate.MockBaseStoreURL(mockServer.URL)
   669  	defer restore()
   670  
   671  	restore = devicestate.MockRepeatRequestSerial("after-add-serial")
   672  	defer restore()
   673  
   674  	// setup state as done by first-boot/Ensure/doGenerateDeviceKey
   675  	s.state.Lock()
   676  	defer s.state.Unlock()
   677  
   678  	s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
   679  		"architecture": "amd64",
   680  		"kernel":       "pc-kernel",
   681  		"gadget":       "pc",
   682  	})
   683  
   684  	devicestatetest.MockGadget(c, s.state, "pc", snap.R(2), nil)
   685  
   686  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   687  		Brand: "canonical",
   688  		Model: "pc",
   689  		KeyID: privKey.PublicKey().ID(),
   690  	})
   691  	devicestate.KeypairManager(s.mgr).Put(privKey)
   692  
   693  	t := s.state.NewTask("request-serial", "test")
   694  	chg := s.state.NewChange("become-operational", "...")
   695  	chg.AddTask(t)
   696  
   697  	// avoid full seeding
   698  	s.seeding()
   699  
   700  	s.state.Unlock()
   701  	s.se.Ensure()
   702  	s.se.Wait()
   703  	s.state.Lock()
   704  
   705  	c.Check(chg.Status(), Equals, state.DoingStatus)
   706  	c.Check(chg.Err(), IsNil)
   707  	device, err := devicestatetest.Device(s.state)
   708  	c.Check(err, IsNil)
   709  	_, err = s.db.Find(asserts.SerialType, map[string]string{
   710  		"brand-id": "canonical",
   711  		"model":    "pc",
   712  		"serial":   "9999",
   713  	})
   714  	c.Assert(err, IsNil)
   715  
   716  	ok := false
   717  	select {
   718  	case <-s.mgr.Registered():
   719  	default:
   720  		ok = true
   721  	}
   722  	c.Check(ok, Equals, true)
   723  
   724  	s.state.Unlock()
   725  	s.se.Ensure()
   726  	s.se.Wait()
   727  	s.state.Lock()
   728  
   729  	// Repeated handler run but set original serial.
   730  	c.Check(chg.Status(), Equals, state.DoneStatus)
   731  	device, err = devicestatetest.Device(s.state)
   732  	c.Check(err, IsNil)
   733  	c.Check(device.Serial, Equals, "9999")
   734  
   735  	ok = false
   736  	select {
   737  	case <-s.mgr.Registered():
   738  		ok = true
   739  	case <-time.After(5 * time.Second):
   740  		c.Fatal("should have been marked registered")
   741  	}
   742  	c.Check(ok, Equals, true)
   743  }
   744  
   745  func (s *deviceMgrSuite) TestDoRequestSerialIdempotentAfterGotSerial(c *C) {
   746  	privKey, _ := assertstest.GenerateKey(testKeyLength)
   747  
   748  	mockServer := s.mockServer(c, "REQID-1", nil)
   749  	defer mockServer.Close()
   750  
   751  	restore := devicestate.MockBaseStoreURL(mockServer.URL)
   752  	defer restore()
   753  
   754  	restore = devicestate.MockRepeatRequestSerial("after-got-serial")
   755  	defer restore()
   756  
   757  	// setup state as done by first-boot/Ensure/doGenerateDeviceKey
   758  	s.state.Lock()
   759  	defer s.state.Unlock()
   760  
   761  	s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
   762  		"architecture": "amd64",
   763  		"kernel":       "pc-kernel",
   764  		"gadget":       "pc",
   765  	})
   766  
   767  	devicestatetest.MockGadget(c, s.state, "pc", snap.R(2), nil)
   768  
   769  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   770  		Brand: "canonical",
   771  		Model: "pc",
   772  		KeyID: privKey.PublicKey().ID(),
   773  	})
   774  	devicestate.KeypairManager(s.mgr).Put(privKey)
   775  
   776  	t := s.state.NewTask("request-serial", "test")
   777  	chg := s.state.NewChange("become-operational", "...")
   778  	chg.AddTask(t)
   779  
   780  	// avoid full seeding
   781  	s.seeding()
   782  
   783  	s.state.Unlock()
   784  	s.se.Ensure()
   785  	s.se.Wait()
   786  	s.state.Lock()
   787  
   788  	c.Check(chg.Status(), Equals, state.DoingStatus)
   789  	device, err := devicestatetest.Device(s.state)
   790  	c.Check(err, IsNil)
   791  	_, err = s.db.Find(asserts.SerialType, map[string]string{
   792  		"brand-id": "canonical",
   793  		"model":    "pc",
   794  		"serial":   "9999",
   795  	})
   796  	c.Assert(asserts.IsNotFound(err), Equals, true)
   797  
   798  	s.state.Unlock()
   799  	s.se.Ensure()
   800  	s.se.Wait()
   801  	s.state.Lock()
   802  
   803  	// Repeated handler run but set original serial.
   804  	c.Check(chg.Status(), Equals, state.DoneStatus)
   805  	c.Check(chg.Err(), IsNil)
   806  	device, err = devicestatetest.Device(s.state)
   807  	c.Check(err, IsNil)
   808  	c.Check(device.Serial, Equals, "9999")
   809  }
   810  
   811  func (s *deviceMgrSuite) TestDoRequestSerialErrorsOnNoHost(c *C) {
   812  	if os.Getenv("http_proxy") != "" {
   813  		c.Skip("cannot run test when http proxy is in use, the error pattern is different")
   814  	}
   815  
   816  	const nonexistent_host = "nowhere.nowhere.test"
   817  
   818  	// check internet access
   819  	_, err := net.LookupHost(nonexistent_host)
   820  	if netErr, ok := err.(net.Error); !ok || netErr.Temporary() {
   821  		c.Skip("cannot run test with no internet access, the error pattern is different")
   822  	}
   823  
   824  	privKey, _ := assertstest.GenerateKey(testKeyLength)
   825  
   826  	nowhere := "http://" + nonexistent_host
   827  
   828  	restore := devicestate.MockBaseStoreURL(nowhere)
   829  	defer restore()
   830  
   831  	// setup state as done by first-boot/Ensure/doGenerateDeviceKey
   832  	s.state.Lock()
   833  	defer s.state.Unlock()
   834  
   835  	devicestatetest.MockGadget(c, s.state, "gadget", snap.R(2), nil)
   836  
   837  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   838  		Brand: "canonical",
   839  		Model: "pc",
   840  		KeyID: privKey.PublicKey().ID(),
   841  	})
   842  	devicestate.KeypairManager(s.mgr).Put(privKey)
   843  
   844  	t := s.state.NewTask("request-serial", "test")
   845  	chg := s.state.NewChange("become-operational", "...")
   846  	chg.AddTask(t)
   847  
   848  	// avoid full seeding
   849  	s.seeding()
   850  
   851  	s.state.Unlock()
   852  	s.se.Ensure()
   853  	s.se.Wait()
   854  	s.state.Lock()
   855  
   856  	c.Check(chg.Status(), Equals, state.ErrorStatus)
   857  }
   858  
   859  func (s *deviceMgrSuite) TestDoRequestSerialMaxTentatives(c *C) {
   860  	privKey, _ := assertstest.GenerateKey(testKeyLength)
   861  
   862  	// immediate
   863  	r := devicestate.MockRetryInterval(0)
   864  	defer r()
   865  
   866  	r = devicestate.MockMaxTentatives(2)
   867  	defer r()
   868  
   869  	mockServer := s.mockServer(c, devicestatetest.ReqIDFailID501, nil)
   870  	defer mockServer.Close()
   871  
   872  	restore := devicestate.MockBaseStoreURL(mockServer.URL)
   873  	defer restore()
   874  
   875  	restore = devicestate.MockRepeatRequestSerial("after-add-serial")
   876  	defer restore()
   877  
   878  	// setup state as done by first-boot/Ensure/doGenerateDeviceKey
   879  	s.state.Lock()
   880  	defer s.state.Unlock()
   881  
   882  	s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
   883  		"architecture": "amd64",
   884  		"kernel":       "pc-kernel",
   885  		"gadget":       "pc",
   886  	})
   887  
   888  	devicestatetest.MockGadget(c, s.state, "pc", snap.R(2), nil)
   889  
   890  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   891  		Brand: "canonical",
   892  		Model: "pc",
   893  		KeyID: privKey.PublicKey().ID(),
   894  	})
   895  	devicestate.KeypairManager(s.mgr).Put(privKey)
   896  
   897  	t := s.state.NewTask("request-serial", "test")
   898  	chg := s.state.NewChange("become-operational", "...")
   899  	chg.AddTask(t)
   900  
   901  	// avoid full seeding
   902  	s.seeding()
   903  
   904  	s.state.Unlock()
   905  	s.se.Ensure()
   906  	s.se.Wait()
   907  	s.state.Lock()
   908  
   909  	c.Check(chg.Status(), Equals, state.DoingStatus)
   910  
   911  	s.state.Unlock()
   912  	s.se.Ensure()
   913  	s.se.Wait()
   914  	s.state.Lock()
   915  
   916  	c.Check(chg.Status(), Equals, state.ErrorStatus)
   917  	c.Check(chg.Err(), ErrorMatches, `(?s).*cannot retrieve request-id for making a request for a serial: unexpected status 501.*`)
   918  }
   919  
   920  func (s *deviceMgrSuite) TestFullDeviceRegistrationPollHappy(c *C) {
   921  	r1 := devicestate.MockKeyLength(testKeyLength)
   922  	defer r1()
   923  
   924  	mockServer := s.mockServer(c, devicestatetest.ReqIDPoll, nil)
   925  	defer mockServer.Close()
   926  
   927  	r2 := devicestate.MockBaseStoreURL(mockServer.URL)
   928  	defer r2()
   929  
   930  	// immediately
   931  	r3 := devicestate.MockRetryInterval(0)
   932  	defer r3()
   933  
   934  	// setup state as will be done by first-boot
   935  	s.state.Lock()
   936  	defer s.state.Unlock()
   937  
   938  	s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
   939  		"architecture": "amd64",
   940  		"kernel":       "pc-kernel",
   941  		"gadget":       "pc",
   942  	})
   943  
   944  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   945  		Brand: "canonical",
   946  		Model: "pc",
   947  	})
   948  
   949  	devicestatetest.MockGadget(c, s.state, "pc", snap.R(2), nil)
   950  	// mark as seeded
   951  	s.state.Set("seeded", true)
   952  
   953  	// runs the whole device registration process with polling
   954  	s.state.Unlock()
   955  	s.settle(c)
   956  	s.state.Lock()
   957  
   958  	becomeOperational := s.findBecomeOperationalChange()
   959  	c.Assert(becomeOperational, NotNil)
   960  
   961  	// needs 3 more Retry passes of polling
   962  	for i := 0; i < 3; i++ {
   963  		s.state.Unlock()
   964  		s.settle(c)
   965  		s.state.Lock()
   966  	}
   967  
   968  	c.Check(becomeOperational.Status().Ready(), Equals, true)
   969  	c.Check(becomeOperational.Err(), IsNil)
   970  
   971  	device, err := devicestatetest.Device(s.state)
   972  	c.Assert(err, IsNil)
   973  	c.Check(device.Brand, Equals, "canonical")
   974  	c.Check(device.Model, Equals, "pc")
   975  	c.Check(device.Serial, Equals, "10002")
   976  
   977  	a, err := s.db.Find(asserts.SerialType, map[string]string{
   978  		"brand-id": "canonical",
   979  		"model":    "pc",
   980  		"serial":   "10002",
   981  	})
   982  	c.Assert(err, IsNil)
   983  	serial := a.(*asserts.Serial)
   984  
   985  	privKey, err := devicestate.KeypairManager(s.mgr).Get(serial.DeviceKey().ID())
   986  	c.Assert(err, IsNil)
   987  	c.Check(privKey, NotNil)
   988  
   989  	c.Check(device.KeyID, Equals, privKey.PublicKey().ID())
   990  }
   991  
   992  func (s *deviceMgrSuite) TestFullDeviceRegistrationHappyPrepareDeviceHook(c *C) {
   993  	r1 := devicestate.MockKeyLength(testKeyLength)
   994  	defer r1()
   995  
   996  	bhv := &devicestatetest.DeviceServiceBehavior{
   997  		RequestIDURLPath: "/svc/request-id",
   998  		SerialURLPath:    "/svc/serial",
   999  	}
  1000  	bhv.PostPreflight = func(c *C, bhv *devicestatetest.DeviceServiceBehavior, w http.ResponseWriter, r *http.Request) {
  1001  		c.Check(r.Header.Get("X-Extra-Header"), Equals, "extra")
  1002  	}
  1003  
  1004  	mockServer := s.mockServer(c, "REQID-1", bhv)
  1005  	defer mockServer.Close()
  1006  
  1007  	// setup state as will be done by first-boot
  1008  	// & have a gadget with a prepare-device hook
  1009  	s.state.Lock()
  1010  	defer s.state.Unlock()
  1011  
  1012  	pDBhv := &devicestatetest.PrepareDeviceBehavior{
  1013  		DeviceSvcURL: mockServer.URL + "/svc/",
  1014  		Headers: map[string]string{
  1015  			"x-extra-header": "extra",
  1016  		},
  1017  		RegBody: map[string]string{
  1018  			"mac": "00:00:00:00:ff:00",
  1019  		},
  1020  		ProposedSerial: "Y9999",
  1021  	}
  1022  
  1023  	r2 := devicestatetest.MockGadget(c, s.state, "gadget", snap.R(2), pDBhv)
  1024  	defer r2()
  1025  
  1026  	// as device-service.url is set, should not need to do this but just in case
  1027  	r3 := devicestate.MockBaseStoreURL(mockServer.URL + "/direct/baad/")
  1028  	defer r3()
  1029  
  1030  	s.makeModelAssertionInState(c, "canonical", "pc2", map[string]interface{}{
  1031  		"architecture": "amd64",
  1032  		"kernel":       "pc-kernel",
  1033  		"gadget":       "gadget",
  1034  	})
  1035  
  1036  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  1037  		Brand: "canonical",
  1038  		Model: "pc2",
  1039  	})
  1040  
  1041  	// avoid full seeding
  1042  	s.seeding()
  1043  
  1044  	// runs the whole device registration process, note that the
  1045  	// device is not seeded yet
  1046  	s.state.Unlock()
  1047  	s.settle(c)
  1048  	s.state.Lock()
  1049  
  1050  	// without a seeded device, there is no become-operational change
  1051  	becomeOperational := s.findBecomeOperationalChange()
  1052  	c.Assert(becomeOperational, IsNil)
  1053  
  1054  	// now mark it as seeded
  1055  	s.state.Set("seeded", true)
  1056  	// and run the device registration again
  1057  	s.state.Unlock()
  1058  	s.settle(c)
  1059  	s.state.Lock()
  1060  
  1061  	becomeOperational = s.findBecomeOperationalChange()
  1062  	c.Assert(becomeOperational, NotNil)
  1063  
  1064  	c.Check(becomeOperational.Status().Ready(), Equals, true)
  1065  	c.Check(becomeOperational.Err(), IsNil)
  1066  
  1067  	device, err := devicestatetest.Device(s.state)
  1068  	c.Assert(err, IsNil)
  1069  	c.Check(device.Brand, Equals, "canonical")
  1070  	c.Check(device.Model, Equals, "pc2")
  1071  	c.Check(device.Serial, Equals, "Y9999")
  1072  
  1073  	a, err := s.db.Find(asserts.SerialType, map[string]string{
  1074  		"brand-id": "canonical",
  1075  		"model":    "pc2",
  1076  		"serial":   "Y9999",
  1077  	})
  1078  	c.Assert(err, IsNil)
  1079  	serial := a.(*asserts.Serial)
  1080  
  1081  	var details map[string]interface{}
  1082  	err = yaml.Unmarshal(serial.Body(), &details)
  1083  	c.Assert(err, IsNil)
  1084  
  1085  	c.Check(details, DeepEquals, map[string]interface{}{
  1086  		"mac": "00:00:00:00:ff:00",
  1087  	})
  1088  
  1089  	privKey, err := devicestate.KeypairManager(s.mgr).Get(serial.DeviceKey().ID())
  1090  	c.Assert(err, IsNil)
  1091  	c.Check(privKey, NotNil)
  1092  
  1093  	c.Check(device.KeyID, Equals, privKey.PublicKey().ID())
  1094  }
  1095  
  1096  func (s *deviceMgrSuite) TestFullDeviceRegistrationHappyWithHookAndNewProxy(c *C) {
  1097  	s.testFullDeviceRegistrationHappyWithHookAndProxy(c, true)
  1098  }
  1099  
  1100  func (s *deviceMgrSuite) TestFullDeviceRegistrationHappyWithHookAndOldProxy(c *C) {
  1101  	s.testFullDeviceRegistrationHappyWithHookAndProxy(c, false)
  1102  }
  1103  
  1104  func (s *deviceMgrSuite) testFullDeviceRegistrationHappyWithHookAndProxy(c *C, newEnough bool) {
  1105  	r1 := devicestate.MockKeyLength(testKeyLength)
  1106  	defer r1()
  1107  
  1108  	var reqID string
  1109  	var storeVersion string
  1110  	head := func(c *C, bhv *devicestatetest.DeviceServiceBehavior, w http.ResponseWriter, r *http.Request) {
  1111  		w.Header().Set("Snap-Store-Version", storeVersion)
  1112  	}
  1113  	bhv := &devicestatetest.DeviceServiceBehavior{
  1114  		Head: head,
  1115  	}
  1116  	svcPath := "/svc/"
  1117  	if newEnough {
  1118  		reqID = "REQID-42"
  1119  		storeVersion = "6"
  1120  		bhv.PostPreflight = func(c *C, bhv *devicestatetest.DeviceServiceBehavior, w http.ResponseWriter, r *http.Request) {
  1121  			c.Check(r.Header.Get("X-Snap-Device-Service-URL"), Matches, "http://[^/]*/bad/svc/")
  1122  			c.Check(r.Header.Get("X-Extra-Header"), Equals, "extra")
  1123  		}
  1124  		svcPath = "/bad/svc/"
  1125  	} else {
  1126  		reqID = "REQID-41"
  1127  		storeVersion = "5"
  1128  		bhv.RequestIDURLPath = "/svc/request-id"
  1129  		bhv.SerialURLPath = "/svc/serial"
  1130  		bhv.PostPreflight = func(c *C, bhv *devicestatetest.DeviceServiceBehavior, w http.ResponseWriter, r *http.Request) {
  1131  			c.Check(r.Header.Get("X-Extra-Header"), Equals, "extra")
  1132  		}
  1133  	}
  1134  
  1135  	mockServer := s.mockServer(c, reqID, bhv)
  1136  	defer mockServer.Close()
  1137  
  1138  	// setup state as will be done by first-boot
  1139  	// & have a gadget with a prepare-device hook
  1140  	s.state.Lock()
  1141  	defer s.state.Unlock()
  1142  
  1143  	pDBhv := &devicestatetest.PrepareDeviceBehavior{
  1144  		DeviceSvcURL: mockServer.URL + svcPath,
  1145  		Headers: map[string]string{
  1146  			"x-extra-header": "extra",
  1147  		},
  1148  	}
  1149  	r2 := devicestatetest.MockGadget(c, s.state, "gadget", snap.R(2), pDBhv)
  1150  	defer r2()
  1151  
  1152  	// as device-service.url is set, should not need to do this but just in case
  1153  	r3 := devicestate.MockBaseStoreURL(mockServer.URL + "/direct/baad/")
  1154  	defer r3()
  1155  
  1156  	tr := config.NewTransaction(s.state)
  1157  	c.Assert(tr.Set("core", "proxy.store", "foo"), IsNil)
  1158  	tr.Commit()
  1159  	operatorAcct := assertstest.NewAccount(s.storeSigning, "foo-operator", nil, "")
  1160  
  1161  	// have a store assertion.
  1162  	stoAs, err := s.storeSigning.Sign(asserts.StoreType, map[string]interface{}{
  1163  		"store":       "foo",
  1164  		"url":         mockServer.URL,
  1165  		"operator-id": operatorAcct.AccountID(),
  1166  		"timestamp":   time.Now().Format(time.RFC3339),
  1167  	}, nil, "")
  1168  	c.Assert(err, IsNil)
  1169  
  1170  	assertstatetest.AddMany(s.state, operatorAcct, stoAs)
  1171  
  1172  	s.makeModelAssertionInState(c, "canonical", "pc2", map[string]interface{}{
  1173  		"architecture": "amd64",
  1174  		"kernel":       "pc-kernel",
  1175  		"gadget":       "gadget",
  1176  	})
  1177  
  1178  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  1179  		Brand: "canonical",
  1180  		Model: "pc2",
  1181  	})
  1182  
  1183  	// mark it as seeded
  1184  	s.state.Set("seeded", true)
  1185  
  1186  	// runs the whole device registration process
  1187  	s.state.Unlock()
  1188  	s.settle(c)
  1189  	s.state.Lock()
  1190  
  1191  	becomeOperational := s.findBecomeOperationalChange()
  1192  	c.Assert(becomeOperational, NotNil)
  1193  
  1194  	c.Check(becomeOperational.Status().Ready(), Equals, true)
  1195  	c.Check(becomeOperational.Err(), IsNil)
  1196  
  1197  	device, err := devicestatetest.Device(s.state)
  1198  	c.Assert(err, IsNil)
  1199  	c.Check(device.Brand, Equals, "canonical")
  1200  	c.Check(device.Model, Equals, "pc2")
  1201  	c.Check(device.Serial, Equals, "9999")
  1202  
  1203  	a, err := s.db.Find(asserts.SerialType, map[string]string{
  1204  		"brand-id": "canonical",
  1205  		"model":    "pc2",
  1206  		"serial":   "9999",
  1207  	})
  1208  	c.Assert(err, IsNil)
  1209  	serial := a.(*asserts.Serial)
  1210  
  1211  	privKey, err := devicestate.KeypairManager(s.mgr).Get(serial.DeviceKey().ID())
  1212  	c.Assert(err, IsNil)
  1213  	c.Check(privKey, NotNil)
  1214  
  1215  	c.Check(device.KeyID, Equals, privKey.PublicKey().ID())
  1216  }
  1217  
  1218  func (s *deviceMgrSuite) TestFullDeviceRegistrationErrorBackoff(c *C) {
  1219  	r1 := devicestate.MockKeyLength(testKeyLength)
  1220  	defer r1()
  1221  
  1222  	bhv := &devicestatetest.DeviceServiceBehavior{}
  1223  	mockServer := s.mockServer(c, devicestatetest.ReqIDBadRequest, bhv)
  1224  	defer mockServer.Close()
  1225  
  1226  	r2 := devicestate.MockBaseStoreURL(mockServer.URL)
  1227  	defer r2()
  1228  
  1229  	// setup state as will be done by first-boot
  1230  	s.state.Lock()
  1231  	defer s.state.Unlock()
  1232  
  1233  	// sanity
  1234  	c.Check(devicestate.EnsureOperationalAttempts(s.state), Equals, 0)
  1235  
  1236  	s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
  1237  		"architecture": "amd64",
  1238  		"kernel":       "pc-kernel",
  1239  		"gadget":       "pc",
  1240  	})
  1241  
  1242  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  1243  		Brand: "canonical",
  1244  		Model: "pc",
  1245  	})
  1246  
  1247  	devicestatetest.MockGadget(c, s.state, "pc", snap.R(2), nil)
  1248  	// mark as seeded
  1249  	s.state.Set("seeded", true)
  1250  
  1251  	// try the whole device registration process
  1252  	s.state.Unlock()
  1253  	s.settle(c)
  1254  	s.state.Lock()
  1255  
  1256  	becomeOperational := s.findBecomeOperationalChange()
  1257  	c.Assert(becomeOperational, NotNil)
  1258  	firstTryID := becomeOperational.ID()
  1259  
  1260  	c.Check(becomeOperational.Status().Ready(), Equals, true)
  1261  	c.Check(becomeOperational.Err(), ErrorMatches, `(?s).*cannot deliver device serial request: bad serial-request.*`)
  1262  
  1263  	device, err := devicestatetest.Device(s.state)
  1264  	c.Assert(err, IsNil)
  1265  	c.Check(device.KeyID, Not(Equals), "")
  1266  	keyID := device.KeyID
  1267  
  1268  	c.Check(devicestate.EnsureOperationalShouldBackoff(s.mgr, time.Now()), Equals, true)
  1269  	c.Check(devicestate.EnsureOperationalShouldBackoff(s.mgr, time.Now().Add(6*time.Minute)), Equals, false)
  1270  	c.Check(devicestate.EnsureOperationalAttempts(s.state), Equals, 1)
  1271  
  1272  	// try again the whole device registration process
  1273  	bhv.ReqID = "REQID-1"
  1274  	devicestate.SetLastBecomeOperationalAttempt(s.mgr, time.Now().Add(-15*time.Minute))
  1275  	s.state.Unlock()
  1276  	s.settle(c)
  1277  	s.state.Lock()
  1278  
  1279  	becomeOperational = s.findBecomeOperationalChange(firstTryID)
  1280  	c.Assert(becomeOperational, NotNil)
  1281  
  1282  	c.Check(becomeOperational.Status().Ready(), Equals, true)
  1283  	c.Check(becomeOperational.Err(), IsNil)
  1284  
  1285  	c.Check(devicestate.EnsureOperationalAttempts(s.state), Equals, 2)
  1286  
  1287  	device, err = devicestatetest.Device(s.state)
  1288  	c.Assert(err, IsNil)
  1289  	c.Check(device.KeyID, Equals, keyID)
  1290  	c.Check(device.Serial, Equals, "10000")
  1291  }
  1292  
  1293  func (s *deviceMgrSuite) TestEnsureBecomeOperationalShouldBackoff(c *C) {
  1294  	t0 := time.Now()
  1295  	c.Check(devicestate.EnsureOperationalShouldBackoff(s.mgr, t0), Equals, false)
  1296  	c.Check(devicestate.BecomeOperationalBackoff(s.mgr), Equals, 5*time.Minute)
  1297  
  1298  	backoffs := []time.Duration{5, 10, 20, 40, 80, 160, 320, 640, 1440, 1440}
  1299  	t1 := t0
  1300  	for _, m := range backoffs {
  1301  		c.Check(devicestate.EnsureOperationalShouldBackoff(s.mgr, t1.Add(time.Duration(m-1)*time.Minute)), Equals, true)
  1302  
  1303  		t1 = t1.Add(time.Duration(m+1) * time.Minute)
  1304  		c.Check(devicestate.EnsureOperationalShouldBackoff(s.mgr, t1), Equals, false)
  1305  		m *= 2
  1306  		if m > (12 * 60) {
  1307  			m = 24 * 60
  1308  		}
  1309  		c.Check(devicestate.BecomeOperationalBackoff(s.mgr), Equals, m*time.Minute)
  1310  	}
  1311  }
  1312  
  1313  func (s *deviceMgrSuite) TestFullDeviceRegistrationMismatchedSerial(c *C) {
  1314  	r1 := devicestate.MockKeyLength(testKeyLength)
  1315  	defer r1()
  1316  
  1317  	mockServer := s.mockServer(c, devicestatetest.ReqIDSerialWithBadModel, nil)
  1318  	defer mockServer.Close()
  1319  
  1320  	r2 := devicestate.MockBaseStoreURL(mockServer.URL)
  1321  	defer r2()
  1322  
  1323  	// setup state as will be done by first-boot
  1324  	s.state.Lock()
  1325  	defer s.state.Unlock()
  1326  
  1327  	// sanity
  1328  	c.Check(devicestate.EnsureOperationalAttempts(s.state), Equals, 0)
  1329  
  1330  	devicestatetest.MockGadget(c, s.state, "gadget", snap.R(2), nil)
  1331  
  1332  	s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
  1333  		"architecture": "amd64",
  1334  		"kernel":       "pc-kernel",
  1335  		"gadget":       "gadget",
  1336  	})
  1337  
  1338  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  1339  		Brand: "canonical",
  1340  		Model: "pc",
  1341  	})
  1342  
  1343  	// mark as seeded
  1344  	s.state.Set("seeded", true)
  1345  
  1346  	// try the whole device registration process
  1347  	s.state.Unlock()
  1348  	s.settle(c)
  1349  	s.state.Lock()
  1350  
  1351  	becomeOperational := s.findBecomeOperationalChange()
  1352  	c.Assert(becomeOperational, NotNil)
  1353  
  1354  	c.Check(becomeOperational.Status().Ready(), Equals, true)
  1355  	c.Check(becomeOperational.Err(), ErrorMatches, `(?s).*obtained serial assertion does not match provided device identity information.*`)
  1356  }
  1357  
  1358  func (s *deviceMgrSuite) TestModelAndSerial(c *C) {
  1359  	s.state.Lock()
  1360  	defer s.state.Unlock()
  1361  	// nothing in the state
  1362  	_, err := s.mgr.Model()
  1363  	c.Check(err, Equals, state.ErrNoState)
  1364  	_, err = s.mgr.Serial()
  1365  	c.Check(err, Equals, state.ErrNoState)
  1366  
  1367  	// just brand and model
  1368  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  1369  		Brand: "canonical",
  1370  		Model: "pc",
  1371  	})
  1372  	_, err = s.mgr.Model()
  1373  	c.Check(err, Equals, state.ErrNoState)
  1374  	_, err = s.mgr.Serial()
  1375  	c.Check(err, Equals, state.ErrNoState)
  1376  
  1377  	// have a model assertion
  1378  	model := s.brands.Model("canonical", "pc", map[string]interface{}{
  1379  		"gadget":       "pc",
  1380  		"kernel":       "kernel",
  1381  		"architecture": "amd64",
  1382  	})
  1383  	assertstatetest.AddMany(s.state, model)
  1384  
  1385  	mod, err := s.mgr.Model()
  1386  	c.Assert(err, IsNil)
  1387  	c.Assert(mod.BrandID(), Equals, "canonical")
  1388  
  1389  	_, err = s.mgr.Serial()
  1390  	c.Check(err, Equals, state.ErrNoState)
  1391  
  1392  	// have a serial as well
  1393  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  1394  		Brand:  "canonical",
  1395  		Model:  "pc",
  1396  		Serial: "8989",
  1397  	})
  1398  	_, err = s.mgr.Model()
  1399  	c.Assert(err, IsNil)
  1400  	_, err = s.mgr.Serial()
  1401  	c.Check(err, Equals, state.ErrNoState)
  1402  
  1403  	// have a serial assertion
  1404  	s.makeSerialAssertionInState(c, "canonical", "pc", "8989")
  1405  
  1406  	_, err = s.mgr.Model()
  1407  	c.Assert(err, IsNil)
  1408  	ser, err := s.mgr.Serial()
  1409  	c.Assert(err, IsNil)
  1410  	c.Check(ser.Serial(), Equals, "8989")
  1411  }
  1412  
  1413  func (s *deviceMgrSuite) TestStoreContextBackendSetDevice(c *C) {
  1414  	s.state.Lock()
  1415  	defer s.state.Unlock()
  1416  
  1417  	scb := s.mgr.StoreContextBackend()
  1418  
  1419  	device, err := scb.Device()
  1420  	c.Check(err, IsNil)
  1421  	c.Check(device, DeepEquals, &auth.DeviceState{})
  1422  
  1423  	err = scb.SetDevice(&auth.DeviceState{Brand: "some-brand"})
  1424  	c.Check(err, IsNil)
  1425  	device, err = scb.Device()
  1426  	c.Check(err, IsNil)
  1427  	c.Check(device, DeepEquals, &auth.DeviceState{Brand: "some-brand"})
  1428  }
  1429  
  1430  func (s *deviceMgrSuite) TestStoreContextBackendModelAndSerial(c *C) {
  1431  	s.state.Lock()
  1432  	defer s.state.Unlock()
  1433  
  1434  	scb := s.mgr.StoreContextBackend()
  1435  
  1436  	// nothing in the state
  1437  	_, err := scb.Model()
  1438  	c.Check(err, Equals, state.ErrNoState)
  1439  	_, err = scb.Serial()
  1440  	c.Check(err, Equals, state.ErrNoState)
  1441  
  1442  	// just brand and model
  1443  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  1444  		Brand: "canonical",
  1445  		Model: "pc",
  1446  	})
  1447  	_, err = scb.Model()
  1448  	c.Check(err, Equals, state.ErrNoState)
  1449  	_, err = scb.Serial()
  1450  	c.Check(err, Equals, state.ErrNoState)
  1451  
  1452  	// have a model assertion
  1453  	model := s.brands.Model("canonical", "pc", map[string]interface{}{
  1454  		"gadget":       "pc",
  1455  		"kernel":       "kernel",
  1456  		"architecture": "amd64",
  1457  	})
  1458  	assertstatetest.AddMany(s.state, model)
  1459  
  1460  	mod, err := scb.Model()
  1461  	c.Assert(err, IsNil)
  1462  	c.Assert(mod.BrandID(), Equals, "canonical")
  1463  
  1464  	_, err = scb.Serial()
  1465  	c.Check(err, Equals, state.ErrNoState)
  1466  
  1467  	// have a serial as well
  1468  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  1469  		Brand:  "canonical",
  1470  		Model:  "pc",
  1471  		Serial: "8989",
  1472  	})
  1473  	_, err = scb.Model()
  1474  	c.Assert(err, IsNil)
  1475  	_, err = scb.Serial()
  1476  	c.Check(err, Equals, state.ErrNoState)
  1477  
  1478  	// have a serial assertion
  1479  	s.makeSerialAssertionInState(c, "canonical", "pc", "8989")
  1480  
  1481  	_, err = scb.Model()
  1482  	c.Assert(err, IsNil)
  1483  	ser, err := scb.Serial()
  1484  	c.Assert(err, IsNil)
  1485  	c.Check(ser.Serial(), Equals, "8989")
  1486  }
  1487  
  1488  var (
  1489  	devKey, _ = assertstest.GenerateKey(testKeyLength)
  1490  )
  1491  
  1492  func (s *deviceMgrSuite) TestStoreContextBackendDeviceSessionRequestParams(c *C) {
  1493  	s.state.Lock()
  1494  	defer s.state.Unlock()
  1495  
  1496  	scb := s.mgr.StoreContextBackend()
  1497  
  1498  	// nothing there
  1499  	_, err := scb.SignDeviceSessionRequest(nil, "NONCE-1")
  1500  	c.Check(err, ErrorMatches, "internal error: cannot sign a session request without a serial")
  1501  
  1502  	// setup state as done by device initialisation
  1503  	encDevKey, err := asserts.EncodePublicKey(devKey.PublicKey())
  1504  	c.Check(err, IsNil)
  1505  	seriala, err := s.storeSigning.Sign(asserts.SerialType, map[string]interface{}{
  1506  		"brand-id":            "canonical",
  1507  		"model":               "pc",
  1508  		"serial":              "8989",
  1509  		"device-key":          string(encDevKey),
  1510  		"device-key-sha3-384": devKey.PublicKey().ID(),
  1511  		"timestamp":           time.Now().Format(time.RFC3339),
  1512  	}, nil, "")
  1513  	c.Assert(err, IsNil)
  1514  	assertstatetest.AddMany(s.state, seriala)
  1515  	serial := seriala.(*asserts.Serial)
  1516  
  1517  	_, err = scb.SignDeviceSessionRequest(serial, "NONCE-1")
  1518  	c.Check(err, ErrorMatches, "internal error: inconsistent state with serial but no device key")
  1519  
  1520  	// have a key
  1521  	devicestate.KeypairManager(s.mgr).Put(devKey)
  1522  
  1523  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  1524  		Brand:  "canonical",
  1525  		Model:  "pc",
  1526  		Serial: "8989",
  1527  		KeyID:  devKey.PublicKey().ID(),
  1528  	})
  1529  
  1530  	sessReq, err := scb.SignDeviceSessionRequest(serial, "NONCE-1")
  1531  	c.Assert(err, IsNil)
  1532  
  1533  	// correctly signed with device key
  1534  	err = asserts.SignatureCheck(sessReq, devKey.PublicKey())
  1535  	c.Check(err, IsNil)
  1536  
  1537  	c.Check(sessReq.BrandID(), Equals, "canonical")
  1538  	c.Check(sessReq.Model(), Equals, "pc")
  1539  	c.Check(sessReq.Serial(), Equals, "8989")
  1540  	c.Check(sessReq.Nonce(), Equals, "NONCE-1")
  1541  }
  1542  
  1543  func (s *deviceMgrSuite) TestStoreContextBackendProxyStore(c *C) {
  1544  	mockServer := s.mockServer(c, "", nil)
  1545  	defer mockServer.Close()
  1546  	s.state.Lock()
  1547  	defer s.state.Unlock()
  1548  
  1549  	scb := s.mgr.StoreContextBackend()
  1550  
  1551  	// nothing in the state
  1552  	_, err := scb.ProxyStore()
  1553  	c.Check(err, Equals, state.ErrNoState)
  1554  
  1555  	// have a store referenced
  1556  	tr := config.NewTransaction(s.state)
  1557  	err = tr.Set("core", "proxy.store", "foo")
  1558  	tr.Commit()
  1559  	c.Assert(err, IsNil)
  1560  
  1561  	_, err = scb.ProxyStore()
  1562  	c.Check(err, Equals, state.ErrNoState)
  1563  
  1564  	operatorAcct := assertstest.NewAccount(s.storeSigning, "foo-operator", nil, "")
  1565  
  1566  	// have a store assertion.
  1567  	stoAs, err := s.storeSigning.Sign(asserts.StoreType, map[string]interface{}{
  1568  		"store":       "foo",
  1569  		"operator-id": operatorAcct.AccountID(),
  1570  		"url":         mockServer.URL,
  1571  		"timestamp":   time.Now().Format(time.RFC3339),
  1572  	}, nil, "")
  1573  	c.Assert(err, IsNil)
  1574  
  1575  	assertstatetest.AddMany(s.state, operatorAcct, stoAs)
  1576  
  1577  	sto, err := scb.ProxyStore()
  1578  	c.Assert(err, IsNil)
  1579  	c.Assert(sto.Store(), Equals, "foo")
  1580  	c.Assert(sto.URL().String(), Equals, mockServer.URL)
  1581  }
  1582  
  1583  func (s *deviceMgrSuite) TestInitialRegistrationContext(c *C) {
  1584  	s.state.Lock()
  1585  	defer s.state.Unlock()
  1586  
  1587  	// have a model assertion
  1588  	model, err := s.storeSigning.Sign(asserts.ModelType, map[string]interface{}{
  1589  		"series":       "16",
  1590  		"brand-id":     "canonical",
  1591  		"model":        "pc",
  1592  		"gadget":       "pc-gadget",
  1593  		"kernel":       "kernel",
  1594  		"architecture": "amd64",
  1595  		"timestamp":    time.Now().Format(time.RFC3339),
  1596  	}, nil, "")
  1597  	c.Assert(err, IsNil)
  1598  	err = assertstate.Add(s.state, model)
  1599  	c.Assert(err, IsNil)
  1600  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  1601  		Brand: "canonical",
  1602  		Model: "pc",
  1603  	})
  1604  
  1605  	// TODO: will need to pass in a task later
  1606  	regCtx, err := devicestate.RegistrationCtx(s.mgr, nil)
  1607  	c.Assert(err, IsNil)
  1608  	c.Assert(regCtx, NotNil)
  1609  
  1610  	c.Check(regCtx.ForRemodeling(), Equals, false)
  1611  
  1612  	device, err := regCtx.Device()
  1613  	c.Check(err, IsNil)
  1614  	c.Check(device, DeepEquals, &auth.DeviceState{
  1615  		Brand: "canonical",
  1616  		Model: "pc",
  1617  	})
  1618  
  1619  	c.Check(regCtx.GadgetForSerialRequestConfig(), Equals, "pc-gadget")
  1620  	c.Check(regCtx.SerialRequestExtraHeaders(), HasLen, 0)
  1621  	c.Check(regCtx.SerialRequestAncillaryAssertions(), HasLen, 0)
  1622  
  1623  }
  1624  
  1625  func (s *deviceMgrSuite) TestDeviceManagerEnsureSeedYamlAlreadySeeded(c *C) {
  1626  	s.state.Lock()
  1627  	s.state.Set("seeded", true)
  1628  	s.state.Unlock()
  1629  
  1630  	called := false
  1631  	restore := devicestate.MockPopulateStateFromSeed(func(*state.State, timings.Measurer) ([]*state.TaskSet, error) {
  1632  		called = true
  1633  		return nil, nil
  1634  	})
  1635  	defer restore()
  1636  
  1637  	err := devicestate.EnsureSeedYaml(s.mgr)
  1638  	c.Assert(err, IsNil)
  1639  	c.Assert(called, Equals, false)
  1640  }
  1641  
  1642  func (s *deviceMgrSuite) TestDeviceManagerEnsureSeedYamlChangeInFlight(c *C) {
  1643  	s.state.Lock()
  1644  	chg := s.state.NewChange("seed", "just for testing")
  1645  	chg.AddTask(s.state.NewTask("test-task", "the change needs a task"))
  1646  	s.state.Unlock()
  1647  
  1648  	called := false
  1649  	restore := devicestate.MockPopulateStateFromSeed(func(*state.State, timings.Measurer) ([]*state.TaskSet, error) {
  1650  		called = true
  1651  		return nil, nil
  1652  	})
  1653  	defer restore()
  1654  
  1655  	err := devicestate.EnsureSeedYaml(s.mgr)
  1656  	c.Assert(err, IsNil)
  1657  	c.Assert(called, Equals, false)
  1658  }
  1659  
  1660  func (s *deviceMgrSuite) TestDeviceManagerEnsureSeedYamlAlsoOnClassic(c *C) {
  1661  	release.OnClassic = true
  1662  
  1663  	called := false
  1664  	restore := devicestate.MockPopulateStateFromSeed(func(*state.State, timings.Measurer) ([]*state.TaskSet, error) {
  1665  		called = true
  1666  		return nil, nil
  1667  	})
  1668  	defer restore()
  1669  
  1670  	err := devicestate.EnsureSeedYaml(s.mgr)
  1671  	c.Assert(err, IsNil)
  1672  	c.Assert(called, Equals, true)
  1673  }
  1674  
  1675  func (s *deviceMgrSuite) TestDeviceManagerEnsureSeedYamlHappy(c *C) {
  1676  	restore := devicestate.MockPopulateStateFromSeed(func(*state.State, timings.Measurer) (ts []*state.TaskSet, err error) {
  1677  		t := s.state.NewTask("test-task", "a random task")
  1678  		ts = append(ts, state.NewTaskSet(t))
  1679  		return ts, nil
  1680  	})
  1681  	defer restore()
  1682  
  1683  	err := devicestate.EnsureSeedYaml(s.mgr)
  1684  	c.Assert(err, IsNil)
  1685  
  1686  	s.state.Lock()
  1687  	defer s.state.Unlock()
  1688  
  1689  	c.Check(s.state.Changes(), HasLen, 1)
  1690  }
  1691  
  1692  func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkSkippedOnClassic(c *C) {
  1693  	release.OnClassic = true
  1694  
  1695  	err := devicestate.EnsureBootOk(s.mgr)
  1696  	c.Assert(err, IsNil)
  1697  }
  1698  
  1699  func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkBootloaderHappy(c *C) {
  1700  	s.bootloader.SetBootVars(map[string]string{
  1701  		"snap_mode":     "trying",
  1702  		"snap_try_core": "core_1.snap",
  1703  	})
  1704  
  1705  	s.state.Lock()
  1706  	defer s.state.Unlock()
  1707  	siCore1 := &snap.SideInfo{RealName: "core", Revision: snap.R(1)}
  1708  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  1709  		SnapType: "os",
  1710  		Active:   true,
  1711  		Sequence: []*snap.SideInfo{siCore1},
  1712  		Current:  siCore1.Revision,
  1713  	})
  1714  
  1715  	s.state.Unlock()
  1716  	err := devicestate.EnsureBootOk(s.mgr)
  1717  	s.state.Lock()
  1718  	c.Assert(err, IsNil)
  1719  
  1720  	m, err := s.bootloader.GetBootVars("snap_mode")
  1721  	c.Assert(err, IsNil)
  1722  	c.Assert(m, DeepEquals, map[string]string{"snap_mode": ""})
  1723  }
  1724  
  1725  func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkUpdateBootRevisionsHappy(c *C) {
  1726  	// simulate that we have a new core_2, tried to boot it but that failed
  1727  	s.bootloader.SetBootVars(map[string]string{
  1728  		"snap_mode":     "",
  1729  		"snap_kernel":   "kernel_1.snap",
  1730  		"snap_try_core": "core_2.snap",
  1731  		"snap_core":     "core_1.snap",
  1732  	})
  1733  
  1734  	s.state.Lock()
  1735  	defer s.state.Unlock()
  1736  	siKernel1 := &snap.SideInfo{RealName: "kernel", Revision: snap.R(1)}
  1737  	snapstate.Set(s.state, "kernel", &snapstate.SnapState{
  1738  		SnapType: "kernel",
  1739  		Active:   true,
  1740  		Sequence: []*snap.SideInfo{siKernel1},
  1741  		Current:  siKernel1.Revision,
  1742  	})
  1743  
  1744  	siCore1 := &snap.SideInfo{RealName: "core", Revision: snap.R(1)}
  1745  	siCore2 := &snap.SideInfo{RealName: "core", Revision: snap.R(2)}
  1746  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  1747  		SnapType: "os",
  1748  		Active:   true,
  1749  		Sequence: []*snap.SideInfo{siCore1, siCore2},
  1750  		Current:  siCore2.Revision,
  1751  	})
  1752  
  1753  	s.state.Unlock()
  1754  	err := devicestate.EnsureBootOk(s.mgr)
  1755  	s.state.Lock()
  1756  	c.Assert(err, IsNil)
  1757  
  1758  	c.Check(s.state.Changes(), HasLen, 1)
  1759  	c.Check(s.state.Changes()[0].Kind(), Equals, "update-revisions")
  1760  }
  1761  
  1762  func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkNotRunAgain(c *C) {
  1763  	s.bootloader.SetBootVars(map[string]string{
  1764  		"snap_mode":     "trying",
  1765  		"snap_try_core": "core_1.snap",
  1766  	})
  1767  	s.bootloader.SetErr = fmt.Errorf("ensure bootloader is not used")
  1768  
  1769  	devicestate.SetBootOkRan(s.mgr, true)
  1770  
  1771  	err := devicestate.EnsureBootOk(s.mgr)
  1772  	c.Assert(err, IsNil)
  1773  }
  1774  
  1775  func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkError(c *C) {
  1776  	s.state.Lock()
  1777  	// seeded
  1778  	s.state.Set("seeded", true)
  1779  	// has serial
  1780  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  1781  		Brand:  "canonical",
  1782  		Model:  "pc",
  1783  		Serial: "8989",
  1784  	})
  1785  	s.state.Unlock()
  1786  
  1787  	s.bootloader.GetErr = fmt.Errorf("bootloader err")
  1788  
  1789  	devicestate.SetBootOkRan(s.mgr, false)
  1790  
  1791  	err := s.mgr.Ensure()
  1792  	c.Assert(err, ErrorMatches, "devicemgr: bootloader err")
  1793  }
  1794  
  1795  func (s *deviceMgrSuite) setupBrands(c *C) {
  1796  	assertstatetest.AddMany(s.state, s.brands.AccountsAndKeys("my-brand")...)
  1797  	otherAcct := assertstest.NewAccount(s.storeSigning, "other-brand", map[string]interface{}{
  1798  		"account-id": "other-brand",
  1799  	}, "")
  1800  	assertstatetest.AddMany(s.state, otherAcct)
  1801  }
  1802  
  1803  func (s *deviceMgrSuite) setupSnapDecl(c *C, info *snap.Info, publisherID string) {
  1804  	snapDecl, err := s.storeSigning.Sign(asserts.SnapDeclarationType, map[string]interface{}{
  1805  		"series":       "16",
  1806  		"snap-name":    info.SnapName(),
  1807  		"snap-id":      info.SnapID,
  1808  		"publisher-id": publisherID,
  1809  		"timestamp":    time.Now().UTC().Format(time.RFC3339),
  1810  	}, nil, "")
  1811  	c.Assert(err, IsNil)
  1812  	assertstatetest.AddMany(s.state, snapDecl)
  1813  }
  1814  
  1815  func fakeMyModel(extra map[string]interface{}) *asserts.Model {
  1816  	model := map[string]interface{}{
  1817  		"type":         "model",
  1818  		"authority-id": "my-brand",
  1819  		"series":       "16",
  1820  		"brand-id":     "my-brand",
  1821  		"model":        "my-model",
  1822  	}
  1823  	return assertstest.FakeAssertion(model, extra).(*asserts.Model)
  1824  }
  1825  
  1826  func (s *deviceMgrSuite) TestCheckGadget(c *C) {
  1827  	s.state.Lock()
  1828  	defer s.state.Unlock()
  1829  
  1830  	gadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: other-gadget, version: 0}", nil)
  1831  
  1832  	s.setupBrands(c)
  1833  	// model assertion in device context
  1834  	model := fakeMyModel(map[string]interface{}{
  1835  		"architecture": "amd64",
  1836  		"gadget":       "gadget",
  1837  		"kernel":       "krnl",
  1838  	})
  1839  	deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model}
  1840  
  1841  	err := devicestate.CheckGadgetOrKernel(s.state, gadgetInfo, nil, snapstate.Flags{}, deviceCtx)
  1842  	c.Check(err, ErrorMatches, `cannot install gadget "other-gadget", model assertion requests "gadget"`)
  1843  
  1844  	// brand gadget
  1845  	brandGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
  1846  	brandGadgetInfo.SnapID = "brand-gadget-id"
  1847  	s.setupSnapDecl(c, brandGadgetInfo, "my-brand")
  1848  
  1849  	// canonical gadget
  1850  	canonicalGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
  1851  	canonicalGadgetInfo.SnapID = "canonical-gadget-id"
  1852  	s.setupSnapDecl(c, canonicalGadgetInfo, "canonical")
  1853  
  1854  	// other gadget
  1855  	otherGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
  1856  	otherGadgetInfo.SnapID = "other-gadget-id"
  1857  	s.setupSnapDecl(c, otherGadgetInfo, "other-brand")
  1858  
  1859  	// install brand gadget ok
  1860  	err = devicestate.CheckGadgetOrKernel(s.state, brandGadgetInfo, nil, snapstate.Flags{}, deviceCtx)
  1861  	c.Check(err, IsNil)
  1862  
  1863  	// install canonical gadget ok
  1864  	err = devicestate.CheckGadgetOrKernel(s.state, canonicalGadgetInfo, nil, snapstate.Flags{}, deviceCtx)
  1865  	c.Check(err, IsNil)
  1866  
  1867  	// install other gadget fails
  1868  	err = devicestate.CheckGadgetOrKernel(s.state, otherGadgetInfo, nil, snapstate.Flags{}, deviceCtx)
  1869  	c.Check(err, ErrorMatches, `cannot install gadget "gadget" published by "other-brand" for model by "my-brand"`)
  1870  
  1871  	// unasserted installation of other works
  1872  	otherGadgetInfo.SnapID = ""
  1873  	err = devicestate.CheckGadgetOrKernel(s.state, otherGadgetInfo, nil, snapstate.Flags{}, deviceCtx)
  1874  	c.Check(err, IsNil)
  1875  
  1876  	// parallel install fails
  1877  	otherGadgetInfo.InstanceKey = "foo"
  1878  	err = devicestate.CheckGadgetOrKernel(s.state, otherGadgetInfo, nil, snapstate.Flags{}, deviceCtx)
  1879  	c.Check(err, ErrorMatches, `cannot install "gadget_foo", parallel installation of kernel or gadget snaps is not supported`)
  1880  }
  1881  
  1882  func (s *deviceMgrSuite) TestCheckGadgetOnClassic(c *C) {
  1883  	release.OnClassic = true
  1884  
  1885  	s.state.Lock()
  1886  	defer s.state.Unlock()
  1887  
  1888  	gadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: other-gadget, version: 0}", nil)
  1889  
  1890  	s.setupBrands(c)
  1891  	// model assertion in device context
  1892  	model := fakeMyModel(map[string]interface{}{
  1893  		"classic": "true",
  1894  		"gadget":  "gadget",
  1895  	})
  1896  	deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model}
  1897  
  1898  	err := devicestate.CheckGadgetOrKernel(s.state, gadgetInfo, nil, snapstate.Flags{}, deviceCtx)
  1899  	c.Check(err, ErrorMatches, `cannot install gadget "other-gadget", model assertion requests "gadget"`)
  1900  
  1901  	// brand gadget
  1902  	brandGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
  1903  	brandGadgetInfo.SnapID = "brand-gadget-id"
  1904  	s.setupSnapDecl(c, brandGadgetInfo, "my-brand")
  1905  
  1906  	// canonical gadget
  1907  	canonicalGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
  1908  	canonicalGadgetInfo.SnapID = "canonical-gadget-id"
  1909  	s.setupSnapDecl(c, canonicalGadgetInfo, "canonical")
  1910  
  1911  	// other gadget
  1912  	otherGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
  1913  	otherGadgetInfo.SnapID = "other-gadget-id"
  1914  	s.setupSnapDecl(c, otherGadgetInfo, "other-brand")
  1915  
  1916  	// install brand gadget ok
  1917  	err = devicestate.CheckGadgetOrKernel(s.state, brandGadgetInfo, nil, snapstate.Flags{}, deviceCtx)
  1918  	c.Check(err, IsNil)
  1919  
  1920  	// install canonical gadget ok
  1921  	err = devicestate.CheckGadgetOrKernel(s.state, canonicalGadgetInfo, nil, snapstate.Flags{}, deviceCtx)
  1922  	c.Check(err, IsNil)
  1923  
  1924  	// install other gadget fails
  1925  	err = devicestate.CheckGadgetOrKernel(s.state, otherGadgetInfo, nil, snapstate.Flags{}, deviceCtx)
  1926  	c.Check(err, ErrorMatches, `cannot install gadget "gadget" published by "other-brand" for model by "my-brand"`)
  1927  
  1928  	// unasserted installation of other works
  1929  	otherGadgetInfo.SnapID = ""
  1930  	err = devicestate.CheckGadgetOrKernel(s.state, otherGadgetInfo, nil, snapstate.Flags{}, deviceCtx)
  1931  	c.Check(err, IsNil)
  1932  }
  1933  
  1934  func (s *deviceMgrSuite) TestCheckGadgetOnClassicGadgetNotSpecified(c *C) {
  1935  	release.OnClassic = true
  1936  
  1937  	s.state.Lock()
  1938  	defer s.state.Unlock()
  1939  
  1940  	gadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
  1941  
  1942  	s.setupBrands(c)
  1943  	// model assertion in device context
  1944  	model := fakeMyModel(map[string]interface{}{
  1945  		"classic": "true",
  1946  	})
  1947  	deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model}
  1948  
  1949  	err := devicestate.CheckGadgetOrKernel(s.state, gadgetInfo, nil, snapstate.Flags{}, deviceCtx)
  1950  	c.Check(err, ErrorMatches, `cannot install gadget snap on classic if not requested by the model`)
  1951  }
  1952  
  1953  func (s *deviceMgrSuite) TestCheckKernel(c *C) {
  1954  	s.state.Lock()
  1955  	defer s.state.Unlock()
  1956  	kernelInfo := snaptest.MockInfo(c, "{type: kernel, name: lnrk, version: 0}", nil)
  1957  
  1958  	// not on classic
  1959  	release.OnClassic = true
  1960  	err := devicestate.CheckGadgetOrKernel(s.state, kernelInfo, nil, snapstate.Flags{}, nil)
  1961  	c.Check(err, ErrorMatches, `cannot install a kernel snap on classic`)
  1962  	release.OnClassic = false
  1963  
  1964  	s.setupBrands(c)
  1965  	// model assertion in device context
  1966  	model := fakeMyModel(map[string]interface{}{
  1967  		"architecture": "amd64",
  1968  		"gadget":       "gadget",
  1969  		"kernel":       "krnl",
  1970  	})
  1971  	deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model}
  1972  
  1973  	err = devicestate.CheckGadgetOrKernel(s.state, kernelInfo, nil, snapstate.Flags{}, deviceCtx)
  1974  	c.Check(err, ErrorMatches, `cannot install kernel "lnrk", model assertion requests "krnl"`)
  1975  
  1976  	// brand kernel
  1977  	brandKrnlInfo := snaptest.MockInfo(c, "{type: kernel, name: krnl, version: 0}", nil)
  1978  	brandKrnlInfo.SnapID = "brand-krnl-id"
  1979  	s.setupSnapDecl(c, brandKrnlInfo, "my-brand")
  1980  
  1981  	// canonical kernel
  1982  	canonicalKrnlInfo := snaptest.MockInfo(c, "{type: kernel, name: krnl, version: 0}", nil)
  1983  	canonicalKrnlInfo.SnapID = "canonical-krnl-id"
  1984  	s.setupSnapDecl(c, canonicalKrnlInfo, "canonical")
  1985  
  1986  	// other kernel
  1987  	otherKrnlInfo := snaptest.MockInfo(c, "{type: kernel, name: krnl, version: 0}", nil)
  1988  	otherKrnlInfo.SnapID = "other-krnl-id"
  1989  	s.setupSnapDecl(c, otherKrnlInfo, "other-brand")
  1990  
  1991  	// install brand kernel ok
  1992  	err = devicestate.CheckGadgetOrKernel(s.state, brandKrnlInfo, nil, snapstate.Flags{}, deviceCtx)
  1993  	c.Check(err, IsNil)
  1994  
  1995  	// install canonical kernel ok
  1996  	err = devicestate.CheckGadgetOrKernel(s.state, canonicalKrnlInfo, nil, snapstate.Flags{}, deviceCtx)
  1997  	c.Check(err, IsNil)
  1998  
  1999  	// install other kernel fails
  2000  	err = devicestate.CheckGadgetOrKernel(s.state, otherKrnlInfo, nil, snapstate.Flags{}, deviceCtx)
  2001  	c.Check(err, ErrorMatches, `cannot install kernel "krnl" published by "other-brand" for model by "my-brand"`)
  2002  
  2003  	// unasserted installation of other works
  2004  	otherKrnlInfo.SnapID = ""
  2005  	err = devicestate.CheckGadgetOrKernel(s.state, otherKrnlInfo, nil, snapstate.Flags{}, deviceCtx)
  2006  	c.Check(err, IsNil)
  2007  
  2008  	// parallel install fails
  2009  	otherKrnlInfo.InstanceKey = "foo"
  2010  	err = devicestate.CheckGadgetOrKernel(s.state, otherKrnlInfo, nil, snapstate.Flags{}, deviceCtx)
  2011  	c.Check(err, ErrorMatches, `cannot install "krnl_foo", parallel installation of kernel or gadget snaps is not supported`)
  2012  }
  2013  
  2014  func (s *deviceMgrSuite) makeModelAssertionInState(c *C, brandID, model string, extras map[string]interface{}) {
  2015  	modelAs := s.brands.Model(brandID, model, extras)
  2016  
  2017  	s.setupBrands(c)
  2018  	assertstatetest.AddMany(s.state, modelAs)
  2019  }
  2020  
  2021  func makeSerialAssertionInState(c *C, brands *assertstest.SigningAccounts, st *state.State, brandID, model, serialN string) *asserts.Serial {
  2022  	encDevKey, err := asserts.EncodePublicKey(devKey.PublicKey())
  2023  	c.Assert(err, IsNil)
  2024  	serial, err := brands.Signing(brandID).Sign(asserts.SerialType, map[string]interface{}{
  2025  		"brand-id":            brandID,
  2026  		"model":               model,
  2027  		"serial":              serialN,
  2028  		"device-key":          string(encDevKey),
  2029  		"device-key-sha3-384": devKey.PublicKey().ID(),
  2030  		"timestamp":           time.Now().Format(time.RFC3339),
  2031  	}, nil, "")
  2032  	c.Assert(err, IsNil)
  2033  	err = assertstate.Add(st, serial)
  2034  	c.Assert(err, IsNil)
  2035  	return serial.(*asserts.Serial)
  2036  }
  2037  
  2038  func (s *deviceMgrSuite) makeSerialAssertionInState(c *C, brandID, model, serialN string) *asserts.Serial {
  2039  	return makeSerialAssertionInState(c, s.brands, s.state, brandID, model, serialN)
  2040  }
  2041  
  2042  func (s *deviceMgrSuite) TestCanAutoRefreshOnCore(c *C) {
  2043  	s.state.Lock()
  2044  	defer s.state.Unlock()
  2045  
  2046  	canAutoRefresh := func() bool {
  2047  		ok, err := devicestate.CanAutoRefresh(s.state)
  2048  		c.Assert(err, IsNil)
  2049  		return ok
  2050  	}
  2051  
  2052  	// not seeded, no model, no serial -> no auto-refresh
  2053  	s.state.Set("seeded", false)
  2054  	c.Check(canAutoRefresh(), Equals, false)
  2055  
  2056  	// seeded, model, no serial -> no auto-refresh
  2057  	s.state.Set("seeded", true)
  2058  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  2059  		Brand: "canonical",
  2060  		Model: "pc",
  2061  	})
  2062  	s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
  2063  		"architecture": "amd64",
  2064  		"kernel":       "pc-kernel",
  2065  		"gadget":       "pc",
  2066  	})
  2067  	c.Check(canAutoRefresh(), Equals, false)
  2068  
  2069  	// seeded, model, serial -> auto-refresh
  2070  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  2071  		Brand:  "canonical",
  2072  		Model:  "pc",
  2073  		Serial: "8989",
  2074  	})
  2075  	s.makeSerialAssertionInState(c, "canonical", "pc", "8989")
  2076  	c.Check(canAutoRefresh(), Equals, true)
  2077  
  2078  	// not seeded, model, serial -> no auto-refresh
  2079  	s.state.Set("seeded", false)
  2080  	c.Check(canAutoRefresh(), Equals, false)
  2081  }
  2082  
  2083  func (s *deviceMgrSuite) TestCanAutoRefreshNoSerialFallback(c *C) {
  2084  	s.state.Lock()
  2085  	defer s.state.Unlock()
  2086  
  2087  	canAutoRefresh := func() bool {
  2088  		ok, err := devicestate.CanAutoRefresh(s.state)
  2089  		c.Assert(err, IsNil)
  2090  		return ok
  2091  	}
  2092  
  2093  	// seeded, model, no serial, two attempts at getting serial
  2094  	// -> no auto-refresh
  2095  	devicestate.IncEnsureOperationalAttempts(s.state)
  2096  	devicestate.IncEnsureOperationalAttempts(s.state)
  2097  	s.state.Set("seeded", true)
  2098  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  2099  		Brand: "canonical",
  2100  		Model: "pc",
  2101  	})
  2102  	s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
  2103  		"architecture": "amd64",
  2104  		"kernel":       "pc-kernel",
  2105  		"gadget":       "pc",
  2106  	})
  2107  	c.Check(canAutoRefresh(), Equals, false)
  2108  
  2109  	// third attempt ongoing, or done
  2110  	// fallback, try auto-refresh
  2111  	devicestate.IncEnsureOperationalAttempts(s.state)
  2112  	// sanity
  2113  	c.Check(devicestate.EnsureOperationalAttempts(s.state), Equals, 3)
  2114  	c.Check(canAutoRefresh(), Equals, true)
  2115  }
  2116  
  2117  func (s *deviceMgrSuite) TestCanAutoRefreshOnClassic(c *C) {
  2118  	release.OnClassic = true
  2119  
  2120  	s.state.Lock()
  2121  	defer s.state.Unlock()
  2122  
  2123  	canAutoRefresh := func() bool {
  2124  		ok, err := devicestate.CanAutoRefresh(s.state)
  2125  		c.Assert(err, IsNil)
  2126  		return ok
  2127  	}
  2128  
  2129  	// not seeded, no model, no serial -> no auto-refresh
  2130  	s.state.Set("seeded", false)
  2131  	c.Check(canAutoRefresh(), Equals, false)
  2132  
  2133  	// seeded, no model -> auto-refresh
  2134  	s.state.Set("seeded", true)
  2135  	c.Check(canAutoRefresh(), Equals, false)
  2136  
  2137  	// seeded, model, no serial -> no auto-refresh
  2138  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  2139  		Brand: "canonical",
  2140  		Model: "pc",
  2141  	})
  2142  	s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
  2143  		"classic": "true",
  2144  	})
  2145  	c.Check(canAutoRefresh(), Equals, false)
  2146  
  2147  	// seeded, model, serial -> auto-refresh
  2148  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  2149  		Brand:  "canonical",
  2150  		Model:  "pc",
  2151  		Serial: "8989",
  2152  	})
  2153  	s.makeSerialAssertionInState(c, "canonical", "pc", "8989")
  2154  	c.Check(canAutoRefresh(), Equals, true)
  2155  
  2156  	// not seeded, model, serial -> no auto-refresh
  2157  	s.state.Set("seeded", false)
  2158  	c.Check(canAutoRefresh(), Equals, false)
  2159  }
  2160  
  2161  func makeInstalledMockCoreSnapWithSnapdControl(c *C, st *state.State) *snap.Info {
  2162  	sideInfoCore11 := &snap.SideInfo{RealName: "core", Revision: snap.R(11), SnapID: "core-id"}
  2163  	snapstate.Set(st, "core", &snapstate.SnapState{
  2164  		Active:   true,
  2165  		Sequence: []*snap.SideInfo{sideInfoCore11},
  2166  		Current:  sideInfoCore11.Revision,
  2167  		SnapType: "os",
  2168  	})
  2169  	core11 := snaptest.MockSnap(c, `
  2170  name: core
  2171  version: 1.0
  2172  slots:
  2173   snapd-control:
  2174  `, sideInfoCore11)
  2175  	c.Assert(core11.Slots, HasLen, 1)
  2176  
  2177  	return core11
  2178  }
  2179  
  2180  var snapWithSnapdControlRefreshScheduleManagedYAML = `
  2181  name: snap-with-snapd-control
  2182  version: 1.0
  2183  plugs:
  2184   snapd-control:
  2185    refresh-schedule: managed
  2186  `
  2187  
  2188  var snapWithSnapdControlOnlyYAML = `
  2189  name: snap-with-snapd-control
  2190  version: 1.0
  2191  plugs:
  2192   snapd-control:
  2193  `
  2194  
  2195  func makeInstalledMockSnap(c *C, st *state.State, yml string) *snap.Info {
  2196  	sideInfo11 := &snap.SideInfo{RealName: "snap-with-snapd-control", Revision: snap.R(11), SnapID: "snap-with-snapd-control-id"}
  2197  	snapstate.Set(st, "snap-with-snapd-control", &snapstate.SnapState{
  2198  		Active:   true,
  2199  		Sequence: []*snap.SideInfo{sideInfo11},
  2200  		Current:  sideInfo11.Revision,
  2201  		SnapType: "app",
  2202  	})
  2203  	info11 := snaptest.MockSnap(c, yml, sideInfo11)
  2204  	c.Assert(info11.Plugs, HasLen, 1)
  2205  
  2206  	return info11
  2207  }
  2208  
  2209  func makeMockRepoWithConnectedSnaps(c *C, st *state.State, info11, core11 *snap.Info, ifname string) {
  2210  	repo := interfaces.NewRepository()
  2211  	for _, iface := range builtin.Interfaces() {
  2212  		err := repo.AddInterface(iface)
  2213  		c.Assert(err, IsNil)
  2214  	}
  2215  	err := repo.AddSnap(info11)
  2216  	c.Assert(err, IsNil)
  2217  	err = repo.AddSnap(core11)
  2218  	c.Assert(err, IsNil)
  2219  	_, err = repo.Connect(&interfaces.ConnRef{
  2220  		PlugRef: interfaces.PlugRef{Snap: info11.InstanceName(), Name: ifname},
  2221  		SlotRef: interfaces.SlotRef{Snap: core11.InstanceName(), Name: ifname},
  2222  	}, nil, nil, nil, nil, nil)
  2223  	c.Assert(err, IsNil)
  2224  	conns, err := repo.Connected("snap-with-snapd-control", "snapd-control")
  2225  	c.Assert(err, IsNil)
  2226  	c.Assert(conns, HasLen, 1)
  2227  	ifacerepo.Replace(st, repo)
  2228  }
  2229  
  2230  func (s *deviceMgrSuite) TestCanManageRefreshes(c *C) {
  2231  	st := s.state
  2232  	st.Lock()
  2233  	defer st.Unlock()
  2234  
  2235  	// not possbile to manage by default
  2236  	c.Check(devicestate.CanManageRefreshes(st), Equals, false)
  2237  
  2238  	// not possible with just a snap with "snapd-control" plug with the
  2239  	// right attribute
  2240  	info11 := makeInstalledMockSnap(c, st, snapWithSnapdControlRefreshScheduleManagedYAML)
  2241  	c.Check(devicestate.CanManageRefreshes(st), Equals, false)
  2242  
  2243  	// not possible with a core snap with snapd control
  2244  	core11 := makeInstalledMockCoreSnapWithSnapdControl(c, st)
  2245  	c.Check(devicestate.CanManageRefreshes(st), Equals, false)
  2246  
  2247  	// not possible even with connected interfaces
  2248  	makeMockRepoWithConnectedSnaps(c, st, info11, core11, "snapd-control")
  2249  	c.Check(devicestate.CanManageRefreshes(st), Equals, false)
  2250  
  2251  	// if all of the above plus a snap declaration are in place we can
  2252  	// manage schedules
  2253  	s.setupSnapDecl(c, info11, "canonical")
  2254  	c.Check(devicestate.CanManageRefreshes(st), Equals, true)
  2255  
  2256  	// works if the snap is not active as well (to fix race when a
  2257  	// snap is refreshed)
  2258  	var sideInfo11 snapstate.SnapState
  2259  	err := snapstate.Get(st, "snap-with-snapd-control", &sideInfo11)
  2260  	c.Assert(err, IsNil)
  2261  	sideInfo11.Active = false
  2262  	snapstate.Set(st, "snap-with-snapd-control", &sideInfo11)
  2263  	c.Check(devicestate.CanManageRefreshes(st), Equals, true)
  2264  }
  2265  
  2266  func (s *deviceMgrSuite) TestCanManageRefreshesNoRefreshScheduleManaged(c *C) {
  2267  	st := s.state
  2268  	st.Lock()
  2269  	defer st.Unlock()
  2270  
  2271  	// just having a connected "snapd-control" interface is not enough
  2272  	// for setting refresh.schedule=managed
  2273  	info11 := makeInstalledMockSnap(c, st, snapWithSnapdControlOnlyYAML)
  2274  	core11 := makeInstalledMockCoreSnapWithSnapdControl(c, st)
  2275  	makeMockRepoWithConnectedSnaps(c, st, info11, core11, "snapd-control")
  2276  	s.setupSnapDecl(c, info11, "canonical")
  2277  
  2278  	c.Check(devicestate.CanManageRefreshes(st), Equals, false)
  2279  }
  2280  
  2281  func (s *deviceMgrSuite) TestReloadRegistered(c *C) {
  2282  	st := state.New(nil)
  2283  
  2284  	runner1 := state.NewTaskRunner(st)
  2285  	hookMgr1, err := hookstate.Manager(st, runner1)
  2286  	c.Assert(err, IsNil)
  2287  	mgr1, err := devicestate.Manager(st, hookMgr1, runner1, nil)
  2288  	c.Assert(err, IsNil)
  2289  
  2290  	ok := false
  2291  	select {
  2292  	case <-mgr1.Registered():
  2293  	default:
  2294  		ok = true
  2295  	}
  2296  	c.Check(ok, Equals, true)
  2297  
  2298  	st.Lock()
  2299  	devicestatetest.SetDevice(st, &auth.DeviceState{
  2300  		Brand:  "canonical",
  2301  		Model:  "pc",
  2302  		Serial: "serial",
  2303  	})
  2304  	st.Unlock()
  2305  
  2306  	runner2 := state.NewTaskRunner(st)
  2307  	hookMgr2, err := hookstate.Manager(st, runner2)
  2308  	c.Assert(err, IsNil)
  2309  	mgr2, err := devicestate.Manager(st, hookMgr2, runner2, nil)
  2310  	c.Assert(err, IsNil)
  2311  
  2312  	ok = false
  2313  	select {
  2314  	case <-mgr2.Registered():
  2315  		ok = true
  2316  	case <-time.After(5 * time.Second):
  2317  		c.Fatal("should have been marked registered")
  2318  	}
  2319  	c.Check(ok, Equals, true)
  2320  }
  2321  
  2322  func (s *deviceMgrSuite) TestMarkSeededInConfig(c *C) {
  2323  	st := s.state
  2324  	st.Lock()
  2325  	defer st.Unlock()
  2326  
  2327  	// avoid device registration
  2328  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  2329  		Serial: "123",
  2330  	})
  2331  
  2332  	// avoid full seeding
  2333  	s.seeding()
  2334  
  2335  	// not seeded -> no config is set
  2336  	s.state.Unlock()
  2337  	s.mgr.Ensure()
  2338  	s.state.Lock()
  2339  
  2340  	var seedLoaded bool
  2341  	tr := config.NewTransaction(st)
  2342  	tr.Get("core", "seed.loaded", &seedLoaded)
  2343  	c.Check(seedLoaded, Equals, false)
  2344  
  2345  	// pretend we are seeded now
  2346  	s.state.Set("seeded", true)
  2347  
  2348  	// seeded -> config got updated
  2349  	s.state.Unlock()
  2350  	s.mgr.Ensure()
  2351  	s.state.Lock()
  2352  
  2353  	tr = config.NewTransaction(st)
  2354  	tr.Get("core", "seed.loaded", &seedLoaded)
  2355  	c.Check(seedLoaded, Equals, true)
  2356  
  2357  	// only the fake seeding change is in the state, no further
  2358  	// changes
  2359  	c.Check(s.state.Changes(), HasLen, 1)
  2360  }
  2361  
  2362  func (s *deviceMgrSuite) TestNewEnoughProxyParse(c *C) {
  2363  	s.state.Lock()
  2364  	defer s.state.Unlock()
  2365  
  2366  	log, restore := logger.MockLogger()
  2367  	defer restore()
  2368  	os.Setenv("SNAPD_DEBUG", "1")
  2369  	defer os.Unsetenv("SNAPD_DEBUG")
  2370  
  2371  	badURL := &url.URL{Opaque: "%a"} // url.Parse(badURL.String()) needs to fail, which isn't easy :-)
  2372  	c.Check(devicestate.NewEnoughProxy(s.state, badURL, http.DefaultClient), Equals, false)
  2373  	c.Check(log.String(), Matches, "(?m).* DEBUG: Cannot check whether proxy store supports a custom serial vault: parse .*")
  2374  }
  2375  
  2376  func (s *deviceMgrSuite) TestNewEnoughProxy(c *C) {
  2377  	s.state.Lock()
  2378  	defer s.state.Unlock()
  2379  
  2380  	expectedUserAgent := httputil.UserAgent()
  2381  	log, restore := logger.MockLogger()
  2382  	defer restore()
  2383  	os.Setenv("SNAPD_DEBUG", "1")
  2384  	defer os.Unsetenv("SNAPD_DEBUG")
  2385  
  2386  	expecteds := []string{
  2387  		`Head http://\S+: EOF`,
  2388  		`Head request returned 403 Forbidden.`,
  2389  		`Bogus Snap-Store-Version header "5pre1".`,
  2390  		``,
  2391  	}
  2392  
  2393  	n := 0
  2394  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2395  		c.Check(r.Header.Get("User-Agent"), Equals, expectedUserAgent)
  2396  		n++
  2397  		switch n {
  2398  		case 1:
  2399  			conn, _, err := w.(http.Hijacker).Hijack()
  2400  			c.Assert(err, IsNil)
  2401  			conn.Close()
  2402  		case 2:
  2403  			w.WriteHeader(403)
  2404  		case 3:
  2405  			w.Header().Set("Snap-Store-Version", "5pre1")
  2406  			w.WriteHeader(200)
  2407  		case 4:
  2408  			w.Header().Set("Snap-Store-Version", "5")
  2409  			w.WriteHeader(200)
  2410  		case 5:
  2411  			w.Header().Set("Snap-Store-Version", "6")
  2412  			w.WriteHeader(200)
  2413  		default:
  2414  			c.Errorf("expected %d results, now on %d", len(expecteds), n)
  2415  		}
  2416  	}))
  2417  	defer server.Close()
  2418  
  2419  	u, err := url.Parse(server.URL)
  2420  	c.Assert(err, IsNil)
  2421  	for _, expected := range expecteds {
  2422  		log.Reset()
  2423  		c.Check(devicestate.NewEnoughProxy(s.state, u, http.DefaultClient), Equals, false)
  2424  		if len(expected) > 0 {
  2425  			expected = "(?m).* DEBUG: Cannot check whether proxy store supports a custom serial vault: " + expected
  2426  		}
  2427  		c.Check(log.String(), Matches, expected)
  2428  	}
  2429  	c.Check(n, Equals, len(expecteds))
  2430  
  2431  	// and success at last
  2432  	log.Reset()
  2433  	c.Check(devicestate.NewEnoughProxy(s.state, u, http.DefaultClient), Equals, true)
  2434  	c.Check(log.String(), Equals, "")
  2435  	c.Check(n, Equals, len(expecteds)+1)
  2436  }
  2437  
  2438  func (s *deviceMgrSuite) TestDevicemgrCanStandby(c *C) {
  2439  	st := state.New(nil)
  2440  
  2441  	runner := state.NewTaskRunner(st)
  2442  	hookMgr, err := hookstate.Manager(st, runner)
  2443  	c.Assert(err, IsNil)
  2444  	mgr, err := devicestate.Manager(st, hookMgr, runner, nil)
  2445  	c.Assert(err, IsNil)
  2446  
  2447  	st.Lock()
  2448  	defer st.Unlock()
  2449  	c.Check(mgr.CanStandby(), Equals, false)
  2450  
  2451  	st.Set("seeded", true)
  2452  	c.Check(mgr.CanStandby(), Equals, true)
  2453  }
  2454  
  2455  type testModel struct {
  2456  	brand, model               string
  2457  	arch, base, kernel, gadget string
  2458  }
  2459  
  2460  func (s *deviceMgrSuite) TestRemodelUnhappyNotSeeded(c *C) {
  2461  	s.state.Lock()
  2462  	defer s.state.Unlock()
  2463  	s.state.Set("seeded", false)
  2464  
  2465  	newModel := s.brands.Model("canonical", "pc", map[string]interface{}{
  2466  		"architecture": "amd64",
  2467  		"kernel":       "pc-kernel",
  2468  		"gadget":       "pc",
  2469  	})
  2470  	_, err := devicestate.Remodel(s.state, newModel)
  2471  	c.Assert(err, ErrorMatches, "cannot remodel until fully seeded")
  2472  }
  2473  
  2474  func (s *deviceMgrSuite) TestRemodelUnhappy(c *C) {
  2475  	s.state.Lock()
  2476  	defer s.state.Unlock()
  2477  	s.state.Set("seeded", true)
  2478  
  2479  	// set a model assertion
  2480  	cur := map[string]string{
  2481  		"brand":        "canonical",
  2482  		"model":        "pc-model",
  2483  		"architecture": "amd64",
  2484  		"kernel":       "pc-kernel",
  2485  		"gadget":       "pc",
  2486  		"base":         "core18",
  2487  	}
  2488  	s.makeModelAssertionInState(c, cur["brand"], cur["model"], map[string]interface{}{
  2489  		"architecture": cur["architecture"],
  2490  		"kernel":       cur["kernel"],
  2491  		"gadget":       cur["gadget"],
  2492  		"base":         cur["base"],
  2493  	})
  2494  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  2495  		Brand: cur["brand"],
  2496  		Model: cur["model"],
  2497  	})
  2498  
  2499  	// ensure all error cases are checked
  2500  	for _, t := range []struct {
  2501  		new    map[string]string
  2502  		errStr string
  2503  	}{
  2504  		{map[string]string{"architecture": "pdp-7"}, "cannot remodel to different architectures yet"},
  2505  		{map[string]string{"base": "core20"}, "cannot remodel to different bases yet"},
  2506  		{map[string]string{"gadget": "other-gadget"}, "cannot remodel to different gadgets yet"},
  2507  	} {
  2508  		// copy current model unless new model test data is different
  2509  		for k, v := range cur {
  2510  			if t.new[k] != "" {
  2511  				continue
  2512  			}
  2513  			t.new[k] = v
  2514  		}
  2515  		new := s.brands.Model(t.new["brand"], t.new["model"], map[string]interface{}{
  2516  			"architecture": t.new["architecture"],
  2517  			"kernel":       t.new["kernel"],
  2518  			"gadget":       t.new["gadget"],
  2519  			"base":         t.new["base"],
  2520  		})
  2521  		chg, err := devicestate.Remodel(s.state, new)
  2522  		c.Check(chg, IsNil)
  2523  		c.Check(err, ErrorMatches, t.errStr)
  2524  	}
  2525  }
  2526  
  2527  func (s *deviceMgrSuite) TestRemodelTasksSwitchKernelTrack(c *C) {
  2528  	s.state.Lock()
  2529  	defer s.state.Unlock()
  2530  	s.state.Set("seeded", true)
  2531  	s.state.Set("refresh-privacy-key", "some-privacy-key")
  2532  
  2533  	var testDeviceCtx snapstate.DeviceContext
  2534  
  2535  	restore := devicestate.MockSnapstateInstallWithDeviceContext(func(ctx context.Context, st *state.State, name string, opts *snapstate.RevisionOptions, userID int, flags snapstate.Flags, deviceCtx snapstate.DeviceContext, fromChange string) (*state.TaskSet, error) {
  2536  		c.Check(flags.Required, Equals, true)
  2537  		c.Check(deviceCtx, Equals, testDeviceCtx)
  2538  		c.Check(fromChange, Equals, "99")
  2539  
  2540  		tDownload := s.state.NewTask("fake-download", fmt.Sprintf("Download %s", name))
  2541  		tValidate := s.state.NewTask("validate-snap", fmt.Sprintf("Validate %s", name))
  2542  		tValidate.WaitFor(tDownload)
  2543  		tInstall := s.state.NewTask("fake-install", fmt.Sprintf("Install %s", name))
  2544  		tInstall.WaitFor(tValidate)
  2545  		ts := state.NewTaskSet(tDownload, tValidate, tInstall)
  2546  		ts.MarkEdge(tValidate, snapstate.DownloadAndChecksDoneEdge)
  2547  		return ts, nil
  2548  	})
  2549  	defer restore()
  2550  
  2551  	restore = devicestate.MockSnapstateUpdateWithDeviceContext(func(st *state.State, name string, opts *snapstate.RevisionOptions, userID int, flags snapstate.Flags, deviceCtx snapstate.DeviceContext, fromChange string) (*state.TaskSet, error) {
  2552  		c.Check(flags.Required, Equals, false)
  2553  		c.Check(flags.NoReRefresh, Equals, true)
  2554  		c.Check(deviceCtx, Equals, testDeviceCtx)
  2555  		c.Check(fromChange, Equals, "99")
  2556  
  2557  		tDownload := s.state.NewTask("fake-download", fmt.Sprintf("Download %s to track %s", name, opts.Channel))
  2558  		tValidate := s.state.NewTask("validate-snap", fmt.Sprintf("Validate %s", name))
  2559  		tValidate.WaitFor(tDownload)
  2560  		tUpdate := s.state.NewTask("fake-update", fmt.Sprintf("Update %s to track %s", name, opts.Channel))
  2561  		tUpdate.WaitFor(tValidate)
  2562  		ts := state.NewTaskSet(tDownload, tValidate, tUpdate)
  2563  		ts.MarkEdge(tValidate, snapstate.DownloadAndChecksDoneEdge)
  2564  		return ts, nil
  2565  	})
  2566  	defer restore()
  2567  
  2568  	// set a model assertion
  2569  	current := s.brands.Model("canonical", "pc-model", map[string]interface{}{
  2570  		"architecture": "amd64",
  2571  		"kernel":       "pc-kernel",
  2572  		"gadget":       "pc",
  2573  		"base":         "core18",
  2574  	})
  2575  	err := assertstate.Add(s.state, current)
  2576  	c.Assert(err, IsNil)
  2577  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  2578  		Brand: "canonical",
  2579  		Model: "pc-model",
  2580  	})
  2581  
  2582  	new := s.brands.Model("canonical", "pc-model", map[string]interface{}{
  2583  		"architecture":   "amd64",
  2584  		"kernel":         "pc-kernel=18",
  2585  		"gadget":         "pc",
  2586  		"base":           "core18",
  2587  		"required-snaps": []interface{}{"new-required-snap-1", "new-required-snap-2"},
  2588  		"revision":       "1",
  2589  	})
  2590  
  2591  	testDeviceCtx = &snapstatetest.TrivialDeviceContext{Remodeling: true}
  2592  
  2593  	tss, err := devicestate.RemodelTasks(context.Background(), s.state, current, new, testDeviceCtx, "99")
  2594  	c.Assert(err, IsNil)
  2595  	// 2 snaps, plus one track switch plus the remodel task, the
  2596  	// wait chain is tested in TestRemodel*
  2597  	c.Assert(tss, HasLen, 4)
  2598  }
  2599  
  2600  func (s *deviceMgrSuite) TestRemodelTasksSwitchKernel(c *C) {
  2601  	s.state.Lock()
  2602  	defer s.state.Unlock()
  2603  	s.state.Set("seeded", true)
  2604  	s.state.Set("refresh-privacy-key", "some-privacy-key")
  2605  
  2606  	var testDeviceCtx snapstate.DeviceContext
  2607  
  2608  	restore := devicestate.MockSnapstateInstallWithDeviceContext(func(ctx context.Context, st *state.State, name string, opts *snapstate.RevisionOptions, userID int, flags snapstate.Flags, deviceCtx snapstate.DeviceContext, fromChange string) (*state.TaskSet, error) {
  2609  		c.Check(deviceCtx, Equals, testDeviceCtx)
  2610  		c.Check(name, Equals, "other-kernel")
  2611  		c.Check(opts.Channel, Equals, "18")
  2612  
  2613  		tDownload := s.state.NewTask("fake-download", fmt.Sprintf("Download %s", name))
  2614  		tValidate := s.state.NewTask("validate-snap", fmt.Sprintf("Validate %s", name))
  2615  		tValidate.WaitFor(tDownload)
  2616  		tInstall := s.state.NewTask("fake-install", fmt.Sprintf("Install %s", name))
  2617  		tInstall.WaitFor(tValidate)
  2618  		ts := state.NewTaskSet(tDownload, tValidate, tInstall)
  2619  		ts.MarkEdge(tValidate, snapstate.DownloadAndChecksDoneEdge)
  2620  		return ts, nil
  2621  	})
  2622  	defer restore()
  2623  
  2624  	// set a model assertion
  2625  	current := s.brands.Model("canonical", "pc-model", map[string]interface{}{
  2626  		"architecture": "amd64",
  2627  		"kernel":       "pc-kernel",
  2628  		"gadget":       "pc",
  2629  		"base":         "core18",
  2630  	})
  2631  	err := assertstate.Add(s.state, current)
  2632  	c.Assert(err, IsNil)
  2633  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  2634  		Brand: "canonical",
  2635  		Model: "pc-model",
  2636  	})
  2637  
  2638  	new := s.brands.Model("canonical", "pc-model", map[string]interface{}{
  2639  		"architecture": "amd64",
  2640  		"kernel":       "other-kernel=18",
  2641  		"gadget":       "pc",
  2642  		"base":         "core18",
  2643  		"revision":     "1",
  2644  	})
  2645  
  2646  	testDeviceCtx = &snapstatetest.TrivialDeviceContext{Remodeling: true}
  2647  
  2648  	tss, err := devicestate.RemodelTasks(context.Background(), s.state, current, new, testDeviceCtx, "99")
  2649  	c.Assert(err, IsNil)
  2650  	// 1 new kernel plus the remodel task
  2651  	c.Assert(tss, HasLen, 2)
  2652  }
  2653  
  2654  func (s *deviceMgrSuite) TestRemodelRequiredSnaps(c *C) {
  2655  	s.state.Lock()
  2656  	defer s.state.Unlock()
  2657  	s.state.Set("seeded", true)
  2658  	s.state.Set("refresh-privacy-key", "some-privacy-key")
  2659  
  2660  	restore := devicestate.MockSnapstateInstallWithDeviceContext(func(ctx context.Context, st *state.State, name string, opts *snapstate.RevisionOptions, userID int, flags snapstate.Flags, deviceCtx snapstate.DeviceContext, fromChange string) (*state.TaskSet, error) {
  2661  		c.Check(flags.Required, Equals, true)
  2662  		c.Check(deviceCtx, NotNil)
  2663  		c.Check(deviceCtx.ForRemodeling(), Equals, true)
  2664  
  2665  		tDownload := s.state.NewTask("fake-download", fmt.Sprintf("Download %s", name))
  2666  		tValidate := s.state.NewTask("validate-snap", fmt.Sprintf("Validate %s", name))
  2667  		tValidate.WaitFor(tDownload)
  2668  		tInstall := s.state.NewTask("fake-install", fmt.Sprintf("Install %s", name))
  2669  		tInstall.WaitFor(tValidate)
  2670  		ts := state.NewTaskSet(tDownload, tValidate, tInstall)
  2671  		ts.MarkEdge(tValidate, snapstate.DownloadAndChecksDoneEdge)
  2672  		return ts, nil
  2673  	})
  2674  	defer restore()
  2675  
  2676  	// set a model assertion
  2677  	s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{
  2678  		"architecture": "amd64",
  2679  		"kernel":       "pc-kernel",
  2680  		"gadget":       "pc",
  2681  		"base":         "core18",
  2682  	})
  2683  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  2684  		Brand: "canonical",
  2685  		Model: "pc-model",
  2686  	})
  2687  
  2688  	new := s.brands.Model("canonical", "pc-model", map[string]interface{}{
  2689  		"architecture":   "amd64",
  2690  		"kernel":         "pc-kernel",
  2691  		"gadget":         "pc",
  2692  		"base":           "core18",
  2693  		"required-snaps": []interface{}{"new-required-snap-1", "new-required-snap-2"},
  2694  		"revision":       "1",
  2695  	})
  2696  	chg, err := devicestate.Remodel(s.state, new)
  2697  	c.Assert(err, IsNil)
  2698  	c.Assert(chg.Summary(), Equals, "Refresh model assertion from revision 0 to 1")
  2699  
  2700  	tl := chg.Tasks()
  2701  	// 2 snaps,
  2702  	c.Assert(tl, HasLen, 2*3+1)
  2703  
  2704  	deviceCtx, err := devicestate.DeviceCtx(s.state, tl[0], nil)
  2705  	c.Assert(err, IsNil)
  2706  	// deviceCtx is actually a remodelContext here
  2707  	remodCtx, ok := deviceCtx.(devicestate.RemodelContext)
  2708  	c.Assert(ok, Equals, true)
  2709  	c.Check(remodCtx.ForRemodeling(), Equals, true)
  2710  	c.Check(remodCtx.Kind(), Equals, devicestate.UpdateRemodel)
  2711  	c.Check(remodCtx.Model(), DeepEquals, new)
  2712  	c.Check(remodCtx.Store(), IsNil)
  2713  
  2714  	// check the tasks
  2715  	tDownloadSnap1 := tl[0]
  2716  	tValidateSnap1 := tl[1]
  2717  	tInstallSnap1 := tl[2]
  2718  	tDownloadSnap2 := tl[3]
  2719  	tValidateSnap2 := tl[4]
  2720  	tInstallSnap2 := tl[5]
  2721  	tSetModel := tl[6]
  2722  
  2723  	// check the tasks
  2724  	c.Assert(tDownloadSnap1.Kind(), Equals, "fake-download")
  2725  	c.Assert(tDownloadSnap1.Summary(), Equals, "Download new-required-snap-1")
  2726  	c.Assert(tDownloadSnap1.WaitTasks(), HasLen, 0)
  2727  	c.Assert(tValidateSnap1.Kind(), Equals, "validate-snap")
  2728  	c.Assert(tValidateSnap1.Summary(), Equals, "Validate new-required-snap-1")
  2729  	c.Assert(tDownloadSnap1.WaitTasks(), HasLen, 0)
  2730  	c.Assert(tDownloadSnap2.Kind(), Equals, "fake-download")
  2731  	c.Assert(tDownloadSnap2.Summary(), Equals, "Download new-required-snap-2")
  2732  	// check the ordering, download/validate everything first, then install
  2733  
  2734  	// snap2 downloads wait for the downloads of snap1
  2735  	c.Assert(tDownloadSnap1.WaitTasks(), HasLen, 0)
  2736  	c.Assert(tValidateSnap1.WaitTasks(), DeepEquals, []*state.Task{
  2737  		tDownloadSnap1,
  2738  	})
  2739  	c.Assert(tDownloadSnap2.WaitTasks(), DeepEquals, []*state.Task{
  2740  		tValidateSnap1,
  2741  	})
  2742  	c.Assert(tValidateSnap2.WaitTasks(), DeepEquals, []*state.Task{
  2743  		tDownloadSnap2,
  2744  	})
  2745  	c.Assert(tInstallSnap1.WaitTasks(), DeepEquals, []*state.Task{
  2746  		// wait for own check-snap
  2747  		tValidateSnap1,
  2748  		// and also the last check-snap of the download chain
  2749  		tValidateSnap2,
  2750  	})
  2751  	c.Assert(tInstallSnap2.WaitTasks(), DeepEquals, []*state.Task{
  2752  		// last snap of the download chain
  2753  		tValidateSnap2,
  2754  		// previous install chain
  2755  		tInstallSnap1,
  2756  	})
  2757  
  2758  	c.Assert(tSetModel.Kind(), Equals, "set-model")
  2759  	c.Assert(tSetModel.Summary(), Equals, "Set new model assertion")
  2760  	// setModel waits for everything in the change
  2761  	c.Assert(tSetModel.WaitTasks(), DeepEquals, []*state.Task{tDownloadSnap1, tValidateSnap1, tInstallSnap1, tDownloadSnap2, tValidateSnap2, tInstallSnap2})
  2762  }
  2763  
  2764  func (s *deviceMgrSuite) TestRemodelSwitchKernelTrack(c *C) {
  2765  	s.state.Lock()
  2766  	defer s.state.Unlock()
  2767  	s.state.Set("seeded", true)
  2768  	s.state.Set("refresh-privacy-key", "some-privacy-key")
  2769  
  2770  	restore := devicestate.MockSnapstateInstallWithDeviceContext(func(ctx context.Context, st *state.State, name string, opts *snapstate.RevisionOptions, userID int, flags snapstate.Flags, deviceCtx snapstate.DeviceContext, fromChange string) (*state.TaskSet, error) {
  2771  		c.Check(flags.Required, Equals, true)
  2772  		c.Check(deviceCtx, NotNil)
  2773  		c.Check(deviceCtx.ForRemodeling(), Equals, true)
  2774  
  2775  		tDownload := s.state.NewTask("fake-download", fmt.Sprintf("Download %s", name))
  2776  		tValidate := s.state.NewTask("validate-snap", fmt.Sprintf("Validate %s", name))
  2777  		tValidate.WaitFor(tDownload)
  2778  		tInstall := s.state.NewTask("fake-install", fmt.Sprintf("Install %s", name))
  2779  		tInstall.WaitFor(tValidate)
  2780  		ts := state.NewTaskSet(tDownload, tValidate, tInstall)
  2781  		ts.MarkEdge(tValidate, snapstate.DownloadAndChecksDoneEdge)
  2782  		return ts, nil
  2783  	})
  2784  	defer restore()
  2785  
  2786  	restore = devicestate.MockSnapstateUpdateWithDeviceContext(func(st *state.State, name string, opts *snapstate.RevisionOptions, userID int, flags snapstate.Flags, deviceCtx snapstate.DeviceContext, fromChange string) (*state.TaskSet, error) {
  2787  		c.Check(flags.Required, Equals, false)
  2788  		c.Check(flags.NoReRefresh, Equals, true)
  2789  		c.Check(deviceCtx, NotNil)
  2790  		c.Check(deviceCtx.ForRemodeling(), Equals, true)
  2791  
  2792  		tDownload := s.state.NewTask("fake-download", fmt.Sprintf("Download %s to track %s", name, opts.Channel))
  2793  		tValidate := s.state.NewTask("validate-snap", fmt.Sprintf("Validate %s", name))
  2794  		tValidate.WaitFor(tDownload)
  2795  		tUpdate := s.state.NewTask("fake-update", fmt.Sprintf("Update %s to track %s", name, opts.Channel))
  2796  		tUpdate.WaitFor(tValidate)
  2797  		ts := state.NewTaskSet(tDownload, tValidate, tUpdate)
  2798  		ts.MarkEdge(tValidate, snapstate.DownloadAndChecksDoneEdge)
  2799  		return ts, nil
  2800  	})
  2801  	defer restore()
  2802  
  2803  	// set a model assertion
  2804  	s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{
  2805  		"architecture": "amd64",
  2806  		"kernel":       "pc-kernel",
  2807  		"gadget":       "pc",
  2808  		"base":         "core18",
  2809  	})
  2810  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  2811  		Brand: "canonical",
  2812  		Model: "pc-model",
  2813  	})
  2814  
  2815  	new := s.brands.Model("canonical", "pc-model", map[string]interface{}{
  2816  		"architecture":   "amd64",
  2817  		"kernel":         "pc-kernel=18",
  2818  		"gadget":         "pc",
  2819  		"base":           "core18",
  2820  		"required-snaps": []interface{}{"new-required-snap-1"},
  2821  		"revision":       "1",
  2822  	})
  2823  	chg, err := devicestate.Remodel(s.state, new)
  2824  	c.Assert(err, IsNil)
  2825  	c.Assert(chg.Summary(), Equals, "Refresh model assertion from revision 0 to 1")
  2826  
  2827  	tl := chg.Tasks()
  2828  	c.Assert(tl, HasLen, 2*3+1)
  2829  
  2830  	tDownloadKernel := tl[0]
  2831  	tValidateKernel := tl[1]
  2832  	tUpdateKernel := tl[2]
  2833  	tDownloadSnap1 := tl[3]
  2834  	tValidateSnap1 := tl[4]
  2835  	tInstallSnap1 := tl[5]
  2836  	tSetModel := tl[6]
  2837  
  2838  	c.Assert(tDownloadKernel.Kind(), Equals, "fake-download")
  2839  	c.Assert(tDownloadKernel.Summary(), Equals, "Download pc-kernel to track 18")
  2840  	c.Assert(tValidateKernel.Kind(), Equals, "validate-snap")
  2841  	c.Assert(tValidateKernel.Summary(), Equals, "Validate pc-kernel")
  2842  	c.Assert(tUpdateKernel.Kind(), Equals, "fake-update")
  2843  	c.Assert(tUpdateKernel.Summary(), Equals, "Update pc-kernel to track 18")
  2844  	c.Assert(tDownloadSnap1.Kind(), Equals, "fake-download")
  2845  	c.Assert(tDownloadSnap1.Summary(), Equals, "Download new-required-snap-1")
  2846  	c.Assert(tValidateSnap1.Kind(), Equals, "validate-snap")
  2847  	c.Assert(tValidateSnap1.Summary(), Equals, "Validate new-required-snap-1")
  2848  	c.Assert(tInstallSnap1.Kind(), Equals, "fake-install")
  2849  	c.Assert(tInstallSnap1.Summary(), Equals, "Install new-required-snap-1")
  2850  
  2851  	c.Assert(tSetModel.Kind(), Equals, "set-model")
  2852  	c.Assert(tSetModel.Summary(), Equals, "Set new model assertion")
  2853  
  2854  	// check the ordering
  2855  	c.Assert(tDownloadSnap1.WaitTasks(), DeepEquals, []*state.Task{
  2856  		// previous download finished
  2857  		tValidateKernel,
  2858  	})
  2859  	c.Assert(tInstallSnap1.WaitTasks(), DeepEquals, []*state.Task{
  2860  		// last download in the chain finished
  2861  		tValidateSnap1,
  2862  		// and kernel got updated
  2863  		tUpdateKernel,
  2864  	})
  2865  	c.Assert(tUpdateKernel.WaitTasks(), DeepEquals, []*state.Task{
  2866  		// kernel is valid
  2867  		tValidateKernel,
  2868  		// and last download in the chain finished
  2869  		tValidateSnap1,
  2870  	})
  2871  }
  2872  
  2873  func (s *deviceMgrSuite) TestRemodelLessRequiredSnaps(c *C) {
  2874  	s.state.Lock()
  2875  	defer s.state.Unlock()
  2876  	s.state.Set("seeded", true)
  2877  	s.state.Set("refresh-privacy-key", "some-privacy-key")
  2878  
  2879  	// set a model assertion
  2880  	s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{
  2881  		"architecture":   "amd64",
  2882  		"kernel":         "pc-kernel",
  2883  		"gadget":         "pc",
  2884  		"base":           "core18",
  2885  		"required-snaps": []interface{}{"some-required-snap"},
  2886  	})
  2887  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  2888  		Brand: "canonical",
  2889  		Model: "pc-model",
  2890  	})
  2891  
  2892  	new := s.brands.Model("canonical", "pc-model", map[string]interface{}{
  2893  		"architecture": "amd64",
  2894  		"kernel":       "pc-kernel",
  2895  		"gadget":       "pc",
  2896  		"base":         "core18",
  2897  		"revision":     "1",
  2898  	})
  2899  	chg, err := devicestate.Remodel(s.state, new)
  2900  	c.Assert(err, IsNil)
  2901  	c.Assert(chg.Summary(), Equals, "Refresh model assertion from revision 0 to 1")
  2902  
  2903  	tl := chg.Tasks()
  2904  	c.Assert(tl, HasLen, 1)
  2905  	tSetModel := tl[0]
  2906  	c.Assert(tSetModel.Kind(), Equals, "set-model")
  2907  	c.Assert(tSetModel.Summary(), Equals, "Set new model assertion")
  2908  }
  2909  
  2910  type freshSessionStore struct {
  2911  	storetest.Store
  2912  
  2913  	ensureDeviceSession int
  2914  }
  2915  
  2916  func (sto *freshSessionStore) EnsureDeviceSession() (*auth.DeviceState, error) {
  2917  	sto.ensureDeviceSession += 1
  2918  	return nil, nil
  2919  }
  2920  
  2921  func (s *deviceMgrSuite) TestRemodelStoreSwitch(c *C) {
  2922  	s.state.Lock()
  2923  	defer s.state.Unlock()
  2924  	s.state.Set("seeded", true)
  2925  	s.state.Set("refresh-privacy-key", "some-privacy-key")
  2926  
  2927  	var testStore snapstate.StoreService
  2928  
  2929  	restore := devicestate.MockSnapstateInstallWithDeviceContext(func(ctx context.Context, st *state.State, name string, opts *snapstate.RevisionOptions, userID int, flags snapstate.Flags, deviceCtx snapstate.DeviceContext, fromChange string) (*state.TaskSet, error) {
  2930  		c.Check(flags.Required, Equals, true)
  2931  		c.Check(deviceCtx, NotNil)
  2932  		c.Check(deviceCtx.ForRemodeling(), Equals, true)
  2933  
  2934  		c.Check(deviceCtx.Store(), Equals, testStore)
  2935  
  2936  		tDownload := s.state.NewTask("fake-download", fmt.Sprintf("Download %s", name))
  2937  		tValidate := s.state.NewTask("validate-snap", fmt.Sprintf("Validate %s", name))
  2938  		tValidate.WaitFor(tDownload)
  2939  		tInstall := s.state.NewTask("fake-install", fmt.Sprintf("Install %s", name))
  2940  		tInstall.WaitFor(tValidate)
  2941  		ts := state.NewTaskSet(tDownload, tValidate, tInstall)
  2942  		ts.MarkEdge(tValidate, snapstate.DownloadAndChecksDoneEdge)
  2943  		return ts, nil
  2944  	})
  2945  	defer restore()
  2946  
  2947  	// set a model assertion
  2948  	s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{
  2949  		"architecture": "amd64",
  2950  		"kernel":       "pc-kernel",
  2951  		"gadget":       "pc",
  2952  		"base":         "core18",
  2953  	})
  2954  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  2955  		Brand: "canonical",
  2956  		Model: "pc-model",
  2957  	})
  2958  
  2959  	new := s.brands.Model("canonical", "pc-model", map[string]interface{}{
  2960  		"architecture":   "amd64",
  2961  		"kernel":         "pc-kernel",
  2962  		"gadget":         "pc",
  2963  		"base":           "core18",
  2964  		"store":          "switched-store",
  2965  		"required-snaps": []interface{}{"new-required-snap-1", "new-required-snap-2"},
  2966  		"revision":       "1",
  2967  	})
  2968  
  2969  	freshStore := &freshSessionStore{}
  2970  	testStore = freshStore
  2971  
  2972  	s.newFakeStore = func(devBE storecontext.DeviceBackend) snapstate.StoreService {
  2973  		mod, err := devBE.Model()
  2974  		c.Check(err, IsNil)
  2975  		if err == nil {
  2976  			c.Check(mod, DeepEquals, new)
  2977  		}
  2978  		return testStore
  2979  	}
  2980  
  2981  	chg, err := devicestate.Remodel(s.state, new)
  2982  	c.Assert(err, IsNil)
  2983  	c.Assert(chg.Summary(), Equals, "Refresh model assertion from revision 0 to 1")
  2984  
  2985  	c.Check(freshStore.ensureDeviceSession, Equals, 1)
  2986  
  2987  	tl := chg.Tasks()
  2988  	// 2 snaps * 3 tasks (from the mock install above) +
  2989  	// 1 "set-model" task at the end
  2990  	c.Assert(tl, HasLen, 2*3+1)
  2991  
  2992  	deviceCtx, err := devicestate.DeviceCtx(s.state, tl[0], nil)
  2993  	c.Assert(err, IsNil)
  2994  	// deviceCtx is actually a remodelContext here
  2995  	remodCtx, ok := deviceCtx.(devicestate.RemodelContext)
  2996  	c.Assert(ok, Equals, true)
  2997  	c.Check(remodCtx.ForRemodeling(), Equals, true)
  2998  	c.Check(remodCtx.Kind(), Equals, devicestate.StoreSwitchRemodel)
  2999  	c.Check(remodCtx.Model(), DeepEquals, new)
  3000  	c.Check(remodCtx.Store(), Equals, testStore)
  3001  }
  3002  
  3003  func (s *deviceMgrSuite) TestRemodelRereg(c *C) {
  3004  	s.state.Lock()
  3005  	defer s.state.Unlock()
  3006  	s.state.Set("seeded", true)
  3007  
  3008  	// set a model assertion
  3009  	s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{
  3010  		"architecture": "amd64",
  3011  		"kernel":       "pc-kernel",
  3012  		"gadget":       "pc",
  3013  		"base":         "core18",
  3014  	})
  3015  	s.makeSerialAssertionInState(c, "canonical", "pc-model", "orig-serial")
  3016  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  3017  		Brand:           "canonical",
  3018  		Model:           "pc-model",
  3019  		Serial:          "orig-serial",
  3020  		SessionMacaroon: "old-session",
  3021  	})
  3022  
  3023  	new := s.brands.Model("canonical", "rereg-model", map[string]interface{}{
  3024  		"architecture":   "amd64",
  3025  		"kernel":         "pc-kernel",
  3026  		"gadget":         "pc",
  3027  		"base":           "core18",
  3028  		"required-snaps": []interface{}{"new-required-snap-1", "new-required-snap-2"},
  3029  	})
  3030  
  3031  	s.newFakeStore = func(devBE storecontext.DeviceBackend) snapstate.StoreService {
  3032  		mod, err := devBE.Model()
  3033  		c.Check(err, IsNil)
  3034  		if err == nil {
  3035  			c.Check(mod, DeepEquals, new)
  3036  		}
  3037  		return nil
  3038  	}
  3039  
  3040  	chg, err := devicestate.Remodel(s.state, new)
  3041  	c.Assert(err, IsNil)
  3042  
  3043  	c.Assert(chg.Summary(), Equals, "Remodel device to canonical/rereg-model (0)")
  3044  
  3045  	tl := chg.Tasks()
  3046  	c.Assert(tl, HasLen, 2)
  3047  
  3048  	// check the tasks
  3049  	tRequestSerial := tl[0]
  3050  	tPrepareRemodeling := tl[1]
  3051  
  3052  	// check the tasks
  3053  	c.Assert(tRequestSerial.Kind(), Equals, "request-serial")
  3054  	c.Assert(tRequestSerial.Summary(), Equals, "Request new device serial")
  3055  	c.Assert(tRequestSerial.WaitTasks(), HasLen, 0)
  3056  
  3057  	c.Assert(tPrepareRemodeling.Kind(), Equals, "prepare-remodeling")
  3058  	c.Assert(tPrepareRemodeling.Summary(), Equals, "Prepare remodeling")
  3059  	c.Assert(tPrepareRemodeling.WaitTasks(), DeepEquals, []*state.Task{tRequestSerial})
  3060  }
  3061  
  3062  func (s *deviceMgrSuite) TestRemodelClash(c *C) {
  3063  	s.state.Lock()
  3064  	defer s.state.Unlock()
  3065  	s.state.Set("seeded", true)
  3066  	s.state.Set("refresh-privacy-key", "some-privacy-key")
  3067  
  3068  	var clashing *asserts.Model
  3069  
  3070  	restore := devicestate.MockSnapstateInstallWithDeviceContext(func(ctx context.Context, st *state.State, name string, opts *snapstate.RevisionOptions, userID int, flags snapstate.Flags, deviceCtx snapstate.DeviceContext, fromChange string) (*state.TaskSet, error) {
  3071  		// simulate things changing under our feet
  3072  		assertstatetest.AddMany(st, clashing)
  3073  		devicestatetest.SetDevice(s.state, &auth.DeviceState{
  3074  			Brand: "canonical",
  3075  			Model: clashing.Model(),
  3076  		})
  3077  
  3078  		tDownload := s.state.NewTask("fake-download", fmt.Sprintf("Download %s", name))
  3079  		tValidate := s.state.NewTask("validate-snap", fmt.Sprintf("Validate %s", name))
  3080  		tValidate.WaitFor(tDownload)
  3081  		tInstall := s.state.NewTask("fake-install", fmt.Sprintf("Install %s", name))
  3082  		tInstall.WaitFor(tValidate)
  3083  		ts := state.NewTaskSet(tDownload, tValidate, tInstall)
  3084  		ts.MarkEdge(tValidate, snapstate.DownloadAndChecksDoneEdge)
  3085  		return ts, nil
  3086  	})
  3087  	defer restore()
  3088  
  3089  	// set a model assertion
  3090  	s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{
  3091  		"architecture": "amd64",
  3092  		"kernel":       "pc-kernel",
  3093  		"gadget":       "pc",
  3094  		"base":         "core18",
  3095  	})
  3096  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  3097  		Brand: "canonical",
  3098  		Model: "pc-model",
  3099  	})
  3100  
  3101  	new := s.brands.Model("canonical", "pc-model", map[string]interface{}{
  3102  		"architecture":   "amd64",
  3103  		"kernel":         "pc-kernel",
  3104  		"gadget":         "pc",
  3105  		"base":           "core18",
  3106  		"required-snaps": []interface{}{"new-required-snap-1", "new-required-snap-2"},
  3107  		"revision":       "1",
  3108  	})
  3109  	other := s.brands.Model("canonical", "pc-model-other", map[string]interface{}{
  3110  		"architecture":   "amd64",
  3111  		"kernel":         "pc-kernel",
  3112  		"gadget":         "pc",
  3113  		"base":           "core18",
  3114  		"required-snaps": []interface{}{"new-required-snap-1", "new-required-snap-2"},
  3115  	})
  3116  
  3117  	clashing = other
  3118  	_, err := devicestate.Remodel(s.state, new)
  3119  	c.Check(err, DeepEquals, &snapstate.ChangeConflictError{
  3120  		Message: "cannot start remodel, clashing with concurrent remodel to canonical/pc-model-other (0)",
  3121  	})
  3122  
  3123  	// reset
  3124  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  3125  		Brand: "canonical",
  3126  		Model: "pc-model",
  3127  	})
  3128  	clashing = new
  3129  	_, err = devicestate.Remodel(s.state, new)
  3130  	c.Check(err, DeepEquals, &snapstate.ChangeConflictError{
  3131  		Message: "cannot start remodel, clashing with concurrent remodel to canonical/pc-model (1)",
  3132  	})
  3133  }
  3134  
  3135  func (s *deviceMgrSuite) TestRemodelClashInProgress(c *C) {
  3136  	s.state.Lock()
  3137  	defer s.state.Unlock()
  3138  	s.state.Set("seeded", true)
  3139  	s.state.Set("refresh-privacy-key", "some-privacy-key")
  3140  
  3141  	restore := devicestate.MockSnapstateInstallWithDeviceContext(func(ctx context.Context, st *state.State, name string, opts *snapstate.RevisionOptions, userID int, flags snapstate.Flags, deviceCtx snapstate.DeviceContext, fromChange string) (*state.TaskSet, error) {
  3142  		// simulate another started remodeling
  3143  		st.NewChange("remodel", "other remodel")
  3144  
  3145  		tDownload := s.state.NewTask("fake-download", fmt.Sprintf("Download %s", name))
  3146  		tValidate := s.state.NewTask("validate-snap", fmt.Sprintf("Validate %s", name))
  3147  		tValidate.WaitFor(tDownload)
  3148  		tInstall := s.state.NewTask("fake-install", fmt.Sprintf("Install %s", name))
  3149  		tInstall.WaitFor(tValidate)
  3150  		ts := state.NewTaskSet(tDownload, tValidate, tInstall)
  3151  		ts.MarkEdge(tValidate, snapstate.DownloadAndChecksDoneEdge)
  3152  		return ts, nil
  3153  	})
  3154  	defer restore()
  3155  
  3156  	// set a model assertion
  3157  	s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{
  3158  		"architecture": "amd64",
  3159  		"kernel":       "pc-kernel",
  3160  		"gadget":       "pc",
  3161  		"base":         "core18",
  3162  	})
  3163  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  3164  		Brand: "canonical",
  3165  		Model: "pc-model",
  3166  	})
  3167  
  3168  	new := s.brands.Model("canonical", "pc-model", map[string]interface{}{
  3169  		"architecture":   "amd64",
  3170  		"kernel":         "pc-kernel",
  3171  		"gadget":         "pc",
  3172  		"base":           "core18",
  3173  		"required-snaps": []interface{}{"new-required-snap-1", "new-required-snap-2"},
  3174  		"revision":       "1",
  3175  	})
  3176  
  3177  	_, err := devicestate.Remodel(s.state, new)
  3178  	c.Check(err, DeepEquals, &snapstate.ChangeConflictError{
  3179  		Message: "cannot start remodel, clashing with concurrent one",
  3180  	})
  3181  }
  3182  
  3183  func (s *deviceMgrSuite) TestReregRemodelClashAnyChange(c *C) {
  3184  	s.state.Lock()
  3185  	defer s.state.Unlock()
  3186  	s.state.Set("seeded", true)
  3187  
  3188  	// set a model assertion
  3189  	s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{
  3190  		"architecture": "amd64",
  3191  		"kernel":       "pc-kernel",
  3192  		"gadget":       "pc",
  3193  		"base":         "core18",
  3194  	})
  3195  	s.makeSerialAssertionInState(c, "canonical", "pc-model", "orig-serial")
  3196  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  3197  		Brand:           "canonical",
  3198  		Model:           "pc-model",
  3199  		Serial:          "orig-serial",
  3200  		SessionMacaroon: "old-session",
  3201  	})
  3202  
  3203  	new := s.brands.Model("canonical", "pc-model-2", map[string]interface{}{
  3204  		"architecture":   "amd64",
  3205  		"kernel":         "pc-kernel",
  3206  		"gadget":         "pc",
  3207  		"base":           "core18",
  3208  		"required-snaps": []interface{}{"new-required-snap-1", "new-required-snap-2"},
  3209  		"revision":       "1",
  3210  	})
  3211  
  3212  	// simulate any other change
  3213  	s.state.NewChange("chg", "other change")
  3214  
  3215  	_, err := devicestate.Remodel(s.state, new)
  3216  	c.Check(err, DeepEquals, &snapstate.ChangeConflictError{
  3217  		Message: "cannot start complete remodel, other changes are in progress",
  3218  	})
  3219  }
  3220  
  3221  func (s *deviceMgrSuite) TestRemodeling(c *C) {
  3222  	s.state.Lock()
  3223  	defer s.state.Unlock()
  3224  
  3225  	// no changes
  3226  	c.Check(devicestate.Remodeling(s.state), Equals, false)
  3227  
  3228  	// other change
  3229  	s.state.NewChange("other", "...")
  3230  	c.Check(devicestate.Remodeling(s.state), Equals, false)
  3231  
  3232  	// remodel change
  3233  	chg := s.state.NewChange("remodel", "...")
  3234  	c.Check(devicestate.Remodeling(s.state), Equals, true)
  3235  
  3236  	// done
  3237  	chg.SetStatus(state.DoneStatus)
  3238  	c.Check(devicestate.Remodeling(s.state), Equals, false)
  3239  }
  3240  
  3241  func (s *deviceMgrSuite) testDoRequestSerialReregistration(c *C, setAncillary func(origSerial *asserts.Serial)) *state.Task {
  3242  	mockServer := s.mockServer(c, "REQID-1", nil)
  3243  	defer mockServer.Close()
  3244  
  3245  	restore := devicestate.MockBaseStoreURL(mockServer.URL)
  3246  	defer restore()
  3247  
  3248  	// setup state as after initial registration
  3249  	s.state.Lock()
  3250  	defer s.state.Unlock()
  3251  
  3252  	s.makeModelAssertionInState(c, "my-brand", "my-model", map[string]interface{}{
  3253  		"architecture": "amd64",
  3254  		"kernel":       "pc-kernel",
  3255  		"gadget":       "pc",
  3256  	})
  3257  
  3258  	devicestatetest.MockGadget(c, s.state, "pc", snap.R(2), nil)
  3259  
  3260  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  3261  		Brand:  "my-brand",
  3262  		Model:  "my-model",
  3263  		KeyID:  devKey.PublicKey().ID(),
  3264  		Serial: "9999",
  3265  	})
  3266  	devicestate.KeypairManager(s.mgr).Put(devKey)
  3267  
  3268  	// have a serial assertion
  3269  	serial0 := s.makeSerialAssertionInState(c, "my-brand", "my-model", "9999")
  3270  	// give a chance to the test to setup returning a stream vs
  3271  	// just the serial assertion
  3272  	if setAncillary != nil {
  3273  		setAncillary(serial0)
  3274  	}
  3275  
  3276  	new := s.brands.Model("rereg-brand", "rereg-model", map[string]interface{}{
  3277  		"architecture": "amd64",
  3278  		"kernel":       "pc-kernel",
  3279  		"gadget":       "pc",
  3280  	})
  3281  	cur, err := s.mgr.Model()
  3282  	c.Assert(err, IsNil)
  3283  
  3284  	s.newFakeStore = func(devBE storecontext.DeviceBackend) snapstate.StoreService {
  3285  		mod, err := devBE.Model()
  3286  		c.Check(err, IsNil)
  3287  		if err == nil {
  3288  			c.Check(mod, DeepEquals, new)
  3289  		}
  3290  		return nil
  3291  	}
  3292  
  3293  	remodCtx, err := devicestate.RemodelCtx(s.state, cur, new)
  3294  	c.Assert(err, IsNil)
  3295  	c.Check(remodCtx.Kind(), Equals, devicestate.ReregRemodel)
  3296  
  3297  	t := s.state.NewTask("request-serial", "test")
  3298  	chg := s.state.NewChange("remodel", "...")
  3299  	// associate with context
  3300  	remodCtx.Init(chg)
  3301  	chg.AddTask(t)
  3302  
  3303  	// sanity
  3304  	regCtx, err := devicestate.RegistrationCtx(s.mgr, t)
  3305  	c.Assert(err, IsNil)
  3306  	c.Check(regCtx, Equals, remodCtx.(devicestate.RegistrationContext))
  3307  
  3308  	// avoid full seeding
  3309  	s.seeding()
  3310  
  3311  	s.state.Unlock()
  3312  	s.se.Ensure()
  3313  	s.se.Wait()
  3314  	s.state.Lock()
  3315  
  3316  	return t
  3317  }
  3318  
  3319  func (s *deviceMgrSuite) TestDoRequestSerialReregistration(c *C) {
  3320  	assertstest.AddMany(s.storeSigning, s.brands.AccountsAndKeys("rereg-brand")...)
  3321  
  3322  	t := s.testDoRequestSerialReregistration(c, nil)
  3323  
  3324  	s.state.Lock()
  3325  	defer s.state.Unlock()
  3326  	chg := t.Change()
  3327  
  3328  	c.Check(chg.Status(), Equals, state.DoneStatus, Commentf("%s", t.Log()))
  3329  	c.Check(chg.Err(), IsNil)
  3330  	device, err := devicestatetest.Device(s.state)
  3331  	c.Check(err, IsNil)
  3332  	c.Check(device.Serial, Equals, "9999")
  3333  	_, err = s.db.Find(asserts.SerialType, map[string]string{
  3334  		"brand-id": "rereg-brand",
  3335  		"model":    "rereg-model",
  3336  		"serial":   "9999",
  3337  	})
  3338  	c.Assert(err, IsNil)
  3339  }
  3340  
  3341  func (s *deviceMgrSuite) TestDoRequestSerialReregistrationStreamFromService(c *C) {
  3342  	setAncillary := func(_ *asserts.Serial) {
  3343  		// sets up such that re-registration returns a stream
  3344  		// of assertions
  3345  		s.ancillary = s.brands.AccountsAndKeys("rereg-brand")
  3346  	}
  3347  
  3348  	t := s.testDoRequestSerialReregistration(c, setAncillary)
  3349  
  3350  	s.state.Lock()
  3351  	defer s.state.Unlock()
  3352  	chg := t.Change()
  3353  
  3354  	c.Check(chg.Status(), Equals, state.DoneStatus, Commentf("%s", t.Log()))
  3355  	c.Check(chg.Err(), IsNil)
  3356  	device, err := devicestatetest.Device(s.state)
  3357  	c.Check(err, IsNil)
  3358  	c.Check(device.Serial, Equals, "9999")
  3359  	_, err = s.db.Find(asserts.SerialType, map[string]string{
  3360  		"brand-id": "rereg-brand",
  3361  		"model":    "rereg-model",
  3362  		"serial":   "9999",
  3363  	})
  3364  	c.Assert(err, IsNil)
  3365  }
  3366  
  3367  func (s *deviceMgrSuite) TestDoRequestSerialReregistrationIncompleteStreamFromService(c *C) {
  3368  	setAncillary := func(_ *asserts.Serial) {
  3369  		// will produce an incomplete stream!
  3370  		s.ancillary = s.brands.AccountsAndKeys("rereg-brand")[:1]
  3371  	}
  3372  
  3373  	t := s.testDoRequestSerialReregistration(c, setAncillary)
  3374  
  3375  	s.state.Lock()
  3376  	defer s.state.Unlock()
  3377  	chg := t.Change()
  3378  
  3379  	c.Check(chg.Status(), Equals, state.ErrorStatus, Commentf("%s", t.Log()))
  3380  	c.Check(chg.Err(), ErrorMatches, `(?ms).*cannot accept stream of assertions from device service:.*`)
  3381  }
  3382  
  3383  func (s *deviceMgrSuite) TestDoRequestSerialReregistrationDoubleSerialStreamFromService(c *C) {
  3384  	setAncillary := func(serial0 *asserts.Serial) {
  3385  		// will produce a stream with confusingly two serial
  3386  		// assertions
  3387  		s.ancillary = s.brands.AccountsAndKeys("rereg-brand")
  3388  		s.ancillary = append(s.ancillary, serial0)
  3389  	}
  3390  
  3391  	t := s.testDoRequestSerialReregistration(c, setAncillary)
  3392  
  3393  	s.state.Lock()
  3394  	defer s.state.Unlock()
  3395  	chg := t.Change()
  3396  
  3397  	c.Check(chg.Status(), Equals, state.ErrorStatus, Commentf("%s", t.Log()))
  3398  	c.Check(chg.Err(), ErrorMatches, `(?ms).*cannot accept more than a single device serial assertion from the device service.*`)
  3399  }
  3400  
  3401  func (s *deviceMgrSuite) TestDeviceCtxNoTask(c *C) {
  3402  	s.state.Lock()
  3403  	defer s.state.Unlock()
  3404  	// nothing in the state
  3405  
  3406  	_, err := devicestate.DeviceCtx(s.state, nil, nil)
  3407  	c.Check(err, Equals, state.ErrNoState)
  3408  
  3409  	// have a model assertion
  3410  	model := s.brands.Model("canonical", "pc", map[string]interface{}{
  3411  		"gadget":       "pc",
  3412  		"kernel":       "kernel",
  3413  		"architecture": "amd64",
  3414  	})
  3415  	assertstatetest.AddMany(s.state, model)
  3416  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  3417  		Brand: "canonical",
  3418  		Model: "pc",
  3419  	})
  3420  
  3421  	deviceCtx, err := devicestate.DeviceCtx(s.state, nil, nil)
  3422  	c.Assert(err, IsNil)
  3423  	c.Assert(deviceCtx.Model().BrandID(), Equals, "canonical")
  3424  }
  3425  
  3426  func (s *deviceMgrSuite) TestDeviceCtxProvided(c *C) {
  3427  	s.state.Lock()
  3428  	defer s.state.Unlock()
  3429  
  3430  	model := assertstest.FakeAssertion(map[string]interface{}{
  3431  		"type":         "model",
  3432  		"authority-id": "canonical",
  3433  		"series":       "16",
  3434  		"brand-id":     "canonical",
  3435  		"model":        "pc",
  3436  		"gadget":       "pc",
  3437  		"kernel":       "kernel",
  3438  		"architecture": "amd64",
  3439  	}).(*asserts.Model)
  3440  
  3441  	deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model}
  3442  
  3443  	deviceCtx1, err := devicestate.DeviceCtx(s.state, nil, deviceCtx)
  3444  	c.Assert(err, IsNil)
  3445  	c.Assert(deviceCtx1, Equals, deviceCtx)
  3446  }
  3447  
  3448  var snapYaml = `
  3449  name: foo-gadget
  3450  type: gadget
  3451  `
  3452  
  3453  var gadgetYaml = `
  3454  volumes:
  3455    pc:
  3456      bootloader: grub
  3457  `
  3458  
  3459  func setupGadgetUpdate(c *C, st *state.State) (chg *state.Change, tsk *state.Task) {
  3460  	siCurrent := &snap.SideInfo{
  3461  		RealName: "foo-gadget",
  3462  		Revision: snap.R(33),
  3463  		SnapID:   "foo-id",
  3464  	}
  3465  	si := &snap.SideInfo{
  3466  		RealName: "foo-gadget",
  3467  		Revision: snap.R(34),
  3468  		SnapID:   "foo-id",
  3469  	}
  3470  	snaptest.MockSnapWithFiles(c, snapYaml, siCurrent, [][]string{
  3471  		{"meta/gadget.yaml", gadgetYaml},
  3472  	})
  3473  	snaptest.MockSnapWithFiles(c, snapYaml, si, [][]string{
  3474  		{"meta/gadget.yaml", gadgetYaml},
  3475  	})
  3476  
  3477  	st.Lock()
  3478  
  3479  	snapstate.Set(st, "foo-gadget", &snapstate.SnapState{
  3480  		SnapType: "gadget",
  3481  		Sequence: []*snap.SideInfo{siCurrent},
  3482  		Current:  siCurrent.Revision,
  3483  		Active:   true,
  3484  	})
  3485  
  3486  	tsk = st.NewTask("update-gadget-assets", "update gadget")
  3487  	tsk.Set("snap-setup", &snapstate.SnapSetup{
  3488  		SideInfo: si,
  3489  		Type:     snap.TypeGadget,
  3490  	})
  3491  	chg = st.NewChange("dummy", "...")
  3492  	chg.AddTask(tsk)
  3493  
  3494  	st.Unlock()
  3495  
  3496  	return chg, tsk
  3497  }
  3498  
  3499  func (s *deviceMgrSuite) TestUpdateGadgetOnCoreSimple(c *C) {
  3500  	var updateCalled bool
  3501  	var passedRollbackDir string
  3502  	restore := devicestate.MockGadgetUpdate(func(current, update gadget.GadgetData, path string) error {
  3503  		updateCalled = true
  3504  		passedRollbackDir = path
  3505  		st, err := os.Stat(path)
  3506  		c.Assert(err, IsNil)
  3507  		m := st.Mode()
  3508  		c.Assert(m.IsDir(), Equals, true)
  3509  		c.Check(m.Perm(), Equals, os.FileMode(0750))
  3510  		return nil
  3511  	})
  3512  	defer restore()
  3513  
  3514  	chg, t := setupGadgetUpdate(c, s.state)
  3515  
  3516  	for i := 0; i < 6; i++ {
  3517  		s.se.Ensure()
  3518  		s.se.Wait()
  3519  	}
  3520  
  3521  	s.state.Lock()
  3522  	defer s.state.Unlock()
  3523  	c.Assert(chg.IsReady(), Equals, true)
  3524  	c.Check(chg.Err(), IsNil)
  3525  	c.Check(t.Status(), Equals, state.DoneStatus)
  3526  	c.Check(updateCalled, Equals, true)
  3527  	rollbackDir := filepath.Join(dirs.SnapRollbackDir, "foo-gadget_34")
  3528  	c.Check(rollbackDir, Equals, passedRollbackDir)
  3529  	// should have been removed right after update
  3530  	c.Check(osutil.IsDirectory(rollbackDir), Equals, false)
  3531  	c.Check(s.restartRequests, DeepEquals, []state.RestartType{state.RestartSystem})
  3532  }
  3533  
  3534  func (s *deviceMgrSuite) TestUpdateGadgetOnCoreNoUpdateNeeded(c *C) {
  3535  	var called bool
  3536  	restore := devicestate.MockGadgetUpdate(func(current, update gadget.GadgetData, path string) error {
  3537  		called = true
  3538  		return gadget.ErrNoUpdate
  3539  	})
  3540  	defer restore()
  3541  
  3542  	chg, t := setupGadgetUpdate(c, s.state)
  3543  
  3544  	s.se.Ensure()
  3545  	s.se.Wait()
  3546  
  3547  	s.state.Lock()
  3548  	defer s.state.Unlock()
  3549  	c.Assert(chg.IsReady(), Equals, true)
  3550  	c.Check(chg.Err(), IsNil)
  3551  	c.Check(t.Status(), Equals, state.DoneStatus)
  3552  	c.Check(t.Log(), HasLen, 1)
  3553  	c.Check(t.Log()[0], Matches, ".* INFO No gadget assets update needed")
  3554  	c.Check(called, Equals, true)
  3555  	c.Check(s.restartRequests, HasLen, 0)
  3556  }
  3557  
  3558  func (s *deviceMgrSuite) TestUpdateGadgetOnCoreRollbackDirCreateFailed(c *C) {
  3559  	if os.Geteuid() == 0 {
  3560  		c.Skip("this test cannot run as root (permissions are not honored)")
  3561  	}
  3562  
  3563  	restore := devicestate.MockGadgetUpdate(func(current, update gadget.GadgetData, path string) error {
  3564  		return errors.New("unexpected call")
  3565  	})
  3566  	defer restore()
  3567  
  3568  	chg, t := setupGadgetUpdate(c, s.state)
  3569  
  3570  	rollbackDir := filepath.Join(dirs.SnapRollbackDir, "foo-gadget_34")
  3571  	err := os.MkdirAll(dirs.SnapRollbackDir, 0000)
  3572  	c.Assert(err, IsNil)
  3573  
  3574  	for i := 0; i < 6; i++ {
  3575  		s.se.Ensure()
  3576  		s.se.Wait()
  3577  	}
  3578  
  3579  	s.state.Lock()
  3580  	defer s.state.Unlock()
  3581  	c.Assert(chg.IsReady(), Equals, true)
  3582  	c.Check(chg.Err(), ErrorMatches, `(?s).*cannot prepare update rollback directory: .*`)
  3583  	c.Check(t.Status(), Equals, state.ErrorStatus)
  3584  	c.Check(osutil.IsDirectory(rollbackDir), Equals, false)
  3585  	c.Check(s.restartRequests, HasLen, 0)
  3586  }
  3587  
  3588  func (s *deviceMgrSuite) TestUpdateGadgetOnCoreUpdateFailed(c *C) {
  3589  	restore := devicestate.MockGadgetUpdate(func(current, update gadget.GadgetData, path string) error {
  3590  		return errors.New("gadget exploded")
  3591  	})
  3592  	defer restore()
  3593  	chg, t := setupGadgetUpdate(c, s.state)
  3594  
  3595  	for i := 0; i < 6; i++ {
  3596  		s.se.Ensure()
  3597  		s.se.Wait()
  3598  	}
  3599  
  3600  	s.state.Lock()
  3601  	defer s.state.Unlock()
  3602  	c.Assert(chg.IsReady(), Equals, true)
  3603  	c.Check(chg.Err(), ErrorMatches, `(?s).*update gadget \(gadget exploded\).*`)
  3604  	c.Check(t.Status(), Equals, state.ErrorStatus)
  3605  	rollbackDir := filepath.Join(dirs.SnapRollbackDir, "foo-gadget_34")
  3606  	// update rollback left for inspection
  3607  	c.Check(osutil.IsDirectory(rollbackDir), Equals, true)
  3608  	c.Check(s.restartRequests, HasLen, 0)
  3609  }
  3610  
  3611  func (s *deviceMgrSuite) TestUpdateGadgetOnCoreNotDuringFirstboot(c *C) {
  3612  	restore := devicestate.MockGadgetUpdate(func(current, update gadget.GadgetData, path string) error {
  3613  		return errors.New("unexpected call")
  3614  	})
  3615  	defer restore()
  3616  
  3617  	// simulate first-boot/seeding, there is no existing snap state information
  3618  
  3619  	si := &snap.SideInfo{
  3620  		RealName: "foo-gadget",
  3621  		Revision: snap.R(34),
  3622  		SnapID:   "foo-id",
  3623  	}
  3624  	snaptest.MockSnapWithFiles(c, snapYaml, si, [][]string{
  3625  		{"meta/gadget.yaml", gadgetYaml},
  3626  	})
  3627  
  3628  	s.state.Lock()
  3629  
  3630  	t := s.state.NewTask("update-gadget-assets", "update gadget")
  3631  	t.Set("snap-setup", &snapstate.SnapSetup{
  3632  		SideInfo: si,
  3633  		Type:     snap.TypeGadget,
  3634  	})
  3635  	chg := s.state.NewChange("dummy", "...")
  3636  	chg.AddTask(t)
  3637  
  3638  	s.state.Unlock()
  3639  
  3640  	for i := 0; i < 6; i++ {
  3641  		s.se.Ensure()
  3642  		s.se.Wait()
  3643  	}
  3644  
  3645  	s.state.Lock()
  3646  	defer s.state.Unlock()
  3647  	c.Assert(chg.IsReady(), Equals, true)
  3648  	c.Check(chg.Err(), IsNil)
  3649  	c.Check(t.Status(), Equals, state.DoneStatus)
  3650  	rollbackDir := filepath.Join(dirs.SnapRollbackDir, "foo-gadget")
  3651  	c.Check(osutil.IsDirectory(rollbackDir), Equals, false)
  3652  	c.Check(s.restartRequests, HasLen, 0)
  3653  }
  3654  
  3655  func (s *deviceMgrSuite) TestUpdateGadgetOnCoreBadGadgetYaml(c *C) {
  3656  	restore := devicestate.MockGadgetUpdate(func(current, update gadget.GadgetData, path string) error {
  3657  		return errors.New("unexpected call")
  3658  	})
  3659  	defer restore()
  3660  	siCurrent := &snap.SideInfo{
  3661  		RealName: "foo-gadget",
  3662  		Revision: snap.R(33),
  3663  		SnapID:   "foo-id",
  3664  	}
  3665  	si := &snap.SideInfo{
  3666  		RealName: "foo-gadget",
  3667  		Revision: snap.R(34),
  3668  		SnapID:   "foo-id",
  3669  	}
  3670  	snaptest.MockSnapWithFiles(c, snapYaml, siCurrent, [][]string{
  3671  		{"meta/gadget.yaml", gadgetYaml},
  3672  	})
  3673  	// invalid gadget.yaml data
  3674  	snaptest.MockSnapWithFiles(c, snapYaml, si, [][]string{
  3675  		{"meta/gadget.yaml", "foobar"},
  3676  	})
  3677  
  3678  	s.state.Lock()
  3679  
  3680  	snapstate.Set(s.state, "foo-gadget", &snapstate.SnapState{
  3681  		SnapType: "gadget",
  3682  		Sequence: []*snap.SideInfo{siCurrent},
  3683  		Current:  siCurrent.Revision,
  3684  		Active:   true,
  3685  	})
  3686  
  3687  	t := s.state.NewTask("update-gadget-assets", "update gadget")
  3688  	t.Set("snap-setup", &snapstate.SnapSetup{
  3689  		SideInfo: si,
  3690  		Type:     snap.TypeGadget,
  3691  	})
  3692  	chg := s.state.NewChange("dummy", "...")
  3693  	chg.AddTask(t)
  3694  
  3695  	s.state.Unlock()
  3696  
  3697  	for i := 0; i < 6; i++ {
  3698  		s.se.Ensure()
  3699  		s.se.Wait()
  3700  	}
  3701  
  3702  	s.state.Lock()
  3703  	defer s.state.Unlock()
  3704  	c.Assert(chg.IsReady(), Equals, true)
  3705  	c.Check(chg.Err(), ErrorMatches, `(?s).*update gadget \(cannot read candidate gadget snap details: .*\).*`)
  3706  	c.Check(t.Status(), Equals, state.ErrorStatus)
  3707  	rollbackDir := filepath.Join(dirs.SnapRollbackDir, "foo-gadget")
  3708  	c.Check(osutil.IsDirectory(rollbackDir), Equals, false)
  3709  	c.Check(s.restartRequests, HasLen, 0)
  3710  }
  3711  
  3712  func (s *deviceMgrSuite) TestUpdateGadgetOnClassicErrorsOut(c *C) {
  3713  	restore := release.MockOnClassic(true)
  3714  	defer restore()
  3715  
  3716  	restore = devicestate.MockGadgetUpdate(func(current, update gadget.GadgetData, path string) error {
  3717  		return errors.New("unexpected call")
  3718  	})
  3719  	defer restore()
  3720  
  3721  	s.state.Lock()
  3722  
  3723  	t := s.state.NewTask("update-gadget-assets", "update gadget")
  3724  	chg := s.state.NewChange("dummy", "...")
  3725  	chg.AddTask(t)
  3726  
  3727  	s.state.Unlock()
  3728  
  3729  	// we cannot use "s.o.Settle()" here because this change has an
  3730  	// error which means that the settle will never converge
  3731  	for i := 0; i < 50; i++ {
  3732  		s.se.Ensure()
  3733  		s.se.Wait()
  3734  
  3735  		s.state.Lock()
  3736  		ready := chg.IsReady()
  3737  		s.state.Unlock()
  3738  		if ready {
  3739  			break
  3740  		}
  3741  	}
  3742  
  3743  	s.state.Lock()
  3744  	defer s.state.Unlock()
  3745  	c.Assert(chg.IsReady(), Equals, true)
  3746  	c.Check(chg.Err(), ErrorMatches, `(?s).*update gadget \(cannot run update gadget assets task on a classic system\).*`)
  3747  	c.Check(t.Status(), Equals, state.ErrorStatus)
  3748  }
  3749  
  3750  type mockUpdater struct{}
  3751  
  3752  func (m *mockUpdater) Backup() error { return nil }
  3753  
  3754  func (m *mockUpdater) Rollback() error { return nil }
  3755  
  3756  func (m *mockUpdater) Update() error { return nil }
  3757  
  3758  func (s *deviceMgrSuite) TestUpdateGadgetCallsToGadget(c *C) {
  3759  	siCurrent := &snap.SideInfo{
  3760  		RealName: "foo-gadget",
  3761  		Revision: snap.R(33),
  3762  		SnapID:   "foo-id",
  3763  	}
  3764  	si := &snap.SideInfo{
  3765  		RealName: "foo-gadget",
  3766  		Revision: snap.R(34),
  3767  		SnapID:   "foo-id",
  3768  	}
  3769  	var gadgetCurrentYaml = `
  3770  volumes:
  3771    pc:
  3772      bootloader: grub
  3773      structure:
  3774         - name: foo
  3775           size: 10M
  3776           type: bare
  3777           content:
  3778              - image: content.img
  3779  `
  3780  	var gadgetUpdateYaml = `
  3781  volumes:
  3782    pc:
  3783      bootloader: grub
  3784      structure:
  3785         - name: foo
  3786           size: 10M
  3787           type: bare
  3788           content:
  3789              - image: content.img
  3790           update:
  3791             edition: 2
  3792  `
  3793  	snaptest.MockSnapWithFiles(c, snapYaml, siCurrent, [][]string{
  3794  		{"meta/gadget.yaml", gadgetCurrentYaml},
  3795  		{"content.img", "some content"},
  3796  	})
  3797  	updateInfo := snaptest.MockSnapWithFiles(c, snapYaml, si, [][]string{
  3798  		{"meta/gadget.yaml", gadgetUpdateYaml},
  3799  		{"content.img", "updated content"},
  3800  	})
  3801  
  3802  	expectedRollbackDir := filepath.Join(dirs.SnapRollbackDir, "foo-gadget_34")
  3803  	updaterForStructureCalls := 0
  3804  	gadget.MockUpdaterForStructure(func(ps *gadget.LaidOutStructure, rootDir, rollbackDir string) (gadget.Updater, error) {
  3805  		updaterForStructureCalls++
  3806  
  3807  		c.Assert(ps.Name, Equals, "foo")
  3808  		c.Assert(rootDir, Equals, updateInfo.MountDir())
  3809  		c.Assert(filepath.Join(rootDir, "content.img"), testutil.FileEquals, "updated content")
  3810  		c.Assert(strings.HasPrefix(rollbackDir, expectedRollbackDir), Equals, true)
  3811  		c.Assert(osutil.IsDirectory(rollbackDir), Equals, true)
  3812  		return &mockUpdater{}, nil
  3813  	})
  3814  
  3815  	s.state.Lock()
  3816  
  3817  	snapstate.Set(s.state, "foo-gadget", &snapstate.SnapState{
  3818  		SnapType: "gadget",
  3819  		Sequence: []*snap.SideInfo{siCurrent},
  3820  		Current:  siCurrent.Revision,
  3821  		Active:   true,
  3822  	})
  3823  
  3824  	t := s.state.NewTask("update-gadget-assets", "update gadget")
  3825  	t.Set("snap-setup", &snapstate.SnapSetup{
  3826  		SideInfo: si,
  3827  		Type:     snap.TypeGadget,
  3828  	})
  3829  	chg := s.state.NewChange("dummy", "...")
  3830  	chg.AddTask(t)
  3831  
  3832  	s.state.Unlock()
  3833  
  3834  	for i := 0; i < 6; i++ {
  3835  		s.se.Ensure()
  3836  		s.se.Wait()
  3837  	}
  3838  
  3839  	s.state.Lock()
  3840  	defer s.state.Unlock()
  3841  	c.Assert(chg.IsReady(), Equals, true)
  3842  	c.Check(t.Status(), Equals, state.DoneStatus)
  3843  	c.Check(s.restartRequests, HasLen, 1)
  3844  	c.Check(updaterForStructureCalls, Equals, 1)
  3845  }
  3846  
  3847  func (s *deviceMgrSuite) TestCurrentAndUpdateInfo(c *C) {
  3848  	siCurrent := &snap.SideInfo{
  3849  		RealName: "foo-gadget",
  3850  		Revision: snap.R(33),
  3851  		SnapID:   "foo-id",
  3852  	}
  3853  	si := &snap.SideInfo{
  3854  		RealName: "foo-gadget",
  3855  		Revision: snap.R(34),
  3856  		SnapID:   "foo-id",
  3857  	}
  3858  
  3859  	s.state.Lock()
  3860  	defer s.state.Unlock()
  3861  
  3862  	snapsup := &snapstate.SnapSetup{
  3863  		SideInfo: si,
  3864  		Type:     snap.TypeGadget,
  3865  	}
  3866  
  3867  	current, update, err := devicestate.GadgetCurrentAndUpdate(s.state, snapsup)
  3868  	c.Assert(current, IsNil)
  3869  	c.Assert(update, IsNil)
  3870  	c.Assert(err, IsNil)
  3871  
  3872  	snapstate.Set(s.state, "foo-gadget", &snapstate.SnapState{
  3873  		SnapType: "gadget",
  3874  		Sequence: []*snap.SideInfo{siCurrent},
  3875  		Current:  siCurrent.Revision,
  3876  		Active:   true,
  3877  	})
  3878  
  3879  	// mock current first, but gadget.yaml is still missing
  3880  	ci := snaptest.MockSnapWithFiles(c, snapYaml, siCurrent, nil)
  3881  
  3882  	current, update, err = devicestate.GadgetCurrentAndUpdate(s.state, snapsup)
  3883  	c.Assert(current, IsNil)
  3884  	c.Assert(update, IsNil)
  3885  	c.Assert(err, ErrorMatches, "cannot read current gadget snap details: .*/33/meta/gadget.yaml: no such file or directory")
  3886  
  3887  	// drop gadget.yaml for current snap
  3888  	ioutil.WriteFile(filepath.Join(ci.MountDir(), "meta/gadget.yaml"), []byte(gadgetYaml), 0644)
  3889  
  3890  	// update missing snap.yaml
  3891  	current, update, err = devicestate.GadgetCurrentAndUpdate(s.state, snapsup)
  3892  	c.Assert(current, IsNil)
  3893  	c.Assert(update, IsNil)
  3894  	c.Assert(err, ErrorMatches, "cannot read candidate gadget snap details: cannot find installed snap .* .*/34/meta/snap.yaml")
  3895  
  3896  	ui := snaptest.MockSnapWithFiles(c, snapYaml, si, nil)
  3897  
  3898  	current, update, err = devicestate.GadgetCurrentAndUpdate(s.state, snapsup)
  3899  	c.Assert(current, IsNil)
  3900  	c.Assert(update, IsNil)
  3901  	c.Assert(err, ErrorMatches, "cannot read candidate gadget snap details: .*/34/meta/gadget.yaml: no such file or directory")
  3902  
  3903  	var updateGadgetYaml = `
  3904  volumes:
  3905    pc:
  3906      bootloader: grub
  3907      id: 123
  3908  `
  3909  
  3910  	// drop gadget.yaml for update snap
  3911  	ioutil.WriteFile(filepath.Join(ui.MountDir(), "meta/gadget.yaml"), []byte(updateGadgetYaml), 0644)
  3912  
  3913  	current, update, err = devicestate.GadgetCurrentAndUpdate(s.state, snapsup)
  3914  	c.Assert(err, IsNil)
  3915  	c.Assert(current, DeepEquals, &gadget.GadgetData{
  3916  		Info: &gadget.Info{
  3917  			Volumes: map[string]gadget.Volume{
  3918  				"pc": {
  3919  					Bootloader: "grub",
  3920  				},
  3921  			},
  3922  		},
  3923  		RootDir: ci.MountDir(),
  3924  	})
  3925  	c.Assert(update, DeepEquals, &gadget.GadgetData{
  3926  		Info: &gadget.Info{
  3927  			Volumes: map[string]gadget.Volume{
  3928  				"pc": {
  3929  					Bootloader: "grub",
  3930  					ID:         "123",
  3931  				},
  3932  			},
  3933  		},
  3934  		RootDir: ui.MountDir(),
  3935  	})
  3936  }
  3937  
  3938  func (s *deviceMgrSuite) TestGadgetUpdateBlocksWhenOtherTasks(c *C) {
  3939  	restore := release.MockOnClassic(true)
  3940  	defer restore()
  3941  
  3942  	s.state.Lock()
  3943  	defer s.state.Unlock()
  3944  
  3945  	tUpdate := s.state.NewTask("update-gadget-assets", "update gadget")
  3946  	t1 := s.state.NewTask("other-task-1", "other 1")
  3947  	t2 := s.state.NewTask("other-task-2", "other 2")
  3948  
  3949  	// no other running tasks, does not block
  3950  	c.Assert(devicestate.GadgetUpdateBlocked(tUpdate, nil), Equals, false)
  3951  
  3952  	// list of running tasks actually contains ones that are in the 'running' state
  3953  	t1.SetStatus(state.DoingStatus)
  3954  	t2.SetStatus(state.UndoingStatus)
  3955  	// block on any other running tasks
  3956  	c.Assert(devicestate.GadgetUpdateBlocked(tUpdate, []*state.Task{t1, t2}), Equals, true)
  3957  }
  3958  
  3959  func (s *deviceMgrSuite) TestGadgetUpdateBlocksOtherTasks(c *C) {
  3960  	restore := release.MockOnClassic(true)
  3961  	defer restore()
  3962  
  3963  	s.state.Lock()
  3964  	defer s.state.Unlock()
  3965  
  3966  	tUpdate := s.state.NewTask("update-gadget-assets", "update gadget")
  3967  	tUpdate.SetStatus(state.DoingStatus)
  3968  	t1 := s.state.NewTask("other-task-1", "other 1")
  3969  	t2 := s.state.NewTask("other-task-2", "other 2")
  3970  
  3971  	// block on any other running tasks
  3972  	c.Assert(devicestate.GadgetUpdateBlocked(t1, []*state.Task{tUpdate}), Equals, true)
  3973  	c.Assert(devicestate.GadgetUpdateBlocked(t2, []*state.Task{tUpdate}), Equals, true)
  3974  
  3975  	t2.SetStatus(state.UndoingStatus)
  3976  	// update-gadget should be the only running task, for the sake of
  3977  	// completeness pretend it's one of many running tasks
  3978  	c.Assert(devicestate.GadgetUpdateBlocked(t1, []*state.Task{tUpdate, t2}), Equals, true)
  3979  
  3980  	// not blocking without gadget update task
  3981  	c.Assert(devicestate.GadgetUpdateBlocked(t1, []*state.Task{t2}), Equals, false)
  3982  }