version-handler: add version-handler, blob handler
Implement version-handler: a blob handler for retrieving version
information about a blob.
Problem: ipmi-flash(firmware-handler) provides a mechanism to transfer
firmware updates, verify them and perform updates but there is no
mechanism to interrogate the firmware for its version.
Solution: version-handler provides handlers for retrieving information
about firmware blobs. Adding "version" syntax to "/flash/blob" entries
in the json configuration file enables this feature.
The mechanism to retrieve version is identical to the mechanism used by
firmware-handler to perform preparation, verification and updates (kick
off systemd targets).
Signed-off-by: Jason Ling <jasonling@google.com>
Change-Id: I28868ca8dd76d63af668d2e46b9359401d45f0bc
diff --git a/bmc/version-handler/test/version_handler_unittest.cpp b/bmc/version-handler/test/version_handler_unittest.cpp
new file mode 100644
index 0000000..f27f033
--- /dev/null
+++ b/bmc/version-handler/test/version_handler_unittest.cpp
@@ -0,0 +1,267 @@
+#include "version_handler.hpp"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace ipmi_flash
+{
+namespace
+{
+using ::testing::IsEmpty;
+
+using json = nlohmann::json;
+
+TEST(VersionHandlerCreateTest, VerifyAllBlobHandlersPresent)
+{
+
+ // Need a triggerable mock
+ VersionInfoMap test;
+ test.try_emplace("blob0", "blob0",
+ std::make_unique<VersionActionPackMock>(),
+ std::make_unique<ImageHandlerMock>());
+ ASSERT_THAT(h, ::testing::SizeIs(1));
+ EXPECT_THAT(h[0].blobId, "/version/sink_seq");
+ EXPECT_FALSE(h[0].actions == nullptr);
+ EXPECT_FALSE(h[0].handler == nullptr);
+}
+
+TEST(VersionJsonTest, ValidConfigurationVersionBlobName)
+{
+ auto h = VersionHandlersBuilder().buildHandlerFromJson(j2);
+ ASSERT_THAT(h, ::testing::SizeIs(1));
+ EXPECT_THAT(h[0].blobId, "/version/sink_seq");
+ EXPECT_FALSE(h[0].actions == nullptr);
+ EXPECT_FALSE(h[0].handler == nullptr);
+}
+
+TEST(VersionJsonTest, MissingHandlerType)
+{
+ auto j2 = R"(
+ [{
+ "blob" : "/flash/image",
+ "version":{
+ "handler": {
+ "path" : "/tmp/version_info"
+ },
+ "actions": {
+ "open" : {
+ "type" : "systemd",
+ "unit" : "absolute"}
+ }
+ }
+ }]
+ )"_json;
+ EXPECT_THAT(VersionHandlersBuilder().buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(VersionJsonTest, BadBlobName)
+{
+ auto j2 = R"(
+ [{
+ "blob" : "/bad/image",
+ "version":{
+ "handler": {
+ "type" : "file",
+ "path" : "/tmp/version_info"
+ },
+ "actions": {
+ "open" : {
+ "type" : "systemd",
+ "unit" : "absolute"}
+ }
+ }
+ }]
+ )"_json;
+ EXPECT_THAT(VersionHandlersBuilder().buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(VersionJsonTest, MissingActions)
+{
+ auto j2 = R"(
+ [{
+ "blob" : "/flash/image",
+ "version":{
+ "handler": {
+ "type" : "file",
+ "path" : "/tmp/version_info"
+ }
+ }
+ }]
+ )"_json;
+ EXPECT_THAT(VersionHandlersBuilder().buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(VersionJsonTest, MissingOpenAction)
+{
+ auto j2 = R"(
+ [{
+ "blob" : "/flash/image",
+ "version":{
+ "handler": {
+ "type" : "file",
+ "path" : "/tmp/version_info"
+ },
+ "actions": {}
+ }
+ }]
+ )"_json;
+ EXPECT_THAT(VersionHandlersBuilder().buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(VersionJsonTest, OneInvalidTwoValidSucceeds)
+{
+ auto j2 = R"(
+ [{
+ "blob" : "/flash/sink_seq0",
+ "version":{
+ "handler": {
+ "type" : "file",
+ "path" : "/tmp/version_info"
+ },
+ "actions":{
+ "open" :{
+ "type" : "systemd",
+ "unit" : "absolute"
+ }
+ }
+ }
+ },
+ {
+ "blob" : "/version/sink_seq1",
+ "version":{
+ "handler": {
+ "type" : "file",
+ "path" : "/tmp/version_info"
+ },
+ "actions":{
+ "open" :{
+ "type" : "systemd",
+ "unit" : "absolute"
+ }
+ }
+ }
+ },
+ {
+ "blob" : "/bad/sink_seq",
+ "version":{
+ "handler": {
+ "type" : "file",
+ "path" : "/tmp/version_info"
+ },
+ "actions":{
+ "open" :{
+ "type" : "systemd",
+ "unit" : "absolute"
+ }
+ }
+ }
+ }
+ ]
+ )"_json;
+ auto h = VersionHandlersBuilder().buildHandlerFromJson(j2);
+ ASSERT_THAT(h, ::testing::SizeIs(2));
+ EXPECT_THAT(h[0].blobId, "/version/sink_seq0");
+ EXPECT_THAT(h[1].blobId, "/version/sink_seq1");
+}
+
+TEST(VersionJsonTest, BlobNameIsTooShort)
+{
+ auto j2 = R"(
+ [{
+ "blob" : "/flash/",
+ "version":{
+ "handler": {
+ "type" : "file",
+ "path" : "/tmp/version_info"
+ },
+ "actions":{
+ "open" :{
+ "type" : "systemd",
+ "unit" : "absolute"
+ }
+ }
+ }
+ }]
+ )"_json;
+ EXPECT_THAT(VersionHandlersBuilder().buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(VersionJsonTest, OpenSkipAction)
+{
+ auto j2 = R"(
+ [{
+ "blob" : "/flash/sink_seqs",
+ "version":{
+ "handler": {
+ "type" : "file",
+ "path" : "/tmp/version_info"
+ },
+ "actions":{
+ "open" :{
+ "type" : "skip"
+ }
+ }
+ }
+ }]
+ )"_json;
+ auto h = VersionHandlersBuilder().buildHandlerFromJson(j2);
+ EXPECT_THAT(h, ::testing::SizeIs(1));
+ EXPECT_TRUE(h[0].blobId == "/version/sink_seqs");
+ ASSERT_FALSE(h[0].actions == nullptr);
+ EXPECT_FALSE(h[0].actions->onOpen == nullptr);
+}
+
+TEST(VersionJsonTest, OpenActionsWithDifferentModes)
+{
+ auto j2 = R"(
+ [{
+ "blob" : "/flash/blob1",
+ "version":{
+ "handler": {
+ "type" : "file",
+ "path" : "/tmp/version_info"
+ },
+ "actions":{
+ "open" :{
+ "type" : "systemd",
+ "unit" : "absolute",
+ "mode" : "replace-nope"
+ }
+ }
+ }
+ },
+ {
+ "blob" : "/flash/blob2",
+ "version":{
+ "handler": {
+ "type" : "file",
+ "path" : "/tmp/version_info"
+ },
+ "actions":{
+ "open" :{
+ "type" : "systemd",
+ "unit" : "absolute",
+ "mode" : "replace-fake"
+ }
+ }
+ }
+ }
+ ]
+ )"_json;
+ auto h = VersionHandlersBuilder().buildHandlerFromJson(j2);
+ ASSERT_THAT(h, ::testing::SizeIs(2));
+
+ EXPECT_FALSE(h[0].handler == nullptr);
+ EXPECT_FALSE(h[0].actions == nullptr);
+ EXPECT_THAT(h[0].blobId, "/version/blob1");
+ auto onOpen0 = reinterpret_cast<SystemdNoFile*>(h[0].actions->onOpen.get());
+ EXPECT_THAT(onOpen0->getMode(), "replace-nope");
+
+ EXPECT_FALSE(h[1].handler == nullptr);
+ EXPECT_FALSE(h[1].actions == nullptr);
+ EXPECT_THAT(h[1].blobId, "/version/blob2");
+ auto onOpen1 = reinterpret_cast<SystemdNoFile*>(h[1].actions->onOpen.get());
+ EXPECT_THAT(onOpen1->getMode(), "replace-fake");
+}
+} // namespace
+} // namespace ipmi_flash