From d612fad98753e24cd8d37a4eb9f70c9d56c3d3c6 Mon Sep 17 00:00:00 2001 From: Mykhailo Lohvynenko Date: Wed, 4 Nov 2026 14:04:42 +0200 Subject: [PATCH 1/8] common: pbconvert: handle BlobInfo conversion from proto Signed-off-by: Mykhailo Lohvynenko Reviewed-by: Oleksandr Grytsov --- src/common/pbconvert/sm.cpp | 38 ++++++++++++++++++++++ src/common/pbconvert/sm.hpp | 19 +++++++++++ src/common/pbconvert/tests/sm.cpp | 53 +++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) diff --git a/src/common/pbconvert/sm.cpp b/src/common/pbconvert/sm.cpp index 7c284a61c..f49eac3c8 100644 --- a/src/common/pbconvert/sm.cpp +++ b/src/common/pbconvert/sm.cpp @@ -1309,4 +1309,42 @@ Error ConvertFromProto(const servicemanager::v5::InstanceNetworkStateInfo& src, return ErrorEnum::eNone; } +void ConvertToProto(const BlobInfo& src, servicemanager::v5::BlobInfo& dst) +{ + for (const auto& url : src.mURLs) { + dst.add_urls(url.CStr()); + } + + dst.set_sha256(std::string(reinterpret_cast(src.mSHA256.Get()), src.mSHA256.Size())); + dst.set_size(src.mSize); +} + +Error ConvertFromProto(const servicemanager::v5::BlobInfo& src, const String& digest, BlobInfo& dst) +{ + if (auto err = dst.mDigest.Assign(digest); !err.IsNone()) { + return AOS_ERROR_WRAP(err); + } + + // The proto doesn't carry the decrypt and sign info, so the fields of a reused blob info are reset. + dst.mURLs.Clear(); + dst.mDecryptInfo.Reset(); + dst.mSignInfo.Reset(); + + for (const auto& url : src.urls()) { + if (auto err = dst.mURLs.PushBack(url.c_str()); !err.IsNone()) { + return AOS_ERROR_WRAP(err); + } + } + + if (auto err = dst.mSHA256.Assign( + Array(reinterpret_cast(src.sha256().data()), src.sha256().size())); + !err.IsNone()) { + return AOS_ERROR_WRAP(err); + } + + dst.mSize = src.size(); + + return ErrorEnum::eNone; +} + } // namespace aos::common::pbconvert diff --git a/src/common/pbconvert/sm.hpp b/src/common/pbconvert/sm.hpp index 5984d8dcd..2389a6f60 100644 --- a/src/common/pbconvert/sm.hpp +++ b/src/common/pbconvert/sm.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -344,6 +345,24 @@ Error ConvertToProto(const InstanceNetworkStateInfo& src, servicemanager::v5::In */ Error ConvertFromProto(const servicemanager::v5::InstanceNetworkStateInfo& src, InstanceNetworkStateInfo& dst); +/** + * Converts Aos blob info to grpc BlobInfo. + * + * @param src Aos blob info. + * @param[out] dst grpc blob info. + */ +void ConvertToProto(const BlobInfo& src, servicemanager::v5::BlobInfo& dst); + +/** + * Converts grpc BlobInfo to Aos. + * + * @param src grpc blob info. + * @param digest blob digest. + * @param[out] dst Aos blob info. + * @return Error. + */ +Error ConvertFromProto(const servicemanager::v5::BlobInfo& src, const String& digest, BlobInfo& dst); + } // namespace aos::common::pbconvert #endif diff --git a/src/common/pbconvert/tests/sm.cpp b/src/common/pbconvert/tests/sm.cpp index e51a1994e..767893df2 100644 --- a/src/common/pbconvert/tests/sm.cpp +++ b/src/common/pbconvert/tests/sm.cpp @@ -805,4 +805,57 @@ TEST_F(PBConvertSMTest, ConvertInstanceNetworkParamsToAllocateInstanceNetworkRes EXPECT_EQ(result.dns_servers_size(), 0); } +TEST_F(PBConvertSMTest, ConvertBlobInfoToProto) +{ + const uint8_t cSHA256[] = {0x01, 0x02, 0x03, 0x04}; + + auto blobInfo = std::make_unique(); + + blobInfo->mDigest = "sha256:blob"; + blobInfo->mSize = 1024; + + ASSERT_TRUE(blobInfo->mURLs.PushBack("https://example.com/blob1").IsNone()); + ASSERT_TRUE(blobInfo->mURLs.PushBack("https://example.com/blob2").IsNone()); + ASSERT_TRUE(blobInfo->mSHA256.Assign(Array(cSHA256, sizeof(cSHA256))).IsNone()); + + servicemanager::v5::BlobInfo result; + + ConvertToProto(*blobInfo, result); + + ASSERT_EQ(result.urls_size(), 2); + EXPECT_EQ(result.urls(0), "https://example.com/blob1"); + EXPECT_EQ(result.urls(1), "https://example.com/blob2"); + EXPECT_EQ(result.sha256(), std::string("\x01\x02\x03\x04")); + EXPECT_EQ(result.size(), 1024); + EXPECT_FALSE(result.has_error()); +} + +TEST_F(PBConvertSMTest, ConvertBlobInfoFromProto) +{ + const uint8_t cSHA256[] = {0x01, 0x02, 0x03, 0x04}; + + servicemanager::v5::BlobInfo grpcData; + + grpcData.add_urls("https://example.com/blob1"); + grpcData.add_urls("https://example.com/blob2"); + grpcData.set_sha256(std::string("\x01\x02\x03\x04")); + grpcData.set_size(1024); + + auto blobInfo = std::make_unique(); + + ASSERT_TRUE(blobInfo->mURLs.PushBack("https://example.com/stale").IsNone()); + + auto err = ConvertFromProto(grpcData, "sha256:blob", *blobInfo); + ASSERT_TRUE(err.IsNone()) << err.Message(); + + EXPECT_EQ(blobInfo->mDigest, "sha256:blob"); + ASSERT_EQ(blobInfo->mURLs.Size(), 2); + EXPECT_EQ(blobInfo->mURLs[0], "https://example.com/blob1"); + EXPECT_EQ(blobInfo->mURLs[1], "https://example.com/blob2"); + EXPECT_EQ(blobInfo->mSHA256, Array(cSHA256, sizeof(cSHA256))); + EXPECT_EQ(blobInfo->mSize, 1024); + EXPECT_FALSE(blobInfo->mDecryptInfo.HasValue()); + EXPECT_FALSE(blobInfo->mSignInfo.HasValue()); +} + } // namespace aos::common::pbconvert From 88073de402423b9ac37eefa90dcad125eb436a56 Mon Sep 17 00:00:00 2001 From: Mykhailo Lohvynenko Date: Wed, 4 Nov 2026 14:05:35 +0200 Subject: [PATCH 2/8] sm: smclient: handle GetBlobsInfo rpc call Signed-off-by: Mykhailo Lohvynenko Reviewed-by: Oleksandr Grytsov --- src/sm/smclient/smclient.cpp | 24 +++++++++---------- src/sm/smclient/smclient.hpp | 11 ++++----- src/sm/smclient/tests/smclient.cpp | 24 +++++++++++-------- src/sm/smclient/tests/stubs/smservicestub.hpp | 18 ++++++++++---- 4 files changed, 45 insertions(+), 32 deletions(-) diff --git a/src/sm/smclient/smclient.cpp b/src/sm/smclient/smclient.cpp index ad735f568..7ccc22800 100644 --- a/src/sm/smclient/smclient.cpp +++ b/src/sm/smclient/smclient.cpp @@ -239,34 +239,34 @@ Error SMClient::SendUpdateInstancesStatuses(const Array& st return ErrorEnum::eNone; } -Error SMClient::GetBlobsInfo(const Array>& digests, Array>& urls) +Error SMClient::GetBlobInfo(const String& digest, BlobInfo& blobInfo) { std::lock_guard lock {mMutex}; - LOG_DBG() << "Get blobs info" << Log::Field("count", digests.Size()); + LOG_DBG() << "Get blob info" << Log::Field("digest", digest); if (!mStub) { return Error(ErrorEnum::eFailed, "stub not available"); } - smproto::BlobsInfosRequest request; + smproto::BlobsInfoRequest request; - for (const auto& digest : digests) { - request.add_digests(digest.CStr()); - } + request.set_digest(digest.CStr()); grpc::ClientContext context; - smproto::BlobsInfos response; + smproto::BlobInfo response; - auto status = mStub->GetBlobsInfos(&context, request, &response); + auto status = mStub->GetBlobsInfo(&context, request, &response); if (!status.ok()) { return AOS_ERROR_WRAP(Error(ErrorEnum::eFailed, status.error_message().c_str())); } - for (const auto& url : response.urls()) { - if (auto err = urls.PushBack(url.c_str()); !err.IsNone()) { - return AOS_ERROR_WRAP(err); - } + if (response.has_error()) { + return common::pbconvert::ConvertFromProto(response.error()); + } + + if (auto err = common::pbconvert::ConvertFromProto(response, digest, blobInfo); !err.IsNone()) { + return AOS_ERROR_WRAP(err); } return ErrorEnum::eNone; diff --git a/src/sm/smclient/smclient.hpp b/src/sm/smclient/smclient.hpp index 21b913635..e10ef45fd 100644 --- a/src/sm/smclient/smclient.hpp +++ b/src/sm/smclient/smclient.hpp @@ -146,17 +146,16 @@ class SMClient : public SMClientItf, public aos::iamclient::CertKeyListenerItf, */ Error SendUpdateInstancesStatuses(const Array& statuses) override; - // aos::sm::imagemanager::BlobInfoProviderItf interface + // aos::common::imagemanager::BlobInfoProviderItf interface /** - * Gets blobs info. + * Gets blob info. * - * @param digests list of blob digests. - * @param[out] urls blobs URLs. + * @param digest blob digest. + * @param[out] blobInfo blob info. * @return Error. */ - Error GetBlobsInfo( - const Array>& digests, Array>& urls) override; + Error GetBlobInfo(const String& digest, BlobInfo& blobInfo) override; // aos::cloudconnection::CloudConnectionItf interface diff --git a/src/sm/smclient/tests/smclient.cpp b/src/sm/smclient/tests/smclient.cpp index f32476b1b..c50314477 100644 --- a/src/sm/smclient/tests/smclient.cpp +++ b/src/sm/smclient/tests/smclient.cpp @@ -780,7 +780,7 @@ TEST_F(SMClientTest, SendAlert) ASSERT_TRUE(err.IsNone()) << "Stop failed"; } -TEST_F(SMClientTest, GetBlobsInfo) +TEST_F(SMClientTest, GetBlobInfo) { auto server = std::make_unique(GetConfig().mCMServerURL); auto client = std::make_unique(); @@ -828,18 +828,22 @@ TEST_F(SMClientTest, GetBlobsInfo) server->WaitSMInfo(); server->WaitNodeInstancesStatus(); - StaticArray, 2> digests; - digests.EmplaceBack("sha256:1234567890abcdef"); - digests.EmplaceBack("sha256:fedcba0987654321"); + auto blobInfo = std::make_unique(); - StaticArray, 2> urls; + err = client->GetBlobInfo("sha256:1234567890abcdef", *blobInfo); + ASSERT_TRUE(err.IsNone()) << "GetBlobInfo failed"; - err = client->GetBlobsInfo(digests, urls); - ASSERT_TRUE(err.IsNone()) << "GetBlobsInfo failed"; + const uint8_t cSHA256[] = {1, 2, 3, 4}; - ASSERT_EQ(urls.Size(), 2); - EXPECT_EQ(urls[0], "http://example.com/blobs/sha256:1234567890abcdef"); - EXPECT_EQ(urls[1], "http://example.com/blobs/sha256:fedcba0987654321"); + EXPECT_EQ(blobInfo->mDigest, "sha256:1234567890abcdef"); + ASSERT_EQ(blobInfo->mURLs.Size(), 2); + EXPECT_EQ(blobInfo->mURLs[0], "http://example.com/blobs/sha256:1234567890abcdef"); + EXPECT_EQ(blobInfo->mURLs[1], "http://mirror.example.com/blobs/sha256:1234567890abcdef"); + EXPECT_EQ(blobInfo->mSHA256, Array(cSHA256, sizeof(cSHA256))); + EXPECT_EQ(blobInfo->mSize, 1024); + + err = client->GetBlobInfo(SMServiceStub::cMissingBlobDigest, *blobInfo); + EXPECT_TRUE(err.Is(ErrorEnum::eNotFound)) << "GetBlobInfo should fail for missing blob"; err = client->Stop(); ASSERT_TRUE(err.IsNone()) << "Stop failed"; diff --git a/src/sm/smclient/tests/stubs/smservicestub.hpp b/src/sm/smclient/tests/stubs/smservicestub.hpp index 930dfd250..2eadba111 100644 --- a/src/sm/smclient/tests/stubs/smservicestub.hpp +++ b/src/sm/smclient/tests/stubs/smservicestub.hpp @@ -110,18 +110,28 @@ class SMServiceStub final : public smproto::SMService::Service, public smproto:: return grpc::Status::OK; } - grpc::Status GetBlobsInfos( - grpc::ServerContext* context, const smproto::BlobsInfosRequest* request, smproto::BlobsInfos* response) override + grpc::Status GetBlobsInfo( + grpc::ServerContext* context, const smproto::BlobsInfoRequest* request, smproto::BlobInfo* response) override { (void)context; - for (const auto& digest : request->digests()) { - response->add_urls("http://example.com/blobs/" + digest); + if (request->digest() == cMissingBlobDigest) { + response->mutable_error()->set_aos_code(static_cast(aos::ErrorEnum::eNotFound)); + response->mutable_error()->set_message("blob not found"); + + return grpc::Status::OK; } + response->add_urls("http://example.com/blobs/" + request->digest()); + response->add_urls("http://mirror.example.com/blobs/" + request->digest()); + response->set_sha256(std::string("\x01\x02\x03\x04", 4)); + response->set_size(1024); + return grpc::Status::OK; } + static constexpr auto cMissingBlobDigest = "sha256:missing"; + grpc::Status GetNodeNetworkParams(grpc::ServerContext*, const smproto::GetNodeNetworkParamsRequest* request, smproto::GetNodeNetworkParamsResponse* response) override { From 349b64100e5ee0976816c667d60f0d040863fb7a Mon Sep 17 00:00:00 2001 From: Mykhailo Lohvynenko Date: Wed, 4 Nov 2026 14:09:43 +0200 Subject: [PATCH 3/8] sm: launcher: fix ItemInfoProviderItf namespace Signed-off-by: Mykhailo Lohvynenko Reviewed-by: Oleksandr Grytsov --- src/sm/launcher/runtimes.cpp | 12 +++--- src/sm/launcher/runtimes.hpp | 4 +- src/sm/launcher/runtimes/boot/boot.cpp | 2 +- src/sm/launcher/runtimes/boot/boot.hpp | 40 +++++++++---------- src/sm/launcher/runtimes/boot/tests/boot.cpp | 26 ++++++------ .../launcher/runtimes/container/container.cpp | 2 +- .../launcher/runtimes/container/container.hpp | 16 ++++---- .../launcher/runtimes/container/instance.cpp | 4 +- .../launcher/runtimes/container/instance.hpp | 28 ++++++------- .../runtimes/container/tests/container.cpp | 24 +++++------ src/sm/launcher/runtimes/rootfs/rootfs.cpp | 2 +- src/sm/launcher/runtimes/rootfs/rootfs.hpp | 28 ++++++------- src/sm/launcher/runtimes/rootfs/rootfs.md | 7 ++-- .../launcher/runtimes/rootfs/tests/rootfs.cpp | 20 +++++----- src/sm/launcher/tests/launcher.cpp | 24 +++++------ 15 files changed, 120 insertions(+), 119 deletions(-) diff --git a/src/sm/launcher/runtimes.cpp b/src/sm/launcher/runtimes.cpp index fba30a2c5..9974d8ddb 100644 --- a/src/sm/launcher/runtimes.cpp +++ b/src/sm/launcher/runtimes.cpp @@ -23,12 +23,12 @@ namespace aos::sm::launcher { **********************************************************************************************************************/ // Which parameters are used depends on which WITH_RUNTIME_* runtimes are compiled in below. -Error Runtimes::Init(const Config& config, - [[maybe_unused]] iamclient::CurrentNodeInfoProviderItf& currentNodeInfoProvider, - [[maybe_unused]] imagemanager::ItemInfoProviderItf& itemInfoProvider, - [[maybe_unused]] networkmanager::NetworkManagerItf& networkManager, - [[maybe_unused]] iamclient::PermHandlerItf& permHandler, - [[maybe_unused]] resourcemanager::ResourceInfoProviderItf& resourceInfoProvider, +Error Runtimes::Init(const Config& config, + [[maybe_unused]] iamclient::CurrentNodeInfoProviderItf& currentNodeInfoProvider, + [[maybe_unused]] common::imagemanager::ItemInfoProviderItf& itemInfoProvider, + [[maybe_unused]] networkmanager::NetworkManagerItf& networkManager, + [[maybe_unused]] iamclient::PermHandlerItf& permHandler, + [[maybe_unused]] resourcemanager::ResourceInfoProviderItf& resourceInfoProvider, [[maybe_unused]] oci::OCISpecItf& ociSpec, [[maybe_unused]] InstanceStatusReceiverItf& statusReceiver, [[maybe_unused]] sm::utils::SystemdConnItf& systemdConn, [[maybe_unused]] launcher::InstanceIDProviderItf& instanceIDProvider, diff --git a/src/sm/launcher/runtimes.hpp b/src/sm/launcher/runtimes.hpp index 2a0a78909..1018f5043 100644 --- a/src/sm/launcher/runtimes.hpp +++ b/src/sm/launcher/runtimes.hpp @@ -12,9 +12,9 @@ #include #include +#include #include #include -#include #include #include #include @@ -52,7 +52,7 @@ class Runtimes { * @return Error. */ Error Init(const Config& config, aos::iamclient::CurrentNodeInfoProviderItf& currentNodeInfoProvider, - imagemanager::ItemInfoProviderItf& itemInfoProvider, networkmanager::NetworkManagerItf& networkManager, + common::imagemanager::ItemInfoProviderItf& itemInfoProvider, networkmanager::NetworkManagerItf& networkManager, aos::iamclient::PermHandlerItf& permHandler, resourcemanager::ResourceInfoProviderItf& resourceInfoProvider, oci::OCISpecItf& ociSpec, InstanceStatusReceiverItf& statusReceiver, utils::SystemdConnItf& systemdConn, launcher::InstanceIDProviderItf& instanceIDProvider, runtimes::StorageItf& runtimeStorage, diff --git a/src/sm/launcher/runtimes/boot/boot.cpp b/src/sm/launcher/runtimes/boot/boot.cpp index 4643dd3ee..930a12f6d 100644 --- a/src/sm/launcher/runtimes/boot/boot.cpp +++ b/src/sm/launcher/runtimes/boot/boot.cpp @@ -28,7 +28,7 @@ namespace aos::sm::launcher { **********************************************************************************************************************/ Error BootRuntime::Init(const RuntimeConfig& config, iamclient::CurrentNodeInfoProviderItf& currentNodeInfoProvider, - imagemanager::ItemInfoProviderItf& itemInfoProvider, oci::OCISpecItf& ociSpec, + common::imagemanager::ItemInfoProviderItf& itemInfoProvider, oci::OCISpecItf& ociSpec, InstanceStatusReceiverItf& statusReceiver, sm::utils::SystemdConnItf& systemdConn) { LOG_DBG() << "Init runtime" << Log::Field("type", config.mType.c_str()); diff --git a/src/sm/launcher/runtimes/boot/boot.hpp b/src/sm/launcher/runtimes/boot/boot.hpp index f364f4f09..6bea46d2c 100644 --- a/src/sm/launcher/runtimes/boot/boot.hpp +++ b/src/sm/launcher/runtimes/boot/boot.hpp @@ -12,8 +12,8 @@ #include #include +#include #include -#include #include #include @@ -49,7 +49,7 @@ class BootRuntime : public RuntimeItf { * @return Error. */ Error Init(const RuntimeConfig& config, iamclient::CurrentNodeInfoProviderItf& currentNodeInfoProvider, - imagemanager::ItemInfoProviderItf& itemInfoProvider, oci::OCISpecItf& ociSpec, + common::imagemanager::ItemInfoProviderItf& itemInfoProvider, oci::OCISpecItf& ociSpec, InstanceStatusReceiverItf& statusReceiver, sm::utils::SystemdConnItf& systemdConn); /** @@ -163,24 +163,24 @@ class BootRuntime : public RuntimeItf { std::filesystem::path GetPath(const std::string_view relativePath) const; size_t GetNextPartitionIndex(size_t currentPartition) const; - mutable std::mutex mMutex; - std::shared_ptr mPartitionManager; - std::shared_ptr mBootController; - iamclient::CurrentNodeInfoProviderItf* mCurrentNodeInfoProvider {}; - imagemanager::ItemInfoProviderItf* mItemInfoProvider {}; - oci::OCISpecItf* mOCISpec {}; - InstanceStatusReceiverItf* mStatusReceiver {}; - RuntimeConfig mConfig; - BootConfig mBootConfig; - InstanceIdent mDefaultInstanceIdent; - utils::SystemdRebooter mSystemdRebooter; - utils::SystemdUpdateChecker mSystemdUpdateChecker; - RuntimeInfo mRuntimeInfo; - size_t mMainPartition {}; - size_t mCurrentPartition {}; - BootData mInstalled; - Optional mPending; - std::vector mPartitionDevices; + mutable std::mutex mMutex; + std::shared_ptr mPartitionManager; + std::shared_ptr mBootController; + iamclient::CurrentNodeInfoProviderItf* mCurrentNodeInfoProvider {}; + common::imagemanager::ItemInfoProviderItf* mItemInfoProvider {}; + oci::OCISpecItf* mOCISpec {}; + InstanceStatusReceiverItf* mStatusReceiver {}; + RuntimeConfig mConfig; + BootConfig mBootConfig; + InstanceIdent mDefaultInstanceIdent; + utils::SystemdRebooter mSystemdRebooter; + utils::SystemdUpdateChecker mSystemdUpdateChecker; + RuntimeInfo mRuntimeInfo; + size_t mMainPartition {}; + size_t mCurrentPartition {}; + BootData mInstalled; + Optional mPending; + std::vector mPartitionDevices; }; } // namespace aos::sm::launcher diff --git a/src/sm/launcher/runtimes/boot/tests/boot.cpp b/src/sm/launcher/runtimes/boot/tests/boot.cpp index 0bdbdb31f..d7373c29b 100644 --- a/src/sm/launcher/runtimes/boot/tests/boot.cpp +++ b/src/sm/launcher/runtimes/boot/tests/boot.cpp @@ -12,11 +12,11 @@ #include #include +#include #include #include #include #include -#include #include #include #include @@ -193,18 +193,18 @@ class BootRuntimeTest : public Test { .WillRepeatedly(DoAll(SetArgReferee<0>(mNodeInfo), Return(ErrorEnum::eNone))); } - PartInfo mBootAPartition; - PartInfo mBootBPartition; - NodeInfo mNodeInfo; - RuntimeConfig mConfig; - iamclient::CurrentNodeInfoProviderMock mCurrentNodeInfoProvider; - imagemanager::ItemInfoProviderMock mItemInfoProvider; - oci::OCISpecMock mOCISpec; - InstanceStatusReceiverStub mStatusReceiver; - sm::utils::SystemdConnMock mSystemdConn; - std::shared_ptr mPartitionManager {std::make_shared()}; - std::shared_ptr mMockBootController {std::make_shared()}; - TestBootRuntime mBootRuntime {mPartitionManager, mMockBootController}; + PartInfo mBootAPartition; + PartInfo mBootBPartition; + NodeInfo mNodeInfo; + RuntimeConfig mConfig; + iamclient::CurrentNodeInfoProviderMock mCurrentNodeInfoProvider; + common::imagemanager::ItemInfoProviderMock mItemInfoProvider; + oci::OCISpecMock mOCISpec; + InstanceStatusReceiverStub mStatusReceiver; + sm::utils::SystemdConnMock mSystemdConn; + std::shared_ptr mPartitionManager {std::make_shared()}; + std::shared_ptr mMockBootController {std::make_shared()}; + TestBootRuntime mBootRuntime {mPartitionManager, mMockBootController}; }; /*********************************************************************************************************************** diff --git a/src/sm/launcher/runtimes/container/container.cpp b/src/sm/launcher/runtimes/container/container.cpp index eeadd528f..67a8d8b6e 100644 --- a/src/sm/launcher/runtimes/container/container.cpp +++ b/src/sm/launcher/runtimes/container/container.cpp @@ -38,7 +38,7 @@ const char* const cDefaultHostFSBinds[] = {"bin", "sbin", "lib", "lib64", "usr"} Error ContainerRuntime::Init(const RuntimeConfig& config, iamclient::CurrentNodeInfoProviderItf& currentNodeInfoProvider, // cppcheck-suppress constParameterReference - imagemanager::ItemInfoProviderItf& itemInfoProvider, networkmanager::NetworkManagerItf& networkManager, + common::imagemanager::ItemInfoProviderItf& itemInfoProvider, networkmanager::NetworkManagerItf& networkManager, iamclient::PermHandlerItf& permHandler, resourcemanager::ResourceInfoProviderItf& resourceInfoProvider, oci::OCISpecItf& ociSpec, InstanceStatusReceiverItf& instanceStatusReceiver, launcher::InstanceIDProviderItf& instanceIDProvider) diff --git a/src/sm/launcher/runtimes/container/container.hpp b/src/sm/launcher/runtimes/container/container.hpp index 52d71f741..25568e7ed 100644 --- a/src/sm/launcher/runtimes/container/container.hpp +++ b/src/sm/launcher/runtimes/container/container.hpp @@ -49,7 +49,7 @@ class ContainerRuntime : public RuntimeItf, public RunStatusReceiverItf, public * @return Error. */ Error Init(const RuntimeConfig& config, aos::iamclient::CurrentNodeInfoProviderItf& currentNodeInfoProvider, - imagemanager::ItemInfoProviderItf& itemInfoProvider, networkmanager::NetworkManagerItf& networkManager, + common::imagemanager::ItemInfoProviderItf& itemInfoProvider, networkmanager::NetworkManagerItf& networkManager, aos::iamclient::PermHandlerItf& permHandler, resourcemanager::ResourceInfoProviderItf& resourceInfoProvider, oci::OCISpecItf& ociSpec, InstanceStatusReceiverItf& instanceStatusReceiver, launcher::InstanceIDProviderItf& instanceIDProvider); @@ -149,13 +149,13 @@ class ContainerRuntime : public RuntimeItf, public RunStatusReceiverItf, public std::shared_ptr mMonitoring; std::shared_ptr mContainerRunner; - imagemanager::ItemInfoProviderItf* mItemInfoProvider {}; - networkmanager::NetworkManagerItf* mNetworkManager {}; - aos::iamclient::PermHandlerItf* mPermHandler {}; - resourcemanager::ResourceInfoProviderItf* mResourceInfoProvider {}; - oci::OCISpecItf* mOCISpec {}; - InstanceStatusReceiverItf* mInstanceStatusReceiver {}; - launcher::InstanceIDProviderItf* mInstanceIDProvider {}; + common::imagemanager::ItemInfoProviderItf* mItemInfoProvider {}; + networkmanager::NetworkManagerItf* mNetworkManager {}; + aos::iamclient::PermHandlerItf* mPermHandler {}; + resourcemanager::ResourceInfoProviderItf* mResourceInfoProvider {}; + oci::OCISpecItf* mOCISpec {}; + InstanceStatusReceiverItf* mInstanceStatusReceiver {}; + launcher::InstanceIDProviderItf* mInstanceIDProvider {}; ContainerConfig mConfig; NodeInfo mNodeInfo; diff --git a/src/sm/launcher/runtimes/container/instance.cpp b/src/sm/launcher/runtimes/container/instance.cpp index c151dd604..7bddfcb70 100644 --- a/src/sm/launcher/runtimes/container/instance.cpp +++ b/src/sm/launcher/runtimes/container/instance.cpp @@ -35,7 +35,7 @@ static const char* const cBindEtcEntries[] = {"nsswitch.conf", "ssl"}; Instance::Instance(const InstanceInfo& instance, const ContainerConfig& config, const NodeInfo& nodeInfo, FileSystemItf& fileSystem, RunnerItf& runner, MonitoringItf& monitoring, - imagemanager::ItemInfoProviderItf& itemInfoProvider, networkmanager::NetworkManagerItf& networkManager, + common::imagemanager::ItemInfoProviderItf& itemInfoProvider, networkmanager::NetworkManagerItf& networkManager, iamclient::PermHandlerItf& permHandler, resourcemanager::ResourceInfoProviderItf& resourceInfoProvider, oci::OCISpecItf& ociSpec, InstanceIDProviderItf& instanceIDProvider) : mInstanceInfo(instance) @@ -64,7 +64,7 @@ Instance::Instance(const InstanceInfo& instance, const ContainerConfig& config, Instance::Instance(const std::string& instanceID, const ContainerConfig& config, const NodeInfo& nodeInfo, FileSystemItf& fileSystem, RunnerItf& runner, MonitoringItf& monitoring, - imagemanager::ItemInfoProviderItf& itemInfoProvider, networkmanager::NetworkManagerItf& networkManager, + common::imagemanager::ItemInfoProviderItf& itemInfoProvider, networkmanager::NetworkManagerItf& networkManager, iamclient::PermHandlerItf& permHandler, resourcemanager::ResourceInfoProviderItf& resourceInfoProvider, oci::OCISpecItf& ociSpec, InstanceIDProviderItf& instanceIDProvider) : mInstanceID(instanceID) diff --git a/src/sm/launcher/runtimes/container/instance.hpp b/src/sm/launcher/runtimes/container/instance.hpp index a0ea8736c..78b8b2524 100644 --- a/src/sm/launcher/runtimes/container/instance.hpp +++ b/src/sm/launcher/runtimes/container/instance.hpp @@ -10,9 +10,9 @@ #include #include +#include #include #include -#include #include #include #include @@ -49,7 +49,7 @@ class Instance { */ Instance(const InstanceInfo& instance, const ContainerConfig& config, const NodeInfo& nodeInfo, FileSystemItf& fileSystem, RunnerItf& runner, MonitoringItf& monitoring, - imagemanager::ItemInfoProviderItf& itemInfoProvider, networkmanager::NetworkManagerItf& networkManager, + common::imagemanager::ItemInfoProviderItf& itemInfoProvider, networkmanager::NetworkManagerItf& networkManager, aos::iamclient::PermHandlerItf& permHandler, resourcemanager::ResourceInfoProviderItf& resourceInfoProvider, oci::OCISpecItf& ociSpec, InstanceIDProviderItf& instanceIDProvider); @@ -71,7 +71,7 @@ class Instance { */ Instance(const std::string& instanceID, const ContainerConfig& config, const NodeInfo& nodeInfo, FileSystemItf& fileSystem, RunnerItf& runner, MonitoringItf& monitoring, - imagemanager::ItemInfoProviderItf& itemInfoProvider, networkmanager::NetworkManagerItf& networkManager, + common::imagemanager::ItemInfoProviderItf& itemInfoProvider, networkmanager::NetworkManagerItf& networkManager, aos::iamclient::PermHandlerItf& permHandler, resourcemanager::ResourceInfoProviderItf& resourceInfoProvider, oci::OCISpecItf& ociSpec, InstanceIDProviderItf& instanceIDProvider); @@ -184,17 +184,17 @@ class Instance { RunStatus mRunStatus; EnvVarStatusArray mEnvVarsStatuses; - const ContainerConfig& mConfig; - const NodeInfo& mNodeInfo; - FileSystemItf& mFileSystem; - RunnerItf& mRunner; - MonitoringItf& mMonitoring; - imagemanager::ItemInfoProviderItf& mItemInfoProvider; - networkmanager::NetworkManagerItf& mNetworkManager; - aos::iamclient::PermHandlerItf& mPermHandler; - resourcemanager::ResourceInfoProviderItf& mResourceInfoProvider; - oci::OCISpecItf& mOCISpec; - InstanceIDProviderItf& mInstanceIDProvider; + const ContainerConfig& mConfig; + const NodeInfo& mNodeInfo; + FileSystemItf& mFileSystem; + RunnerItf& mRunner; + MonitoringItf& mMonitoring; + common::imagemanager::ItemInfoProviderItf& mItemInfoProvider; + networkmanager::NetworkManagerItf& mNetworkManager; + aos::iamclient::PermHandlerItf& mPermHandler; + resourcemanager::ResourceInfoProviderItf& mResourceInfoProvider; + oci::OCISpecItf& mOCISpec; + InstanceIDProviderItf& mInstanceIDProvider; mutable std::mutex mMutex; diff --git a/src/sm/launcher/runtimes/container/tests/container.cpp b/src/sm/launcher/runtimes/container/tests/container.cpp index 7414b7309..6d0ad4ced 100644 --- a/src/sm/launcher/runtimes/container/tests/container.cpp +++ b/src/sm/launcher/runtimes/container/tests/container.cpp @@ -9,13 +9,13 @@ #include #include +#include #include #include #include #include #include #include -#include #include #include @@ -254,17 +254,17 @@ class ContainerRuntimeTest : public Test { ASSERT_TRUE(err.IsNone()) << "Failed to stop runtime: " << tests::utils::ErrorToStr(err); } - TestRuntime mRuntime; - NodeInfo mNodeInfo; - NiceMock mCurrentNodeInfoProviderMock; - NiceMock mItemInfoProviderMock; - NiceMock mNetworkManagerMock; - NiceMock mPermHandlerMock; - NiceMock mResourceInfoProviderMock; - NiceMock mOCISpecMock; - NiceMock mInstanceStatusReceiverMock; - RunStatusReceiverItf* mRunStatusReceiver {}; - NiceMock mInstanceIDProviderMock; + TestRuntime mRuntime; + NodeInfo mNodeInfo; + NiceMock mCurrentNodeInfoProviderMock; + NiceMock mItemInfoProviderMock; + NiceMock mNetworkManagerMock; + NiceMock mPermHandlerMock; + NiceMock mResourceInfoProviderMock; + NiceMock mOCISpecMock; + NiceMock mInstanceStatusReceiverMock; + RunStatusReceiverItf* mRunStatusReceiver {}; + NiceMock mInstanceIDProviderMock; }; /*********************************************************************************************************************** diff --git a/src/sm/launcher/runtimes/rootfs/rootfs.cpp b/src/sm/launcher/runtimes/rootfs/rootfs.cpp index 02e1c364d..43132dc16 100644 --- a/src/sm/launcher/runtimes/rootfs/rootfs.cpp +++ b/src/sm/launcher/runtimes/rootfs/rootfs.cpp @@ -295,7 +295,7 @@ RetWithError GetUpdateType(const oci::ImageManifest& manifest) **********************************************************************************************************************/ Error RootfsRuntime::Init(const RuntimeConfig& config, iamclient::CurrentNodeInfoProviderItf& currentNodeInfoProvider, - imagemanager::ItemInfoProviderItf& itemInfoProvider, oci::OCISpecItf& ociSpec, + common::imagemanager::ItemInfoProviderItf& itemInfoProvider, oci::OCISpecItf& ociSpec, InstanceStatusReceiverItf& statusReceiver, sm::utils::SystemdConnItf& systemdConn, runtimes::StorageItf& storage, fs::FileInfoProviderItf& fileInfoProvider) { diff --git a/src/sm/launcher/runtimes/rootfs/rootfs.hpp b/src/sm/launcher/runtimes/rootfs/rootfs.hpp index 9d572b712..be38eb237 100644 --- a/src/sm/launcher/runtimes/rootfs/rootfs.hpp +++ b/src/sm/launcher/runtimes/rootfs/rootfs.hpp @@ -15,9 +15,9 @@ #include #include +#include #include #include -#include #include #include @@ -61,7 +61,7 @@ class RootfsRuntime : public RuntimeItf { * @return Error. */ Error Init(const RuntimeConfig& config, iamclient::CurrentNodeInfoProviderItf& currentNodeInfoProvider, - imagemanager::ItemInfoProviderItf& itemInfoProvider, oci::OCISpecItf& ociSpec, + common::imagemanager::ItemInfoProviderItf& itemInfoProvider, oci::OCISpecItf& ociSpec, InstanceStatusReceiverItf& statusReceiver, sm::utils::SystemdConnItf& systemdConn, runtimes::StorageItf& storage, fs::FileInfoProviderItf& fileInfoProvider); @@ -226,18 +226,18 @@ class RootfsRuntime : public RuntimeItf { Error PrepareUpdate(const InstanceInfo& instance); std::filesystem::path GetPath(const std::string& fileName) const; - RuntimeConfig mRuntimeConfig; - RootfsConfig mRootfsConfig; - iamclient::CurrentNodeInfoProviderItf* mCurrentNodeInfoProvider {}; - imagemanager::ItemInfoProviderItf* mItemInfoProvider {}; - oci::OCISpecItf* mOCISpec {}; - InstanceStatusReceiverItf* mStatusReceiver {}; - runtimes::StorageItf* mStorage {}; - fs::FileInfoProviderItf* mFileInfoProvider {}; - common::utils::FSPlatform mFSPlatform; - utils::SystemdUpdateChecker mUpdateChecker; - utils::SystemdRebooter mRebooter; - InstanceIdent mDefaultInstanceIdent; + RuntimeConfig mRuntimeConfig; + RootfsConfig mRootfsConfig; + iamclient::CurrentNodeInfoProviderItf* mCurrentNodeInfoProvider {}; + common::imagemanager::ItemInfoProviderItf* mItemInfoProvider {}; + oci::OCISpecItf* mOCISpec {}; + InstanceStatusReceiverItf* mStatusReceiver {}; + runtimes::StorageItf* mStorage {}; + fs::FileInfoProviderItf* mFileInfoProvider {}; + common::utils::FSPlatform mFSPlatform; + utils::SystemdUpdateChecker mUpdateChecker; + utils::SystemdRebooter mRebooter; + InstanceIdent mDefaultInstanceIdent; mutable std::mutex mMutex; std::mutex mHealthCheckMutex; diff --git a/src/sm/launcher/runtimes/rootfs/rootfs.md b/src/sm/launcher/runtimes/rootfs/rootfs.md index 802453192..e8ca67e35 100644 --- a/src/sm/launcher/runtimes/rootfs/rootfs.md +++ b/src/sm/launcher/runtimes/rootfs/rootfs.md @@ -26,7 +26,8 @@ any moment. The main rules are: [RootfsRuntime](rootfs.hpp) implements [aos::sm::launcher::RuntimeItf][runtime-itf] and uses: - [aos::sm::launcher::runtimes::StorageItf](../itf/storage.hpp) - stores the current and the pending instances; -- [aos::sm::imagemanager::ItemInfoProviderItf][item-info-provider-itf] - provides paths of the installed image blobs; +- [aos::common::imagemanager::ItemInfoProviderItf][item-info-provider-itf] - provides paths of the installed image + blobs; - [aos::oci::OCISpecItf][oci-spec-itf] - loads the image manifest; - [aos::fs::FileInfoProviderItf][file-info-provider-itf] - calculates the size and the sha256 hash of the copied image; - [aos::sm::launcher::InstanceStatusReceiverItf][status-receiver-itf] - receives instance statuses and reboot requests; @@ -37,7 +38,7 @@ any moment. The main rules are: - [SystemdRebooter](../utils/systemdrebooter.hpp) - reboots the node through systemd. [runtime-itf]: https://github.com/aosedge/aos_core_lib_cpp/blob/develop/src/core/sm/launcher/itf/runtime.hpp -[item-info-provider-itf]: https://github.com/aosedge/aos_core_lib_cpp/blob/develop/src/core/sm/imagemanager/itf/iteminfoprovider.hpp +[item-info-provider-itf]: https://github.com/aosedge/aos_core_lib_cpp/blob/develop/src/core/common/imagemanager/itf/iteminfoprovider.hpp [oci-spec-itf]: https://github.com/aosedge/aos_core_lib_cpp/blob/develop/src/core/common/ocispec/itf/ocispec.hpp [file-info-provider-itf]: https://github.com/aosedge/aos_core_lib_cpp/blob/develop/src/core/common/tools/fs.hpp [status-receiver-itf]: https://github.com/aosedge/aos_core_lib_cpp/blob/develop/src/core/sm/launcher/itf/instancestatusreceiver.hpp @@ -60,7 +61,7 @@ classDiagram class StorageItf ["aos::sm::launcher::runtimes::StorageItf"] { <> } - class ItemInfoProviderItf ["aos::sm::imagemanager::ItemInfoProviderItf"] { + class ItemInfoProviderItf ["aos::common::imagemanager::ItemInfoProviderItf"] { <> } class OCISpecItf ["aos::oci::OCISpecItf"] { diff --git a/src/sm/launcher/runtimes/rootfs/tests/rootfs.cpp b/src/sm/launcher/runtimes/rootfs/tests/rootfs.cpp index 671db609a..35653aed1 100644 --- a/src/sm/launcher/runtimes/rootfs/tests/rootfs.cpp +++ b/src/sm/launcher/runtimes/rootfs/tests/rootfs.cpp @@ -30,10 +30,10 @@ #include #include +#include #include #include #include -#include #include #include #include @@ -603,15 +603,15 @@ class RootfsRuntimeTest : public Test { ASSERT_TRUE(err.IsNone()) << tests::utils::ErrorToStr(err); } - RuntimeConfig mConfig; - iamclient::CurrentNodeInfoProviderMock mCurrentNodeInfoProvider; - imagemanager::ItemInfoProviderMock mItemInfoProvider; - oci::OCISpecMock mOCISpec; - InstanceStatusReceiverFailingStub mStatusReceiver; - sm::utils::SystemdConnMock mSystemdConn; - RuntimeStorageFake mStorage; - FileInfoProviderFake mFileInfoProvider; - RootfsRuntime mRootfsRuntime; + RuntimeConfig mConfig; + iamclient::CurrentNodeInfoProviderMock mCurrentNodeInfoProvider; + common::imagemanager::ItemInfoProviderMock mItemInfoProvider; + oci::OCISpecMock mOCISpec; + InstanceStatusReceiverFailingStub mStatusReceiver; + sm::utils::SystemdConnMock mSystemdConn; + RuntimeStorageFake mStorage; + FileInfoProviderFake mFileInfoProvider; + RootfsRuntime mRootfsRuntime; }; /*********************************************************************************************************************** diff --git a/src/sm/launcher/tests/launcher.cpp b/src/sm/launcher/tests/launcher.cpp index 68b4de24a..b015df51b 100644 --- a/src/sm/launcher/tests/launcher.cpp +++ b/src/sm/launcher/tests/launcher.cpp @@ -8,13 +8,13 @@ #include #include +#include #include #include #include #include #include #include -#include #include #include @@ -66,17 +66,17 @@ class RuntimesTest : public Test { void TearDown() override { } - NiceMock mCurrentNodeInfoProviderMock; - NiceMock mItemInfoProviderMock; - NiceMock mNetworkManagerMock; - NiceMock mPermHandlerMock; - NiceMock mResourceInfoProviderMock; - NiceMock mOCISpecMock; - NiceMock mInstanceStatusReceiverMock; - NiceMock mSystemdConnMock; - NiceMock mInstanceIDProviderMock; - NiceMock mRuntimeStorageMock; - NiceMock mFileInfoProviderMock; + NiceMock mCurrentNodeInfoProviderMock; + NiceMock mItemInfoProviderMock; + NiceMock mNetworkManagerMock; + NiceMock mPermHandlerMock; + NiceMock mResourceInfoProviderMock; + NiceMock mOCISpecMock; + NiceMock mInstanceStatusReceiverMock; + NiceMock mSystemdConnMock; + NiceMock mInstanceIDProviderMock; + NiceMock mRuntimeStorageMock; + NiceMock mFileInfoProviderMock; }; /*********************************************************************************************************************** From 01506f332cfc57ae42762b32650ad7b635e3858c Mon Sep 17 00:00:00 2001 From: Mykhailo Lohvynenko Date: Wed, 4 Nov 2026 14:10:51 +0200 Subject: [PATCH 4/8] sm: config: use common imagemanager config structure Signed-off-by: Mykhailo Lohvynenko Reviewed-by: Oleksandr Grytsov --- src/sm/config/config.cpp | 4 ++-- src/sm/config/config.hpp | 4 ++-- src/sm/config/tests/config.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sm/config/config.cpp b/src/sm/config/config.cpp index 4d94b2696..1dc76440b 100644 --- a/src/sm/config/config.cpp +++ b/src/sm/config/config.cpp @@ -62,7 +62,7 @@ void ParseSMClientConfig(const common::utils::CaseInsensitiveObjectWrapper& obje }; void ParseImageManagerConfig(const common::utils::CaseInsensitiveObjectWrapper& object, const std::string& workingDir, - imagemanager::Config& config) + aos::common::imagemanager::Config& config) { Error err; @@ -72,7 +72,7 @@ void ParseImageManagerConfig(const common::utils::CaseInsensitiveObjectWrapper& config.mPartLimit = object.GetValue("imagesPartLimit", 0); - Tie(config.mUpdateItemTTL, err) + Tie(config.mItemCacheTTL, err) = common::utils::ParseDuration(object.GetValue("updateItemTtl", cDefaultUpdateItemTTL)); AOS_ERROR_CHECK_AND_THROW(err, "error parsing updateItemTtl tag"); diff --git a/src/sm/config/config.hpp b/src/sm/config/config.hpp index 17f3cb843..ce73a7900 100644 --- a/src/sm/config/config.hpp +++ b/src/sm/config/config.hpp @@ -12,10 +12,10 @@ #include +#include #include #include #include -#include #include #include @@ -41,7 +41,7 @@ struct Config { common::config::JournalAlerts mJournalAlerts; common::config::Migration mMigration; common::iamclient::Config mIAMClientConfig; - imagemanager::Config mImageManager; + common::imagemanager::Config mImageManager; launcher::Config mLauncher; logging::Config mLogging; monitoring::Config mMonitoring; diff --git a/src/sm/config/tests/config.cpp b/src/sm/config/tests/config.cpp index e18e3ab2f..71a7b0c7b 100644 --- a/src/sm/config/tests/config.cpp +++ b/src/sm/config/tests/config.cpp @@ -156,7 +156,7 @@ TEST_F(ConfigTest, ParseConfig) EXPECT_EQ(config->mImageManager.mImagePath, "/path/to/images"); EXPECT_EQ(config->mImageManager.mPartLimit, 50); - EXPECT_EQ(config->mImageManager.mUpdateItemTTL, 15 * 24 * aos::Time::cHours); + EXPECT_EQ(config->mImageManager.mItemCacheTTL, 15 * 24 * aos::Time::cHours); EXPECT_EQ(config->mImageManager.mRemoveOutdatedPeriod, 12 * aos::Time::cHours); EXPECT_EQ(config->mLauncher.mRuntimes.size(), 3); From 8e364886ad75c135805f5c8ad442997dcb6075b9 Mon Sep 17 00:00:00 2001 From: Mykhailo Lohvynenko Date: Wed, 4 Nov 2026 14:13:18 +0200 Subject: [PATCH 5/8] sm: app: fix module initialization Signed-off-by: Mykhailo Lohvynenko Reviewed-by: Oleksandr Grytsov --- src/sm/app/aoscore.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sm/app/aoscore.cpp b/src/sm/app/aoscore.cpp index 636a50cf8..491e65f55 100644 --- a/src/sm/app/aoscore.cpp +++ b/src/sm/app/aoscore.cpp @@ -124,8 +124,8 @@ void AosCore::Init(const std::string& configFile) // Initialize images space allocator - err = mImagesSpaceAllocator.Init( - mAllocator, mConfig.mImageManager.mImagePath, mPlatformFS, mConfig.mImageManager.mPartLimit, &mImageManager); + err = mImagesSpaceAllocator.Init(mAllocator, mConfig.mImageManager.mImagePath, mPlatformFS, + mConfig.mImageManager.mPartLimit, &mImageManager.GetItemRemover()); AOS_ERROR_CHECK_AND_THROW(err, "can't initialize images space allocator"); // Initialize downloader @@ -139,14 +139,14 @@ void AosCore::Init(const std::string& configFile) // Initialize image manager - err = mImageManager.Init(mAllocator, mConfig.mImageManager, mSMClient, mImagesSpaceAllocator, mDownloader, - mFileInfoProvider, mOCISpec, mImageHandler, mDatabase); + err = mImageManager.Init(mConfig.mImageManager, mSMClient, mDatabase, mImagesSpaceAllocator, mDownloader, + mImageHandler, mFileInfoProvider, mOCISpec, mPlatformFS, mAllocator); AOS_ERROR_CHECK_AND_THROW(err, "can't initialize image manager"); // Initialize launcher - err = mLauncher.Init(mAllocator, *runtimes, mImageManager, mSMClient, mDatabase, mOCISpec, mImageManager, mSMClient, - mNetworkManager, mInstanceIDProvider, mResourceManager); + err = mLauncher.Init(mAllocator, *runtimes, mImageManager, mImageManager, mSMClient, mDatabase, mOCISpec, + mImageManager, mSMClient, mNetworkManager, mInstanceIDProvider, mResourceManager); AOS_ERROR_CHECK_AND_THROW(err, "can't initialize launcher"); // Initialize node config handler From 3afa46a4416305684bd93a83883bd9b1faacfcd1 Mon Sep 17 00:00:00 2001 From: Mykhailo Lohvynenko Date: Fri, 6 Nov 2026 15:13:30 +0200 Subject: [PATCH 6/8] cm: smcontroller: handle single blob GetBlobsInfo rpc call The SM API returns the info of a single blob now. Return the blob URLs, SHA-256 and size, and put the provider error into the response error info, so SM gets its code, e.g. integrity error. Signed-off-by: Mykhailo Lohvynenko Reviewed-by: Oleksandr Grytsov --- src/cm/smcontroller/smcontroller.cpp | 26 +++++---- src/cm/smcontroller/smcontroller.hpp | 4 +- src/cm/smcontroller/tests/smcontroller.cpp | 53 +++++++++++++------ .../smcontroller/tests/stubs/smclientstub.hpp | 11 ++-- 4 files changed, 60 insertions(+), 34 deletions(-) diff --git a/src/cm/smcontroller/smcontroller.cpp b/src/cm/smcontroller/smcontroller.cpp index 22399626d..7c661a5f2 100644 --- a/src/cm/smcontroller/smcontroller.cpp +++ b/src/cm/smcontroller/smcontroller.cpp @@ -279,25 +279,29 @@ grpc::Status SMController::RegisterSM(grpc::ServerContext* return grpc::Status::OK; } -grpc::Status SMController::GetBlobsInfos(grpc::ServerContext*, const servicemanager::v5::BlobsInfosRequest* request, - servicemanager::v5::BlobsInfos* response) +grpc::Status SMController::GetBlobsInfo( + grpc::ServerContext*, const servicemanager::v5::BlobsInfoRequest* request, servicemanager::v5::BlobInfo* response) { - LOG_DBG() << "Get blobs info request received" << Log::Field("digestsCount", request->digests_size()); + LOG_DBG() << "Get blob info request received" << Log::Field("digest", request->digest().c_str()); auto blobInfo = std::make_unique(); - for (int i = 0; i < request->digests_size(); ++i) { - if (auto err = mBlobInfoProvider->GetBlobInfo(request->digests(i).c_str(), *blobInfo); !err.IsNone()) { - return grpc::Status(grpc::StatusCode::INTERNAL, err.Message()); - } + auto err = mBlobInfoProvider->GetBlobInfo(request->digest().c_str(), *blobInfo); + if (err.IsNone() && blobInfo->mURLs.IsEmpty()) { + err = Error(ErrorEnum::eNotFound, "blob has no URLs"); + } - if (blobInfo->mURLs.IsEmpty()) { - return grpc::Status(grpc::StatusCode::INTERNAL, "blob has no URLs"); - } + // The error is returned in the response, so SM gets its code, e.g. integrity error of the CM copy. + if (!err.IsNone()) { + LOG_ERR() << "Failed to get blob info" << Log::Field("digest", request->digest().c_str()) << Log::Field(err); - response->add_urls(blobInfo->mURLs[0].CStr()); + common::pbconvert::SetErrorInfo(err, *response); + + return grpc::Status::OK; } + common::pbconvert::ConvertToProto(*blobInfo, *response); + return grpc::Status::OK; } diff --git a/src/cm/smcontroller/smcontroller.hpp b/src/cm/smcontroller/smcontroller.hpp index 281fcb138..5a5d61b7e 100644 --- a/src/cm/smcontroller/smcontroller.hpp +++ b/src/cm/smcontroller/smcontroller.hpp @@ -190,8 +190,8 @@ class SMController : public SMControllerItf, grpc::ServerReaderWriter* stream) override; - grpc::Status GetBlobsInfos(grpc::ServerContext* context, const servicemanager::v5::BlobsInfosRequest* request, - servicemanager::v5::BlobsInfos* response) override; + grpc::Status GetBlobsInfo(grpc::ServerContext* context, const servicemanager::v5::BlobsInfoRequest* request, + servicemanager::v5::BlobInfo* response) override; // // NetworkService::Service GRPC service methods diff --git a/src/cm/smcontroller/tests/smcontroller.cpp b/src/cm/smcontroller/tests/smcontroller.cpp index d62dcc518..160b2e065 100644 --- a/src/cm/smcontroller/tests/smcontroller.cpp +++ b/src/cm/smcontroller/tests/smcontroller.cpp @@ -9,6 +9,7 @@ #include #include +#include #include "stubs/alertsreceiverstub.hpp" #include "stubs/blobinfoproviderstub.hpp" @@ -842,7 +843,7 @@ TEST_F(SMControllerTest, OnCertKeyChanged) EXPECT_TRUE(err.IsNone()) << err.Message(); } -TEST_F(SMControllerTest, GetBlobsInfos) +TEST_F(SMControllerTest, GetBlobsInfo) { const std::string digest1 = "sha256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"; const std::string digest2 = "sha256:abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"; @@ -866,14 +867,23 @@ TEST_F(SMControllerTest, GetBlobsInfos) err = mSMInfoReceiver.WaitSMInfo(cMainNodeID); ASSERT_TRUE(err.IsNone()) << err.Message(); - // 4) Call GetBlobsInfos - servicemanager::v5::BlobsInfos responseBlobsInfos; - std::vector digests = {digest2, digest1}; - ASSERT_TRUE(client.GetBlobsInfos(digests, responseBlobsInfos).IsNone()); + // 4) Call GetBlobsInfo + servicemanager::v5::BlobInfo blobInfo; - ASSERT_EQ(responseBlobsInfos.urls_size(), 2); - EXPECT_EQ(responseBlobsInfos.urls(0), url2); - EXPECT_EQ(responseBlobsInfos.urls(1), url1); + ASSERT_TRUE(client.GetBlobsInfo(digest1, blobInfo).IsNone()); + + EXPECT_FALSE(blobInfo.has_error()); + ASSERT_EQ(blobInfo.urls_size(), 2); + EXPECT_EQ(blobInfo.urls(0), url1); + EXPECT_EQ(blobInfo.urls(1), "https://backup.example.com/blob1.tar"); + + blobInfo.Clear(); + + ASSERT_TRUE(client.GetBlobsInfo(digest2, blobInfo).IsNone()); + + EXPECT_FALSE(blobInfo.has_error()); + ASSERT_EQ(blobInfo.urls_size(), 1); + EXPECT_EQ(blobInfo.urls(0), url2); // 5) Stop client err = client.Stop(); @@ -884,7 +894,7 @@ TEST_F(SMControllerTest, GetBlobsInfos) EXPECT_TRUE(err.IsNone()) << err.Message(); } -TEST_F(SMControllerTest, GetBlobsInfosFails) +TEST_F(SMControllerTest, GetBlobsInfoFails) { const std::string knownDigest = "sha256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"; const std::string noURLsDigest = "sha256:abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"; @@ -904,15 +914,28 @@ TEST_F(SMControllerTest, GetBlobsInfosFails) err = mSMInfoReceiver.WaitSMInfo(cMainNodeID); ASSERT_TRUE(err.IsNone()) << err.Message(); - servicemanager::v5::BlobsInfos responseBlobsInfos; + // The provider error is returned in the response with its code. + servicemanager::v5::BlobInfo blobInfo; + + ASSERT_TRUE(client.GetBlobsInfo(unknownDigest, blobInfo).IsNone()); + + ASSERT_TRUE(blobInfo.has_error()); + EXPECT_TRUE(common::pbconvert::ConvertFromProto(blobInfo.error()).Is(ErrorEnum::eNotFound)); + EXPECT_EQ(blobInfo.urls_size(), 0); + + blobInfo.Clear(); + + ASSERT_TRUE(client.GetBlobsInfo(noURLsDigest, blobInfo).IsNone()); + + ASSERT_TRUE(blobInfo.has_error()); + EXPECT_NE(blobInfo.error().message().find("blob has no URLs"), std::string::npos) << blobInfo.error().message(); - EXPECT_FALSE(client.GetBlobsInfos({knownDigest, unknownDigest}, responseBlobsInfos).IsNone()); + blobInfo.Clear(); - responseBlobsInfos.Clear(); + ASSERT_TRUE(client.GetBlobsInfo(knownDigest, blobInfo).IsNone()); - err = client.GetBlobsInfos({noURLsDigest}, responseBlobsInfos); - EXPECT_FALSE(err.IsNone()); - EXPECT_NE(std::string(err.Message()).find("blob has no URLs"), std::string::npos) << err.Message(); + EXPECT_FALSE(blobInfo.has_error()); + EXPECT_EQ(blobInfo.urls_size(), 1); err = client.Stop(); ASSERT_TRUE(err.IsNone()) << err.Message(); diff --git a/src/cm/smcontroller/tests/stubs/smclientstub.hpp b/src/cm/smcontroller/tests/stubs/smclientstub.hpp index a45ae5164..ddadaceb7 100644 --- a/src/cm/smcontroller/tests/stubs/smclientstub.hpp +++ b/src/cm/smcontroller/tests/stubs/smclientstub.hpp @@ -371,7 +371,7 @@ class SMClientStub { return ErrorEnum::eNone; } - Error GetBlobsInfos(const std::vector& digests, servicemanager::v5::BlobsInfos& blobsInfos) + Error GetBlobsInfo(const std::string& digest, servicemanager::v5::BlobInfo& blobInfo) { std::lock_guard lock {mMutex}; @@ -379,13 +379,12 @@ class SMClientStub { return AOS_ERROR_WRAP(Error(ErrorEnum::eFailed, "stub not available")); } - servicemanager::v5::BlobsInfosRequest request; - for (const auto& digest : digests) { - request.add_digests(digest); - } + servicemanager::v5::BlobsInfoRequest request; + + request.set_digest(digest); grpc::ClientContext context; - auto status = mStub->GetBlobsInfos(&context, request, &blobsInfos); + auto status = mStub->GetBlobsInfo(&context, request, &blobInfo); if (!status.ok()) { return AOS_ERROR_WRAP(Error(ErrorEnum::eFailed, status.error_message().c_str())); From 5d6a5aa5be6c5cd67faa122fff36581dda6615b4 Mon Sep 17 00:00:00 2001 From: Mykhailo Lohvynenko Date: Fri, 6 Nov 2026 15:13:30 +0200 Subject: [PATCH 7/8] sm: database: implement common image manager storage The SM image manager stores items and blobs in the common image manager storage. Inherit common::database::Database, which implements it, drop the obsolete update items table, create the image manager items and blobs tables in migration 7 and implement IsLayerUsed. Signed-off-by: Mykhailo Lohvynenko Reviewed-by: Oleksandr Grytsov --- src/sm/database/CMakeLists.txt | 4 +- src/sm/database/database.cpp | 176 +------------- src/sm/database/database.hpp | 79 ++----- src/sm/database/migration/7_update.down.sql | 16 ++ src/sm/database/migration/7_update.up.sql | 33 +++ src/sm/database/tests/database.cpp | 246 +++++--------------- 6 files changed, 136 insertions(+), 418 deletions(-) create mode 100644 src/sm/database/migration/7_update.down.sql create mode 100644 src/sm/database/migration/7_update.up.sql diff --git a/src/sm/database/CMakeLists.txt b/src/sm/database/CMakeLists.txt index 5e9b39dbe..d77abb290 100644 --- a/src/sm/database/CMakeLists.txt +++ b/src/sm/database/CMakeLists.txt @@ -16,7 +16,9 @@ set(SOURCES database.cpp) # Libraries # ###################################################################################################################### -set(LIBRARIES aos::sm::config aos::common::utils aos::common::migration Poco::DataSQLite Poco::JSON) +set(LIBRARIES aos::sm::config aos::common::database aos::common::utils aos::common::migration Poco::DataSQLite + Poco::JSON +) # ###################################################################################################################### # Target diff --git a/src/sm/database/database.cpp b/src/sm/database/database.cpp index 4131d33e4..69bcbba02 100644 --- a/src/sm/database/database.cpp +++ b/src/sm/database/database.cpp @@ -449,152 +449,22 @@ Error Database::Init(const std::string& workDir, const common::config::Migration * sm::imagemanager::StorageItf implementation **********************************************************************************************************************/ -Error Database::AddUpdateItem(const imagemanager::UpdateItemData& updateItem) +RetWithError Database::IsLayerUsed(const String& diffID) { - std::lock_guard lock {mMutex}; - - LOG_DBG() << "Add update item" << Log::Field("id", updateItem.mID) << Log::Field("type", updateItem.mType) - << Log::Field("version", updateItem.mVersion) << Log::Field("state", updateItem.mState); - - try { - ItemDataRow row; - - FromAos(updateItem, row); - - *mSession - << "INSERT INTO items (itemID, type, version, manifestDigest, state, timestamp) VALUES (?, ?, ?, ?, ?, ?);", - bind(row), now; - - return ErrorEnum::eNone; - } catch (const std::exception& e) { - return AOS_ERROR_WRAP(common::utils::ToAosError(e)); - } -} - -Error Database::UpdateUpdateItem(const imagemanager::UpdateItemData& updateItem) -{ - std::lock_guard lock {mMutex}; - - LOG_DBG() << "Update update item" << Log::Field("id", updateItem.mID) << Log::Field("type", updateItem.mType) - << Log::Field("version", updateItem.mVersion) << Log::Field("state", updateItem.mState); - - try { - Poco::Data::Statement statement {*mSession}; - - statement << "UPDATE items SET manifestDigest = ?, state = ?, timestamp = ? WHERE itemID = ? AND type = ? AND " - "version = ?;", - bind(updateItem.mManifestDigest.CStr()), bind(updateItem.mState.ToString().CStr()), - bind(updateItem.mTimestamp.UnixNano()), bind(updateItem.mID.CStr()), - bind(updateItem.mType.ToString().CStr()), bind(updateItem.mVersion.CStr()); - - if (statement.execute() != 1) { - return ErrorEnum::eNotFound; - } - - return ErrorEnum::eNone; - } catch (const std::exception& e) { - return AOS_ERROR_WRAP(common::utils::ToAosError(e)); - } -} - -Error Database::RemoveUpdateItem(const String& itemID, const String& version) -{ - std::lock_guard lock {mMutex}; - - LOG_DBG() << "Remove update item" << Log::Field("id", itemID) << Log::Field("version", version); - - try { - Poco::Data::Statement statement {*mSession}; - - statement << "DELETE FROM items WHERE itemID = ? AND version = ?;", bind(itemID.CStr()), bind(version.CStr()); - - if (statement.execute() == 0) { - return AOS_ERROR_WRAP(ErrorEnum::eNotFound); - } - - return ErrorEnum::eNone; - } catch (const std::exception& e) { - return AOS_ERROR_WRAP(common::utils::ToAosError(e)); - } -} - -Error Database::GetUpdateItem(const String& itemID, Array& itemData) -{ - std::lock_guard lock {mMutex}; - - LOG_DBG() << "Get update item" << Log::Field("id", itemID); + LOG_DBG() << "Check if layer is used" << Log::Field("diffID", diffID); - try { - std::vector rows; - Poco::Data::Statement statement {*mSession}; - - statement << "SELECT * FROM items WHERE itemID = ?;", bind(itemID.CStr()), into(rows); - - if (statement.execute() == 0) { - return ErrorEnum::eNotFound; - } - - for (const auto& row : rows) { - imagemanager::UpdateItemData updateItem; - - ToAos(row, updateItem); - - if (auto err = itemData.PushBack(updateItem); !err.IsNone()) { - return AOS_ERROR_WRAP(Error(err, "db items count exceeds application limit")); - } - } - - return ErrorEnum::eNone; - } catch (const std::exception& e) { - return AOS_ERROR_WRAP(common::utils::ToAosError(e)); - } -} - -Error Database::GetAllUpdateItems(Array& itemsData) -{ - std::lock_guard lock {mMutex}; - - LOG_DBG() << "Get all update items"; - - try { - std::vector rows; - Poco::Data::Statement statement {*mSession}; - - statement << "SELECT * FROM items;", into(rows); - - statement.execute(); - - for (const auto& row : rows) { - imagemanager::UpdateItemData updateItem; - - ToAos(row, updateItem); - - if (auto err = itemsData.PushBack(updateItem); !err.IsNone()) { - return AOS_ERROR_WRAP(Error(err, "db items count exceeds application limit")); - } - } - - return ErrorEnum::eNone; - } catch (const std::exception& e) { - return AOS_ERROR_WRAP(common::utils::ToAosError(e)); - } -} - -RetWithError Database::GetUpdateItemsCount() -{ std::lock_guard lock {mMutex}; - LOG_DBG() << "Get update items count"; - try { - Poco::Data::Statement statement {*mSession}; - size_t count; + int used = 0; - statement << "SELECT COUNT(*) FROM items;", into(count), now; + *mSession << "SELECT EXISTS (SELECT 1 FROM imagemanager_blobs b JOIN imagemanager_items i " + "ON b.itemID = i.itemID AND b.version = i.version WHERE b.isLayer = 1 AND b.diffID = ?);", + bind(diffID.CStr()), into(used), now; - return {count, ErrorEnum::eNone}; + return {used != 0, ErrorEnum::eNone}; } catch (const std::exception& e) { - return {0, AOS_ERROR_WRAP(common::utils::ToAosError(e))}; + return {false, AOS_ERROR_WRAP(common::utils::ToAosError(e))}; } } @@ -1207,36 +1077,6 @@ void Database::CreateTables() now; } -void Database::FromAos(const imagemanager::UpdateItemData& src, ItemDataRow& dst) -{ - dst.set(src.mID.CStr()); - dst.set(src.mType.ToString().CStr()); - dst.set(src.mVersion.CStr()); - dst.set(src.mManifestDigest.CStr()); - dst.set(src.mState.ToString().CStr()); - dst.set(src.mTimestamp.UnixNano()); -} - -void Database::ToAos(const ItemDataRow& src, imagemanager::UpdateItemData& dst) -{ - auto err = dst.mID.Assign(src.get().c_str()); - AOS_ERROR_CHECK_AND_THROW(err, "failed to assign item ID"); - - err = dst.mType.FromString(src.get().c_str()); - AOS_ERROR_CHECK_AND_THROW(err, "failed to parse item type"); - - err = dst.mVersion.Assign(src.get().c_str()); - AOS_ERROR_CHECK_AND_THROW(err, "failed to assign item version"); - - err = dst.mManifestDigest.Assign(src.get().c_str()); - AOS_ERROR_CHECK_AND_THROW(err, "failed to assign manifest digest"); - - err = dst.mState.FromString(src.get().c_str()); - AOS_ERROR_CHECK_AND_THROW(err, "failed to parse item state"); - - dst.mTimestamp = ConvertTimestamp(src.get()); -} - void Database::FromAos(const InstanceInfo& src, InstanceInfoRow& dst) { dst.set(src.mItemID.CStr()); diff --git a/src/sm/database/database.hpp b/src/sm/database/database.hpp index 50a2def18..714df72b0 100644 --- a/src/sm/database/database.hpp +++ b/src/sm/database/database.hpp @@ -17,6 +17,7 @@ #include +#include #include #include #include @@ -24,7 +25,10 @@ namespace aos::sm::database { -class Database : public DatabaseItf, public sm::alerts::StorageItf, public sm::launcher::runtimes::StorageItf { +class Database : public DatabaseItf, + public common::database::Database, + public sm::alerts::StorageItf, + public sm::launcher::runtimes::StorageItf { public: /** * Creates database instance. @@ -34,7 +38,7 @@ class Database : public DatabaseItf, public sm::alerts::StorageItf, public sm::l /** * Destructor. */ - ~Database(); + ~Database() override; /** * Initializes certificate info storage. @@ -48,53 +52,13 @@ class Database : public DatabaseItf, public sm::alerts::StorageItf, public sm::l // sm::imagemanager::StorageItf interface /** - * Adds update item to storage. + * Checks if layer is referenced by any existing deployable item, regardless of the item state. Blob references + * left without an item are ignored. * - * @param updateItem update item to add. - * @return Error. - */ - Error AddUpdateItem(const imagemanager::UpdateItemData& updateItem) override; - - /** - * Updates update item in storage. - * - * @param updateItem update item to update. - * @return Error. - */ - Error UpdateUpdateItem(const imagemanager::UpdateItemData& updateItem) override; - - /** - * Removes previously stored update item. - * - * @param itemID update item ID to remove. - * @param version update item version. - * @return Error. - */ - Error RemoveUpdateItem(const String& itemID, const String& version) override; - - /** - * Returns update item versions by item ID. - * - * @param itemID update item ID. - * @param[out] itemData array to store update item data. - * @return Error. - */ - Error GetUpdateItem(const String& itemID, Array& itemData) override; - - /** - * Returns all update items. - * - * @param itemsData[out] array to store update items data. - * @return Error. + * @param diffID layer diff ID. + * @return RetWithError. */ - Error GetAllUpdateItems(Array& itemsData) override; - - /** - * Returns count of stored update items. - * - * @return RetWithError. - */ - RetWithError GetUpdateItemsCount() override; + RetWithError IsLayerUsed(const String& diffID) override; // sm::launcher::StorageItf interface @@ -288,21 +252,9 @@ class Database : public DatabaseItf, public sm::alerts::StorageItf, public sm::l const String& runtimeID, Array& instances) const override; private: - static constexpr int sVersion = 6; + static constexpr int sVersion = 7; static constexpr auto cDBFileName = "servicemanager.db"; - // Item data columns - enum class ItemDataColumns : int { - eItemID = 0, - eType, - eVersion, - eManifestDigest, - eState, - eTimestamp, - }; - - using ItemDataRow = Poco::Tuple; - // Instance info columns enum class InstanceInfoColumns : int { eItemID = 0, @@ -376,9 +328,6 @@ class Database : public DatabaseItf, public sm::alerts::StorageItf, public sm::l // FromAos/ToAos conversion methods - static void FromAos(const imagemanager::UpdateItemData& src, ItemDataRow& dst); - static void ToAos(const ItemDataRow& src, imagemanager::UpdateItemData& dst); - static void FromAos(const InstanceInfo& src, InstanceInfoRow& dst); static void ToAos(const InstanceInfoRow& src, InstanceInfo& dst); @@ -391,9 +340,7 @@ class Database : public DatabaseItf, public sm::alerts::StorageItf, public sm::l static void FromAos(const sm::launcher::runtimes::RuntimeInstanceData& src, RuntimeInstanceRow& dst); static void ToAos(const RuntimeInstanceRow& src, sm::launcher::runtimes::RuntimeInstanceData& dst); - mutable std::unique_ptr mSession; - std::optional mMigration; - mutable std::mutex mMutex; + std::optional mMigration; }; } // namespace aos::sm::database diff --git a/src/sm/database/migration/7_update.down.sql b/src/sm/database/migration/7_update.down.sql new file mode 100644 index 000000000..9caaf874c --- /dev/null +++ b/src/sm/database/migration/7_update.down.sql @@ -0,0 +1,16 @@ +BEGIN TRANSACTION; + +DROP TABLE IF EXISTS imagemanager_blobs; +DROP TABLE IF EXISTS imagemanager_items; + +CREATE TABLE IF NOT EXISTS items ( + itemID TEXT, + type TEXT, + version TEXT, + manifestDigest TEXT, + state TEXT, + timestamp INTEGER, + PRIMARY KEY(itemID, type, version) +); + +COMMIT; diff --git a/src/sm/database/migration/7_update.up.sql b/src/sm/database/migration/7_update.up.sql new file mode 100644 index 000000000..b5afddef4 --- /dev/null +++ b/src/sm/database/migration/7_update.up.sql @@ -0,0 +1,33 @@ +BEGIN TRANSACTION; + +DROP TABLE IF EXISTS items; + +CREATE TABLE IF NOT EXISTS imagemanager_items ( + itemID TEXT, + type TEXT, + version TEXT, + preinstalled INTEGER, + state TEXT, + errorAosCode INTEGER, + errorSysCode INTEGER, + errorMessage TEXT, + rootDigest TEXT, + timestamp INTEGER, + PRIMARY KEY (itemID, version) +); + +CREATE TABLE IF NOT EXISTS imagemanager_blobs ( + itemID TEXT, + version TEXT, + digest TEXT, + mediaType TEXT, + size INTEGER, + isLayer INTEGER, + diffID TEXT, + PRIMARY KEY (itemID, version, digest) +); + +CREATE INDEX IF NOT EXISTS imagemanager_blobs_digest ON imagemanager_blobs(digest); +CREATE INDEX IF NOT EXISTS imagemanager_blobs_diffID ON imagemanager_blobs(diffID); + +COMMIT; diff --git a/src/sm/database/tests/database.cpp b/src/sm/database/tests/database.cpp index 5ee455ba5..1bfcbee3e 100644 --- a/src/sm/database/tests/database.cpp +++ b/src/sm/database/tests/database.cpp @@ -57,21 +57,34 @@ aos::InstanceInfo CreateInstanceInfo( return info; } -aos::sm::imagemanager::UpdateItemData CreateUpdateItemData(const aos::String& id, aos::UpdateItemType type, - const aos::String& version, const aos::String& manifestDigest, aos::ItemState state, aos::Time timestamp) +aos::common::imagemanager::DeployableItemInfo CreateDeployableItem(const char* itemID, const char* version) { - aos::sm::imagemanager::UpdateItemData item; + aos::common::imagemanager::DeployableItemInfo item; - item.mID = id; - item.mType = type; - item.mVersion = version; - item.mManifestDigest = manifestDigest; - item.mState = state; - item.mTimestamp = timestamp; + item.mItemID = itemID; + item.mType = aos::UpdateItemTypeEnum::eService; + item.mVersion = version; + item.mState = aos::ItemStateEnum::eInstalled; + item.mRootDigest = "sha256:root"; + item.mTimestamp = aos::Time::Now(); return item; } +aos::common::imagemanager::ItemBlobInfo CreateItemBlob(const char* digest, bool isLayer, const char* diffID) +{ + aos::common::imagemanager::ItemBlobInfo blob; + + blob.mMediaType + = isLayer ? "application/vnd.oci.image.layer.v1.tar+gzip" : "application/vnd.oci.image.manifest.v1+json"; + blob.mDigest = digest; + blob.mSize = 1024; + blob.mIsLayer = isLayer; + blob.mDiffID = diffID; + + return blob; +} + aos::sm::launcher::runtimes::RuntimeInstanceData CreateRuntimeInstance(const std::string& runtimeID, const std::string& version, aos::InstanceState state = aos::InstanceStateEnum::eActive, const aos::Error& error = aos::ErrorEnum::eNone) @@ -131,217 +144,84 @@ class DatabaseTest : public Test { * Tests - imagemanager::StorageItf **********************************************************************************************************************/ -TEST_F(DatabaseTest, AddUpdateItem) -{ - ASSERT_TRUE(mDB.Init(mWorkingDir.string(), mMigrationConfig).IsNone()); - - auto item1v1 = CreateUpdateItemData("item1", aos::UpdateItemTypeEnum::eService, "1.0.0", "sha256:digest1", - aos::ItemStateEnum::eInstalled, aos::Time::Now()); - auto item1v2 = CreateUpdateItemData("item1", aos::UpdateItemTypeEnum::eService, "2.0.0", "sha256:digest2", - aos::ItemStateEnum::eInstalled, aos::Time::Now()); - auto item2v1 = CreateUpdateItemData("item2", aos::UpdateItemTypeEnum::eService, "1.0.0", "sha256:digest3", - aos::ItemStateEnum::eInstalled, aos::Time::Now()); - - // Add item1 v1 - - auto err = mDB.AddUpdateItem(item1v1); - ASSERT_TRUE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); - - // Try to add same item again - - err = mDB.AddUpdateItem(item1v1); - ASSERT_FALSE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); - - // Add item1 v2 - - err = mDB.AddUpdateItem(item1v2); - ASSERT_TRUE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); - - // Add item2 v1 - - err = mDB.AddUpdateItem(item2v1); - ASSERT_TRUE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); - - // Get item1 - - auto itemData = std::make_unique>(); - - err = mDB.GetUpdateItem("item1", *itemData); - ASSERT_TRUE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); - - ASSERT_EQ(itemData->Size(), 2); - EXPECT_EQ((*itemData)[0], item1v1); - EXPECT_EQ((*itemData)[1], item1v2); - - // Get item2 - - itemData->Clear(); - - err = mDB.GetUpdateItem("item2", *itemData); - ASSERT_TRUE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); - - ASSERT_EQ(itemData->Size(), 1); - EXPECT_EQ((*itemData)[0], item2v1); -} - -TEST_F(DatabaseTest, UpdateUpdateItem) +TEST_F(DatabaseTest, DeployableItems) { ASSERT_TRUE(mDB.Init(mWorkingDir.string(), mMigrationConfig).IsNone()); - auto item1 = CreateUpdateItemData("item1", aos::UpdateItemTypeEnum::eService, "1.0.0", "sha256:digest1", - aos::ItemStateEnum::eInstalled, aos::Time::Now()); - auto item2 = CreateUpdateItemData("item2", aos::UpdateItemTypeEnum::eService, "1.0.0", "sha256:digest3", - aos::ItemStateEnum::eInstalled, aos::Time::Now()); - - // Add item1 - - auto err = mDB.AddUpdateItem(item1); - ASSERT_TRUE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); - - // Add item2 - - err = mDB.AddUpdateItem(item2); - ASSERT_TRUE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); - - item1.mState = aos::ItemStateEnum::eRemoved; - item2.mState = aos::ItemStateEnum::eRemoved; + const auto item1 = CreateDeployableItem("item1", "1.0.0"); + const auto item2 = CreateDeployableItem("item2", "1.0.0"); - // Update item1 - - err = mDB.UpdateUpdateItem(item1); - ASSERT_TRUE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); - - // Update item2 - - err = mDB.UpdateUpdateItem(item2); - ASSERT_TRUE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); - - // Get item1 - - auto itemData = std::make_unique>(); - - err = mDB.GetUpdateItem("item1", *itemData); + auto err = mDB.AddDeployableItem(item1); ASSERT_TRUE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); - ASSERT_EQ(itemData->Size(), 1); - EXPECT_EQ((*itemData)[0], item1); - - // Get item2 - - itemData->Clear(); - - err = mDB.GetUpdateItem("item2", *itemData); - ASSERT_TRUE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); - - ASSERT_EQ(itemData->Size(), 1); - EXPECT_EQ((*itemData)[0], item2); -} - -TEST_F(DatabaseTest, RemoveUpdateItem) -{ - ASSERT_TRUE(mDB.Init(mWorkingDir.string(), mMigrationConfig).IsNone()); - - auto item1v1 = CreateUpdateItemData("item1", aos::UpdateItemTypeEnum::eService, "1.0.0", "sha256:digest1", - aos::ItemStateEnum::eInstalled, aos::Time::Now()); - auto item1v2 = CreateUpdateItemData("item1", aos::UpdateItemTypeEnum::eService, "2.0.0", "sha256:digest2", - aos::ItemStateEnum::eInstalled, aos::Time::Now()); - auto item2v1 = CreateUpdateItemData("item2", aos::UpdateItemTypeEnum::eService, "1.0.0", "sha256:digest3", - aos::ItemStateEnum::eInstalled, aos::Time::Now()); - - // Add item 1 v1 - - auto err = mDB.AddUpdateItem(item1v1); + err = mDB.AddDeployableItem(item2); ASSERT_TRUE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); - // Add item 1 v2 - - err = mDB.AddUpdateItem(item1v2); + err = mDB.AddItemBlob(item1.mItemID, item1.mVersion, CreateItemBlob("sha256:manifest", false, "")); ASSERT_TRUE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); - // Add item 2 v1 + auto items = std::make_unique>(); - err = mDB.AddUpdateItem(item2v1); + err = mDB.GetAllDeployableItems(*items); ASSERT_TRUE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); - // Remove item1 v1 with wrong version - - err = mDB.RemoveUpdateItem(item1v1.mID, "3.0.0"); - ASSERT_TRUE(err.Is(aos::ErrorEnum::eNotFound)) << aos::tests::utils::ErrorToStr(err); - - // Remove item1 v1 - - err = mDB.RemoveUpdateItem(item1v1.mID, item1v1.mVersion); - ASSERT_TRUE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); + ASSERT_EQ(items->Size(), 2); + EXPECT_EQ((*items)[0], item1); + EXPECT_EQ((*items)[1], item2); - // Remove item2 v1 + auto [used, usedErr] = mDB.IsBlobUsed("sha256:manifest"); + ASSERT_TRUE(usedErr.IsNone()) << aos::tests::utils::ErrorToStr(usedErr); + EXPECT_TRUE(used); - err = mDB.RemoveUpdateItem(item2v1.mID, item2v1.mVersion); + err = mDB.RemoveDeployableItem(item1.mItemID, item1.mVersion); ASSERT_TRUE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); - // Get item1 - - auto itemData = std::make_unique>(); + size_t count = 0; - err = mDB.GetUpdateItem("item1", *itemData); + Tie(count, err) = mDB.GetDeployableItemsCount(); ASSERT_TRUE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); - - ASSERT_EQ(itemData->Size(), 1); - EXPECT_EQ((*itemData)[0], item1v2); - - // Get item2 - - itemData->Clear(); - - err = mDB.GetUpdateItem("item2", *itemData); - ASSERT_TRUE(err.Is(aos::ErrorEnum::eNotFound)) << aos::tests::utils::ErrorToStr(err); - - ASSERT_EQ(itemData->Size(), 0); + EXPECT_EQ(count, 1); } -TEST_F(DatabaseTest, GetAllUpdateItems) +TEST_F(DatabaseTest, IsLayerUsed) { ASSERT_TRUE(mDB.Init(mWorkingDir.string(), mMigrationConfig).IsNone()); - auto item1v1 = CreateUpdateItemData("item1", aos::UpdateItemTypeEnum::eService, "1.0.0", "sha256:digest1", - aos::ItemStateEnum::eInstalled, aos::Time::Now()); - auto item1v2 = CreateUpdateItemData("item1", aos::UpdateItemTypeEnum::eService, "2.0.0", "sha256:digest2", - aos::ItemStateEnum::eInstalled, aos::Time::Now()); - auto item2v1 = CreateUpdateItemData("item2", aos::UpdateItemTypeEnum::eService, "1.0.0", "sha256:digest3", - aos::ItemStateEnum::eInstalled, aos::Time::Now()); + const auto item1 = CreateDeployableItem("item1", "1.0.0"); + const auto item2 = CreateDeployableItem("item2", "1.0.0"); - // Add item1 v1 + ASSERT_TRUE(mDB.AddDeployableItem(item1).IsNone()); + ASSERT_TRUE(mDB.AddDeployableItem(item2).IsNone()); - auto err = mDB.AddUpdateItem(item1v1); - ASSERT_TRUE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); + // Both items reference the same layer through different blobs. + ASSERT_TRUE( + mDB.AddItemBlob(item1.mItemID, item1.mVersion, CreateItemBlob("sha256:layer1", true, "sha256:diff")).IsNone()); + ASSERT_TRUE( + mDB.AddItemBlob(item2.mItemID, item2.mVersion, CreateItemBlob("sha256:layer2", true, "sha256:diff")).IsNone()); - // Add item1 v2 + // A blob which is not a layer doesn't reference a layer even if its diff ID matches. + ASSERT_TRUE(mDB.AddItemBlob(item2.mItemID, item2.mVersion, CreateItemBlob("sha256:config", false, "sha256:other")) + .IsNone()); - err = mDB.AddUpdateItem(item1v2); + auto [used, err] = mDB.IsLayerUsed("sha256:diff"); ASSERT_TRUE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); + EXPECT_TRUE(used); - // Add item2 v1 - - err = mDB.AddUpdateItem(item2v1); + Tie(used, err) = mDB.IsLayerUsed("sha256:other"); ASSERT_TRUE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); + EXPECT_FALSE(used); - // Get all update items + ASSERT_TRUE(mDB.RemoveDeployableItem(item1.mItemID, item1.mVersion).IsNone()); - auto itemsData = std::make_unique>(); - - err = mDB.GetAllUpdateItems(*itemsData); + Tie(used, err) = mDB.IsLayerUsed("sha256:diff"); ASSERT_TRUE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); + EXPECT_TRUE(used); - ASSERT_EQ(itemsData->Size(), 3); - EXPECT_EQ((*itemsData)[0], item1v1); - EXPECT_EQ((*itemsData)[1], item1v2); - EXPECT_EQ((*itemsData)[2], item2v1); - - size_t count = 0; + ASSERT_TRUE(mDB.RemoveDeployableItem(item2.mItemID, item2.mVersion).IsNone()); - Tie(count, err) = mDB.GetUpdateItemsCount(); + Tie(used, err) = mDB.IsLayerUsed("sha256:diff"); ASSERT_TRUE(err.IsNone()) << aos::tests::utils::ErrorToStr(err); - - EXPECT_EQ(count, itemsData->Size()); + EXPECT_FALSE(used); } /*********************************************************************************************************************** From ebab9fbf7a2c66ef49875119b61b0fb0423eb791 Mon Sep 17 00:00:00 2001 From: Mykhailo Lohvynenko Date: Fri, 6 Nov 2026 19:05:37 +0200 Subject: [PATCH 8/8] all: fix cppcheck style warnings cppcheck lint fails with missingOverride on the CM database destructor, which overrides the virtual destructor of the common database now, and with noExplicitConstructor on the fileserver FDHandle constructors. Mark the CM database destructor override and make the FDHandle int constructor explicit. Keep the nullptr constructor implicit, as unique_ptr requires its pointer type to be constructible from nullptr, and suppress the warning for it. Signed-off-by: Mykhailo Lohvynenko Reviewed-by: Oleksandr Grytsov --- src/cm/database/database.hpp | 2 +- src/common/fileserver/fileserver.cpp | 2 +- src/common/fileserver/fileserver.hpp | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cm/database/database.hpp b/src/cm/database/database.hpp index f075a2503..70c37186c 100644 --- a/src/cm/database/database.hpp +++ b/src/cm/database/database.hpp @@ -38,7 +38,7 @@ class Database : public DatabaseItf, public common::database::Database, public n /** * Destructor. */ - ~Database(); + ~Database() override; /** * Initializes database. diff --git a/src/common/fileserver/fileserver.cpp b/src/common/fileserver/fileserver.cpp index ff95ab849..4475e3c24 100644 --- a/src/common/fileserver/fileserver.cpp +++ b/src/common/fileserver/fileserver.cpp @@ -287,7 +287,7 @@ bool Fileserver::FileRequestHandler::ResolvePath(const std::string& target, fs:: Fileserver::FileRequestHandler::FDPtr Fileserver::FileRequestHandler::OpenFile( const std::string& path, struct stat& st, Poco::Net::HTTPServerResponse& response) { - FDPtr fd(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NONBLOCK)); + FDPtr fd(FDHandle(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NONBLOCK))); if (!fd) { auto err = errno; diff --git a/src/common/fileserver/fileserver.hpp b/src/common/fileserver/fileserver.hpp index 5c1baef39..9186a74c9 100644 --- a/src/common/fileserver/fileserver.hpp +++ b/src/common/fileserver/fileserver.hpp @@ -173,8 +173,10 @@ class Fileserver : public cm::fileserver::FileServerItf, public aos::iamclient:: struct FDHandle { int mFD = -1; + // Implicit conversion from nullptr is required by unique_ptr pointer type. + // cppcheck-suppress noExplicitConstructor FDHandle(std::nullptr_t = nullptr) { } - FDHandle(int fd) + explicit FDHandle(int fd) : mFD(fd) { }