CheckpointManager.cpp (6644B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "CheckpointManager.h" 5 #include "Command.h" 6 #include "ExecutionContextData.h" 7 #include <AppInstallerRuntime.h> 8 9 using namespace AppInstaller::CLI; 10 using namespace AppInstaller::Repository::Microsoft; 11 using namespace AppInstaller::SQLite; 12 13 namespace AppInstaller::Checkpoints 14 { 15 16 // This checkpoint name is reserved for the starting checkpoint which captures the automatic metadata. 17 constexpr std::string_view s_AutomaticCheckpoint = "automatic"sv; 18 constexpr std::string_view s_CheckpointsFileName = "checkpoints.db"sv; 19 20 std::filesystem::path CheckpointManager::GetCheckpointDatabasePath(const std::string_view& resumeId, bool createCheckpointDirectory) 21 { 22 const auto checkpointsDirectory = Runtime::GetPathTo(Runtime::PathName::CheckpointsLocation) / resumeId; 23 24 if (createCheckpointDirectory) 25 { 26 if (!std::filesystem::exists(checkpointsDirectory)) 27 { 28 AICLI_LOG(Repo, Info, << "Creating checkpoint database directory: " << checkpointsDirectory); 29 std::filesystem::create_directories(checkpointsDirectory); 30 } 31 else 32 { 33 THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_CANNOT_MAKE), !std::filesystem::is_directory(checkpointsDirectory)); 34 } 35 } 36 37 auto recordPath = checkpointsDirectory / s_CheckpointsFileName; 38 return recordPath; 39 } 40 41 CheckpointManager::CheckpointManager() 42 { 43 GUID resumeId; 44 std::ignore = CoCreateGuid(&resumeId); 45 m_resumeId = Utility::ConvertGuidToString(resumeId); 46 const auto& checkpointDatabasePath = GetCheckpointDatabasePath(m_resumeId, true); 47 m_checkpointDatabase = CheckpointDatabase::CreateNew(checkpointDatabasePath.u8string()); 48 } 49 50 CheckpointManager::CheckpointManager(const std::string& resumeId) 51 { 52 m_resumeId = resumeId; 53 const auto& checkpointDatabasePath = GetCheckpointDatabasePath(m_resumeId); 54 m_checkpointDatabase = CheckpointDatabase::Open(checkpointDatabasePath.u8string()); 55 } 56 57 void CheckpointManager::CreateAutomaticCheckpoint(CLI::Execution::Context& context) 58 { 59 CheckpointDatabase::IdType startCheckpointId = m_checkpointDatabase->AddCheckpoint(s_AutomaticCheckpoint); 60 Checkpoint<AutomaticCheckpointData> automaticCheckpoint{ m_checkpointDatabase, startCheckpointId }; 61 62 automaticCheckpoint.Set(AutomaticCheckpointData::ClientVersion, {}, AppInstaller::Runtime::GetClientVersion()); 63 64 const auto& executingCommand = context.GetExecutingCommand(); 65 if (executingCommand != nullptr) 66 { 67 automaticCheckpoint.Set(AutomaticCheckpointData::Command, {}, std::string{ executingCommand->FullName() }); 68 } 69 70 const auto& argTypes = context.Args.GetTypes(); 71 for (auto type : argTypes) 72 { 73 const auto& argument = std::to_string(static_cast<int>(type)); 74 auto argumentType = Argument::ForType(type).Type(); 75 76 if (argumentType == ArgumentType::Flag) 77 { 78 automaticCheckpoint.Set(AutomaticCheckpointData::Arguments, argument, {}); 79 } 80 else 81 { 82 const auto& values = *context.Args.GetArgs(type); 83 automaticCheckpoint.SetMany(AutomaticCheckpointData::Arguments, argument, values); 84 } 85 } 86 87 automaticCheckpoint.Set(AutomaticCheckpointData::ResumeCount, {}, std::to_string(0)); 88 } 89 90 void LoadCommandArgsFromAutomaticCheckpoint(CLI::Execution::Context& context, Checkpoint<AutomaticCheckpointData>& automaticCheckpoint) 91 { 92 for (const auto& fieldName : automaticCheckpoint.GetFieldNames(AutomaticCheckpointData::Arguments)) 93 { 94 // Command arguments are represented as integer strings in the checkpoint record. 95 Execution::Args::Type type = static_cast<Execution::Args::Type>(std::stoi(fieldName)); 96 auto argumentType = Argument::ForType(type).Type(); 97 if (argumentType == ArgumentType::Flag) 98 { 99 context.Args.AddArg(type); 100 } 101 else 102 { 103 const auto& values = automaticCheckpoint.GetMany(AutomaticCheckpointData::Arguments, fieldName); 104 for (const auto& value : values) 105 { 106 context.Args.AddArg(type, value); 107 } 108 } 109 } 110 } 111 112 std::optional<Checkpoint<AutomaticCheckpointData>> CheckpointManager::GetAutomaticCheckpoint() 113 { 114 const auto& checkpointIds = m_checkpointDatabase->GetCheckpointIds(); 115 if (checkpointIds.empty()) 116 { 117 return {}; 118 } 119 120 CheckpointDatabase::IdType automaticCheckpointId = checkpointIds.back(); 121 return Checkpoint<AutomaticCheckpointData>{ m_checkpointDatabase, automaticCheckpointId }; 122 } 123 124 Checkpoint<CLI::Execution::Data> CheckpointManager::CreateCheckpoint(std::string_view checkpointName) 125 { 126 CheckpointDatabase::IdType checkpointId = m_checkpointDatabase->AddCheckpoint(checkpointName); 127 Checkpoint<CLI::Execution::Data> checkpoint{ m_checkpointDatabase, checkpointId }; 128 return checkpoint; 129 } 130 131 std::vector<Checkpoint<CLI::Execution::Data>> CheckpointManager::GetCheckpoints() 132 { 133 auto checkpointIds = m_checkpointDatabase->GetCheckpointIds(); 134 if (checkpointIds.empty()) 135 { 136 return {}; 137 } 138 139 // Remove the last checkpoint (automatic) 140 checkpointIds.pop_back(); 141 142 std::vector<Checkpoint<CLI::Execution::Data>> checkpoints; 143 for (const auto& checkpointId : checkpointIds) 144 { 145 checkpoints.emplace_back(Checkpoint<CLI::Execution::Data>{ m_checkpointDatabase, checkpointId }); 146 } 147 148 return checkpoints; 149 } 150 151 void CheckpointManager::CleanUpDatabase() 152 { 153 if (m_checkpointDatabase) 154 { 155 m_checkpointDatabase.reset(); 156 } 157 158 if (!m_resumeId.empty()) 159 { 160 const auto& checkpointDatabasePath = GetCheckpointDatabasePath(m_resumeId); 161 if (std::filesystem::exists(checkpointDatabasePath)) 162 { 163 const auto& checkpointDatabaseParentDirectory = checkpointDatabasePath.parent_path(); 164 AICLI_LOG(CLI, Info, << "Deleting Checkpoint database directory: " << checkpointDatabaseParentDirectory); 165 std::filesystem::remove_all(checkpointDatabaseParentDirectory); 166 } 167 } 168 } 169 }