xref: /openbmc/phosphor-ipmi-flash/bmc/firmware-handler/test/firmware_json_unittest.cpp (revision b487eb47e566df7679e740559582f6d25597feba)
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