github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/overlord/ifacestate/ifacestate_test.go (about)

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