gitee.com/mysnapcore/mysnapd@v0.1.0/interfaces/builtin/all_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 builtin_test
    21  
    22  import (
    23  	"fmt"
    24  	"reflect"
    25  	"strings"
    26  
    27  	. "gopkg.in/check.v1"
    28  
    29  	"gitee.com/mysnapcore/mysnapd/interfaces"
    30  	"gitee.com/mysnapcore/mysnapd/interfaces/apparmor"
    31  	"gitee.com/mysnapcore/mysnapd/interfaces/builtin"
    32  	"gitee.com/mysnapcore/mysnapd/interfaces/dbus"
    33  	"gitee.com/mysnapcore/mysnapd/interfaces/ifacetest"
    34  	"gitee.com/mysnapcore/mysnapd/interfaces/kmod"
    35  	"gitee.com/mysnapcore/mysnapd/interfaces/mount"
    36  	"gitee.com/mysnapcore/mysnapd/interfaces/polkit"
    37  	"gitee.com/mysnapcore/mysnapd/interfaces/seccomp"
    38  	"gitee.com/mysnapcore/mysnapd/interfaces/systemd"
    39  	"gitee.com/mysnapcore/mysnapd/interfaces/udev"
    40  	"gitee.com/mysnapcore/mysnapd/snap"
    41  	"gitee.com/mysnapcore/mysnapd/snap/snaptest"
    42  )
    43  
    44  type AllSuite struct{}
    45  
    46  var _ = Suite(&AllSuite{})
    47  
    48  // This section contains a list of *valid* defines that represent methods that
    49  // backends recognize and call. They are in individual interfaces as each snapd
    50  // interface can define a subset that it is interested in providing. Those are,
    51  // essentially, the only valid methods that a snapd interface can have, apart
    52  // from what is defined in the Interface golang interface.
    53  type apparmorDefiner1 interface {
    54  	AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error
    55  }
    56  type apparmorDefiner2 interface {
    57  	AppArmorConnectedSlot(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error
    58  }
    59  type apparmorDefiner3 interface {
    60  	AppArmorPermanentPlug(spec *apparmor.Specification, plug *snap.PlugInfo) error
    61  }
    62  type apparmorDefiner4 interface {
    63  	AppArmorPermanentSlot(spec *apparmor.Specification, slot *snap.SlotInfo) error
    64  }
    65  
    66  type dbusDefiner1 interface {
    67  	DBusConnectedPlug(spec *dbus.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error
    68  }
    69  type dbusDefiner2 interface {
    70  	DBusConnectedSlot(spec *dbus.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error
    71  }
    72  type dbusDefiner3 interface {
    73  	DBusPermanentPlug(spec *dbus.Specification, plug *snap.PlugInfo) error
    74  }
    75  type dbusDefiner4 interface {
    76  	DBusPermanentSlot(spec *dbus.Specification, slot *snap.SlotInfo) error
    77  }
    78  
    79  type kmodDefiner1 interface {
    80  	KModConnectedPlug(spec *kmod.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error
    81  }
    82  type kmodDefiner2 interface {
    83  	KModConnectedSlot(spec *kmod.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error
    84  }
    85  type kmodDefiner3 interface {
    86  	KModPermanentPlug(spec *kmod.Specification, plug *snap.PlugInfo) error
    87  }
    88  type kmodDefiner4 interface {
    89  	KModPermanentSlot(spec *kmod.Specification, slot *snap.SlotInfo) error
    90  }
    91  
    92  type mountDefiner1 interface {
    93  	MountConnectedPlug(spec *mount.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error
    94  }
    95  type mountDefiner2 interface {
    96  	MountConnectedSlot(spec *mount.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error
    97  }
    98  type mountDefiner3 interface {
    99  	MountPermanentPlug(spec *mount.Specification, plug *snap.PlugInfo) error
   100  }
   101  type mountDefiner4 interface {
   102  	MountPermanentSlot(spec *mount.Specification, slot *snap.SlotInfo) error
   103  }
   104  
   105  type polkitDefiner1 interface {
   106  	PolkitConnectedPlug(spec *polkit.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error
   107  }
   108  type polkitDefiner2 interface {
   109  	PolkitConnectedSlot(spec *polkit.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error
   110  }
   111  type polkitDefiner3 interface {
   112  	PolkitPermanentPlug(spec *polkit.Specification, plug *snap.PlugInfo) error
   113  }
   114  type polkitDefiner4 interface {
   115  	PolkitPermanentSlot(spec *polkit.Specification, slot *snap.SlotInfo) error
   116  }
   117  
   118  type seccompDefiner1 interface {
   119  	SecCompConnectedPlug(spec *seccomp.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error
   120  }
   121  type seccompDefiner2 interface {
   122  	SecCompConnectedSlot(spec *seccomp.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error
   123  }
   124  type seccompDefiner3 interface {
   125  	SecCompPermanentPlug(spec *seccomp.Specification, plug *snap.PlugInfo) error
   126  }
   127  type seccompDefiner4 interface {
   128  	SecCompPermanentSlot(spec *seccomp.Specification, slot *snap.SlotInfo) error
   129  }
   130  
   131  type systemdDefiner1 interface {
   132  	SystemdConnectedPlug(spec *systemd.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error
   133  }
   134  type systemdDefiner2 interface {
   135  	SystemdConnectedSlot(spec *systemd.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error
   136  }
   137  type systemdDefiner3 interface {
   138  	SystemdPermanentPlug(spec *systemd.Specification, plug *snap.PlugInfo) error
   139  }
   140  type systemdDefiner4 interface {
   141  	SystemdPermanentSlot(spec *systemd.Specification, slot *snap.SlotInfo) error
   142  }
   143  
   144  type udevDefiner1 interface {
   145  	UDevConnectedPlug(spec *udev.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error
   146  }
   147  type udevDefiner2 interface {
   148  	UDevConnectedSlot(spec *udev.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error
   149  }
   150  type udevDefiner3 interface {
   151  	UDevPermanentPlug(spec *udev.Specification, plug *snap.PlugInfo) error
   152  }
   153  type udevDefiner4 interface {
   154  	UDevPermanentSlot(spec *udev.Specification, slot *snap.SlotInfo) error
   155  }
   156  
   157  // allGoodDefiners contains all valid specification definers for all known backends.
   158  var allGoodDefiners = []reflect.Type{
   159  	// apparmor
   160  	reflect.TypeOf((*apparmorDefiner1)(nil)).Elem(),
   161  	reflect.TypeOf((*apparmorDefiner2)(nil)).Elem(),
   162  	reflect.TypeOf((*apparmorDefiner3)(nil)).Elem(),
   163  	reflect.TypeOf((*apparmorDefiner4)(nil)).Elem(),
   164  	// dbus
   165  	reflect.TypeOf((*dbusDefiner1)(nil)).Elem(),
   166  	reflect.TypeOf((*dbusDefiner2)(nil)).Elem(),
   167  	reflect.TypeOf((*dbusDefiner3)(nil)).Elem(),
   168  	reflect.TypeOf((*dbusDefiner4)(nil)).Elem(),
   169  	// kmod
   170  	reflect.TypeOf((*kmodDefiner1)(nil)).Elem(),
   171  	reflect.TypeOf((*kmodDefiner2)(nil)).Elem(),
   172  	reflect.TypeOf((*kmodDefiner3)(nil)).Elem(),
   173  	reflect.TypeOf((*kmodDefiner4)(nil)).Elem(),
   174  	// mount
   175  	reflect.TypeOf((*mountDefiner1)(nil)).Elem(),
   176  	reflect.TypeOf((*mountDefiner2)(nil)).Elem(),
   177  	reflect.TypeOf((*mountDefiner3)(nil)).Elem(),
   178  	reflect.TypeOf((*mountDefiner4)(nil)).Elem(),
   179  	// polkit
   180  	reflect.TypeOf((*polkitDefiner1)(nil)).Elem(),
   181  	reflect.TypeOf((*polkitDefiner2)(nil)).Elem(),
   182  	reflect.TypeOf((*polkitDefiner3)(nil)).Elem(),
   183  	reflect.TypeOf((*polkitDefiner4)(nil)).Elem(),
   184  	// seccomp
   185  	reflect.TypeOf((*seccompDefiner1)(nil)).Elem(),
   186  	reflect.TypeOf((*seccompDefiner2)(nil)).Elem(),
   187  	reflect.TypeOf((*seccompDefiner3)(nil)).Elem(),
   188  	reflect.TypeOf((*seccompDefiner4)(nil)).Elem(),
   189  	// systemd
   190  	reflect.TypeOf((*systemdDefiner1)(nil)).Elem(),
   191  	reflect.TypeOf((*systemdDefiner2)(nil)).Elem(),
   192  	reflect.TypeOf((*systemdDefiner3)(nil)).Elem(),
   193  	reflect.TypeOf((*systemdDefiner4)(nil)).Elem(),
   194  	// udev
   195  	reflect.TypeOf((*udevDefiner1)(nil)).Elem(),
   196  	reflect.TypeOf((*udevDefiner2)(nil)).Elem(),
   197  	reflect.TypeOf((*udevDefiner3)(nil)).Elem(),
   198  	reflect.TypeOf((*udevDefiner4)(nil)).Elem(),
   199  }
   200  
   201  // Check that each interface defines at least one definer method we recognize.
   202  func (s *AllSuite) TestEachInterfaceImplementsSomeBackendMethods(c *C) {
   203  	for _, iface := range builtin.Interfaces() {
   204  		bogus := true
   205  		for _, definer := range allGoodDefiners {
   206  			if reflect.TypeOf(iface).Implements(definer) {
   207  				bogus = false
   208  				break
   209  			}
   210  		}
   211  		c.Assert(bogus, Equals, false,
   212  			Commentf("interface %q does not implement any specification methods", iface.Name()))
   213  	}
   214  }
   215  
   216  // pre-specification snippet functions
   217  type snippetDefiner1 interface {
   218  	PermanentPlugSnippet(plug *snap.PlugInfo, sec interfaces.SecuritySystem) error
   219  }
   220  type snippetDefiner2 interface {
   221  	PermanentSlotSnippet(slot *snap.SlotInfo, sec interfaces.SecuritySystem) error
   222  }
   223  
   224  // old auto-connect function
   225  type legacyAutoConnect interface {
   226  	LegacyAutoConnect() bool
   227  }
   228  
   229  type oldSanitizePlug1 interface {
   230  	SanitizePlug(plug *snap.PlugInfo) error
   231  }
   232  type oldSanitizeSlot1 interface {
   233  	SanitizeSlot(slot *snap.SlotInfo) error
   234  }
   235  
   236  // allBadDefiners contains all old/unused specification definers for all known backends.
   237  var allBadDefiners = []reflect.Type{
   238  	// pre-specification snippet methods
   239  	reflect.TypeOf((*snippetDefiner1)(nil)).Elem(),
   240  	reflect.TypeOf((*snippetDefiner2)(nil)).Elem(),
   241  	// old auto-connect function
   242  	reflect.TypeOf((*legacyAutoConnect)(nil)).Elem(),
   243  	// old sanitize methods
   244  	reflect.TypeOf((*oldSanitizePlug1)(nil)).Elem(),
   245  	reflect.TypeOf((*oldSanitizeSlot1)(nil)).Elem(),
   246  }
   247  
   248  // Check that no interface defines older definer methods.
   249  func (s *AllSuite) TestNoInterfaceImplementsOldBackendMethods(c *C) {
   250  	for _, iface := range builtin.Interfaces() {
   251  		for _, definer := range allBadDefiners {
   252  			c.Assert(reflect.TypeOf(iface).Implements(definer), Equals, false,
   253  				Commentf("interface %q implements old/unused methods %s", iface.Name(), definer))
   254  		}
   255  	}
   256  }
   257  
   258  func (s *AllSuite) TestRegisterIface(c *C) {
   259  	restore := builtin.MockInterfaces(nil)
   260  	defer restore()
   261  
   262  	// Registering an interface works correctly.
   263  	iface := &ifacetest.TestInterface{InterfaceName: "foo"}
   264  	builtin.RegisterIface(iface)
   265  	c.Assert(builtin.Interface("foo"), DeepEquals, iface)
   266  
   267  	// Duplicates are detected.
   268  	c.Assert(func() { builtin.RegisterIface(iface) }, PanicMatches, `cannot register duplicate interface "foo"`)
   269  }
   270  
   271  const testConsumerInvalidSlotNameYaml = `
   272  name: consumer
   273  version: 0
   274  slots:
   275   ttyS5:
   276    interface: iface
   277  apps:
   278      app:
   279          slots: [iface]
   280  `
   281  
   282  const testConsumerInvalidPlugNameYaml = `
   283  name: consumer
   284  version: 0
   285  plugs:
   286   ttyS3:
   287    interface: iface
   288  apps:
   289      app:
   290          plugs: [iface]
   291  `
   292  
   293  const testInvalidSlotInterfaceYaml = `
   294  name: testsnap
   295  version: 0
   296  slots:
   297   iface:
   298    interface: iface
   299  apps:
   300      app:
   301          slots: [iface]
   302          activates-on: [iface]
   303  hooks:
   304      install:
   305          slots: [iface]
   306  `
   307  
   308  const testInvalidPlugInterfaceYaml = `
   309  name: testsnap
   310  version: 0
   311  plugs:
   312   iface:
   313    interface: iface
   314  apps:
   315      app:
   316          plugs: [iface]
   317  hooks:
   318      install:
   319          plugs: [iface]
   320  `
   321  
   322  func (s *AllSuite) TestSanitizeErrorsOnInvalidSlotNames(c *C) {
   323  	restore := builtin.MockInterfaces(map[string]interfaces.Interface{
   324  		"iface": &ifacetest.TestInterface{InterfaceName: "iface"},
   325  	})
   326  	defer restore()
   327  
   328  	snapInfo := snaptest.MockInvalidInfo(c, testConsumerInvalidSlotNameYaml, nil)
   329  	snap.SanitizePlugsSlots(snapInfo)
   330  	c.Assert(snapInfo.BadInterfaces, HasLen, 1)
   331  	c.Check(snap.BadInterfacesSummary(snapInfo), Matches, `snap "consumer" has bad plugs or slots: ttyS5 \(invalid slot name: "ttyS5"\)`)
   332  }
   333  
   334  func (s *AllSuite) TestSanitizeErrorsOnInvalidPlugNames(c *C) {
   335  	restore := builtin.MockInterfaces(map[string]interfaces.Interface{
   336  		"iface": &ifacetest.TestInterface{InterfaceName: "iface"},
   337  	})
   338  	defer restore()
   339  
   340  	snapInfo := snaptest.MockInvalidInfo(c, testConsumerInvalidPlugNameYaml, nil)
   341  	snap.SanitizePlugsSlots(snapInfo)
   342  	c.Assert(snapInfo.BadInterfaces, HasLen, 1)
   343  	c.Check(snap.BadInterfacesSummary(snapInfo), Matches, `snap "consumer" has bad plugs or slots: ttyS3 \(invalid plug name: "ttyS3"\)`)
   344  }
   345  
   346  func (s *AllSuite) TestSanitizeErrorsOnInvalidSlotInterface(c *C) {
   347  	snapInfo := snaptest.MockInvalidInfo(c, testInvalidSlotInterfaceYaml, nil)
   348  	c.Check(snapInfo.Apps["app"].Slots, HasLen, 1)
   349  	c.Check(snapInfo.Apps["app"].ActivatesOn, HasLen, 1)
   350  	c.Check(snapInfo.Hooks["install"].Slots, HasLen, 1)
   351  	c.Check(snapInfo.Slots, HasLen, 1)
   352  	snap.SanitizePlugsSlots(snapInfo)
   353  	c.Check(snapInfo.Apps["app"].Slots, HasLen, 0)
   354  	c.Check(snapInfo.Apps["app"].ActivatesOn, HasLen, 0)
   355  	c.Check(snapInfo.Hooks["install"].Slots, HasLen, 0)
   356  	c.Assert(snapInfo.BadInterfaces, HasLen, 1)
   357  	c.Check(snap.BadInterfacesSummary(snapInfo), Matches, `snap "testsnap" has bad plugs or slots: iface \(unknown interface "iface"\)`)
   358  	c.Assert(snapInfo.Plugs, HasLen, 0)
   359  	c.Assert(snapInfo.Slots, HasLen, 0)
   360  }
   361  
   362  func (s *AllSuite) TestSanitizeErrorsOnInvalidPlugInterface(c *C) {
   363  	snapInfo := snaptest.MockInfo(c, testInvalidPlugInterfaceYaml, nil)
   364  	c.Check(snapInfo.Apps["app"].Plugs, HasLen, 1)
   365  	c.Check(snapInfo.Hooks["install"].Plugs, HasLen, 1)
   366  	c.Assert(snapInfo.Plugs, HasLen, 1)
   367  	snap.SanitizePlugsSlots(snapInfo)
   368  	c.Assert(snapInfo.Apps["app"].Plugs, HasLen, 0)
   369  	c.Check(snapInfo.Hooks["install"].Plugs, HasLen, 0)
   370  	c.Assert(snapInfo.BadInterfaces, HasLen, 1)
   371  	c.Assert(snap.BadInterfacesSummary(snapInfo), Matches, `snap "testsnap" has bad plugs or slots: iface \(unknown interface "iface"\)`)
   372  	c.Assert(snapInfo.Plugs, HasLen, 0)
   373  	c.Assert(snapInfo.Slots, HasLen, 0)
   374  }
   375  
   376  func (s *AllSuite) TestUnexpectedSpecSignatures(c *C) {
   377  	type funcSig struct {
   378  		name string
   379  		in   []string
   380  		out  []string
   381  	}
   382  	var sigs []funcSig
   383  
   384  	// All the valid signatures from all the specification definers from all the backends.
   385  	for _, backend := range []string{"AppArmor", "SecComp", "UDev", "DBus", "Systemd", "KMod", "Polkit"} {
   386  		backendLower := strings.ToLower(backend)
   387  		sigs = append(sigs, []funcSig{{
   388  			name: fmt.Sprintf("%sPermanentPlug", backend),
   389  			in: []string{
   390  				fmt.Sprintf("*%s.Specification", backendLower),
   391  				"*snap.PlugInfo",
   392  			},
   393  			out: []string{"error"},
   394  		}, {
   395  			name: fmt.Sprintf("%sPermanentSlot", backend),
   396  			in: []string{
   397  				fmt.Sprintf("*%s.Specification", backendLower),
   398  				"*snap.SlotInfo",
   399  			},
   400  			out: []string{"error"},
   401  		}, {
   402  			name: fmt.Sprintf("%sConnectedPlug", backend),
   403  			in: []string{
   404  				fmt.Sprintf("*%s.Specification", backendLower),
   405  				"*interfaces.ConnectedPlug",
   406  				"*interfaces.ConnectedSlot",
   407  			},
   408  			out: []string{"error"},
   409  		}, {
   410  			name: fmt.Sprintf("%sConnectedSlot", backend),
   411  			in: []string{
   412  				fmt.Sprintf("*%s.Specification", backendLower),
   413  				"*interfaces.ConnectedPlug",
   414  				"*interfaces.ConnectedSlot",
   415  			},
   416  			out: []string{"error"},
   417  		}}...)
   418  	}
   419  	for _, iface := range builtin.Interfaces() {
   420  		ifaceVal := reflect.ValueOf(iface)
   421  		ifaceType := ifaceVal.Type()
   422  		for _, sig := range sigs {
   423  			meth, ok := ifaceType.MethodByName(sig.name)
   424  			if !ok {
   425  				// all specification methods are optional.
   426  				continue
   427  			}
   428  			methType := meth.Type
   429  			// Check that the signature matches our expectation. The -1 and +1 below is for the receiver type.
   430  			c.Assert(methType.NumIn()-1, Equals, len(sig.in), Commentf("expected %s's %s method to take %d arguments", ifaceType, meth.Name, len(sig.in)))
   431  			for i, expected := range sig.in {
   432  				c.Assert(methType.In(i+1).String(), Equals, expected, Commentf("expected %s's %s method %dth argument type to be different", ifaceType, meth.Name, i))
   433  			}
   434  			c.Assert(methType.NumOut(), Equals, len(sig.out), Commentf("expected %s's %s method to return %d values", ifaceType, meth.Name, len(sig.out)))
   435  			for i, expected := range sig.out {
   436  				c.Assert(methType.Out(i).String(), Equals, expected, Commentf("expected %s's %s method %dth return value type to be different", ifaceType, meth.Name, i))
   437  			}
   438  		}
   439  	}
   440  }