gitee.com/mysnapcore/mysnapd@v0.1.0/interfaces/builtin/raw_volume_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2019 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  	"strings"
    25  
    26  	. "gopkg.in/check.v1"
    27  
    28  	"gitee.com/mysnapcore/mysnapd/dirs"
    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/udev"
    33  	"gitee.com/mysnapcore/mysnapd/snap"
    34  	"gitee.com/mysnapcore/mysnapd/snap/snaptest"
    35  	"gitee.com/mysnapcore/mysnapd/testutil"
    36  )
    37  
    38  type rawVolumeInterfaceSuite struct {
    39  	testutil.BaseTest
    40  	iface interfaces.Interface
    41  
    42  	// OS snap
    43  	testSlot1Info *snap.SlotInfo
    44  	testSlot2Info *snap.SlotInfo
    45  	testSlot3Info *snap.SlotInfo
    46  
    47  	// Gadget snap
    48  	testUDev1     *interfaces.ConnectedSlot
    49  	testUDev1Info *snap.SlotInfo
    50  	testUDev2     *interfaces.ConnectedSlot
    51  	testUDev2Info *snap.SlotInfo
    52  	testUDev3     *interfaces.ConnectedSlot
    53  	testUDev3Info *snap.SlotInfo
    54  
    55  	testUDevBadValue1     *interfaces.ConnectedSlot
    56  	testUDevBadValue1Info *snap.SlotInfo
    57  
    58  	// Consuming snap
    59  	testPlugPart1     *interfaces.ConnectedPlug
    60  	testPlugPart1Info *snap.PlugInfo
    61  	testPlugPart2     *interfaces.ConnectedPlug
    62  	testPlugPart2Info *snap.PlugInfo
    63  	testPlugPart3     *interfaces.ConnectedPlug
    64  	testPlugPart3Info *snap.PlugInfo
    65  }
    66  
    67  var _ = Suite(&rawVolumeInterfaceSuite{
    68  	iface: builtin.MustInterface("raw-volume"),
    69  })
    70  
    71  func (s *rawVolumeInterfaceSuite) SetUpTest(c *C) {
    72  	// Mock for OS snap
    73  	osSnapInfo := snaptest.MockInfo(c, `
    74  name: core
    75  version: 0
    76  type: os
    77  slots:
    78    test-part-1:
    79      interface: raw-volume
    80      path: /dev/vda1
    81    test-part-2:
    82      interface: raw-volume
    83      path: /dev/mmcblk0p1
    84    test-part-3:
    85      interface: raw-volume
    86      path: /dev/i2o/hda1
    87  `, nil)
    88  	s.testSlot1Info = osSnapInfo.Slots["test-part-1"]
    89  	s.testSlot2Info = osSnapInfo.Slots["test-part-2"]
    90  	s.testSlot3Info = osSnapInfo.Slots["test-part-3"]
    91  
    92  	// Mock for Gadget snap
    93  	gadgetSnapInfo := snaptest.MockInfo(c, `
    94  name: some-device
    95  version: 0
    96  type: gadget
    97  slots:
    98    test-udev-1:
    99      interface: raw-volume
   100      path: /dev/vda1
   101    test-udev-2:
   102      interface: raw-volume
   103      path: /dev/mmcblk0p1
   104    test-udev-3:
   105      interface: raw-volume
   106      path: /dev/i2o/hda1
   107    test-udev-bad-value-1:
   108      interface: raw-volume
   109      path: /dev/vda0
   110  `, nil)
   111  	s.testUDev1Info = gadgetSnapInfo.Slots["test-udev-1"]
   112  	s.testUDev1 = interfaces.NewConnectedSlot(s.testUDev1Info, nil, nil)
   113  	s.testUDev2Info = gadgetSnapInfo.Slots["test-udev-2"]
   114  	s.testUDev2 = interfaces.NewConnectedSlot(s.testUDev2Info, nil, nil)
   115  	s.testUDev3Info = gadgetSnapInfo.Slots["test-udev-3"]
   116  	s.testUDev3 = interfaces.NewConnectedSlot(s.testUDev3Info, nil, nil)
   117  	s.testUDevBadValue1Info = gadgetSnapInfo.Slots["test-udev-bad-value-1"]
   118  	s.testUDevBadValue1 = interfaces.NewConnectedSlot(s.testUDevBadValue1Info, nil, nil)
   119  
   120  	// Mock for consumer snaps
   121  	consumingSnapInfo := snaptest.MockInfo(c, `
   122  name: client-snap
   123  version: 0
   124  plugs:
   125    plug-for-part-1:
   126      interface: raw-volume
   127    plug-for-part-2:
   128      interface: raw-volume
   129    plug-for-part-3:
   130      interface: raw-volume
   131  apps:
   132    app-accessing-1-part:
   133      command: foo
   134      plugs:
   135      - plug-for-part-1
   136    app-accessing-2-part:
   137      command: foo
   138      plugs:
   139      - plug-for-part-2
   140    app-accessing-3-part:
   141      command: foo
   142      plugs:
   143      - plug-for-part-3
   144  `, nil)
   145  	s.testPlugPart1Info = consumingSnapInfo.Plugs["plug-for-part-1"]
   146  	s.testPlugPart1 = interfaces.NewConnectedPlug(s.testPlugPart1Info, nil, nil)
   147  	s.testPlugPart2Info = consumingSnapInfo.Plugs["plug-for-part-2"]
   148  	s.testPlugPart2 = interfaces.NewConnectedPlug(s.testPlugPart2Info, nil, nil)
   149  	s.testPlugPart3Info = consumingSnapInfo.Plugs["plug-for-part-3"]
   150  	s.testPlugPart3 = interfaces.NewConnectedPlug(s.testPlugPart3Info, nil, nil)
   151  }
   152  
   153  func (s *rawVolumeInterfaceSuite) TestName(c *C) {
   154  	c.Assert(s.iface.Name(), Equals, "raw-volume")
   155  }
   156  
   157  func (s *rawVolumeInterfaceSuite) TestSanitizeCoreSnapSlot(c *C) {
   158  	c.Assert(interfaces.BeforePrepareSlot(s.iface, s.testSlot1Info), IsNil)
   159  	c.Assert(interfaces.BeforePrepareSlot(s.iface, s.testSlot2Info), IsNil)
   160  	c.Assert(interfaces.BeforePrepareSlot(s.iface, s.testSlot3Info), IsNil)
   161  }
   162  
   163  func (s *rawVolumeInterfaceSuite) TestSanitizeGadgetSnapSlot(c *C) {
   164  	c.Assert(interfaces.BeforePrepareSlot(s.iface, s.testUDev1Info), IsNil)
   165  }
   166  
   167  func (s *rawVolumeInterfaceSuite) TestSanitizeBadGadgetSnapSlot(c *C) {
   168  	c.Assert(interfaces.BeforePrepareSlot(s.iface, s.testUDevBadValue1Info), ErrorMatches, `slot "some-device:test-udev-bad-value-1" path attribute must be a valid device node`)
   169  }
   170  
   171  func (s *rawVolumeInterfaceSuite) TestSanitizeSlotHappy(c *C) {
   172  	const mockSnapYaml = `name: raw-volume-slot-snap
   173  type: gadget
   174  version: 1.0
   175  slots:
   176    raw-volume:
   177      path: $t
   178  `
   179  
   180  	var testCases = []struct {
   181  		input string
   182  	}{
   183  		{`/dev/hda1`},
   184  		{`/dev/hda63`},
   185  		{`/dev/hdb42`},
   186  		{`/dev/hdt63`},
   187  		{`/dev/sda1`},
   188  		{`/dev/sda15`},
   189  		{`/dev/sdb8`},
   190  		{`/dev/sdc14`},
   191  		{`/dev/sdde10`},
   192  		{`/dev/sdiv15`},
   193  		{`/dev/i2o/hda1`},
   194  		{`/dev/i2o/hda15`},
   195  		{`/dev/i2o/hdb8`},
   196  		{`/dev/i2o/hdc10`},
   197  		{`/dev/i2o/hdde10`},
   198  		{`/dev/i2o/hddx15`},
   199  		{`/dev/mmcblk0p1`},
   200  		{`/dev/mmcblk0p63`},
   201  		{`/dev/mmcblk12p42`},
   202  		{`/dev/mmcblk999p63`},
   203  		{`/dev/nvme0p1`},
   204  		{`/dev/nvme0p63`},
   205  		{`/dev/nvme12p42`},
   206  		{`/dev/nvme99p63`},
   207  		{`/dev/nvme0n1p1`},
   208  		{`/dev/nvme0n1p63`},
   209  		{`/dev/nvme12n34p42`},
   210  		{`/dev/nvme99n63p63`},
   211  		{`/dev/vda1`},
   212  		{`/dev/vda63`},
   213  		{`/dev/vdb42`},
   214  		{`/dev/vdz63`},
   215  	}
   216  
   217  	for _, t := range testCases {
   218  		yml := strings.Replace(mockSnapYaml, "$t", t.input, -1)
   219  		info := snaptest.MockInfo(c, yml, nil)
   220  		slot := info.Slots["raw-volume"]
   221  
   222  		c.Check(interfaces.BeforePrepareSlot(s.iface, slot), IsNil, Commentf("unexpected error for %q", t.input))
   223  	}
   224  }
   225  
   226  func (s *rawVolumeInterfaceSuite) TestSanitizeSlotUnhappy(c *C) {
   227  	const mockSnapYaml = `name: raw-volume-slot-snap
   228  type: gadget
   229  version: 1.0
   230  slots:
   231    raw-volume:
   232      path: $t
   233  `
   234  
   235  	var testCases = []struct {
   236  		input string
   237  	}{
   238  		{`/dev/hda0`},
   239  		{`/dev/hdt64`},
   240  		{`/dev/hdu1`},
   241  		{`/dev/sda0`},
   242  		{`/dev/sdiv16`},
   243  		{`/dev/sdiw1`},
   244  		{`/dev/i2o/hda0`},
   245  		{`/dev/i20/hddx16`},
   246  		{`/dev/i2o/hddy1`},
   247  		{`/dev/mmcblk0p0`},
   248  		{`/dev/mmcblk999p64`},
   249  		{`/dev/mmcblk1000p1`},
   250  		{`/dev/nvme0p0`},
   251  		{`/dev/nvme99p64`},
   252  		{`/dev/nvme100p1`},
   253  		{`/dev/nvme0n0p1`},
   254  		{`/dev/nvme99n64p1`},
   255  		{`/dev/nvme100n1p1`},
   256  		{`/dev/vda0`},
   257  		{`/dev/vdz64`},
   258  		{`/dev/vdaa1`},
   259  	}
   260  
   261  	for _, t := range testCases {
   262  		yml := strings.Replace(mockSnapYaml, "$t", t.input, -1)
   263  		info := snaptest.MockInfo(c, yml, nil)
   264  		slot := info.Slots["raw-volume"]
   265  
   266  		c.Check(interfaces.BeforePrepareSlot(s.iface, slot), ErrorMatches, `slot "raw-volume-slot-snap:raw-volume" path attribute must be a valid device node`, Commentf("unexpected error for %q", t.input))
   267  	}
   268  }
   269  
   270  func (s *rawVolumeInterfaceSuite) TestSanitizeSlotUnclean(c *C) {
   271  	const mockSnapYaml = `name: raw-volume-slot-snap
   272  type: gadget
   273  version: 1.0
   274  slots:
   275    raw-volume:
   276      path: $t
   277  `
   278  
   279  	var testCases = []struct {
   280  		input string
   281  	}{
   282  		{`/dev/hda1/.`},
   283  		{`/dev/i2o/`},
   284  		{`/dev/./././mmcblk0p1////`},
   285  	}
   286  
   287  	for _, t := range testCases {
   288  		yml := strings.Replace(mockSnapYaml, "$t", t.input, -1)
   289  		info := snaptest.MockInfo(c, yml, nil)
   290  		slot := info.Slots["raw-volume"]
   291  		c.Check(interfaces.BeforePrepareSlot(s.iface, slot), ErrorMatches, `cannot use slot "raw-volume-slot-snap:raw-volume" path ".*": try ".*"`, Commentf("unexpected error for %q", t.input))
   292  	}
   293  }
   294  
   295  func (s *rawVolumeInterfaceSuite) TestUDevSpec(c *C) {
   296  	spec := &udev.Specification{}
   297  	c.Assert(spec.AddConnectedPlug(s.iface, s.testPlugPart1, s.testUDev1), IsNil)
   298  	c.Assert(spec.Snippets(), HasLen, 2)
   299  	c.Assert(spec.Snippets()[0], Equals, `# raw-volume
   300  KERNEL=="vda1", TAG+="snap_client-snap_app-accessing-1-part"`)
   301  	c.Assert(spec.Snippets(), testutil.Contains, fmt.Sprintf(`TAG=="snap_client-snap_app-accessing-1-part", RUN+="%v/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-1-part $devpath $major:$minor"`, dirs.DistroLibExecDir))
   302  
   303  	spec = &udev.Specification{}
   304  	c.Assert(spec.AddConnectedPlug(s.iface, s.testPlugPart2, s.testUDev2), IsNil)
   305  	c.Assert(spec.Snippets(), HasLen, 2)
   306  	c.Assert(spec.Snippets()[0], Equals, `# raw-volume
   307  KERNEL=="mmcblk0p1", TAG+="snap_client-snap_app-accessing-2-part"`)
   308  
   309  	spec = &udev.Specification{}
   310  	c.Assert(spec.AddConnectedPlug(s.iface, s.testPlugPart3, s.testUDev3), IsNil)
   311  	c.Assert(spec.Snippets(), HasLen, 2)
   312  	c.Assert(spec.Snippets()[0], Equals, `# raw-volume
   313  KERNEL=="i2o/hda1", TAG+="snap_client-snap_app-accessing-3-part"`)
   314  }
   315  
   316  func (s *rawVolumeInterfaceSuite) TestAppArmorSpec(c *C) {
   317  	spec := &apparmor.Specification{}
   318  	c.Assert(spec.AddConnectedPlug(s.iface, s.testPlugPart1, s.testUDev1), IsNil)
   319  	c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.client-snap.app-accessing-1-part"})
   320  	c.Assert(spec.SnippetForTag("snap.client-snap.app-accessing-1-part"), testutil.Contains, `/dev/vda1 rw,`)
   321  	c.Assert(spec.SnippetForTag("snap.client-snap.app-accessing-1-part"), testutil.Contains, `capability sys_admin,`)
   322  
   323  	spec = &apparmor.Specification{}
   324  	c.Assert(spec.AddConnectedPlug(s.iface, s.testPlugPart2, s.testUDev2), IsNil)
   325  	c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.client-snap.app-accessing-2-part"})
   326  	c.Assert(spec.SnippetForTag("snap.client-snap.app-accessing-2-part"), testutil.Contains, `/dev/mmcblk0p1 rw,`)
   327  	c.Assert(spec.SnippetForTag("snap.client-snap.app-accessing-2-part"), testutil.Contains, `capability sys_admin,`)
   328  
   329  	spec = &apparmor.Specification{}
   330  	c.Assert(spec.AddConnectedPlug(s.iface, s.testPlugPart3, s.testUDev3), IsNil)
   331  	c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.client-snap.app-accessing-3-part"})
   332  	c.Assert(spec.SnippetForTag("snap.client-snap.app-accessing-3-part"), testutil.Contains, `/dev/i2o/hda1 rw,`)
   333  	c.Assert(spec.SnippetForTag("snap.client-snap.app-accessing-3-part"), testutil.Contains, `capability sys_admin,`)
   334  }
   335  
   336  func (s *rawVolumeInterfaceSuite) TestStaticInfo(c *C) {
   337  	si := interfaces.StaticInfoOf(s.iface)
   338  	c.Assert(si.ImplicitOnCore, Equals, false)
   339  	c.Assert(si.ImplicitOnClassic, Equals, false)
   340  	c.Assert(si.Summary, Equals, `allows read/write access to specific disk partition`)
   341  	c.Assert(si.BaseDeclarationSlots, testutil.Contains, "raw-volume")
   342  }
   343  
   344  func (s *rawVolumeInterfaceSuite) TestAutoConnect(c *C) {
   345  	c.Check(s.iface.AutoConnect(nil, nil), Equals, true)
   346  }
   347  
   348  func (s *rawVolumeInterfaceSuite) TestInterfaces(c *C) {
   349  	c.Check(builtin.Interfaces(), testutil.DeepContains, s.iface)
   350  }