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