Serialize and deserialize instance database
Serialize the instance database that is basically a set of
instance groups. Load from json to build the same database.
Added unit tests.
Bug: 269235573
Test: atest -c --host cvd_db_test
Change-Id: Ie6aa70ea9d28df61b6681e700ef672099c0404c9
diff --git a/host/commands/cvd/selector/instance_database.h b/host/commands/cvd/selector/instance_database.h
index 00be774..74ad25a 100644
--- a/host/commands/cvd/selector/instance_database.h
+++ b/host/commands/cvd/selector/instance_database.h
@@ -55,6 +55,9 @@
Result<ConstRef<LocalInstanceGroup>> AddInstanceGroup(
const AddInstanceGroupParam& param);
+ Json::Value Serialize() const;
+ Result<void> LoadFromJson(const Json::Value&);
+
/**
* Adds instance to the group.
*
@@ -79,6 +82,7 @@
* RemoveInstanceGroup(group)
*/
bool RemoveInstanceGroup(const LocalInstanceGroup& group);
+ bool RemoveInstanceGroup(const std::string& group_name);
void Clear();
Result<Set<ConstRef<LocalInstanceGroup>>> FindGroups(
@@ -138,9 +142,13 @@
Result<LocalInstanceGroup*> FindMutableGroup(const std::string& group_name);
+ Result<void> LoadGroupFromJson(const Json::Value& group_json);
+
std::vector<std::unique_ptr<LocalInstanceGroup>> local_instance_groups_;
Map<FieldName, ConstGroupHandler> group_handlers_;
Map<FieldName, ConstInstanceHandler> instance_handlers_;
+
+ static constexpr const char kJsonGroups[] = "Groups";
};
} // namespace selector
diff --git a/host/commands/cvd/selector/instance_database_impl.cpp b/host/commands/cvd/selector/instance_database_impl.cpp
index d9f6538..af19585 100644
--- a/host/commands/cvd/selector/instance_database_impl.cpp
+++ b/host/commands/cvd/selector/instance_database_impl.cpp
@@ -133,6 +133,15 @@
return group_ptr;
}
+bool InstanceDatabase::RemoveInstanceGroup(const std::string& group_name) {
+ auto group_result = FindGroup({kGroupNameField, group_name});
+ if (!group_result.ok()) {
+ return false;
+ }
+ const LocalInstanceGroup& group = group_result->Get();
+ return RemoveInstanceGroup(group);
+}
+
bool InstanceDatabase::RemoveInstanceGroup(const LocalInstanceGroup& group) {
auto itr = FindIterator(group);
// *itr is the reference to the unique pointer object
@@ -247,5 +256,75 @@
collector, local_instance_groups_);
}
+Json::Value InstanceDatabase::Serialize() const {
+ Json::Value instance_db_json;
+ int i = 0;
+ Json::Value group_array;
+ for (const auto& local_instance_group : local_instance_groups_) {
+ group_array[i] = local_instance_group->Serialize();
+ ++i;
+ }
+ instance_db_json[kJsonGroups] = group_array;
+ return instance_db_json;
+}
+
+Result<void> InstanceDatabase::LoadGroupFromJson(
+ const Json::Value& group_json) {
+ const std::string group_name =
+ group_json[LocalInstanceGroup::kJsonGroupName].asString();
+ const std::string home_dir =
+ group_json[LocalInstanceGroup::kJsonHomeDir].asString();
+ const std::string host_artifacts_path =
+ group_json[LocalInstanceGroup::kJsonHostArtifactPath].asString();
+ const std::string product_out_path =
+ group_json[LocalInstanceGroup::kJsonProductOutPath].asString();
+ const std::string build_id_value =
+ group_json[LocalInstanceGroup::kJsonBuildId].asString();
+ std::optional<std::string> build_id;
+ if (build_id_value != LocalInstanceGroup::kJsonUnknownBuildId) {
+ build_id = build_id_value;
+ }
+ const auto new_group_ref =
+ CF_EXPECT(AddInstanceGroup({.group_name = group_name,
+ .home_dir = home_dir,
+ .host_artifacts_path = host_artifacts_path,
+ .product_out_path = product_out_path}));
+ if (build_id) {
+ CF_EXPECT(SetBuildId(group_name, *build_id));
+ }
+ const Json::Value& instances_json_array =
+ group_json[LocalInstanceGroup::kJsonInstances];
+ for (int i = 0; i < instances_json_array.size(); i++) {
+ const Json::Value& instance_json = instances_json_array[i];
+ const std::string instance_name =
+ instance_json[LocalInstance::kJsonInstanceName].asString();
+ const std::string instance_id =
+ instance_json[LocalInstance::kJsonInstanceId].asString();
+ int id;
+ auto parse_result =
+ android::base::ParseInt(instance_id, std::addressof(id));
+ if (!parse_result) {
+ CF_EXPECT(parse_result == true,
+ "Invalid instance ID in instance json: " << instance_id);
+ RemoveInstanceGroup(new_group_ref.Get());
+ }
+ auto add_instance_result = AddInstance(group_name, id, instance_name);
+ if (!add_instance_result.ok()) {
+ RemoveInstanceGroup(new_group_ref.Get());
+ CF_EXPECT(add_instance_result.ok(), add_instance_result.error().Trace());
+ }
+ }
+ return {};
+}
+
+Result<void> InstanceDatabase::LoadFromJson(const Json::Value& db_json) {
+ const Json::Value& group_array = db_json[kJsonGroups];
+ int n_groups = group_array.size();
+ for (int i = 0; i < n_groups; i++) {
+ CF_EXPECT(LoadGroupFromJson(group_array[i]));
+ }
+ return {};
+}
+
} // namespace selector
} // namespace cuttlefish
diff --git a/host/commands/cvd/selector/instance_group_record.h b/host/commands/cvd/selector/instance_group_record.h
index eb2402e..a302092 100644
--- a/host/commands/cvd/selector/instance_group_record.h
+++ b/host/commands/cvd/selector/instance_group_record.h
@@ -90,6 +90,7 @@
Set<std::unique_ptr<LocalInstance>> CopyInstances(
const Set<std::unique_ptr<LocalInstance>>& src_instances);
Json::Value Serialize(const std::unique_ptr<LocalInstance>& instance) const;
+
std::string home_dir_;
std::string host_artifacts_path_;
std::string product_out_path_;
diff --git a/host/commands/cvd/selector/instance_record.h b/host/commands/cvd/selector/instance_record.h
index 3a83bf4..f6b86b5 100644
--- a/host/commands/cvd/selector/instance_record.h
+++ b/host/commands/cvd/selector/instance_record.h
@@ -33,6 +33,7 @@
*/
class LocalInstance {
friend class LocalInstanceGroup;
+ friend class InstanceDatabase;
public:
/* names:
diff --git a/host/commands/cvd/unittests/selector/instance_database_helper.h b/host/commands/cvd/unittests/selector/instance_database_helper.h
index 119c6e4..6e7b862 100644
--- a/host/commands/cvd/unittests/selector/instance_database_helper.h
+++ b/host/commands/cvd/unittests/selector/instance_database_helper.h
@@ -99,5 +99,7 @@
InstanceDatabase db_;
};
+using CvdInstanceDatabaseJsonTest = CvdInstanceDatabaseTest;
+
} // namespace selector
} // namespace cuttlefish
diff --git a/host/commands/cvd/unittests/selector/instance_database_test.cpp b/host/commands/cvd/unittests/selector/instance_database_test.cpp
index b96ace7..1d128c9 100644
--- a/host/commands/cvd/unittests/selector/instance_database_test.cpp
+++ b/host/commands/cvd/unittests/selector/instance_database_test.cpp
@@ -14,11 +14,13 @@
// limitations under the License.
#include <algorithm>
+#include <iostream>
#include <unordered_set>
#include <gtest/gtest.h>
#include "common/libs/utils/files.h"
+#include "common/libs/utils/json.h"
#include "host/commands/cvd/selector/instance_database.h"
#include "host/commands/cvd/selector/selector_constants.h"
#include "host/commands/cvd/unittests/selector/instance_database_helper.h"
@@ -426,5 +428,48 @@
ASSERT_TRUE(result_tv.ok()) << result_tv.error().Trace();
}
+TEST_F(CvdInstanceDatabaseJsonTest, DumpLoadDumpCompare) {
+ // starting set up
+ if (!SetUpOk() || !AddGroups({"miau"})) {
+ GTEST_SKIP() << Error().msg;
+ }
+ auto& db = GetDb();
+ std::vector<InstanceDatabase::InstanceInfo> miau_group_instance_id_name_pairs{
+ {1, "8"}, {10, "tv_instance"}};
+ auto miau_group = db.FindGroup({kHomeField, Workspace() + "/" + "miau"});
+ if (!miau_group.ok()) {
+ GTEST_SKIP() << "miau group was not found";
+ }
+ auto add_result = db.AddInstances("miau", miau_group_instance_id_name_pairs);
+ if (!add_result.ok()) {
+ GTEST_SKIP() << "Adding instances are not being tested in this test case.";
+ }
+
+ /*
+ * Dumping to json, clearing up the DB, loading from the json,
+ *
+ */
+ auto serialized_db = db.Serialize();
+ if (!db.RemoveInstanceGroup("miau")) {
+ // not testing RemoveInstanceGroup
+ GTEST_SKIP() << "miau had to be added.";
+ }
+ auto json_parsing = ParseJson(serialized_db.toStyledString());
+ ASSERT_TRUE(json_parsing.ok()) << serialized_db << std::endl
+ << " is not a valid json.";
+ auto load_result = db.LoadFromJson(serialized_db);
+ ASSERT_TRUE(load_result.ok()) << load_result.error().Trace();
+ {
+ // re-look up the group and the instances
+ auto miau_group = db.FindGroup({kHomeField, Workspace() + "/" + "miau"});
+ ASSERT_TRUE(miau_group.ok()) << miau_group.error().Trace();
+ auto result_8 = db.FindInstance({kInstanceNameField, "8"});
+ auto result_tv = db.FindInstance({kInstanceNameField, "tv_instance"});
+
+ ASSERT_TRUE(result_8.ok()) << result_8.error().Trace();
+ ASSERT_TRUE(result_tv.ok()) << result_tv.error().Trace();
+ }
+}
+
} // namespace selector
} // namespace cuttlefish