github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/gadget/validate_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2019-2020 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 gadget_test 21 22 import ( 23 "bytes" 24 "fmt" 25 "os" 26 "path/filepath" 27 "regexp" 28 "strings" 29 30 . "gopkg.in/check.v1" 31 32 "github.com/snapcore/snapd/gadget" 33 ) 34 35 type validateGadgetTestSuite struct { 36 dir string 37 } 38 39 var _ = Suite(&validateGadgetTestSuite{}) 40 41 func (s *validateGadgetTestSuite) SetUpTest(c *C) { 42 s.dir = c.MkDir() 43 } 44 45 func (s *validateGadgetTestSuite) TestRuleValidateStructureReservedLabels(c *C) { 46 for _, tc := range []struct { 47 role, label, err string 48 model gadget.Model 49 }{ 50 {label: "ubuntu-seed", err: `label "ubuntu-seed" is reserved`}, 51 {label: "ubuntu-data", err: `label "ubuntu-data" is reserved`}, 52 // ok to allow hybrid 20-ready devices 53 {label: "ubuntu-boot"}, 54 {label: "ubuntu-save"}, 55 // reserved only if seed present/expected 56 {label: "ubuntu-boot", err: `label "ubuntu-boot" is reserved`, model: uc20Mod}, 57 {label: "ubuntu-save", err: `label "ubuntu-save" is reserved`, model: uc20Mod}, 58 // these are ok 59 {role: "system-boot", label: "ubuntu-boot"}, 60 {label: "random-ubuntu-label"}, 61 } { 62 gi := &gadget.Info{ 63 Volumes: map[string]*gadget.Volume{ 64 "vol0": { 65 Structure: []gadget.VolumeStructure{{ 66 Type: "21686148-6449-6E6F-744E-656564454649", 67 Role: tc.role, 68 Filesystem: "ext4", 69 Label: tc.label, 70 Size: 10 * 1024, 71 }}, 72 }, 73 }, 74 } 75 err := gadget.Validate(gi, tc.model, nil) 76 if tc.err == "" { 77 c.Check(err, IsNil) 78 } else { 79 c.Check(err, ErrorMatches, ".*: "+tc.err) 80 } 81 } 82 83 } 84 85 // rolesYaml produces gadget metadata with volumes with structure withs the given 86 // role if data, seed or save are != "-", and with their label set to the value 87 func rolesYaml(c *C, data, seed, save string) *gadget.Info { 88 h := `volumes: 89 roles: 90 schema: gpt 91 bootloader: grub 92 structure: 93 ` 94 if data != "-" { 95 h += ` 96 - name: data 97 size: 1G 98 type: 83,0FC63DAF-8483-4772-8E79-3D69D8477DE4 99 role: system-data 100 ` 101 if data != "" { 102 h += fmt.Sprintf(" filesystem-label: %s\n", data) 103 } 104 } 105 if seed != "-" { 106 h += ` 107 - name: seed 108 size: 1G 109 type: EF,C12A7328-F81F-11D2-BA4B-00A0C93EC93B 110 role: system-seed 111 ` 112 if seed != "" { 113 h += fmt.Sprintf(" filesystem-label: %s\n", seed) 114 } 115 } 116 117 if save != "-" { 118 h += ` 119 - name: save 120 size: 32M 121 type: 83,0FC63DAF-8483-4772-8E79-3D69D8477DE4 122 role: system-save 123 ` 124 if save != "" { 125 h += fmt.Sprintf(" filesystem-label: %s\n", save) 126 } 127 } 128 129 gi, err := gadget.InfoFromGadgetYaml([]byte(h), nil) 130 c.Assert(err, IsNil) 131 return gi 132 } 133 134 func (s *validateGadgetTestSuite) TestVolumeRulesConsistencyNoModel(c *C) { 135 ginfo := func(hasSeed bool, dataLabel string) *gadget.Info { 136 seed := "-" 137 if hasSeed { 138 seed = "" 139 } 140 return rolesYaml(c, dataLabel, seed, "-") 141 } 142 143 for i, tc := range []struct { 144 gi *gadget.Info 145 err string 146 }{ 147 148 // we have the system-seed role 149 {ginfo(true, ""), ""}, 150 {ginfo(true, "foobar"), `.* must have an implicit label or "ubuntu-data", not "foobar"`}, 151 {ginfo(true, "writable"), `.* must have an implicit label or "ubuntu-data", not "writable"`}, 152 {ginfo(true, "ubuntu-data"), ""}, 153 154 // we don't have the system-seed role (old systems) 155 {ginfo(false, ""), ""}, // implicit is ok 156 {ginfo(false, "foobar"), `.* must have an implicit label or "writable", not "foobar"`}, 157 {ginfo(false, "writable"), ""}, 158 {ginfo(false, "ubuntu-data"), `.* must have an implicit label or "writable", not "ubuntu-data"`}, 159 } { 160 c.Logf("tc: %d %v", i, tc.gi.Volumes["roles"]) 161 162 err := gadget.Validate(tc.gi, nil, nil) 163 if tc.err != "" { 164 c.Check(err, ErrorMatches, tc.err) 165 } else { 166 c.Check(err, IsNil) 167 } 168 } 169 170 // Check system-seed label 171 for i, tc := range []struct { 172 l string 173 err string 174 }{ 175 {"", ""}, 176 {"foobar", `system-seed structure must have an implicit label or "ubuntu-seed", not "foobar"`}, 177 {"ubuntu-seed", ""}, 178 } { 179 c.Logf("tc: %v %v", i, tc.l) 180 gi := rolesYaml(c, "", tc.l, "-") 181 err := gadget.Validate(gi, nil, nil) 182 if tc.err != "" { 183 c.Check(err, ErrorMatches, tc.err) 184 } else { 185 c.Check(err, IsNil) 186 } 187 } 188 189 // Check system-seed without system-data 190 gi := rolesYaml(c, "-", "-", "-") 191 err := gadget.Validate(gi, nil, nil) 192 c.Assert(err, IsNil) 193 gi = rolesYaml(c, "-", "", "-") 194 err = gadget.Validate(gi, nil, nil) 195 c.Assert(err, ErrorMatches, "the system-seed role requires system-data to be defined") 196 197 // Check system-save 198 giWithSave := rolesYaml(c, "", "", "") 199 err = gadget.Validate(giWithSave, nil, nil) 200 c.Assert(err, IsNil) 201 // use illegal label on system-save 202 giWithSave = rolesYaml(c, "", "", "foo") 203 err = gadget.Validate(giWithSave, nil, nil) 204 c.Assert(err, ErrorMatches, `system-save structure must have an implicit label or "ubuntu-save", not "foo"`) 205 // complains when save is alone 206 giWithSave = rolesYaml(c, "", "-", "") 207 err = gadget.Validate(giWithSave, nil, nil) 208 c.Assert(err, ErrorMatches, "model does not support the system-save role") 209 giWithSave = rolesYaml(c, "-", "-", "") 210 err = gadget.Validate(giWithSave, nil, nil) 211 c.Assert(err, ErrorMatches, "model does not support the system-save role") 212 } 213 214 func (s *validateGadgetTestSuite) TestValidateConsistencyWithoutModelCharateristics(c *C) { 215 for i, tc := range []struct { 216 role string 217 label string 218 err string 219 }{ 220 // when model is nil, the system-seed role and ubuntu-data label on the 221 // system-data structure should be consistent 222 {"system-seed", "", ""}, 223 {"system-seed", "writable", `must have an implicit label or "ubuntu-data", not "writable"`}, 224 {"system-seed", "ubuntu-data", ""}, 225 {"", "", ""}, 226 {"", "writable", ""}, 227 {"", "ubuntu-data", `must have an implicit label or "writable", not "ubuntu-data"`}, 228 } { 229 c.Logf("tc: %v %v %v", i, tc.role, tc.label) 230 b := &bytes.Buffer{} 231 232 fmt.Fprintf(b, ` 233 volumes: 234 pc: 235 bootloader: grub 236 schema: mbr 237 structure:`) 238 239 if tc.role == "system-seed" { 240 fmt.Fprintf(b, ` 241 - name: Recovery 242 size: 10M 243 type: 83 244 role: system-seed`) 245 } 246 247 fmt.Fprintf(b, ` 248 - name: Data 249 size: 10M 250 type: 83 251 role: system-data 252 filesystem-label: %s`, tc.label) 253 254 makeSizedFile(c, filepath.Join(s.dir, "meta/gadget.yaml"), 0, b.Bytes()) 255 ginfo, err := gadget.ReadInfo(s.dir, nil) 256 c.Assert(err, IsNil) 257 err = gadget.Validate(ginfo, nil, nil) 258 if tc.err != "" { 259 c.Check(err, ErrorMatches, ".* "+tc.err) 260 } else { 261 c.Check(err, IsNil) 262 } 263 } 264 } 265 266 func (s *validateGadgetTestSuite) TestValidateConsistencyWithModelCharateristics(c *C) { 267 bloader := ` 268 volumes: 269 pc: 270 bootloader: grub 271 schema: mbr 272 structure:` 273 274 for i, tc := range []struct { 275 addSeed bool 276 dataLabel string 277 noData bool 278 requireSeed bool 279 addSave bool 280 saveLabel string 281 err string 282 }{ 283 {addSeed: true, noData: true, requireSeed: true, err: "the system-seed role requires system-data to be defined"}, 284 {addSeed: true, noData: true, requireSeed: false, err: "the system-seed role requires system-data to be defined"}, 285 {addSeed: true, requireSeed: true}, 286 {addSeed: true, err: `model does not support the system-seed role`}, 287 {addSeed: true, dataLabel: "writable", requireSeed: true, 288 err: `system-data structure must have an implicit label or "ubuntu-data", not "writable"`}, 289 {addSeed: true, dataLabel: "writable", 290 err: `model does not support the system-seed role`}, 291 {addSeed: true, dataLabel: "ubuntu-data", requireSeed: true}, 292 {addSeed: true, dataLabel: "ubuntu-data", 293 err: `model does not support the system-seed role`}, 294 {dataLabel: "writable", requireSeed: true, 295 err: `model requires system-seed structure, but none was found`}, 296 {dataLabel: "writable"}, 297 {dataLabel: "ubuntu-data", requireSeed: true, 298 err: `model requires system-seed structure, but none was found`}, 299 {dataLabel: "ubuntu-data", err: `system-data structure must have an implicit label or "writable", not "ubuntu-data"`}, 300 {addSave: true, requireSeed: true, addSeed: true}, 301 {addSave: true, err: `model does not support the system-save role`}, 302 {addSeed: true, requireSeed: true, addSave: true, saveLabel: "foo", 303 err: `system-save structure must have an implicit label or "ubuntu-save", not "foo"`}, 304 } { 305 c.Logf("tc: %v %v %v %v", i, tc.addSeed, tc.dataLabel, tc.requireSeed) 306 b := &bytes.Buffer{} 307 308 fmt.Fprintf(b, bloader) 309 if tc.addSeed { 310 fmt.Fprintf(b, ` 311 - name: Recovery 312 size: 10M 313 type: 83 314 role: system-seed`) 315 } 316 317 if !tc.noData { 318 fmt.Fprintf(b, ` 319 - name: Data 320 size: 10M 321 type: 83 322 role: system-data 323 filesystem-label: %s`, tc.dataLabel) 324 } 325 326 if tc.addSave { 327 fmt.Fprintf(b, ` 328 - name: Save 329 size: 10M 330 type: 83 331 role: system-save`) 332 if tc.saveLabel != "" { 333 fmt.Fprintf(b, ` 334 filesystem-label: %s`, tc.saveLabel) 335 336 } 337 } 338 339 makeSizedFile(c, filepath.Join(s.dir, "meta/gadget.yaml"), 0, b.Bytes()) 340 341 mod := &modelCharateristics{ 342 classic: false, 343 systemSeed: tc.requireSeed, 344 } 345 ginfo, err := gadget.ReadInfo(s.dir, mod) 346 c.Assert(err, IsNil) 347 err = gadget.Validate(ginfo, mod, nil) 348 if tc.err != "" { 349 c.Check(err, ErrorMatches, tc.err) 350 } else { 351 c.Check(err, IsNil) 352 } 353 } 354 355 // test error with no volumes 356 makeSizedFile(c, filepath.Join(s.dir, "meta/gadget.yaml"), 0, []byte(bloader)) 357 358 mod := &modelCharateristics{ 359 systemSeed: true, 360 } 361 362 ginfo, err := gadget.ReadInfo(s.dir, mod) 363 c.Assert(err, IsNil) 364 err = gadget.Validate(ginfo, mod, nil) 365 c.Assert(err, ErrorMatches, "model requires system-seed partition, but no system-seed or system-data partition found") 366 } 367 368 func (s *validateGadgetTestSuite) TestValidateSystemRoleSplitAcrossVolumes(c *C) { 369 // ATM this is not allowed for UC20 370 const gadgetYamlContent = ` 371 volumes: 372 pc1: 373 # bootloader configuration is shipped and managed by snapd 374 bootloader: grub 375 structure: 376 - name: mbr 377 type: mbr 378 size: 440 379 update: 380 edition: 1 381 content: 382 - image: pc-boot.img 383 - name: BIOS Boot 384 type: DA,21686148-6449-6E6F-744E-656564454649 385 size: 1M 386 offset: 1M 387 offset-write: mbr+92 388 update: 389 edition: 2 390 content: 391 - image: pc-core.img 392 - name: ubuntu-seed 393 role: system-seed 394 filesystem: vfat 395 # UEFI will boot the ESP partition by default first 396 type: EF,C12A7328-F81F-11D2-BA4B-00A0C93EC93B 397 size: 1200M 398 update: 399 edition: 2 400 content: 401 - source: grubx64.efi 402 target: EFI/boot/grubx64.efi 403 - source: shim.efi.signed 404 target: EFI/boot/bootx64.efi 405 - name: ubuntu-boot 406 role: system-boot 407 filesystem: ext4 408 type: 83,0FC63DAF-8483-4772-8E79-3D69D8477DE4 409 # whats the appropriate size? 410 size: 750M 411 update: 412 edition: 1 413 content: 414 - source: grubx64.efi 415 target: EFI/boot/grubx64.efi 416 - source: shim.efi.signed 417 target: EFI/boot/bootx64.efi 418 pc2: 419 structure: 420 - name: ubuntu-save 421 role: system-save 422 filesystem: ext4 423 type: 83,0FC63DAF-8483-4772-8E79-3D69D8477DE4 424 size: 16M 425 - name: ubuntu-data 426 role: system-data 427 filesystem: ext4 428 type: 83,0FC63DAF-8483-4772-8E79-3D69D8477DE4 429 size: 1G 430 ` 431 432 makeSizedFile(c, filepath.Join(s.dir, "meta/gadget.yaml"), 0, []byte(gadgetYamlContent)) 433 434 ginfo, err := gadget.ReadInfo(s.dir, nil) 435 c.Assert(err, IsNil) 436 err = gadget.Validate(ginfo, nil, nil) 437 c.Assert(err, ErrorMatches, `system-boot, system-data, and system-save are expected to share the same volume as system-seed`) 438 } 439 440 func (s *validateGadgetTestSuite) TestValidateRoleDuplicated(c *C) { 441 442 for _, role := range []string{"system-seed", "system-data", "system-boot", "system-save"} { 443 gadgetYamlContent := fmt.Sprintf(` 444 volumes: 445 pc: 446 bootloader: grub 447 structure: 448 - name: foo 449 type: DA,21686148-6449-6E6F-744E-656564454649 450 size: 1M 451 role: %[1]s 452 - name: bar 453 type: DA,21686148-6449-6E6F-744E-656564454649 454 size: 1M 455 role: %[1]s 456 `, role) 457 makeSizedFile(c, filepath.Join(s.dir, "meta/gadget.yaml"), 0, []byte(gadgetYamlContent)) 458 459 ginfo, err := gadget.ReadInfo(s.dir, nil) 460 c.Assert(err, IsNil) 461 err = gadget.Validate(ginfo, nil, nil) 462 c.Assert(err, ErrorMatches, fmt.Sprintf(`cannot have more than one partition with %s role`, role)) 463 } 464 } 465 466 func (s *validateGadgetTestSuite) TestValidateSystemSeedRoleTwiceAcrossVolumes(c *C) { 467 468 for _, role := range []string{"system-seed", "system-data", "system-boot", "system-save"} { 469 gadgetYamlContent := fmt.Sprintf(` 470 volumes: 471 pc: 472 bootloader: grub 473 structure: 474 - name: foo 475 type: DA,21686148-6449-6E6F-744E-656564454649 476 size: 1M 477 role: %[1]s 478 other: 479 structure: 480 - name: bar 481 type: DA,21686148-6449-6E6F-744E-656564454649 482 size: 1M 483 role: %[1]s 484 `, role) 485 makeSizedFile(c, filepath.Join(s.dir, "meta/gadget.yaml"), 0, []byte(gadgetYamlContent)) 486 487 ginfo, err := gadget.ReadInfo(s.dir, nil) 488 c.Assert(err, IsNil) 489 err = gadget.Validate(ginfo, nil, nil) 490 c.Assert(err, ErrorMatches, fmt.Sprintf(`cannot have more than one partition with %s role across volumes`, role)) 491 } 492 } 493 494 func (s *validateGadgetTestSuite) TestRuleValidateHybridGadget(c *C) { 495 // this is the kind of volumes setup recommended to be 496 // prepared for a possible UC18 -> UC20 transition 497 hybridyGadgetYaml := []byte(`volumes: 498 hybrid: 499 bootloader: grub 500 structure: 501 - name: mbr 502 type: mbr 503 size: 440 504 content: 505 - image: pc-boot.img 506 - name: BIOS Boot 507 type: DA,21686148-6449-6E6F-744E-656564454649 508 size: 1M 509 offset: 1M 510 offset-write: mbr+92 511 content: 512 - image: pc-core.img 513 - name: EFI System 514 type: EF,C12A7328-F81F-11D2-BA4B-00A0C93EC93B 515 filesystem: vfat 516 filesystem-label: system-boot 517 size: 1200M 518 content: 519 - source: grubx64.efi 520 target: EFI/boot/grubx64.efi 521 - source: shim.efi.signed 522 target: EFI/boot/bootx64.efi 523 - source: mmx64.efi 524 target: EFI/boot/mmx64.efi 525 - source: grub.cfg 526 target: EFI/ubuntu/grub.cfg 527 - name: Ubuntu Boot 528 type: 0FC63DAF-8483-4772-8E79-3D69D8477DE4 529 filesystem: ext4 530 filesystem-label: ubuntu-boot 531 size: 750M 532 `) 533 534 mod := &modelCharateristics{ 535 classic: false, 536 } 537 giMeta, err := gadget.InfoFromGadgetYaml(hybridyGadgetYaml, mod) 538 c.Assert(err, IsNil) 539 540 err = gadget.Validate(giMeta, mod, nil) 541 c.Check(err, IsNil) 542 } 543 544 func (s *validateGadgetTestSuite) TestRuleValidateHybridGadgetBrokenDupRole(c *C) { 545 // this is consistency-wise broken because of the duplicated 546 // system-boot role, of which one is implicit 547 brokenGadgetYaml := []byte(`volumes: 548 hybrid: 549 bootloader: grub 550 structure: 551 - name: mbr 552 type: mbr 553 size: 440 554 content: 555 - image: pc-boot.img 556 - name: BIOS Boot 557 type: DA,21686148-6449-6E6F-744E-656564454649 558 size: 1M 559 offset: 1M 560 offset-write: mbr+92 561 content: 562 - image: pc-core.img 563 - name: EFI System 564 type: EF,C12A7328-F81F-11D2-BA4B-00A0C93EC93B 565 filesystem: vfat 566 filesystem-label: system-boot 567 size: 1200M 568 content: 569 - source: grubx64.efi 570 target: EFI/boot/grubx64.efi 571 - source: shim.efi.signed 572 target: EFI/boot/bootx64.efi 573 - source: mmx64.efi 574 target: EFI/boot/mmx64.efi 575 - source: grub.cfg 576 target: EFI/ubuntu/grub.cfg 577 - name: Ubuntu Boot 578 type: 0FC63DAF-8483-4772-8E79-3D69D8477DE4 579 filesystem: ext4 580 filesystem-label: ubuntu-boot 581 role: system-boot 582 size: 750M 583 `) 584 585 mod := &modelCharateristics{ 586 classic: false, 587 } 588 giMeta, err := gadget.InfoFromGadgetYaml(brokenGadgetYaml, mod) 589 c.Assert(err, IsNil) 590 591 err = gadget.Validate(giMeta, mod, nil) 592 c.Check(err, ErrorMatches, `cannot have more than one partition with system-boot role`) 593 } 594 595 func (s *validateGadgetTestSuite) TestValidateContentMissingRawContent(c *C) { 596 var gadgetYamlContent = ` 597 volumes: 598 pc: 599 bootloader: grub 600 structure: 601 - name: foo 602 type: DA,21686148-6449-6E6F-744E-656564454649 603 size: 1M 604 offset: 1M 605 content: 606 - image: foo.img 607 608 ` 609 makeSizedFile(c, filepath.Join(s.dir, "meta/gadget.yaml"), 0, []byte(gadgetYamlContent)) 610 611 ginfo, err := gadget.ReadInfo(s.dir, nil) 612 c.Assert(err, IsNil) 613 err = gadget.ValidateContent(ginfo, s.dir) 614 c.Assert(err, ErrorMatches, `invalid layout of volume "pc": cannot lay out structure #0 \("foo"\): content "foo.img": stat .*/foo.img: no such file or directory`) 615 } 616 617 func (s *validateGadgetTestSuite) TestValidateContentMultiVolumeContent(c *C) { 618 var gadgetYamlContent = ` 619 volumes: 620 first: 621 bootloader: grub 622 structure: 623 - name: first-foo 624 type: DA,21686148-6449-6E6F-744E-656564454649 625 size: 1M 626 content: 627 - image: first.img 628 second: 629 structure: 630 - name: second-foo 631 type: DA,21686148-6449-6E6F-744E-656564454649 632 size: 1M 633 content: 634 - image: second.img 635 636 ` 637 makeSizedFile(c, filepath.Join(s.dir, "meta/gadget.yaml"), 0, []byte(gadgetYamlContent)) 638 // only content for the first volume 639 makeSizedFile(c, filepath.Join(s.dir, "first.img"), 1, nil) 640 641 ginfo, err := gadget.ReadInfo(s.dir, nil) 642 c.Assert(err, IsNil) 643 err = gadget.ValidateContent(ginfo, s.dir) 644 c.Assert(err, ErrorMatches, `invalid layout of volume "second": cannot lay out structure #0 \("second-foo"\): content "second.img": stat .*/second.img: no such file or directory`) 645 } 646 647 func (s *validateGadgetTestSuite) TestValidateContentFilesystemContent(c *C) { 648 var gadgetYamlContent = ` 649 volumes: 650 bad: 651 bootloader: grub 652 structure: 653 - name: bad-struct 654 type: DA,21686148-6449-6E6F-744E-656564454649 655 size: 1M 656 filesystem: ext4 657 content: 658 - source: foo/ 659 target: / 660 661 ` 662 makeSizedFile(c, filepath.Join(s.dir, "meta/gadget.yaml"), 0, []byte(gadgetYamlContent)) 663 664 ginfo, err := gadget.ReadInfo(s.dir, nil) 665 c.Assert(err, IsNil) 666 err = gadget.ValidateContent(ginfo, s.dir) 667 c.Assert(err, ErrorMatches, `invalid volume "bad": structure #0 \("bad-struct"\), content source:foo/: source path does not exist`) 668 669 // make it a file, which conflicts with foo/ as 'source' 670 fooPath := filepath.Join(s.dir, "foo") 671 makeSizedFile(c, fooPath, 1, nil) 672 err = gadget.ValidateContent(ginfo, s.dir) 673 c.Assert(err, ErrorMatches, `invalid volume "bad": structure #0 \("bad-struct"\), content source:foo/: cannot specify trailing / for a source which is not a directory`) 674 675 // make it a directory 676 err = os.Remove(fooPath) 677 c.Assert(err, IsNil) 678 err = os.Mkdir(fooPath, 0755) 679 c.Assert(err, IsNil) 680 // validate should no longer complain 681 err = gadget.ValidateContent(ginfo, s.dir) 682 c.Assert(err, IsNil) 683 } 684 685 var gadgetYamlContentNoSave = ` 686 volumes: 687 vol1: 688 bootloader: grub 689 structure: 690 - name: ubuntu-seed 691 role: system-seed 692 type: DA,21686148-6449-6E6F-744E-656564454649 693 size: 1M 694 filesystem: ext4 695 - name: ubuntu-boot 696 type: DA,21686148-6449-6E6F-744E-656564454649 697 size: 1M 698 filesystem: ext4 699 - name: ubuntu-data 700 role: system-data 701 type: DA,21686148-6449-6E6F-744E-656564454649 702 size: 1M 703 filesystem: ext4 704 ` 705 706 var gadgetYamlContentWithSave = gadgetYamlContentNoSave + ` 707 - name: ubuntu-save 708 role: system-save 709 type: DA,21686148-6449-6E6F-744E-656564454649 710 size: 1M 711 filesystem: ext4 712 ` 713 714 func (s *validateGadgetTestSuite) TestValidateEncryptionSupportErr(c *C) { 715 makeSizedFile(c, filepath.Join(s.dir, "meta/gadget.yaml"), 0, []byte(gadgetYamlContentNoSave)) 716 717 mod := &modelCharateristics{systemSeed: true} 718 ginfo, err := gadget.ReadInfo(s.dir, mod) 719 c.Assert(err, IsNil) 720 err = gadget.Validate(ginfo, mod, &gadget.ValidationConstraints{ 721 EncryptedData: true, 722 }) 723 c.Assert(err, ErrorMatches, `gadget does not support encrypted data: volume "vol1" has no structure with system-save role`) 724 } 725 726 func (s *validateGadgetTestSuite) TestValidateEncryptionSupportHappy(c *C) { 727 makeSizedFile(c, filepath.Join(s.dir, "meta/gadget.yaml"), 0, []byte(gadgetYamlContentWithSave)) 728 mod := &modelCharateristics{systemSeed: true} 729 ginfo, err := gadget.ReadInfo(s.dir, mod) 730 c.Assert(err, IsNil) 731 err = gadget.Validate(ginfo, mod, &gadget.ValidationConstraints{ 732 EncryptedData: true, 733 }) 734 c.Assert(err, IsNil) 735 } 736 737 var gadgetYamlContentKernelRef = gadgetYamlContentNoSave + ` 738 - name: other 739 type: DA,21686148-6449-6E6F-744E-656564454649 740 size: 10M 741 filesystem: ext4 742 content: 743 - source: REPLACE_WITH_TC 744 target: / 745 ` 746 747 func (s *validateGadgetTestSuite) TestValidateContentKernelAssetsRef(c *C) { 748 for _, tc := range []struct { 749 source, asset, content string 750 good bool 751 }{ 752 {"$kernel:a/b", "a", "b", true}, 753 {"$kernel:A/b", "A", "b", true}, 754 {"$kernel:a-a/bb", "a-a", "bb", true}, 755 {"$kernel:a-a/b-b", "a-a", "b-b", true}, 756 {"$kernel:aB-0/cD-3", "aB-0", "cD-3", true}, 757 {"$kernel:aB-0/foo-21B.dtb", "aB-0", "foo-21B.dtb", true}, 758 {"$kernel:aB-0/nested/bar-77A.raw", "aB-0", "nested/bar-77A.raw", true}, 759 {"$kernel:a/a/", "a", "a/", true}, 760 // no starting with "-" 761 {source: "$kernel:-/-"}, 762 // assets and content need to be there 763 {source: "$kernel:ab"}, 764 {source: "$kernel:/"}, 765 {source: "$kernel:a/"}, 766 {source: "$kernel:/a"}, 767 // invalid asset name 768 {source: "$kernel:#garbage/a"}, 769 // invalid content part 770 {source: "$kernel:a//"}, 771 {source: "$kernel:a///"}, 772 {source: "$kernel:a////"}, 773 {source: "$kernel:a/a/../"}, 774 } { 775 gadgetYaml := strings.Replace(gadgetYamlContentKernelRef, "REPLACE_WITH_TC", tc.source, -1) 776 makeSizedFile(c, filepath.Join(s.dir, "meta/gadget.yaml"), 0, []byte(gadgetYaml)) 777 ginfo, err := gadget.ReadInfoAndValidate(s.dir, nil, nil) 778 c.Assert(err, IsNil) 779 err = gadget.ValidateContent(ginfo, s.dir) 780 if tc.good { 781 c.Check(err, IsNil, Commentf(tc.source)) 782 // asset validates correctly, so let's make sure that 783 // individual pieces are correct too 784 assetName, content, err := gadget.SplitKernelRef(tc.source) 785 c.Assert(err, IsNil) 786 c.Check(assetName, Equals, tc.asset) 787 c.Check(content, Equals, tc.content) 788 } else { 789 errStr := fmt.Sprintf(`invalid volume "vol1": cannot use kernel reference "%s": .*`, regexp.QuoteMeta(tc.source)) 790 c.Check(err, ErrorMatches, errStr, Commentf(tc.source)) 791 } 792 } 793 } 794 795 func (s *validateGadgetTestSuite) TestSplitKernelRefErrors(c *C) { 796 for _, tc := range []struct { 797 kernelRef string 798 errStr string 799 }{ 800 {"no-kernel-ref", `internal error: splitKernelRef called for non kernel ref "no-kernel-ref"`}, 801 {"$kernel:a", `invalid asset and content in kernel ref "\$kernel:a"`}, 802 {"$kernel:a/", `missing asset name or content in kernel ref "\$kernel:a/"`}, 803 {"$kernel:/b", `missing asset name or content in kernel ref "\$kernel:/b"`}, 804 {"$kernel:a!invalid/b", `invalid asset name in kernel ref "\$kernel:a!invalid/b"`}, 805 {"$kernel:a/b/..", `invalid content in kernel ref "\$kernel:a/b/.."`}, 806 {"$kernel:a/b//", `invalid content in kernel ref "\$kernel:a/b//"`}, 807 {"$kernel:a/b/./", `invalid content in kernel ref "\$kernel:a/b/./"`}, 808 } { 809 _, _, err := gadget.SplitKernelRef(tc.kernelRef) 810 c.Check(err, ErrorMatches, tc.errStr, Commentf("kernelRef: %s", tc.kernelRef)) 811 } 812 }