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