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;
TEST(FirmwareJsonTest,InvalidHandlerType)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
TEST(FirmwareJsonTest,InvalidPreparationType)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
TEST(FirmwareJsonTest,InvalidVerificationType)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
TEST(FirmwareJsonTest,InvalidUpdateType)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
TEST(FirmwareJsonTest,MissingHandler)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
TEST(FirmwareJsonTest,MissingActions)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
TEST(FirmwareJsonTest,MissingActionPreparation)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
TEST(FirmwareJsonTest,MissingActionVerification)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
TEST(FirmwareJsonTest,MissingActionUpdate)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
TEST(FirmwareJsonTest,TwoConfigsOneInvalidReturnsValid)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
TEST(FirmwareJsonTest,VerifyBlobNameMatches)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
TEST(FirmwareJsonTest,VerifyMinimumBlobNameLength)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
TEST(FirmwareJsonTest,VerifySystemdWithReboot)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
TEST(FirmwareJsonTest,VerifyMultipleHandlersReturned)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
TEST(FirmwareJsonTest,VerifyValidSingleNonReboot)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
TEST(FirmwareJsonTest,VerifyValidWithModes)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
TEST(FirmwareJsonTest,VerifyValidUpdateWithFilePath)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
TEST(FirmwareJsonTest,VerifySkipFields)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
TEST(FirmwareJsonTest,BuildFromFile)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
TEST(FirmwareJsonTest,BuildFromBadFile)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
TEST(FirmwareJsonTest,BuildFromMissingDirectory)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