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 }