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