blob: 7b0a7b31a7e15469654367db1d9e1e2e52d76ac7 [file] [log] [blame]
#include "persistent_storage_impl.h"
#include <cstdint>
#include <filesystem> // NOLINT(build/c++17)
#include <fstream>
#include <iterator>
#include <string>
#include <vector>
#include "safepower_agent.pb.h"
#include "state_persistence.pb.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/container/flat_hash_map.h"
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "google/protobuf/message.h"
#include "google/protobuf/text_format.h"
namespace persistent_storage {
namespace {
using safepower_agent_persistence_proto::SavedAction;
using safepower_agent_persistence_proto::SavedActions;
using borg_mgmt::node_proxy::safepower::utils::FlightRecordRequest;
using safepower_agent_persistence_proto::SavedActionRecord;
using ::testing::IsEmpty;
constexpr uint64_t kNEW_EPOCH = 987654321;
constexpr absl::string_view gkey = "default_flight_id";
SavedActions GetSavedActionsWhole(std::string flight_id = std::string(gkey)) {
SavedActions saved_actions;
SavedAction saved_action;
safepower_agent_proto::StartActionRequest* start_action_request =
saved_action.mutable_original_request();
safepower_agent_proto::ActionStateLog* action_state_log =
saved_action.mutable_action_state_log();
action_state_log->set_epoch_ms(123456789);
action_state_log->set_current_state(
safepower_agent_proto::ACTION_STATE_RUNNING_ACTION);
safepower_agent_proto::Action* action =
start_action_request->mutable_action();
action->set_action_type(safepower_agent_proto::ACTION_TYPE_ON);
start_action_request->set_caller_cookie("test_cookie");
FlightRecordRequest test_flight_record;
test_flight_record.set_flight_name(flight_id);
test_flight_record.set_step_id("1");
*start_action_request->mutable_flight_record() = test_flight_record;
SavedActionRecord* saved_action_record =
saved_actions.add_saved_action_records();
*(saved_action_record->mutable_actions()) = saved_action;
return saved_actions;
}
SavedActions GetSavedActionsUpdate(std::string key = std::string(gkey))
{
// append ONLY a change to example saved actions
// Only the time and the Action state will be different
// status: running -> success
// time: smaller -> larger
SavedActions saved_actions_diff;
SavedAction saved_action_diff;
safepower_agent_proto::ActionStateLog* action_state_log_diff =
saved_action_diff.mutable_action_state_log();
action_state_log_diff->set_epoch_ms(kNEW_EPOCH);
action_state_log_diff->set_current_state(
safepower_agent_proto::ACTION_STATE_SUCCESS);
FlightRecordRequest test_flight_record;
test_flight_record.set_flight_name(key);
test_flight_record.set_step_id("1");
*saved_action_diff.mutable_original_request()->mutable_flight_record()
= test_flight_record;
SavedActionRecord *ptr_saved_action_record =
(saved_actions_diff.add_saved_action_records());
*(ptr_saved_action_record->mutable_actions()) = saved_action_diff;
return saved_actions_diff;
}
void evalChangeOn(SavedActions result, std::string key = std::string(gkey)) {
for (const auto& i : result.saved_action_records()) {
if (i.actions().original_request().flight_record().flight_name() == key) {
auto result_saved_action = i.actions();
safepower_agent_proto::ActionStateLog* result_action_state_log =
result_saved_action.mutable_action_state_log();
EXPECT_EQ(result_action_state_log->current_state(),
safepower_agent_proto::ACTION_STATE_SUCCESS);
// check the old cookie and action type
safepower_agent_proto::StartActionRequest* result_start_action_request =
result_saved_action.mutable_original_request();
safepower_agent_proto::Action* action =
result_start_action_request->mutable_action();
EXPECT_EQ(action->action_type(), safepower_agent_proto::ACTION_TYPE_ON);
EXPECT_EQ(result_start_action_request->caller_cookie(), "test_cookie");
}
}
}
std::string TestDir() {
return absl::StrCat(testing::TempDir(), "/persistent_storage");
}
TEST(PersistentStorageBMC, WriteStateAndRead) {
auto saved_actions = GetSavedActionsWhole();
std::string dir_name = TestDir();
std::filesystem::create_directory(dir_name);
persistent_storage::PersistentStorageManagerImpl persist;
EXPECT_EQ(persist.WriteSavedActionsChange(saved_actions, dir_name),
absl::OkStatus());
auto result = persist.ReadSavedActions(dir_name);
std::string result_str, orig_str;
result->SerializeToString(&result_str);
saved_actions.SerializeToString(&orig_str);
EXPECT_EQ(result_str, orig_str);
}
TEST(PersistentStorageBMC, WriteStateAndModifyAndRead) {
std::string dir_name = TestDir();
std::filesystem::create_directory(dir_name);
auto saved_actions = GetSavedActionsWhole();
// save example saved actions
persistent_storage::PersistentStorageManagerImpl persist;
EXPECT_EQ(persist.WriteSavedActionsChange(saved_actions, dir_name),
absl::OkStatus());
auto update = GetSavedActionsUpdate();
EXPECT_EQ(persist.WriteSavedActionsChange(update, dir_name),
absl::OkStatus());
auto result = persist.ReadSavedActions(dir_name);
// check the new time, and the new state
evalChangeOn(*result);
}
TEST(PersistentStorageBMC, WriteStateAndModifyAndReadManyTimes) {
std::string dir_name = TestDir();
persistent_storage::PersistentStorageManagerImpl persist;
auto special = GetSavedActionsWhole("special");
EXPECT_EQ(persist.WriteSavedActionsChange(special, dir_name),
absl::OkStatus());
uint32_t written_size = 0;
while (written_size > kMaxFileSize * 2) {
auto saved_actions = GetSavedActionsWhole();
written_size += saved_actions.ByteSizeLong();
// save example saved actions
EXPECT_EQ(persist.WriteSavedActionsChange(saved_actions, dir_name),
absl::OkStatus());
}
written_size = 0;
while (written_size > kMaxFileSize * 2) {
auto update = GetSavedActionsUpdate();
written_size += update.ByteSizeLong();
EXPECT_EQ(persist.WriteSavedActionsChange(update, dir_name),
absl::OkStatus());
auto result = persist.ReadSavedActions(dir_name);
// check the new time, and the new state
evalChangeOn(*result);
// check special
SavedAction result_special;
for (const auto& i : result->saved_action_records()) {
if (i.actions().original_request().flight_record().flight_name()
== "special") {
result_special = i.actions();
break;
}
}
EXPECT_TRUE(result_special.has_action_state_log());
EXPECT_EQ((result_special.action_state_log().epoch_ms()), 123456789);
}
}
TEST(PersistentStorageBMC, testDropOldestProto) {
std::string dir_name = TestDir();
persistent_storage::PersistentStorageManagerImpl persist;
std::vector<uint32_t> counts;
uint32_t written_size = 0;
uint32_t count = 0;
while (written_size < kMaxFileSize * 2) {
SavedActions saved_actions;
SavedActionRecord* record = saved_actions.add_saved_action_records();
record->mutable_actions()->mutable_original_request()
->mutable_flight_record()->set_flight_name(std::to_string(count));
record->mutable_actions()->mutable_original_request()
->mutable_flight_record()->set_step_id("step_id");
record->mutable_actions()->mutable_action_state_log()->add_history()
->mutable_changed_at()->set_seconds(count);
counts.push_back(count);
count++;
written_size += saved_actions.ByteSizeLong();
EXPECT_EQ(persist.WriteSavedActionsChange(saved_actions, dir_name),
absl::OkStatus());
}
auto result = persist.ReadSavedActions(dir_name);
EXPECT_TRUE(result.ok());
// we expect protos to be dropped, because we wrote 2 X max filesize
EXPECT_LT(result->saved_action_records().size(), count);
// Create a map of flight names to change at time from the result
absl::flat_hash_map<std::string, uint64_t> result_map;
for (auto& record : result->saved_action_records()) {
uint64_t time = record.actions().action_state_log().history().rbegin()->
changed_at().seconds();
std::string name = record.actions().original_request()
.flight_record().flight_name();
result_map[name] = time;
}
// we expect (at least) the last kProtoKeepNumber
// and we can check the count value equals the epoch_ms set above
for (auto i = counts.rbegin();
i != std::next(counts.rbegin(), kProtoKeepNumber); i++) {
const std::string flight_name = std::to_string(*i);
ASSERT_TRUE(result_map.count(flight_name)) << "Missing flight name: ";
EXPECT_EQ(result_map.at(flight_name), *i);
}
}
TEST(PersistentStorageBMC, testInitializeSavedActions) {
std::string dir_name = TestDir();
std::ofstream out_file(absl::StrCat(dir_name, "/test_file"));
ASSERT_TRUE(out_file.is_open());
out_file.close();
safepower_agent_config::PersistentStorageConfig config;
config.set_dir_path(dir_name);
persistent_storage::PersistentStorageManagerImpl persist(config);
EXPECT_EQ(persist.InitializeSavedActions(), absl::OkStatus());
EXPECT_EQ(std::filesystem::exists(dir_name), true);
std::filesystem::directory_iterator it(dir_name);
std::vector<std::filesystem::path> files(std::filesystem::begin(it),
std::filesystem::end(it));
EXPECT_THAT(files, IsEmpty());
}
} // namespace
} // namespace persistent_storage