github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/daemon/api_base_d_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2014-2020 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 daemon
    21  
    22  import (
    23  	"context"
    24  	"crypto"
    25  	"fmt"
    26  	"io/ioutil"
    27  	"net/http"
    28  	"os"
    29  	"path/filepath"
    30  	"time"
    31  
    32  	"github.com/gorilla/mux"
    33  	"golang.org/x/crypto/sha3"
    34  	"gopkg.in/check.v1"
    35  	"gopkg.in/tomb.v2"
    36  
    37  	"github.com/snapcore/snapd/asserts"
    38  	"github.com/snapcore/snapd/asserts/assertstest"
    39  	"github.com/snapcore/snapd/asserts/sysdb"
    40  	"github.com/snapcore/snapd/dirs"
    41  	"github.com/snapcore/snapd/osutil"
    42  	"github.com/snapcore/snapd/overlord"
    43  	"github.com/snapcore/snapd/overlord/assertstate"
    44  	"github.com/snapcore/snapd/overlord/assertstate/assertstatetest"
    45  	"github.com/snapcore/snapd/overlord/auth"
    46  	"github.com/snapcore/snapd/overlord/devicestate"
    47  	"github.com/snapcore/snapd/overlord/devicestate/devicestatetest"
    48  	"github.com/snapcore/snapd/overlord/ifacestate"
    49  	"github.com/snapcore/snapd/overlord/snapstate"
    50  	"github.com/snapcore/snapd/overlord/state"
    51  	"github.com/snapcore/snapd/sandbox"
    52  	"github.com/snapcore/snapd/snap"
    53  	"github.com/snapcore/snapd/snap/snaptest"
    54  	"github.com/snapcore/snapd/store"
    55  	"github.com/snapcore/snapd/store/storetest"
    56  	"github.com/snapcore/snapd/systemd"
    57  	"github.com/snapcore/snapd/testutil"
    58  )
    59  
    60  // TODO: as we split api_test.go and move more tests to live in daemon_test
    61  // instead of daemon, split out functionality from APIBaseSuite
    62  // to only the relevant suite when possible
    63  
    64  type APIBaseSuite struct {
    65  	testutil.BaseTest
    66  
    67  	storetest.Store
    68  
    69  	rsnaps            []*snap.Info
    70  	err               error
    71  	vars              map[string]string
    72  	storeSearch       store.Search
    73  	suggestedCurrency string
    74  	d                 *Daemon
    75  	user              *auth.UserState
    76  	ctx               context.Context
    77  	currentSnaps      []*store.CurrentSnap
    78  	actions           []*store.SnapAction
    79  
    80  	restoreRelease func()
    81  
    82  	StoreSigning *assertstest.StoreStack
    83  	Brands       *assertstest.SigningAccounts
    84  
    85  	systemctlRestorer func()
    86  	SysctlBufs        [][]byte
    87  
    88  	connectivityResult     map[string]bool
    89  	loginUserStoreMacaroon string
    90  	loginUserDischarge     string
    91  
    92  	restoreSanitize func()
    93  }
    94  
    95  func (s *APIBaseSuite) PokeStateLock() {
    96  	// the store should be called without the state lock held. Try
    97  	// to acquire it.
    98  	st := s.d.overlord.State()
    99  	st.Lock()
   100  	st.Unlock()
   101  }
   102  
   103  func (s *APIBaseSuite) SnapInfo(ctx context.Context, spec store.SnapSpec, user *auth.UserState) (*snap.Info, error) {
   104  	s.PokeStateLock()
   105  	s.user = user
   106  	s.ctx = ctx
   107  	if len(s.rsnaps) > 0 {
   108  		return s.rsnaps[0], s.err
   109  	}
   110  	return nil, s.err
   111  }
   112  
   113  func (s *APIBaseSuite) Find(ctx context.Context, search *store.Search, user *auth.UserState) ([]*snap.Info, error) {
   114  	s.PokeStateLock()
   115  
   116  	s.storeSearch = *search
   117  	s.user = user
   118  	s.ctx = ctx
   119  
   120  	return s.rsnaps, s.err
   121  }
   122  
   123  func (s *APIBaseSuite) SnapAction(ctx context.Context, currentSnaps []*store.CurrentSnap, actions []*store.SnapAction, assertQuery store.AssertionQuery, user *auth.UserState, opts *store.RefreshOptions) ([]store.SnapActionResult, []store.AssertionResult, error) {
   124  	s.PokeStateLock()
   125  	if assertQuery != nil {
   126  		toResolve, toResolveSeq, err := assertQuery.ToResolve()
   127  		if err != nil {
   128  			return nil, nil, err
   129  		}
   130  		if len(toResolve) != 0 || len(toResolveSeq) != 0 {
   131  			panic("no assertion query support")
   132  		}
   133  	}
   134  
   135  	if ctx == nil {
   136  		panic("context required")
   137  	}
   138  	s.currentSnaps = currentSnaps
   139  	s.actions = actions
   140  	s.user = user
   141  
   142  	sars := make([]store.SnapActionResult, len(s.rsnaps))
   143  	for i, rsnap := range s.rsnaps {
   144  		sars[i] = store.SnapActionResult{Info: rsnap}
   145  	}
   146  	return sars, nil, s.err
   147  }
   148  
   149  func (s *APIBaseSuite) SuggestedCurrency() string {
   150  	s.PokeStateLock()
   151  
   152  	return s.suggestedCurrency
   153  }
   154  
   155  func (s *APIBaseSuite) ConnectivityCheck() (map[string]bool, error) {
   156  	s.PokeStateLock()
   157  
   158  	return s.connectivityResult, s.err
   159  }
   160  
   161  func (s *APIBaseSuite) LoginUser(username, password, otp string) (string, string, error) {
   162  	s.PokeStateLock()
   163  
   164  	return s.loginUserStoreMacaroon, s.loginUserDischarge, s.err
   165  }
   166  
   167  func (s *APIBaseSuite) muxVars(*http.Request) map[string]string {
   168  	return s.vars
   169  }
   170  
   171  func (s *APIBaseSuite) SetUpSuite(c *check.C) {
   172  	muxVars = s.muxVars
   173  	s.restoreRelease = sandbox.MockForceDevMode(false)
   174  	s.systemctlRestorer = systemd.MockSystemctl(s.systemctl)
   175  	s.restoreSanitize = snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})
   176  }
   177  
   178  func (s *APIBaseSuite) TearDownSuite(c *check.C) {
   179  	muxVars = nil
   180  	s.restoreRelease()
   181  	s.systemctlRestorer()
   182  	s.restoreSanitize()
   183  }
   184  
   185  func (s *APIBaseSuite) systemctl(args ...string) (buf []byte, err error) {
   186  	if len(s.SysctlBufs) > 0 {
   187  		buf, s.SysctlBufs = s.SysctlBufs[0], s.SysctlBufs[1:]
   188  	}
   189  	return buf, err
   190  }
   191  
   192  var (
   193  	BrandPrivKey, _ = assertstest.GenerateKey(752)
   194  )
   195  
   196  func (s *APIBaseSuite) SetUpTest(c *check.C) {
   197  	s.BaseTest.SetUpTest(c)
   198  
   199  	ctlcmds := testutil.MockCommand(c, "systemctl", "").Also("journalctl", "")
   200  	s.AddCleanup(ctlcmds.Restore)
   201  
   202  	s.SysctlBufs = nil
   203  
   204  	dirs.SetRootDir(c.MkDir())
   205  	err := os.MkdirAll(filepath.Dir(dirs.SnapStateFile), 0755)
   206  	restore := osutil.MockMountInfo("")
   207  	s.AddCleanup(restore)
   208  
   209  	c.Assert(err, check.IsNil)
   210  	c.Assert(os.MkdirAll(dirs.SnapMountDir, 0755), check.IsNil)
   211  	c.Assert(os.MkdirAll(dirs.SnapBlobDir, 0755), check.IsNil)
   212  
   213  	s.rsnaps = nil
   214  	s.suggestedCurrency = ""
   215  	s.storeSearch = store.Search{}
   216  	s.err = nil
   217  	s.vars = nil
   218  	s.user = nil
   219  	s.d = nil
   220  	s.currentSnaps = nil
   221  	s.actions = nil
   222  	// Disable real security backends for all API tests
   223  	s.AddCleanup(ifacestate.MockSecurityBackends(nil))
   224  
   225  	s.StoreSigning = assertstest.NewStoreStack("can0nical", nil)
   226  	s.AddCleanup(sysdb.InjectTrusted(s.StoreSigning.Trusted))
   227  
   228  	s.Brands = assertstest.NewSigningAccounts(s.StoreSigning)
   229  	s.Brands.Register("my-brand", BrandPrivKey, nil)
   230  
   231  	assertstateRefreshSnapDeclarations = nil
   232  	snapstateInstall = nil
   233  	snapstateInstallMany = nil
   234  	snapstateInstallPath = nil
   235  	snapstateRefreshCandidates = nil
   236  	snapstateRemoveMany = nil
   237  	snapstateRevert = nil
   238  	snapstateRevertToRevision = nil
   239  	snapstateTryPath = nil
   240  	snapstateUpdate = nil
   241  	snapstateUpdateMany = nil
   242  	snapstateSwitch = nil
   243  }
   244  
   245  func (s *APIBaseSuite) TearDownTest(c *check.C) {
   246  	s.d = nil
   247  	s.ctx = nil
   248  
   249  	unsafeReadSnapInfo = unsafeReadSnapInfoImpl
   250  	ensureStateSoon = ensureStateSoonImpl
   251  	dirs.SetRootDir("")
   252  
   253  	assertstateRefreshSnapDeclarations = assertstate.RefreshSnapDeclarations
   254  	snapstateInstall = snapstate.Install
   255  	snapstateInstallMany = snapstate.InstallMany
   256  	snapstateInstallPath = snapstate.InstallPath
   257  	snapstateRefreshCandidates = snapstate.RefreshCandidates
   258  	snapstateRemoveMany = snapstate.RemoveMany
   259  	snapstateRevert = snapstate.Revert
   260  	snapstateRevertToRevision = snapstate.RevertToRevision
   261  	snapstateTryPath = snapstate.TryPath
   262  	snapstateUpdate = snapstate.Update
   263  	snapstateUpdateMany = snapstate.UpdateMany
   264  	snapstateSwitch = snapstate.Switch
   265  
   266  	s.BaseTest.TearDownTest(c)
   267  }
   268  
   269  func (s *APIBaseSuite) MockModel(c *check.C, st *state.State, model *asserts.Model) {
   270  	// realistic model setup
   271  	if model == nil {
   272  		model = s.Brands.Model("can0nical", "pc", map[string]interface{}{
   273  			"architecture": "amd64",
   274  			"gadget":       "gadget",
   275  			"kernel":       "kernel",
   276  		})
   277  	}
   278  
   279  	snapstate.DeviceCtx = devicestate.DeviceCtx
   280  
   281  	assertstatetest.AddMany(st, model)
   282  
   283  	devicestatetest.SetDevice(st, &auth.DeviceState{
   284  		Brand:  model.BrandID(),
   285  		Model:  model.Model(),
   286  		Serial: "serialserial",
   287  	})
   288  }
   289  
   290  func (s *APIBaseSuite) DaemonWithStore(c *check.C, sto snapstate.StoreService) *Daemon {
   291  	if s.d != nil {
   292  		panic("called Daemon*() twice")
   293  	}
   294  	d, err := New()
   295  	c.Assert(err, check.IsNil)
   296  	d.addRoutes()
   297  
   298  	st := d.overlord.State()
   299  	// mark as already seeded
   300  	st.Lock()
   301  	st.Set("seeded", true)
   302  	st.Unlock()
   303  	c.Assert(d.overlord.StartUp(), check.IsNil)
   304  
   305  	st.Lock()
   306  	defer st.Unlock()
   307  	snapstate.ReplaceStore(st, sto)
   308  	// mark as already seeded
   309  	st.Set("seeded", true)
   310  	// registered
   311  	s.MockModel(c, st, nil)
   312  
   313  	// don't actually try to talk to the store on snapstate.Ensure
   314  	// needs doing after the call to devicestate.Manager (which
   315  	// happens in daemon.New via overlord.New)
   316  	snapstate.CanAutoRefresh = nil
   317  
   318  	s.d = d
   319  	return d
   320  }
   321  
   322  func (s *APIBaseSuite) ResetDaemon() {
   323  	s.d = nil
   324  }
   325  
   326  func (s *APIBaseSuite) Daemon(c *check.C) *Daemon {
   327  	return s.daemon(c)
   328  }
   329  
   330  // XXX this one will go away
   331  func (s *APIBaseSuite) daemon(c *check.C) *Daemon {
   332  	return s.DaemonWithStore(c, s)
   333  }
   334  
   335  // XXX this one will go away
   336  func (s *APIBaseSuite) daemonWithOverlordMock(c *check.C) *Daemon {
   337  	return s.DaemonWithOverlordMock(c)
   338  }
   339  
   340  func (s *APIBaseSuite) DaemonWithOverlordMock(c *check.C) *Daemon {
   341  	if s.d != nil {
   342  		panic("called Daemon*() twice")
   343  	}
   344  	d, err := New()
   345  	c.Assert(err, check.IsNil)
   346  	d.addRoutes()
   347  
   348  	o := overlord.Mock()
   349  	d.overlord = o
   350  
   351  	st := d.overlord.State()
   352  	// adds an assertion db
   353  	assertstate.Manager(st, o.TaskRunner())
   354  	st.Lock()
   355  	defer st.Unlock()
   356  	snapstate.ReplaceStore(st, s)
   357  
   358  	s.d = d
   359  	return d
   360  }
   361  
   362  type fakeSnapManager struct{}
   363  
   364  func newFakeSnapManager(st *state.State, runner *state.TaskRunner) *fakeSnapManager {
   365  	runner.AddHandler("fake-install-snap", func(t *state.Task, _ *tomb.Tomb) error {
   366  		return nil
   367  	}, nil)
   368  	runner.AddHandler("fake-install-snap-error", func(t *state.Task, _ *tomb.Tomb) error {
   369  		return fmt.Errorf("fake-install-snap-error errored")
   370  	}, nil)
   371  
   372  	return &fakeSnapManager{}
   373  }
   374  
   375  func (m *fakeSnapManager) Ensure() error {
   376  	return nil
   377  }
   378  
   379  // sanity
   380  var _ overlord.StateManager = (*fakeSnapManager)(nil)
   381  
   382  func (s *APIBaseSuite) daemonWithFakeSnapManager(c *check.C) *Daemon {
   383  	d := s.daemonWithOverlordMock(c)
   384  	st := d.overlord.State()
   385  	runner := d.overlord.TaskRunner()
   386  	d.overlord.AddManager(newFakeSnapManager(st, runner))
   387  	d.overlord.AddManager(runner)
   388  	c.Assert(d.overlord.StartUp(), check.IsNil)
   389  	return d
   390  }
   391  
   392  func (s *APIBaseSuite) waitTrivialChange(c *check.C, chg *state.Change) {
   393  	err := s.d.overlord.Settle(5 * time.Second)
   394  	c.Assert(err, check.IsNil)
   395  	c.Assert(chg.IsReady(), check.Equals, true)
   396  }
   397  
   398  func (s *APIBaseSuite) mkInstalledDesktopFile(c *check.C, name, content string) string {
   399  	df := filepath.Join(dirs.SnapDesktopFilesDir, name)
   400  	err := os.MkdirAll(filepath.Dir(df), 0755)
   401  	c.Assert(err, check.IsNil)
   402  	err = ioutil.WriteFile(df, []byte(content), 0644)
   403  	c.Assert(err, check.IsNil)
   404  	return df
   405  }
   406  
   407  // XXX this one will go away
   408  func (s *APIBaseSuite) mkInstalledInState(c *check.C, daemon *Daemon, instanceName, developer, version string, revision snap.Revision, active bool, extraYaml string) *snap.Info {
   409  	return s.MkInstalledInState(c, daemon, instanceName, developer, version, revision, active, extraYaml)
   410  }
   411  
   412  func (s *APIBaseSuite) MockSnap(c *check.C, yamlText string) *snap.Info {
   413  	if s.d == nil {
   414  		panic("call s.Daemon(c) etc in your test first")
   415  	}
   416  
   417  	snapInfo := snaptest.MockSnap(c, yamlText, &snap.SideInfo{Revision: snap.R(1)})
   418  
   419  	st := s.d.overlord.State()
   420  
   421  	st.Lock()
   422  	defer st.Unlock()
   423  
   424  	// Put a side info into the state
   425  	snapstate.Set(st, snapInfo.InstanceName(), &snapstate.SnapState{
   426  		Active: true,
   427  		Sequence: []*snap.SideInfo{
   428  			{
   429  				RealName: snapInfo.SnapName(),
   430  				Revision: snapInfo.Revision,
   431  				SnapID:   "ididid",
   432  			},
   433  		},
   434  		Current:  snapInfo.Revision,
   435  		SnapType: string(snapInfo.Type()),
   436  	})
   437  
   438  	// Put the snap into the interface repository
   439  	repo := s.d.overlord.InterfaceManager().Repository()
   440  	err := repo.AddSnap(snapInfo)
   441  	c.Assert(err, check.IsNil)
   442  	return snapInfo
   443  }
   444  
   445  func (s *APIBaseSuite) MkInstalledInState(c *check.C, daemon *Daemon, instanceName, developer, version string, revision snap.Revision, active bool, extraYaml string) *snap.Info {
   446  	snapName, instanceKey := snap.SplitInstanceName(instanceName)
   447  
   448  	if revision.Local() && developer != "" {
   449  		panic("not supported")
   450  	}
   451  
   452  	var snapID string
   453  	if revision.Store() {
   454  		snapID = snapName + "-id"
   455  	}
   456  	// Collect arguments into a snap.SideInfo structure
   457  	sideInfo := &snap.SideInfo{
   458  		SnapID:   snapID,
   459  		RealName: snapName,
   460  		Revision: revision,
   461  		Channel:  "stable",
   462  	}
   463  
   464  	// Collect other arguments into a yaml string
   465  	yamlText := fmt.Sprintf(`
   466  name: %s
   467  version: %s
   468  %s`, snapName, version, extraYaml)
   469  
   470  	// Mock the snap on disk
   471  	snapInfo := snaptest.MockSnapInstance(c, instanceName, yamlText, sideInfo)
   472  	if active {
   473  		dir, rev := filepath.Split(snapInfo.MountDir())
   474  		c.Assert(os.Symlink(rev, dir+"current"), check.IsNil)
   475  	}
   476  	c.Assert(snapInfo.InstanceName(), check.Equals, instanceName)
   477  
   478  	c.Assert(os.MkdirAll(snapInfo.DataDir(), 0755), check.IsNil)
   479  	metadir := filepath.Join(snapInfo.MountDir(), "meta")
   480  	guidir := filepath.Join(metadir, "gui")
   481  	c.Assert(os.MkdirAll(guidir, 0755), check.IsNil)
   482  	c.Check(ioutil.WriteFile(filepath.Join(guidir, "icon.svg"), []byte("yadda icon"), 0644), check.IsNil)
   483  
   484  	if daemon == nil {
   485  		return snapInfo
   486  	}
   487  	st := daemon.overlord.State()
   488  	st.Lock()
   489  	defer st.Unlock()
   490  
   491  	var snapst snapstate.SnapState
   492  	snapstate.Get(st, instanceName, &snapst)
   493  	snapst.Active = active
   494  	snapst.Sequence = append(snapst.Sequence, &snapInfo.SideInfo)
   495  	snapst.Current = snapInfo.SideInfo.Revision
   496  	snapst.TrackingChannel = "stable"
   497  	snapst.InstanceKey = instanceKey
   498  
   499  	snapstate.Set(st, instanceName, &snapst)
   500  
   501  	if developer == "" {
   502  		return snapInfo
   503  	}
   504  
   505  	devAcct := assertstest.NewAccount(s.StoreSigning, developer, map[string]interface{}{
   506  		"account-id": developer + "-id",
   507  	}, "")
   508  
   509  	snapInfo.Publisher = snap.StoreAccount{
   510  		ID:          devAcct.AccountID(),
   511  		Username:    devAcct.Username(),
   512  		DisplayName: devAcct.DisplayName(),
   513  		Validation:  devAcct.Validation(),
   514  	}
   515  
   516  	snapDecl, err := s.StoreSigning.Sign(asserts.SnapDeclarationType, map[string]interface{}{
   517  		"series":       "16",
   518  		"snap-id":      snapID,
   519  		"snap-name":    snapName,
   520  		"publisher-id": devAcct.AccountID(),
   521  		"timestamp":    time.Now().Format(time.RFC3339),
   522  	}, nil, "")
   523  	c.Assert(err, check.IsNil)
   524  
   525  	content, err := ioutil.ReadFile(snapInfo.MountFile())
   526  	c.Assert(err, check.IsNil)
   527  	h := sha3.Sum384(content)
   528  	dgst, err := asserts.EncodeDigest(crypto.SHA3_384, h[:])
   529  	c.Assert(err, check.IsNil)
   530  	snapRev, err := s.StoreSigning.Sign(asserts.SnapRevisionType, map[string]interface{}{
   531  		"snap-sha3-384": string(dgst),
   532  		"snap-size":     "999",
   533  		"snap-id":       snapID,
   534  		"snap-revision": fmt.Sprintf("%s", revision),
   535  		"developer-id":  devAcct.AccountID(),
   536  		"timestamp":     time.Now().Format(time.RFC3339),
   537  	}, nil, "")
   538  	c.Assert(err, check.IsNil)
   539  
   540  	assertstatetest.AddMany(st, s.StoreSigning.StoreAccountKey(""), devAcct, snapDecl, snapRev)
   541  
   542  	return snapInfo
   543  }
   544  
   545  func handlerCommand(c *check.C, d *Daemon, req *http.Request) (cmd *Command, vars map[string]string) {
   546  	m := &mux.RouteMatch{}
   547  	if !d.router.Match(req, m) {
   548  		c.Fatalf("no command for URL %q", req.URL)
   549  	}
   550  	cmd, ok := m.Handler.(*Command)
   551  	if !ok {
   552  		c.Fatalf("no command for URL %q", req.URL)
   553  	}
   554  	return cmd, m.Vars
   555  }
   556  
   557  func (s *APIBaseSuite) CheckGetOnly(c *check.C, req *http.Request) {
   558  	if s.d == nil {
   559  		panic("call s.Daemon(c) etc in your test first")
   560  	}
   561  
   562  	cmd, _ := handlerCommand(c, s.d, req)
   563  	c.Check(cmd.POST, check.IsNil)
   564  	c.Check(cmd.PUT, check.IsNil)
   565  	c.Check(cmd.GET, check.NotNil)
   566  }
   567  
   568  func (s *APIBaseSuite) Req(c *check.C, req *http.Request, u *auth.UserState) Response {
   569  	if s.d == nil {
   570  		panic("call s.Daemon(c) etc in your test first")
   571  	}
   572  
   573  	cmd, vars := handlerCommand(c, s.d, req)
   574  	s.vars = vars
   575  	var f ResponseFunc
   576  	switch req.Method {
   577  	case "GET":
   578  		f = cmd.GET
   579  	case "POST":
   580  		f = cmd.POST
   581  	case "PUT":
   582  		f = cmd.PUT
   583  	default:
   584  		c.Fatalf("unsupported HTTP method %q", req.Method)
   585  	}
   586  	if f == nil {
   587  		c.Fatalf("no support for %q for %q", req.Method, req.URL)
   588  	}
   589  	return f(cmd, req, u)
   590  }
   591  
   592  func (s *APIBaseSuite) ServeHTTP(c *check.C, w http.ResponseWriter, req *http.Request) {
   593  	if s.d == nil {
   594  		panic("call s.Daemon(c) etc in your test first")
   595  	}
   596  
   597  	cmd, vars := handlerCommand(c, s.d, req)
   598  	s.vars = vars
   599  
   600  	cmd.ServeHTTP(w, req)
   601  }
   602  
   603  func (s *APIBaseSuite) SimulateConflict(name string) {
   604  	if s.d == nil {
   605  		panic("call s.Daemon(c) etc in your test first")
   606  	}
   607  
   608  	o := s.d.overlord
   609  	st := o.State()
   610  	st.Lock()
   611  	defer st.Unlock()
   612  	t := st.NewTask("link-snap", "...")
   613  	snapsup := &snapstate.SnapSetup{SideInfo: &snap.SideInfo{
   614  		RealName: name,
   615  	}}
   616  	t.Set("snap-setup", snapsup)
   617  	chg := st.NewChange("manip", "...")
   618  	chg.AddTask(t)
   619  }