github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/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 activates-on: [iface] 284 hooks: 285 install: 286 slots: [iface] 287 ` 288 289 const testInvalidPlugInterfaceYaml = ` 290 name: testsnap 291 version: 0 292 plugs: 293 iface: 294 interface: iface 295 apps: 296 app: 297 plugs: [iface] 298 hooks: 299 install: 300 plugs: [iface] 301 ` 302 303 func (s *AllSuite) TestSanitizeErrorsOnInvalidSlotNames(c *C) { 304 restore := builtin.MockInterfaces(map[string]interfaces.Interface{ 305 "iface": &ifacetest.TestInterface{InterfaceName: "iface"}, 306 }) 307 defer restore() 308 309 snapInfo := snaptest.MockInvalidInfo(c, testConsumerInvalidSlotNameYaml, nil) 310 snap.SanitizePlugsSlots(snapInfo) 311 c.Assert(snapInfo.BadInterfaces, HasLen, 1) 312 c.Check(snap.BadInterfacesSummary(snapInfo), Matches, `snap "consumer" has bad plugs or slots: ttyS5 \(invalid slot name: "ttyS5"\)`) 313 } 314 315 func (s *AllSuite) TestSanitizeErrorsOnInvalidPlugNames(c *C) { 316 restore := builtin.MockInterfaces(map[string]interfaces.Interface{ 317 "iface": &ifacetest.TestInterface{InterfaceName: "iface"}, 318 }) 319 defer restore() 320 321 snapInfo := snaptest.MockInvalidInfo(c, testConsumerInvalidPlugNameYaml, nil) 322 snap.SanitizePlugsSlots(snapInfo) 323 c.Assert(snapInfo.BadInterfaces, HasLen, 1) 324 c.Check(snap.BadInterfacesSummary(snapInfo), Matches, `snap "consumer" has bad plugs or slots: ttyS3 \(invalid plug name: "ttyS3"\)`) 325 } 326 327 func (s *AllSuite) TestSanitizeErrorsOnInvalidSlotInterface(c *C) { 328 snapInfo := snaptest.MockInvalidInfo(c, testInvalidSlotInterfaceYaml, nil) 329 c.Check(snapInfo.Apps["app"].Slots, HasLen, 1) 330 c.Check(snapInfo.Apps["app"].ActivatesOn, HasLen, 1) 331 c.Check(snapInfo.Hooks["install"].Slots, HasLen, 1) 332 c.Check(snapInfo.Slots, HasLen, 1) 333 snap.SanitizePlugsSlots(snapInfo) 334 c.Check(snapInfo.Apps["app"].Slots, HasLen, 0) 335 c.Check(snapInfo.Apps["app"].ActivatesOn, HasLen, 0) 336 c.Check(snapInfo.Hooks["install"].Slots, HasLen, 0) 337 c.Assert(snapInfo.BadInterfaces, HasLen, 1) 338 c.Check(snap.BadInterfacesSummary(snapInfo), Matches, `snap "testsnap" has bad plugs or slots: iface \(unknown interface "iface"\)`) 339 c.Assert(snapInfo.Plugs, HasLen, 0) 340 c.Assert(snapInfo.Slots, HasLen, 0) 341 } 342 343 func (s *AllSuite) TestSanitizeErrorsOnInvalidPlugInterface(c *C) { 344 snapInfo := snaptest.MockInfo(c, testInvalidPlugInterfaceYaml, nil) 345 c.Check(snapInfo.Apps["app"].Plugs, HasLen, 1) 346 c.Check(snapInfo.Hooks["install"].Plugs, HasLen, 1) 347 c.Assert(snapInfo.Plugs, HasLen, 1) 348 snap.SanitizePlugsSlots(snapInfo) 349 c.Assert(snapInfo.Apps["app"].Plugs, HasLen, 0) 350 c.Check(snapInfo.Hooks["install"].Plugs, HasLen, 0) 351 c.Assert(snapInfo.BadInterfaces, HasLen, 1) 352 c.Assert(snap.BadInterfacesSummary(snapInfo), Matches, `snap "testsnap" has bad plugs or slots: iface \(unknown interface "iface"\)`) 353 c.Assert(snapInfo.Plugs, HasLen, 0) 354 c.Assert(snapInfo.Slots, HasLen, 0) 355 } 356 357 func (s *AllSuite) TestUnexpectedSpecSignatures(c *C) { 358 type funcSig struct { 359 name string 360 in []string 361 out []string 362 } 363 var sigs []funcSig 364 365 // All the valid signatures from all the specification definers from all the backends. 366 for _, backend := range []string{"AppArmor", "SecComp", "UDev", "DBus", "Systemd", "KMod"} { 367 backendLower := strings.ToLower(backend) 368 sigs = append(sigs, []funcSig{{ 369 name: fmt.Sprintf("%sPermanentPlug", backend), 370 in: []string{ 371 fmt.Sprintf("*%s.Specification", backendLower), 372 "*snap.PlugInfo", 373 }, 374 out: []string{"error"}, 375 }, { 376 name: fmt.Sprintf("%sPermanentSlot", backend), 377 in: []string{ 378 fmt.Sprintf("*%s.Specification", backendLower), 379 "*snap.SlotInfo", 380 }, 381 out: []string{"error"}, 382 }, { 383 name: fmt.Sprintf("%sConnectedPlug", backend), 384 in: []string{ 385 fmt.Sprintf("*%s.Specification", backendLower), 386 "*interfaces.ConnectedPlug", 387 "*interfaces.ConnectedSlot", 388 }, 389 out: []string{"error"}, 390 }, { 391 name: fmt.Sprintf("%sConnectedSlot", backend), 392 in: []string{ 393 fmt.Sprintf("*%s.Specification", backendLower), 394 "*interfaces.ConnectedPlug", 395 "*interfaces.ConnectedSlot", 396 }, 397 out: []string{"error"}, 398 }}...) 399 } 400 for _, iface := range builtin.Interfaces() { 401 ifaceVal := reflect.ValueOf(iface) 402 ifaceType := ifaceVal.Type() 403 for _, sig := range sigs { 404 meth, ok := ifaceType.MethodByName(sig.name) 405 if !ok { 406 // all specification methods are optional. 407 continue 408 } 409 methType := meth.Type 410 // Check that the signature matches our expectation. The -1 and +1 below is for the receiver type. 411 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))) 412 for i, expected := range sig.in { 413 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)) 414 } 415 c.Assert(methType.NumOut(), Equals, len(sig.out), Commentf("expected %s's %s method to return %d values", ifaceType, meth.Name, len(sig.out))) 416 for i, expected := range sig.out { 417 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)) 418 } 419 } 420 } 421 }