Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 38 additions & 4 deletions src/common/ocispec/imageconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,32 @@ Poco::JSON::Object ConfigToJSON(const aos::oci::Config& config)
return object;
}

void RootfsFromJSON(const utils::CaseInsensitiveObjectWrapper& object, aos::oci::Rootfs& rootfs)
{
const auto type = object.GetValue<std::string>("type");
rootfs.mType = type.c_str();

for (const auto& diffID : utils::GetArrayValue<std::string>(object, "diff_ids")) {
auto err = rootfs.mDiffIDs.EmplaceBack(diffID.c_str());
AOS_ERROR_CHECK_AND_THROW(err, "diff_ids parsing error");
}
}

Poco::JSON::Object RootfsToJSON(const aos::oci::Rootfs& rootfs)
{
Poco::JSON::Object object {Poco::JSON_PRESERVE_KEY_ORDER};

if (!rootfs.mType.IsEmpty()) {
object.set("type", rootfs.mType.CStr());
}

if (!rootfs.mDiffIDs.IsEmpty()) {
object.set("diff_ids", utils::ToJsonArray(rootfs.mDiffIDs, utils::ToStdString));
}

return object;
}

} // namespace

/***********************************************************************************************************************
Expand All @@ -87,10 +113,6 @@ Error OCISpec::LoadImageConfig(const String& path, aos::oci::ImageConfig& imageC
Poco::JSON::Object::Ptr object = var.extract<Poco::JSON::Object::Ptr>();
utils::CaseInsensitiveObjectWrapper wrapper(object);

if (wrapper.Has("config")) {
ConfigFromJSON(wrapper.GetObject("config"), imageConfig.mConfig);
}

imageConfig.mAuthor = wrapper.GetValue<std::string>("author").c_str();

PlatformFromJSONObject(wrapper, imageConfig);
Expand All @@ -99,6 +121,14 @@ Error OCISpec::LoadImageConfig(const String& path, aos::oci::ImageConfig& imageC
Tie(imageConfig.mCreated, err) = utils::FromUTCString(created->c_str());
AOS_ERROR_CHECK_AND_THROW(err, "created time parsing error");
}

if (wrapper.Has("config")) {
ConfigFromJSON(wrapper.GetObject("config"), imageConfig.mConfig);
}

if (wrapper.Has("rootfs")) {
RootfsFromJSON(wrapper.GetObject("rootfs"), imageConfig.mRootfs);
}
} catch (const std::exception& e) {
return AOS_ERROR_WRAP(utils::ToAosError(e));
}
Expand Down Expand Up @@ -128,6 +158,10 @@ Error OCISpec::SaveImageConfig(const String& path, const aos::oci::ImageConfig&
object->set("config", configObject);
}

if (auto rootfsObject = RootfsToJSON(imageConfig.mRootfs); rootfsObject.size() > 0) {
object->set("rootfs", rootfsObject);
}

auto err = utils::WriteJsonToFile(object, path.CStr());
AOS_ERROR_CHECK_AND_THROW(err, "failed to write json to file");
} catch (const std::exception& e) {
Expand Down
14 changes: 10 additions & 4 deletions src/common/ocispec/tests/ocispec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ constexpr auto cImageConfig = R"(
{
"architecture": "x86_64",
"author": "gtest",
"created": "2024-12-31T23:59:59Z",
"os": "Linux",
"osVersion": "6.0.8",
"variant": "6",
"config": {
"cmd": [
"test-cmd",
Expand All @@ -96,10 +100,12 @@ constexpr auto cImageConfig = R"(
],
"workingDir": "/test-working-dir"
},
"created": "2024-12-31T23:59:59Z",
"os": "Linux",
"osVersion": "6.0.8",
"variant": "6"
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:129abeb509f55870ec19f24eba0caecccee3f0e055c467e1df8513bdcddc746f"
]
}
}
)";
const auto cServiceConfigPath = fs::JoinPath(cTestBaseDir, "service_config.json");
Expand Down
19 changes: 19 additions & 0 deletions src/common/utils/filesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,23 @@ RetWithError<uintmax_t> CalculateSize(const std::string& path)
return {0, ErrorEnum::eNotSupported};
}

void ChangeOwner(const std::string& path, uid_t uid, gid_t gid)
{
auto changeOwner = [](const std::string& path, uid_t uid, gid_t gid) {
if (chown(path.c_str(), uid, gid) == -1) {
AOS_ERROR_THROW(errno, "can't change file owner");
}
};

changeOwner(path, uid, gid);

if (std::filesystem::is_regular_file(path)) {
return;
}

for (const auto& entry : std::filesystem::recursive_directory_iterator(path)) {
changeOwner(entry.path().string(), uid, gid);
}
}

} // namespace aos::common::utils
9 changes: 9 additions & 0 deletions src/common/utils/filesystem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ RetWithError<std::string> MkTmpDir(const std::string& dir = "", const std::strin
*/
RetWithError<uintmax_t> CalculateSize(const std::string& path);

/**
* Changes owner of file or directory.
*
* @param path file or directory path.
* @param uid user ID.
* @param gid group ID.
*/
void ChangeOwner(const std::string& path, uid_t uid, gid_t gid);

} // namespace aos::common::utils

#endif
17 changes: 2 additions & 15 deletions src/common/utils/fsplatform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <core/common/tools/fs.hpp>

#include "exception.hpp"
#include "filesystem.hpp"
#include "fsplatform.hpp"

namespace aos::common::utils {
Expand Down Expand Up @@ -103,21 +104,7 @@ Error FSPlatform::SetUserQuota(const String& path, size_t quota, size_t uid) con
Error FSPlatform::ChangeOwner(const String& path, uint32_t uid, uint32_t gid) const
{
try {
auto changeOwner = [](const char* filePath, uint32_t uid, uint32_t gid) {
if (chown(filePath, uid, gid) == -1) {
AOS_ERROR_THROW(errno, "can't change file owner");
}
};

changeOwner(path.CStr(), uid, gid);

if (std::filesystem::is_regular_file(path.CStr())) {
return ErrorEnum::eNone;
}

for (const auto& entry : std::filesystem::recursive_directory_iterator(path.CStr())) {
changeOwner(entry.path().c_str(), uid, gid);
}
common::utils::ChangeOwner(path.CStr(), uid, gid);
} catch (const std::exception& e) {
return AOS_ERROR_WRAP(utils::ToAosError(e));
}
Expand Down
16 changes: 10 additions & 6 deletions src/common/utils/image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ static void ValidateEncoded(const std::string& algorithm, const std::string& enc
}

const std::regex& r = it->second;

if (encoded.length() != 2 * (algorithm == "sha256" ? 32 : algorithm == "sha384" ? 48 : 64)) {
throw std::runtime_error("Invalid encoded length");
}
Expand Down Expand Up @@ -74,7 +75,7 @@ static std::vector<std::string> CollectFiles(const std::string& dir)
* Public
**********************************************************************************************************************/

std::pair<std::string, std::string> ParseDigest(const std::string& digest)
std::pair<std::string, std::string> ParseDigest(const Digest& digest)
{
auto pos = digest.find(':');
if (pos == std::string::npos) {
Expand Down Expand Up @@ -167,7 +168,7 @@ Error ValidateDigest(const Digest& digest)
std::transform(algorithm.begin(), algorithm.end(), algorithm.begin(), ::tolower);

if (cAnchoredEncodedRegexps.find(algorithm) == cAnchoredEncodedRegexps.end()) {
return Error(ErrorEnum::eInvalidArgument, "Unsupported algorithm");
return Error(ErrorEnum::eInvalidArgument, "unsupported algorithm");
}

try {
Expand All @@ -179,27 +180,30 @@ Error ValidateDigest(const Digest& digest)
return ErrorEnum::eNone;
}

RetWithError<std::string> HashDir(const std::string& dir)
RetWithError<Digest> CalculateDirDigest(const std::string& dir)
{
std::vector<std::string> files = CollectFiles(dir);

std::sort(files.begin(), files.end(), [](const std::string& a, const std::string& b) { return a < b; });

Poco::SHA2Engine h;

for (const auto& file : files) {
if (file.find('\n') != std::string::npos) {
return {"", Error(ErrorEnum::eInvalidArgument, "File names with new lines are not supported")};
return {"", Error(ErrorEnum::eInvalidArgument, "file names with new lines are not supported")};
}

std::ifstream fileStream(file, std::ios::binary);
if (!fileStream.is_open()) {
return {"", Error(ErrorEnum::eFailed, "Failed to open file")};
return {"", Error(ErrorEnum::eFailed, "failed to open file")};
}

Poco::SHA2Engine hf;
Poco::DigestOutputStream dos(hf);
Poco::StreamCopier::copyStream(fileStream, dos);

std::string hash = Poco::DigestEngine::digestToHex(hf.digest());
std::string hash = Poco::DigestEngine::digestToHex(hf.digest()) + " " + fs::relative(file, dir).string();

h.update(hash + "\n");
}

Expand Down
8 changes: 4 additions & 4 deletions src/common/utils/image.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ RetWithError<uint64_t> GetUnpackedArchiveSize(const std::string& archivePath, bo
* @param digest digest string.
* @return std::pair<std::string, std::string> .
*/
std::pair<std::string, std::string> ParseDigest(const std::string& digest);
std::pair<std::string, std::string> ParseDigest(const Digest& digest);

/**
* Validates the digest.
Expand All @@ -50,12 +50,12 @@ std::pair<std::string, std::string> ParseDigest(const std::string& digest);
Error ValidateDigest(const Digest& digest);

/**
* Hashes the directory.
* Calculates directory digest.
*
* @param dir directory path.
* @return std::string.
* @return RetWithError<Digest>.
*/
RetWithError<std::string> HashDir(const std::string& dir);
RetWithError<Digest> CalculateDirDigest(const std::string& dir);

} // namespace aos::common::utils

Expand Down
4 changes: 2 additions & 2 deletions src/common/utils/tests/image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ TEST(ValidateDigestTest, ValidateDigestInvalidLength)
ASSERT_NE(result.Message(), "");
}

TEST(HashDirTest, HashDir)
TEST(ImageTest, CalculateDirDigest)
{
std::string dir = "test_dir";
std::string fileContent = "This is a test content";
Expand All @@ -174,7 +174,7 @@ TEST(HashDirTest, HashDir)
ofs2 << fileContent;
ofs2.close();

auto result = HashDir(dir);
auto result = CalculateDirDigest(dir);

ASSERT_EQ(result.mError, ErrorEnum::eNone);
auto [algorithm, hex] = ParseDigest(result.mValue);
Expand Down
1 change: 1 addition & 0 deletions src/sm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ add_subdirectory(app)
add_subdirectory(config)
add_subdirectory(database)
add_subdirectory(iamclient)
add_subdirectory(imagemanager)
add_subdirectory(logprovider)
add_subdirectory(monitoring)
add_subdirectory(networkmanager)
Expand Down
1 change: 1 addition & 0 deletions src/sm/app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ set(LIBRARIES
aos::sm::config
aos::sm::database
aos::sm::iamclient
aos::sm::imagemanager
aos::sm::monitoring
aos::sm::resourcemanager
aos::sm::networkmanager
Expand Down
43 changes: 43 additions & 0 deletions src/sm/imagemanager/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#
# Copyright (C) 2025 EPAM Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#

set(TARGET_NAME imagemanager)

# ######################################################################################################################
# Sources
# ######################################################################################################################

set(SOURCES imagehandler.cpp)

# ######################################################################################################################
# Libraries
# ######################################################################################################################

set(LIBRARIES aos::common::utils Poco::JSON)

# ######################################################################################################################
# Target
# ######################################################################################################################

add_module(
TARGET_NAME
${TARGET_NAME}
LOG_MODULE
STACK_USAGE
${AOS_STACK_USAGE}
SOURCES
${SOURCES}
LIBRARIES
${LIBRARIES}
)

# ######################################################################################################################
# Tests
# ######################################################################################################################

if(WITH_TEST)
add_subdirectory(tests)
endif()
Loading