-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #332 from foundriesio/detsch-move-main-daemon
Move aklite daemon function to a new file and add unit test for it
- Loading branch information
Showing
7 changed files
with
205 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
#include <sys/file.h> | ||
#include <memory> | ||
#include <thread> | ||
|
||
#include "aktualizr-lite/aklite_client_ext.h" | ||
#include "aktualizr-lite/api.h" | ||
#include "daemon.h" | ||
#include "libaktualizr/config.h" | ||
#include "liteclient.h" | ||
#include "logging/logging.h" | ||
|
||
int run_daemon(LiteClient& client, uint64_t interval, bool return_on_sleep, bool acquire_lock) { | ||
if (client.config.uptane.repo_server.empty()) { | ||
LOG_ERROR << "[uptane]/repo_server is not configured"; | ||
return EXIT_FAILURE; | ||
} | ||
if (access(client.config.bootloader.reboot_command.c_str(), X_OK) != 0) { | ||
LOG_ERROR << "reboot command: " << client.config.bootloader.reboot_command << " is not executable"; | ||
return EXIT_FAILURE; | ||
} | ||
|
||
std::shared_ptr<LiteClient> client_ptr{&client, [](LiteClient* /*unused*/) {}}; | ||
AkliteClientExt akclient{client_ptr, false, acquire_lock}; | ||
if (akclient.IsInstallationInProgress()) { | ||
auto finalize_result = akclient.CompleteInstallation(); | ||
if (finalize_result.status == InstallResult::Status::NeedsCompletion) { | ||
LOG_ERROR << "A system reboot is required to finalize the pending installation."; | ||
return EXIT_FAILURE; | ||
} | ||
} | ||
|
||
Uptane::HardwareIdentifier hwid(client.config.provision.primary_ecu_hardware_id); | ||
|
||
while (true) { | ||
auto current = akclient.GetCurrent(); | ||
LOG_INFO << "Active Target: " << current.Name() << ", sha256: " << current.Sha256Hash(); | ||
LOG_INFO << "Checking for a new Target..."; | ||
auto cir = akclient.GetTargetToInstall(); | ||
if (!cir.selected_target.IsUnknown()) { | ||
LOG_INFO << "Going to install " << cir.selected_target.Name() << ". Reason: " << cir.reason; | ||
// A target is supposed to be installed | ||
auto install_result = akclient.PullAndInstall(cir.selected_target, cir.reason); | ||
if (akclient.RebootIfRequired()) { | ||
// no point to continue running TUF cycle (check for update, download, install) | ||
// since reboot is required to apply/finalize the currently installed update (aka pending update) | ||
// If a reboot command is set in configuration, and is executed successfully, we will not get to this point | ||
break; | ||
} | ||
if (install_result) { | ||
// After a successful install, do not sleep, go directly to the next iteration | ||
continue; | ||
} | ||
} | ||
|
||
if (return_on_sleep) { | ||
break; | ||
} | ||
|
||
std::this_thread::sleep_for(std::chrono::seconds(interval)); | ||
} // while true | ||
|
||
return EXIT_SUCCESS; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#ifndef AKTUALIZR_LITE_DAEMON_H_ | ||
#define AKTUALIZR_LITE_DAEMON_H_ | ||
|
||
#include <cstdint> | ||
|
||
#include "aktualizr-lite/api.h" | ||
|
||
int run_daemon(LiteClient& client, uint64_t interval, bool return_on_sleep, bool acquire_lock); | ||
|
||
#endif // AKTUALIZR_LITE_DAEMON_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
#include <gmock/gmock.h> | ||
#include <gtest/gtest.h> | ||
|
||
#include <boost/filesystem.hpp> | ||
#include <boost/process.hpp> | ||
#include <boost/process/env.hpp> | ||
|
||
#include "libaktualizr/types.h" | ||
#include "logging/logging.h" | ||
#include "test_utils.h" | ||
#include "uptane_generator/image_repo.h" | ||
#include "utilities/utils.h" | ||
|
||
#include "aktualizr-lite/api.h" | ||
#include "composeappmanager.h" | ||
#include "daemon.h" | ||
#include "liteclient.h" | ||
|
||
#include "fixtures/liteclienttest.cc" | ||
|
||
using ::testing::NiceMock; | ||
using ::testing::Return; | ||
|
||
/** | ||
* Class MockAppEngine | ||
* | ||
*/ | ||
class MockAppEngine : public AppEngine { | ||
public: | ||
MockAppEngine(bool default_behaviour = true) { | ||
if (!default_behaviour) return; | ||
|
||
ON_CALL(*this, fetch).WillByDefault(Return(true)); | ||
ON_CALL(*this, verify).WillByDefault(Return(true)); | ||
ON_CALL(*this, install).WillByDefault(Return(true)); | ||
ON_CALL(*this, run).WillByDefault(Return(true)); | ||
ON_CALL(*this, isFetched).WillByDefault(Return(true)); | ||
ON_CALL(*this, isRunning).WillByDefault(Return(true)); | ||
ON_CALL(*this, getRunningAppsInfo) | ||
.WillByDefault( | ||
Return(Utils::parseJSON("{\"app-07\": {\"services\": {\"nginx-07\": {\"hash\": " | ||
"\"16e36b4ab48cb19c7100a22686f85ffcbdce5694c936bda03cb12a2cce88efcf\"}}}}"))); | ||
} | ||
|
||
public: | ||
MOCK_METHOD(AppEngine::Result, fetch, (const App& app), (override)); | ||
MOCK_METHOD(AppEngine::Result, verify, (const App& app), (override)); | ||
MOCK_METHOD(AppEngine::Result, install, (const App& app), (override)); | ||
MOCK_METHOD(AppEngine::Result, run, (const App& app), (override)); | ||
MOCK_METHOD(void, stop, (const App& app), (override)); | ||
MOCK_METHOD(void, remove, (const App& app), (override)); | ||
MOCK_METHOD(bool, isFetched, (const App& app), (const, override)); | ||
MOCK_METHOD(bool, isRunning, (const App& app), (const, override)); | ||
MOCK_METHOD(AppEngine::Apps, getInstalledApps, (), (const, override)); | ||
MOCK_METHOD(Json::Value, getRunningAppsInfo, (), (const, override)); | ||
MOCK_METHOD(void, prune, (const Apps& app), (override)); | ||
}; | ||
|
||
class DaemonTest : public fixtures::ClientTest { | ||
protected: | ||
std::shared_ptr<LiteClient> createLiteClient(InitialVersion initial_version = InitialVersion::kOn, | ||
boost::optional<std::vector<std::string>> apps = boost::none, | ||
bool finalize = true) override { | ||
app_engine_mock_ = std::make_shared<NiceMock<MockAppEngine>>(); | ||
lite_client_ = ClientTest::createLiteClient(app_engine_mock_, initial_version, apps); | ||
return lite_client_; | ||
} | ||
|
||
private: | ||
std::shared_ptr<NiceMock<MockAppEngine>> app_engine_mock_; | ||
std::shared_ptr<LiteClient> lite_client_; | ||
std::string pacman_type_; | ||
}; | ||
|
||
TEST_F(DaemonTest, MainDaemonOstreeInstall) { | ||
auto liteclient = createLiteClient(); | ||
ASSERT_TRUE(targetsMatch(liteclient->getCurrent(), getInitialTarget())); | ||
|
||
// Create a new Target: update rootfs and commit it into Treehub's repo | ||
auto new_target = createTarget(); | ||
|
||
// Run one iteration of daemon code: install should be successful, requiring a reboot | ||
auto daemon_ret = run_daemon(*liteclient, 100, true, false); | ||
ASSERT_EQ(daemon_ret, EXIT_SUCCESS); | ||
|
||
// Trying to run daemon again before rebooting leads to an error, and the original target still running | ||
daemon_ret = run_daemon(*liteclient, 100, true, false); | ||
ASSERT_EQ(daemon_ret, EXIT_FAILURE); | ||
ASSERT_TRUE(targetsMatch(liteclient->getCurrent(), getInitialTarget())); | ||
|
||
reboot(liteclient); | ||
|
||
// After reboot, running the daemon again finishes the installation, the function returns successfully, | ||
// and the new target becomes the current one | ||
daemon_ret = run_daemon(*liteclient, 100, true, false); | ||
ASSERT_EQ(daemon_ret, EXIT_SUCCESS); | ||
ASSERT_TRUE(targetsMatch(liteclient->getCurrent(), new_target)); | ||
} | ||
|
||
int main(int argc, char** argv) { | ||
if (argc != 3) { | ||
std::cerr << argv[0] << " invalid arguments\n"; | ||
return EXIT_FAILURE; | ||
} | ||
|
||
::testing::InitGoogleTest(&argc, argv); | ||
logger_init(); | ||
|
||
// options passed as args in CMakeLists.txt | ||
fixtures::DeviceGatewayMock::RunCmd = argv[1]; | ||
fixtures::SysRootFS::CreateCmd = argv[2]; | ||
return RUN_ALL_TESTS(); | ||
} |