1 #include "firmware_handlers_builder.hpp" 2 #include "general_systemd.hpp" 3 #include "skip_action.hpp" 4 5 #include <nlohmann/json.hpp> 6 7 #include <filesystem> 8 9 #include <gmock/gmock.h> 10 #include <gtest/gtest.h> 11 12 namespace ipmi_flash 13 { 14 namespace 15 { 16 using ::testing::IsEmpty; 17 18 static constexpr auto TESTFNAME = "./test/test.json"; 19 using json = nlohmann::json; 20 TEST(FirmwareJsonTest, InvalidHandlerType) 21 { 22 auto j2 = R"( 23 [{ 24 "blob" : "/flash/image", 25 "handler" : { 26 "type" : "unsupported", 27 "path" : "/run/initramfs/bmc-image" 28 }, 29 "actions" : { 30 "preparation" : { 31 "type" : "systemd", 32 "unit" : "phosphor-ipmi-flash-bmc-prepare.target" 33 }, 34 "verification" : { 35 "type" : "fileSystemdVerify", 36 "unit" : "phosphor-ipmi-flash-bmc-verify.target", 37 "path" : "/tmp/bmc.verify" 38 }, 39 "update" : { 40 "type" : "reboot" 41 } 42 } 43 }] 44 )"_json; 45 EXPECT_THAT(FirmwareHandlersBuilder().buildHandlerFromJson(j2), IsEmpty()); 46 } 47 48 TEST(FirmwareJsonTest, InvalidPreparationType) 49 { 50 auto j2 = R"( 51 [{ 52 "blob" : "/flash/image", 53 "handler" : { 54 "type" : "file", 55 "path" : "/run/initramfs/bmc-image" 56 }, 57 "actions" : { 58 "preparation" : { 59 "type" : "superfun", 60 "unit" : "phosphor-ipmi-flash-bmc-prepare.target" 61 }, 62 "verification" : { 63 "type" : "fileSystemdVerify", 64 "unit" : "phosphor-ipmi-flash-bmc-verify.target", 65 "path" : "/tmp/bmc.verify" 66 }, 67 "update" : { 68 "type" : "reboot" 69 } 70 } 71 }] 72 )"_json; 73 74 EXPECT_THAT(FirmwareHandlersBuilder().buildHandlerFromJson(j2), IsEmpty()); 75 } 76 77 TEST(FirmwareJsonTest, InvalidVerificationType) 78 { 79 auto j2 = R"( 80 [{ 81 "blob" : "/flash/image", 82 "handler" : { 83 "type" : "file", 84 "path" : "/run/initramfs/bmc-image" 85 }, 86 "actions" : { 87 "preparation" : { 88 "type" : "systemd", 89 "unit" : "phosphor-ipmi-flash-bmc-prepare.target" 90 }, 91 "verification" : { 92 "type" : "funtimes", 93 "unit" : "phosphor-ipmi-flash-bmc-verify.target", 94 "path" : "/tmp/bmc.verify" 95 }, 96 "update" : { 97 "type" : "reboot" 98 } 99 } 100 }] 101 )"_json; 102 103 EXPECT_THAT(FirmwareHandlersBuilder().buildHandlerFromJson(j2), IsEmpty()); 104 } 105 106 TEST(FirmwareJsonTest, InvalidUpdateType) 107 { 108 auto j2 = R"( 109 [{ 110 "blob" : "/flash/image", 111 "handler" : { 112 "type" : "file", 113 "path" : "/run/initramfs/bmc-image" 114 }, 115 "actions" : { 116 "preparation" : { 117 "type" : "systemd", 118 "unit" : "phosphor-ipmi-flash-bmc-prepare.target" 119 }, 120 "verification" : { 121 "type" : "fileSystemdVerify", 122 "unit" : "phosphor-ipmi-flash-bmc-verify.target", 123 "path" : "/tmp/bmc.verify" 124 }, 125 "update" : { 126 "type" : "systemd" 127 } 128 } 129 }] 130 )"_json; 131 132 EXPECT_THAT(FirmwareHandlersBuilder().buildHandlerFromJson(j2), IsEmpty()); 133 } 134 135 TEST(FirmwareJsonTest, MissingHandler) 136 { 137 auto j2 = R"( 138 [{ 139 "blob" : "/flash/image", 140 "actions" : { 141 "preparation" : { 142 "type" : "systemd", 143 "unit" : "phosphor-ipmi-flash-bmc-prepare.target" 144 }, 145 "verification" : { 146 "type" : "fileSystemdVerify", 147 "unit" : "phosphor-ipmi-flash-bmc-verify.target", 148 "path" : "/tmp/bmc.verify" 149 }, 150 "update" : { 151 "type" : "reboot" 152 } 153 } 154 }] 155 )"_json; 156 157 EXPECT_THAT(FirmwareHandlersBuilder().buildHandlerFromJson(j2), IsEmpty()); 158 } 159 160 TEST(FirmwareJsonTest, MissingActions) 161 { 162 auto j2 = R"( 163 [{ 164 "blob" : "/flash/image", 165 "handler" : { 166 "type" : "file", 167 "path" : "/run/initramfs/bmc-image" 168 } 169 }] 170 )"_json; 171 172 EXPECT_THAT(FirmwareHandlersBuilder().buildHandlerFromJson(j2), IsEmpty()); 173 } 174 175 TEST(FirmwareJsonTest, MissingActionPreparation) 176 { 177 auto j2 = R"( 178 [{ 179 "blob" : "/flash/image", 180 "handler" : { 181 "type" : "file", 182 "path" : "/run/initramfs/bmc-image" 183 }, 184 "actions" : { 185 "verification" : { 186 "type" : "fileSystemdVerify", 187 "unit" : "phosphor-ipmi-flash-bmc-verify.target", 188 "path" : "/tmp/bmc.verify" 189 }, 190 "update" : { 191 "type" : "reboot" 192 } 193 } 194 }] 195 )"_json; 196 197 EXPECT_THAT(FirmwareHandlersBuilder().buildHandlerFromJson(j2), IsEmpty()); 198 } 199 200 TEST(FirmwareJsonTest, MissingActionVerification) 201 { 202 auto j2 = R"( 203 [{ 204 "blob" : "/flash/image", 205 "handler" : { 206 "type" : "file", 207 "path" : "/run/initramfs/bmc-image" 208 }, 209 "actions" : { 210 "preparation" : { 211 "type" : "systemd", 212 "unit" : "phosphor-ipmi-flash-bmc-prepare.target" 213 }, 214 "update" : { 215 "type" : "reboot" 216 } 217 } 218 }] 219 )"_json; 220 221 EXPECT_THAT(FirmwareHandlersBuilder().buildHandlerFromJson(j2), IsEmpty()); 222 } 223 224 TEST(FirmwareJsonTest, MissingActionUpdate) 225 { 226 auto j2 = R"( 227 [{ 228 "blob" : "/flash/image", 229 "handler" : { 230 "type" : "file", 231 "path" : "/run/initramfs/bmc-image" 232 }, 233 "actions" : { 234 "preparation" : { 235 "type" : "systemd", 236 "unit" : "phosphor-ipmi-flash-bmc-prepare.target" 237 }, 238 "verification" : { 239 "type" : "fileSystemdVerify", 240 "unit" : "phosphor-ipmi-flash-bmc-verify.target", 241 "path" : "/tmp/bmc.verify" 242 } 243 } 244 }] 245 )"_json; 246 247 EXPECT_THAT(FirmwareHandlersBuilder().buildHandlerFromJson(j2), IsEmpty()); 248 } 249 250 TEST(FirmwareJsonTest, TwoConfigsOneInvalidReturnsValid) 251 { 252 auto j2 = R"( 253 [{ 254 "blob" : "/flash/image", 255 "actions" : { 256 "preparation" : { 257 "type" : "systemd", 258 "unit" : "phosphor-ipmi-flash-bmc-prepare.target" 259 }, 260 "verification" : { 261 "type" : "fileSystemdVerify", 262 "unit" : "phosphor-ipmi-flash-bmc-verify.target", 263 "path" : "/tmp/bmc.verify" 264 }, 265 "update" : { 266 "type" : "reboot" 267 } 268 } 269 }, 270 { 271 "blob" : "/flash/image2", 272 "handler" : { 273 "type" : "file", 274 "path" : "/run/initramfs/bmc-image" 275 }, 276 "actions" : { 277 "preparation" : { 278 "type" : "systemd", 279 "unit" : "phosphor-ipmi-flash-bmc-prepare.target" 280 }, 281 "verification" : { 282 "type" : "fileSystemdVerify", 283 "unit" : "phosphor-ipmi-flash-bmc-verify.target", 284 "path" : "/tmp/bmc.verify" 285 }, 286 "update" : { 287 "type" : "reboot" 288 } 289 } 290 }] 291 )"_json; 292 293 auto h = FirmwareHandlersBuilder().buildHandlerFromJson(j2); 294 EXPECT_EQ(h[0].blobId, "/flash/image2"); 295 EXPECT_EQ(h.size(), 1); 296 } 297 298 /* 299 * TODO: It may be worth individually using builders per type, and testing 300 * those. 301 * 302 * TODO: Only allow unique handler blob paths (tested at a higher level). 303 */ 304 305 TEST(FirmwareJsonTest, VerifyBlobNameMatches) 306 { 307 /* A perfect configuration except the blob name doesn't start with "/flash/" 308 */ 309 auto j2 = R"( 310 [{ 311 "blob" : "bmc-image-flash", 312 "handler" : { 313 "type" : "file", 314 "path" : "/run/initramfs/bmc-image" 315 }, 316 "actions" : { 317 "preparation" : { 318 "type" : "systemd", 319 "unit" : "phosphor-ipmi-flash-bmc-prepare.target" 320 }, 321 "verification" : { 322 "type" : "fileSystemdVerify", 323 "unit" : "phosphor-ipmi-flash-bmc-verify.target", 324 "path" : "/tmp/bmc.verify" 325 }, 326 "update" : { 327 "type" : "reboot" 328 } 329 } 330 }] 331 )"_json; 332 333 EXPECT_THAT(FirmwareHandlersBuilder().buildHandlerFromJson(j2), IsEmpty()); 334 } 335 336 TEST(FirmwareJsonTest, VerifyMinimumBlobNameLength) 337 { 338 /* A perfect configuration except the blob name is effectively zero length. 339 */ 340 auto j2 = R"( 341 [{ 342 "blob" : "/flash/", 343 "handler" : { 344 "type" : "file", 345 "path" : "/run/initramfs/bmc-image" 346 }, 347 "actions" : { 348 "preparation" : { 349 "type" : "systemd", 350 "unit" : "phosphor-ipmi-flash-bmc-prepare.target" 351 }, 352 "verification" : { 353 "type" : "fileSystemdVerify", 354 "unit" : "phosphor-ipmi-flash-bmc-verify.target", 355 "path" : "/tmp/bmc.verify" 356 }, 357 "update" : { 358 "type" : "reboot" 359 } 360 } 361 }] 362 )"_json; 363 364 EXPECT_THAT(FirmwareHandlersBuilder().buildHandlerFromJson(j2), IsEmpty()); 365 } 366 367 TEST(FirmwareJsonTest, VerifySystemdWithReboot) 368 { 369 auto j2 = R"( 370 [{ 371 "blob" : "/flash/image", 372 "handler" : { 373 "type" : "file", 374 "path" : "/run/initramfs/bmc-image" 375 }, 376 "actions" : { 377 "preparation" : { 378 "type" : "systemd", 379 "unit" : "phosphor-ipmi-flash-bmc-prepare.target" 380 }, 381 "verification" : { 382 "type" : "fileSystemdVerify", 383 "unit" : "phosphor-ipmi-flash-bmc-verify.target", 384 "path" : "/tmp/bmc.verify" 385 }, 386 "update" : { 387 "type" : "reboot" 388 } 389 } 390 }] 391 )"_json; 392 393 auto h = FirmwareHandlersBuilder().buildHandlerFromJson(j2); 394 EXPECT_EQ(h[0].blobId, "/flash/image"); 395 EXPECT_FALSE(h[0].handler == nullptr); 396 EXPECT_FALSE(h[0].actions == nullptr); 397 EXPECT_FALSE(h[0].actions->preparation == nullptr); 398 EXPECT_FALSE(h[0].actions->verification == nullptr); 399 EXPECT_FALSE(h[0].actions->update == nullptr); 400 } 401 402 TEST(FirmwareJsonTest, VerifyMultipleHandlersReturned) 403 { 404 auto j2 = R"( 405 [{ 406 "blob" : "/flash/image", 407 "handler" : { 408 "type" : "file", 409 "path" : "/run/initramfs/bmc-image" 410 }, 411 "actions" : { 412 "preparation" : { 413 "type" : "systemd", 414 "unit" : "phosphor-ipmi-flash-bmc-prepare.target" 415 }, 416 "verification" : { 417 "type" : "fileSystemdVerify", 418 "unit" : "phosphor-ipmi-flash-bmc-verify.target", 419 "path" : "/tmp/bmc.verify" 420 }, 421 "update" : { 422 "type" : "systemd", 423 "unit" : "phosphor-ipmi-flash-bmc-update.target" 424 } 425 } 426 }, 427 { 428 "blob" : "/flash/bios", 429 "handler" : { 430 "type" : "file", 431 "path" : "/run/initramfs/bmc-image" 432 }, 433 "actions" : { 434 "preparation" : { 435 "type" : "systemd", 436 "unit" : "phosphor-ipmi-flash-bmc-prepare.target" 437 }, 438 "verification" : { 439 "type" : "fileSystemdVerify", 440 "unit" : "phosphor-ipmi-flash-bmc-verify.target", 441 "path" : "/tmp/bmc.verify" 442 }, 443 "update" : { 444 "type" : "systemd", 445 "unit" : "phosphor-ipmi-flash-bmc-update.target" 446 } 447 } 448 }] 449 )"_json; 450 451 auto h = FirmwareHandlersBuilder().buildHandlerFromJson(j2); 452 EXPECT_EQ(h.size(), 2); 453 EXPECT_EQ(h[0].blobId, "/flash/image"); 454 EXPECT_EQ(h[1].blobId, "/flash/bios"); 455 } 456 457 TEST(FirmwareJsonTest, VerifyValidSingleNonReboot) 458 { 459 auto j2 = R"( 460 [{ 461 "blob" : "/flash/image", 462 "handler" : { 463 "type" : "file", 464 "path" : "/run/initramfs/bmc-image" 465 }, 466 "actions" : { 467 "preparation" : { 468 "type" : "systemd", 469 "unit" : "phosphor-ipmi-flash-bmc-prepare.target" 470 }, 471 "verification" : { 472 "type" : "fileSystemdVerify", 473 "unit" : "phosphor-ipmi-flash-bmc-verify.target", 474 "path" : "/tmp/bmc.verify" 475 }, 476 "update" : { 477 "type" : "systemd", 478 "unit" : "phosphor-ipmi-flash-bmc-update.target" 479 } 480 } 481 }] 482 )"_json; 483 484 auto h = FirmwareHandlersBuilder().buildHandlerFromJson(j2); 485 EXPECT_EQ(h[0].blobId, "/flash/image"); 486 EXPECT_FALSE(h[0].handler == nullptr); 487 EXPECT_FALSE(h[0].actions == nullptr); 488 EXPECT_FALSE(h[0].actions->preparation == nullptr); 489 EXPECT_FALSE(h[0].actions->verification == nullptr); 490 auto verifier = reinterpret_cast<SystemdWithStatusFile*>( 491 h[0].actions->verification.get()); 492 EXPECT_THAT(verifier->getMode(), "replace"); 493 EXPECT_FALSE(h[0].actions->update == nullptr); 494 auto updater = reinterpret_cast<SystemdNoFile*>(h[0].actions->update.get()); 495 EXPECT_THAT(updater->getMode(), "replace"); 496 } 497 498 TEST(FirmwareJsonTest, VerifyValidWithModes) 499 { 500 auto j2 = R"( 501 [{ 502 "blob" : "/flash/image", 503 "handler" : { 504 "type" : "file", 505 "path" : "/run/initramfs/bmc-image" 506 }, 507 "actions" : { 508 "preparation" : { 509 "type" : "systemd", 510 "unit" : "phosphor-ipmi-flash-bmc-prepare.target" 511 }, 512 "verification" : { 513 "type" : "fileSystemdVerify", 514 "unit" : "phosphor-ipmi-flash-bmc-verify.target", 515 "path" : "/tmp/bmc.verify", 516 "mode" : "replace-nope" 517 }, 518 "update" : { 519 "type" : "systemd", 520 "mode" : "replace-fake", 521 "unit" : "phosphor-ipmi-flash-bmc-update.target" 522 } 523 } 524 }] 525 )"_json; 526 527 auto h = FirmwareHandlersBuilder().buildHandlerFromJson(j2); 528 EXPECT_EQ(h[0].blobId, "/flash/image"); 529 EXPECT_FALSE(h[0].handler == nullptr); 530 EXPECT_FALSE(h[0].actions == nullptr); 531 EXPECT_FALSE(h[0].actions->preparation == nullptr); 532 EXPECT_FALSE(h[0].actions->verification == nullptr); 533 auto verifier = reinterpret_cast<SystemdWithStatusFile*>( 534 h[0].actions->verification.get()); 535 EXPECT_THAT(verifier->getMode(), "replace-nope"); 536 EXPECT_FALSE(h[0].actions->update == nullptr); 537 auto updater = reinterpret_cast<SystemdNoFile*>(h[0].actions->update.get()); 538 EXPECT_THAT(updater->getMode(), "replace-fake"); 539 } 540 541 TEST(FirmwareJsonTest, VerifyValidUpdateWithFilePath) 542 { 543 auto j2 = R"( 544 [{ 545 "blob" : "/flash/image", 546 "handler" : { 547 "type" : "file", 548 "path" : "/run/initramfs/bmc-image" 549 }, 550 "actions" : { 551 "preparation" : { 552 "type" : "systemd", 553 "unit" : "phosphor-ipmi-flash-bmc-prepare.target" 554 }, 555 "verification" : { 556 "type" : "fileSystemdVerify", 557 "unit" : "phosphor-ipmi-flash-bmc-verify.target", 558 "path" : "/tmp/bmc.verify", 559 "mode" : "replace-nope" 560 }, 561 "update" : { 562 "type" : "fileSystemdUpdate", 563 "mode" : "replace-fake", 564 "unit" : "phosphor-ipmi-flash-bmc-update.target", 565 "path" : "/tmp/update.verify" 566 } 567 } 568 }] 569 )"_json; 570 571 auto h = FirmwareHandlersBuilder().buildHandlerFromJson(j2); 572 EXPECT_EQ(h[0].blobId, "/flash/image"); 573 EXPECT_FALSE(h[0].handler == nullptr); 574 EXPECT_FALSE(h[0].actions == nullptr); 575 EXPECT_FALSE(h[0].actions->preparation == nullptr); 576 EXPECT_FALSE(h[0].actions->verification == nullptr); 577 auto verifier = reinterpret_cast<SystemdWithStatusFile*>( 578 h[0].actions->verification.get()); 579 EXPECT_THAT(verifier->getMode(), "replace-nope"); 580 EXPECT_FALSE(h[0].actions->update == nullptr); 581 auto updater = 582 reinterpret_cast<SystemdWithStatusFile*>(h[0].actions->update.get()); 583 EXPECT_THAT(updater->getMode(), "replace-fake"); 584 } 585 586 TEST(FirmwareJsonTest, VerifySkipFields) 587 { 588 // In this configuration, nothing happens because all actions are set to 589 // skip. 590 auto j2 = R"( 591 [{ 592 "blob" : "/flash/image", 593 "handler" : { 594 "type" : "file", 595 "path" : "/run/initramfs/bmc-image" 596 }, 597 "actions" : { 598 "preparation" : { 599 "type" : "skip" 600 }, 601 "verification" : { 602 "type" : "skip" 603 }, 604 "update" : { 605 "type" : "skip" 606 } 607 } 608 }] 609 )"_json; 610 611 auto h = FirmwareHandlersBuilder().buildHandlerFromJson(j2); 612 EXPECT_EQ(h[0].blobId, "/flash/image"); 613 EXPECT_FALSE(h[0].handler == nullptr); 614 EXPECT_FALSE(h[0].actions == nullptr); 615 EXPECT_FALSE(h[0].actions->preparation == nullptr); 616 EXPECT_FALSE(h[0].actions->verification == nullptr); 617 EXPECT_FALSE(h[0].actions->update == nullptr); 618 } 619 620 TEST(FirmwareJsonTest, BuildFromFile) 621 { 622 std::filesystem::create_directories("./test/"); 623 std::ofstream testfile; 624 testfile.open(TESTFNAME, std::ios::out); 625 auto good = R"( 626 [{ 627 "blob" : "/flash/image", 628 "handler" : { 629 "type" : "file", 630 "path" : "/run/initramfs/bmc-image" 631 }, 632 "actions" : { 633 "preparation" : { 634 "type" : "skip" 635 }, 636 "verification" : { 637 "type" : "skip" 638 }, 639 "update" : { 640 "type" : "skip" 641 } 642 } 643 }] 644 )"_json; 645 testfile << good.dump(4); 646 testfile.flush(); 647 FirmwareHandlersBuilder b; 648 auto h = b.buildHandlerConfigs("./test/"); 649 EXPECT_EQ(h.size(), 1); 650 EXPECT_EQ(h[0].blobId, "/flash/image"); 651 EXPECT_FALSE(h[0].handler == nullptr); 652 EXPECT_FALSE(h[0].actions == nullptr); 653 EXPECT_FALSE(h[0].actions->preparation == nullptr); 654 EXPECT_FALSE(h[0].actions->verification == nullptr); 655 EXPECT_FALSE(h[0].actions->update == nullptr); 656 if (std::remove(TESTFNAME) != 0) 657 { 658 fprintf(stderr, "warning: filecleanup of %s failed\n", TESTFNAME); 659 } 660 } 661 662 TEST(FirmwareJsonTest, BuildFromBadFile) 663 { 664 std::filesystem::create_directories("./test/"); 665 std::ofstream testfile; 666 testfile.open(TESTFNAME, std::ios::out); 667 testfile << "{] a malformed json {{"; 668 testfile.flush(); 669 FirmwareHandlersBuilder b; 670 auto h = b.buildHandlerConfigs("./test/"); 671 EXPECT_THAT(h, IsEmpty()); 672 if (std::remove(TESTFNAME) != 0) 673 { 674 fprintf(stderr, "warning: filecleanup of %s failed\n", TESTFNAME); 675 } 676 } 677 678 TEST(FirmwareJsonTest, BuildFromMissingDirectory) 679 { 680 EXPECT_THAT( 681 FirmwareHandlersBuilder().buildHandlerConfigs("./no-such-directory"), 682 IsEmpty()); 683 } 684 685 } // namespace 686 } // namespace ipmi_flash 687