From 6d57123e7345164b0fb050ed558d937588c5e461 Mon Sep 17 00:00:00 2001 From: Oleksandr Grytsov Date: Mon, 16 Feb 2026 20:41:05 +0200 Subject: [PATCH 01/12] cm: smcontroller: add separate thread to handle incoming messages When sync responses and incoming messages are processed in one thread, it may lead to dead lock when some module hold its mutex while sending sync message and at this time it receives incoming message that also processed under same mutex. This is usual pattern in our implementation. Add queue for incoming messages and process it in separate thread. It allows to avoid above dead lock. Signed-off-by: Oleksandr Grytsov Reviewed-by: Mykola Kobets Reviewed-by: Mykola Solianko Reviewed-by: Mykhailo Lohvynenko --- src/cm/smcontroller/smhandler.cpp | 81 +++++++++++++++++++++++++------ src/cm/smcontroller/smhandler.hpp | 14 ++++-- 2 files changed, 75 insertions(+), 20 deletions(-) diff --git a/src/cm/smcontroller/smhandler.cpp b/src/cm/smcontroller/smhandler.cpp index cb590cb4a..dbeaaf9aa 100644 --- a/src/cm/smcontroller/smhandler.cpp +++ b/src/cm/smcontroller/smhandler.cpp @@ -38,9 +38,11 @@ SMHandler::SMHandler(grpc::ServerContext* void SMHandler::Start() { + std::lock_guard lock {mMutex}; + LOG_DBG() << "Start SM handler"; - mStopProcessing.store(false); + mStopProcessing = false; mSyncMessageSender.Init(mStream, cResponseTime); mSyncMessageSender.RegisterResponseHandler( @@ -54,25 +56,36 @@ void SMHandler::Start() dst.mutable_average_monitoring()->CopyFrom(src.average_monitoring()); }); - mProcessThread = std::thread([this]() { ProcessMessages(); }); + mReadThread = std::thread([this]() { ReadMessages(); }); + mMessageThread = std::thread([this]() { ProcessMessages(); }); } void SMHandler::Wait() { - if (mProcessThread.joinable()) { - mProcessThread.join(); + LOG_DBG() << "Wait SM handler"; + + if (mReadThread.joinable()) { + mReadThread.join(); + } + + if (mMessageThread.joinable()) { + mMessageThread.join(); } } void SMHandler::Stop() { + std::lock_guard lock {mMutex}; + LOG_DBG() << "Stop SM handler"; - mStopProcessing.store(true); + mStopProcessing = true; if (mContext) { mContext->TryCancel(); } + + mCondVar.notify_one(); } String SMHandler::GetNodeID() const @@ -213,13 +226,56 @@ Error SMHandler::UpdateNetworks(const Array& networkPar * Private **********************************************************************************************************************/ -Error SMHandler::ProcessMessages() +void SMHandler::ReadMessages() { try { servicemanager::v5::SMOutgoingMessages outgoingMsg; - while (!mStopProcessing.load() && mStream->Read(&outgoingMsg)) { + while (mStream->Read(&outgoingMsg)) { + if (auto err = mSyncMessageSender.ProcessResponse(outgoingMsg); err.HasValue()) { + if (!err->IsNone()) { + LOG_ERR() << "Failed to process response" << Log::Field("nodeID", GetNodeID()) + << Log::Field(AOS_ERROR_WRAP(*err)); + } + } else { + std::lock_guard lock {mMutex}; + + mMessageQueue.push(outgoingMsg); + mCondVar.notify_one(); + } + } + } catch (const std::exception& e) { + LOG_ERR() << "Handle incoming messages failed" << Log::Field(AOS_ERROR_WRAP(common::utils::ToAosError(e))); + } + + mConnStatusListener->OnNodeDisconnected(GetNodeID()); + + std::lock_guard lock {mMutex}; + + mStopProcessing = true; + mCondVar.notify_one(); +} + +void SMHandler::ProcessMessages() +{ + while (true) { + try { + std::unique_lock lock {mMutex}; + + mCondVar.wait(lock, [this]() { return mStopProcessing || !mMessageQueue.empty(); }); + + if (mStopProcessing) { + break; + } + Error err; + auto outgoingMsg = mMessageQueue.front(); + + mMessageQueue.pop(); + + // Process message without holding the lock to allow sending new messages in parallel + + lock.unlock(); if (outgoingMsg.has_sm_info()) { err = ProcessSMInfo(outgoingMsg.sm_info()); @@ -233,8 +289,6 @@ Error SMHandler::ProcessMessages() err = ProcessInstantMonitoring(outgoingMsg.instant_monitoring()); } else if (outgoingMsg.has_alert()) { err = ProcessAlert(outgoingMsg.alert()); - } else if (auto processErr = mSyncMessageSender.ProcessResponse(outgoingMsg); processErr.HasValue()) { - err = processErr.GetValue(); } else { LOG_WRN() << "Unknown message type received"; } @@ -242,14 +296,11 @@ Error SMHandler::ProcessMessages() if (!err.IsNone()) { LOG_ERR() << "Failed to process message" << Log::Field("nodeID", GetNodeID()) << Log::Field(err); } + } catch (const std::exception& e) { + LOG_ERR() << "Process message failed" << Log::Field("nodeID", GetNodeID()) + << Log::Field(AOS_ERROR_WRAP(common::utils::ToAosError(e))); } - } catch (const std::exception& e) { - LOG_ERR() << "Handle incoming messages failed" << Log::Field(AOS_ERROR_WRAP(common::utils::ToAosError(e))); } - - mConnStatusListener->OnNodeDisconnected(GetNodeID()); - - return ErrorEnum::eNone; } Error SMHandler::SendMessage(const servicemanager::v5::SMIncomingMessages& message) diff --git a/src/cm/smcontroller/smhandler.hpp b/src/cm/smcontroller/smhandler.hpp index b3dacdc3d..7815e8ec4 100644 --- a/src/cm/smcontroller/smhandler.hpp +++ b/src/cm/smcontroller/smhandler.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -181,8 +182,8 @@ class SMHandler { Error SendMessage(const servicemanager::v5::SMIncomingMessages& message); - // Message processing methods - Error ProcessMessages(); + void ReadMessages(); + void ProcessMessages(); Error ProcessSMInfo(const servicemanager::v5::SMInfo& smInfo); Error ProcessUpdateInstancesStatus(const servicemanager::v5::UpdateInstancesStatus& status); @@ -205,12 +206,15 @@ class SMHandler { nodeinfoprovider::SMInfoReceiverItf* mSMInfoReceiver {}; NodeConnectionStatusListenerItf* mConnStatusListener {}; - std::mutex mMutex; bool mCredentialListUpdated {}; grpc::ServerContext* mCtx {}; - std::thread mProcessThread; - std::atomic mStopProcessing {}; + std::mutex mMutex; + std::condition_variable mCondVar; + std::thread mReadThread; + std::thread mMessageThread; + bool mStopProcessing {}; + std::queue mMessageQueue; StaticString mNodeID; }; From 528c8d0b59632488e72ce40d7cc4f6714deaabf1 Mon Sep 17 00:00:00 2001 From: Oleksandr Grytsov Date: Mon, 16 Feb 2026 20:44:00 +0200 Subject: [PATCH 02/12] cm: config: set cDefaultUnitStatusSendTimeout to 10sec Signed-off-by: Oleksandr Grytsov Reviewed-by: Mykola Kobets Reviewed-by: Mykola Solianko Reviewed-by: Mykhailo Lohvynenko --- src/cm/config/config.cpp | 2 +- src/cm/config/tests/config.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cm/config/config.cpp b/src/cm/config/config.cpp index 4e6865c53..70a682c24 100644 --- a/src/cm/config/config.cpp +++ b/src/cm/config/config.cpp @@ -28,7 +28,7 @@ constexpr auto cDefaultLauncherInstanceTTL = "30d"; constexpr auto cDefaultLauncherNodesConnectionTimeout = "10m"; constexpr auto cDefaultMonitoringSendPeriod = "1m"; constexpr auto cDefaultSMConnectionTimeout = "1m"; -constexpr auto cDefaultUnitStatusSendTimeout = "30s"; +constexpr auto cDefaultUnitStatusSendTimeout = "10s"; constexpr auto cDefaultUpdateItemTTL = "30d"; constexpr auto cDefaultRemoveOutdatedPeriod = "24h"; constexpr auto cDefaultMigrationPath = "/usr/share/aos/communicationmanager/migration"; diff --git a/src/cm/config/tests/config.cpp b/src/cm/config/tests/config.cpp index c9559e81b..016ad0540 100644 --- a/src/cm/config/tests/config.cpp +++ b/src/cm/config/tests/config.cpp @@ -168,7 +168,7 @@ TEST_F(CMConfigTest, ParseMinimalConfigWithDefaults) EXPECT_EQ(config.mStateDir, (std::filesystem::path("workingDir") / "states").string()); EXPECT_EQ(config.mUnitConfigFile, (std::filesystem::path("workingDir") / "aos_unit.cfg").string()); - EXPECT_EQ(config.mUnitStatusSendTimeout, aos::Time::cSeconds * 30); + EXPECT_EQ(config.mUnitStatusSendTimeout, aos::Time::cSeconds * 10); EXPECT_EQ(config.mCloudResponseWaitTimeout, aos::Time::cSeconds * 10); EXPECT_EQ(config.mMonitoring.mSendPeriod, aos::Time::cMinutes * 1); From 4d7ae332c7093c0d8063fe942bf3dd9b43e6b84f Mon Sep 17 00:00:00 2001 From: Oleksandr Grytsov Date: Mon, 16 Feb 2026 20:45:02 +0200 Subject: [PATCH 03/12] sm: smclient: refactor error handling in HandleIncomingMessages Signed-off-by: Oleksandr Grytsov Reviewed-by: Mykola Kobets Reviewed-by: Mykola Solianko Reviewed-by: Mykhailo Lohvynenko --- src/sm/smclient/smclient.cpp | 46 +++++++++++++----------------------- 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/src/sm/smclient/smclient.cpp b/src/sm/smclient/smclient.cpp index 388aa062c..46f8d3b83 100644 --- a/src/sm/smclient/smclient.cpp +++ b/src/sm/smclient/smclient.cpp @@ -417,46 +417,32 @@ void SMClient::HandleIncomingMessages() smproto::SMIncomingMessages incomingMsg; while (mStream->Read(&incomingMsg)) { + Error err; + if (incomingMsg.has_get_node_config_status()) { - if (auto err = ProcessGetNodeConfigStatus(); !err.IsNone()) { - LOG_ERR() << "Failed to process get node config status: err=" << err; - } + err = ProcessGetNodeConfigStatus(); } else if (incomingMsg.has_check_node_config()) { - if (auto err = ProcessCheckNodeConfig(incomingMsg.check_node_config()); !err.IsNone()) { - LOG_ERR() << "Failed to process check node config: err=" << err; - } + err = ProcessCheckNodeConfig(incomingMsg.check_node_config()); } else if (incomingMsg.has_set_node_config()) { - if (auto err = ProcessSetNodeConfig(incomingMsg.set_node_config()); !err.IsNone()) { - LOG_ERR() << "Failed to process set node config: err=" << err; - } + err = ProcessSetNodeConfig(incomingMsg.set_node_config()); } else if (incomingMsg.has_update_instances()) { - if (auto err = ProcessUpdateInstances(incomingMsg.update_instances()); !err.IsNone()) { - LOG_ERR() << "Failed to process update instances: err=" << err; - } + err = ProcessUpdateInstances(incomingMsg.update_instances()); } else if (incomingMsg.has_system_log_request()) { - if (auto err = ProcessSystemLogRequest(incomingMsg.system_log_request()); !err.IsNone()) { - LOG_ERR() << "Failed to process system log request: err=" << err; - } + err = ProcessSystemLogRequest(incomingMsg.system_log_request()); } else if (incomingMsg.has_instance_log_request()) { - if (auto err = ProcessInstanceLogRequest(incomingMsg.instance_log_request()); !err.IsNone()) { - LOG_ERR() << "Failed to process instance log request: err=" << err; - } + err = ProcessInstanceLogRequest(incomingMsg.instance_log_request()); } else if (incomingMsg.has_instance_crash_log_request()) { - if (auto err = ProcessInstanceCrashLogRequest(incomingMsg.instance_crash_log_request()); !err.IsNone()) { - LOG_ERR() << "Failed to process instance crash log request: err=" << err; - } + err = ProcessInstanceCrashLogRequest(incomingMsg.instance_crash_log_request()); } else if (incomingMsg.has_get_average_monitoring()) { - if (auto err = ProcessGetAverageMonitoring(); !err.IsNone()) { - LOG_ERR() << "Failed to process get average monitoring: err=" << err; - } + err = ProcessGetAverageMonitoring(); } else if (incomingMsg.has_connection_status()) { - if (auto err = ProcessConnectionStatus(incomingMsg.connection_status()); !err.IsNone()) { - LOG_ERR() << "Failed to process connection status: err=" << err; - } + err = ProcessConnectionStatus(incomingMsg.connection_status()); } else if (incomingMsg.has_update_networks()) { - if (auto err = ProcessUpdateNetworks(incomingMsg.update_networks()); !err.IsNone()) { - LOG_ERR() << "Failed to process update networks: err=" << err; - } + err = ProcessUpdateNetworks(incomingMsg.update_networks()); + } + + if (!err.IsNone()) { + LOG_ERR() << "Failed to process incoming message" << Log::Field(err); } } } From 18b6f5b6648cb5f19b7e09dbeec16eb2bd3b7941 Mon Sep 17 00:00:00 2001 From: Oleksandr Grytsov Date: Tue, 17 Feb 2026 15:05:11 +0200 Subject: [PATCH 04/12] common: cloudprotocol: make version in unit config optional Signed-off-by: Oleksandr Grytsov Reviewed-by: Mykola Kobets Reviewed-by: Mykola Solianko Reviewed-by: Mykhailo Lohvynenko --- src/common/cloudprotocol/unitstatus.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/common/cloudprotocol/unitstatus.cpp b/src/common/cloudprotocol/unitstatus.cpp index 4b76eb476..3e7fd1a13 100644 --- a/src/common/cloudprotocol/unitstatus.cpp +++ b/src/common/cloudprotocol/unitstatus.cpp @@ -25,7 +25,10 @@ Poco::JSON::Object::Ptr UnitConfigToJSON(const UnitConfigStatus& unitConfigStatu { auto json = Poco::makeShared(Poco::JSON_PRESERVE_KEY_ORDER); - json->set("version", unitConfigStatus.mVersion.CStr()); + if (!unitConfigStatus.mVersion.IsEmpty()) { + json->set("version", unitConfigStatus.mVersion.CStr()); + } + json->set("state", unitConfigStatus.mState.ToString().CStr()); if (!unitConfigStatus.mError.IsNone()) { From 68fba248abf8b00fe612eb01b0926431d87a6b0f Mon Sep 17 00:00:00 2001 From: Oleksandr Grytsov Date: Tue, 17 Feb 2026 21:30:04 +0200 Subject: [PATCH 05/12] cm: database: add type field to update item info Signed-off-by: Oleksandr Grytsov Reviewed-by: Mykola Kobets Reviewed-by: Mykola Solianko Reviewed-by: Mykhailo Lohvynenko --- src/cm/database/database.cpp | 16 ++++++---- src/cm/database/database.hpp | 16 ++++++++-- src/cm/database/tests/database.cpp | 48 ++++++++++++++++++++---------- 3 files changed, 56 insertions(+), 24 deletions(-) diff --git a/src/cm/database/database.cpp b/src/cm/database/database.cpp index f1f561f88..14abd9e28 100644 --- a/src/cm/database/database.cpp +++ b/src/cm/database/database.cpp @@ -656,8 +656,8 @@ Error Database::AddItem(const imagemanager::ItemInfo& item) ImageManagerItemInfoRow row; FromAos(item, row); - *mSession - << "INSERT INTO imagemanager (itemID, version, indexDigest, state, timestamp) VALUES (?, ?, ?, ?, ?);", + *mSession << "INSERT INTO imagemanager (itemID, type, version, indexDigest, state, timestamp) VALUES " + "(?, ?, ?, ?, ?, ?);", bind(row), now; } catch (const std::exception& e) { return AOS_ERROR_WRAP(common::utils::ToAosError(e)); @@ -713,7 +713,7 @@ Error Database::GetAllItemsInfos(Array& items) try { std::vector rows; - *mSession << "SELECT itemID, version, indexDigest, state, timestamp FROM imagemanager;", into(rows), now; + *mSession << "SELECT itemID, type, version, indexDigest, state, timestamp FROM imagemanager;", into(rows), now; auto itemInfo = std::make_unique(); @@ -737,7 +737,7 @@ Error Database::GetItemInfos(const String& id, Array& it try { std::vector rows; - *mSession << "SELECT itemID, version, indexDigest, state, timestamp FROM imagemanager WHERE itemID = ?;", + *mSession << "SELECT itemID, type, version, indexDigest, state, timestamp FROM imagemanager WHERE itemID = ?;", bind(id.CStr()), into(rows), now; auto itemInfo = std::make_unique(); @@ -874,6 +874,7 @@ void Database::CreateTables() *mSession << "CREATE TABLE IF NOT EXISTS imagemanager (" "itemID TEXT," + "type TEXT," "version TEXT," "indexDigest TEXT," "state TEXT," @@ -1093,6 +1094,7 @@ void Database::FromAos(const imagemanager::ItemInfo& src, ImageManagerItemInfoRo { dst.set(src.mItemID.CStr()); dst.set(src.mVersion.CStr()); + dst.set(src.mType.ToString().CStr()); dst.set(src.mIndexDigest.CStr()); dst.set(src.mState.ToString().CStr()); dst.set(src.mTimestamp.UnixNano()); @@ -1100,10 +1102,12 @@ void Database::FromAos(const imagemanager::ItemInfo& src, ImageManagerItemInfoRo void Database::ToAos(const ImageManagerItemInfoRow& src, imagemanager::ItemInfo& dst) { - dst.mItemID = src.get().c_str(); + dst.mItemID = src.get().c_str(); + auto err = dst.mType.FromString(src.get().c_str()); + AOS_ERROR_CHECK_AND_THROW(err, "failed to parse item type"); dst.mVersion = src.get().c_str(); dst.mIndexDigest = src.get().c_str(); - auto err = dst.mState.FromString(src.get().c_str()); + err = dst.mState.FromString(src.get().c_str()); AOS_ERROR_CHECK_AND_THROW(err, "failed to parse item state"); auto timestamp = src.get(); dst.mTimestamp = Time::Unix(timestamp / Time::cSeconds.Nanoseconds(), timestamp % Time::cSeconds.Nanoseconds()); diff --git a/src/cm/database/database.hpp b/src/cm/database/database.hpp index 86038ef05..1774397a9 100644 --- a/src/cm/database/database.hpp +++ b/src/cm/database/database.hpp @@ -276,6 +276,10 @@ class Database : public DatabaseItf, public networkmanager::StorageItf { */ Error StoreDesiredStatus(const DesiredStatus& desiredStatus) override; + // + // updatemanager::StorageItf interface + // + /** * Stores update state in storage. * @@ -362,8 +366,16 @@ class Database : public DatabaseItf, public networkmanager::StorageItf { std::string, std::string, std::string, uint32_t, uint32_t, uint64_t, std::string, bool, std::string, std::string, std::string, std::string, size_t>; - enum class ImageManagerItemInfoColumns : int { eItemID = 0, eVersion, eIndexDigest, eState, eTimestamp }; - using ImageManagerItemInfoRow = Poco::Tuple; + enum class ImageManagerItemInfoColumns : int { + eItemID = 0, + eType, + eVersion, + eIndexDigest, + eState, + eTimestamp, + }; + using ImageManagerItemInfoRow + = Poco::Tuple; // make virtual for unit tests virtual int GetVersion() const; diff --git a/src/cm/database/tests/database.cpp b/src/cm/database/tests/database.cpp index d4a8f2f08..238761af4 100644 --- a/src/cm/database/tests/database.cpp +++ b/src/cm/database/tests/database.cpp @@ -136,11 +136,12 @@ launcher::InstanceInfo CreateLauncherInstanceInfo(const char* itemID, const char } imagemanager::ItemInfo CreateImageManagerItemInfo( - const char* itemID, const char* version, const char* indexDigest, ItemState state) + const char* itemID, const UpdateItemType& type, const char* version, const char* indexDigest, ItemState state) { imagemanager::ItemInfo info; info.mItemID = itemID; + info.mType = type; info.mVersion = version; info.mIndexDigest = indexDigest; info.mState = state; @@ -677,15 +678,19 @@ TEST_F(CMDatabaseTest, ImageManagerAddItem) { ASSERT_TRUE(mDB.Init(mDatabaseConfig).IsNone()); - auto item1 = CreateImageManagerItemInfo("service1", "1.0.0", "sha256:abc123", ItemStateEnum::eInstalled); - auto item2 = CreateImageManagerItemInfo("service1", "2.0.0", "sha256:def456", ItemStateEnum::eInstalled); - auto item3 = CreateImageManagerItemInfo("service2", "1.0.0", "sha256:ghi789", ItemStateEnum::ePending); + auto item1 = CreateImageManagerItemInfo( + "service1", UpdateItemTypeEnum::eService, "1.0.0", "sha256:abc123", ItemStateEnum::eInstalled); + auto item2 = CreateImageManagerItemInfo( + "service1", UpdateItemTypeEnum::eService, "2.0.0", "sha256:def456", ItemStateEnum::eInstalled); + auto item3 = CreateImageManagerItemInfo( + "service2", UpdateItemTypeEnum::eService, "1.0.0", "sha256:ghi789", ItemStateEnum::ePending); ASSERT_TRUE(mDB.AddItem(item1).IsNone()); ASSERT_TRUE(mDB.AddItem(item2).IsNone()); ASSERT_TRUE(mDB.AddItem(item3).IsNone()); - auto duplicateItem = CreateImageManagerItemInfo("service1", "1.0.0", "sha256:xyz999", ItemStateEnum::eInstalled); + auto duplicateItem = CreateImageManagerItemInfo( + "service1", UpdateItemTypeEnum::eService, "1.0.0", "sha256:xyz999", ItemStateEnum::eInstalled); ASSERT_FALSE(mDB.AddItem(duplicateItem).IsNone()); StaticArray items; @@ -699,9 +704,12 @@ TEST_F(CMDatabaseTest, ImageManagerRemoveItem) { ASSERT_TRUE(mDB.Init(mDatabaseConfig).IsNone()); - auto item1 = CreateImageManagerItemInfo("service1", "1.0.0", "sha256:abc123", ItemStateEnum::eInstalled); - auto item2 = CreateImageManagerItemInfo("service1", "2.0.0", "sha256:def456", ItemStateEnum::eInstalled); - auto item3 = CreateImageManagerItemInfo("service2", "1.0.0", "sha256:ghi789", ItemStateEnum::ePending); + auto item1 = CreateImageManagerItemInfo( + "service1", UpdateItemTypeEnum::eService, "1.0.0", "sha256:abc123", ItemStateEnum::eInstalled); + auto item2 = CreateImageManagerItemInfo( + "service1", UpdateItemTypeEnum::eService, "2.0.0", "sha256:def456", ItemStateEnum::eInstalled); + auto item3 = CreateImageManagerItemInfo( + "service2", UpdateItemTypeEnum::eService, "1.0.0", "sha256:ghi789", ItemStateEnum::ePending); ASSERT_TRUE(mDB.AddItem(item1).IsNone()); ASSERT_TRUE(mDB.AddItem(item2).IsNone()); @@ -722,8 +730,10 @@ TEST_F(CMDatabaseTest, ImageManagerUpdateItemState) { ASSERT_TRUE(mDB.Init(mDatabaseConfig).IsNone()); - auto item1 = CreateImageManagerItemInfo("service1", "1.0.0", "sha256:abc123", ItemStateEnum::ePending); - auto item2 = CreateImageManagerItemInfo("service2", "1.0.0", "sha256:def456", ItemStateEnum::ePending); + auto item1 = CreateImageManagerItemInfo( + "service1", UpdateItemTypeEnum::eService, "1.0.0", "sha256:abc123", ItemStateEnum::ePending); + auto item2 = CreateImageManagerItemInfo( + "service2", UpdateItemTypeEnum::eService, "1.0.0", "sha256:def456", ItemStateEnum::ePending); ASSERT_TRUE(mDB.AddItem(item1).IsNone()); ASSERT_TRUE(mDB.AddItem(item2).IsNone()); @@ -756,9 +766,12 @@ TEST_F(CMDatabaseTest, ImageManagerGetItemsInfo) ASSERT_TRUE(mDB.GetAllItemsInfos(emptyItems).IsNone()); EXPECT_EQ(emptyItems.Size(), 0); - auto item1 = CreateImageManagerItemInfo("service1", "1.0.0", "sha256:abc123", ItemStateEnum::eInstalled); - auto item2 = CreateImageManagerItemInfo("service1", "2.0.0", "sha256:def456", ItemStateEnum::eInstalled); - auto item3 = CreateImageManagerItemInfo("service2", "1.0.0", "sha256:ghi789", ItemStateEnum::ePending); + auto item1 = CreateImageManagerItemInfo( + "service1", UpdateItemTypeEnum::eService, "1.0.0", "sha256:abc123", ItemStateEnum::eInstalled); + auto item2 = CreateImageManagerItemInfo( + "service1", UpdateItemTypeEnum::eService, "2.0.0", "sha256:def456", ItemStateEnum::eInstalled); + auto item3 = CreateImageManagerItemInfo( + "service2", UpdateItemTypeEnum::eService, "1.0.0", "sha256:ghi789", ItemStateEnum::ePending); ASSERT_TRUE(mDB.AddItem(item1).IsNone()); ASSERT_TRUE(mDB.AddItem(item2).IsNone()); @@ -775,9 +788,12 @@ TEST_F(CMDatabaseTest, ImageManagerGetItemsInfos) { ASSERT_TRUE(mDB.Init(mDatabaseConfig).IsNone()); - auto item1 = CreateImageManagerItemInfo("service1", "1.0.0", "sha256:abc123", ItemStateEnum::eInstalled); - auto item2 = CreateImageManagerItemInfo("service1", "2.0.0", "sha256:def456", ItemStateEnum::eInstalled); - auto item3 = CreateImageManagerItemInfo("service2", "1.0.0", "sha256:ghi789", ItemStateEnum::ePending); + auto item1 = CreateImageManagerItemInfo( + "service1", UpdateItemTypeEnum::eService, "1.0.0", "sha256:abc123", ItemStateEnum::eInstalled); + auto item2 = CreateImageManagerItemInfo( + "service1", UpdateItemTypeEnum::eService, "2.0.0", "sha256:def456", ItemStateEnum::eInstalled); + auto item3 = CreateImageManagerItemInfo( + "service2", UpdateItemTypeEnum::eService, "1.0.0", "sha256:ghi789", ItemStateEnum::ePending); ASSERT_TRUE(mDB.AddItem(item1).IsNone()); ASSERT_TRUE(mDB.AddItem(item2).IsNone()); From 1aeca0b1f6cc2087989432e103be7bea82a499ca Mon Sep 17 00:00:00 2001 From: Oleksandr Grytsov Date: Tue, 17 Feb 2026 21:36:06 +0200 Subject: [PATCH 06/12] common: cloudprotocol: add item type to update item status Signed-off-by: Oleksandr Grytsov Reviewed-by: Mykola Kobets Reviewed-by: Mykola Solianko Reviewed-by: Mykhailo Lohvynenko --- src/common/cloudprotocol/tests/unitstatus.cpp | 13 ++++++++----- src/common/cloudprotocol/unitstatus.cpp | 3 ++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/common/cloudprotocol/tests/unitstatus.cpp b/src/common/cloudprotocol/tests/unitstatus.cpp index ccca64d12..40d209e8a 100644 --- a/src/common/cloudprotocol/tests/unitstatus.cpp +++ b/src/common/cloudprotocol/tests/unitstatus.cpp @@ -189,11 +189,11 @@ TEST_F(CloudProtocolUnitStatus, Nodes) TEST_F(CloudProtocolUnitStatus, Items) { - constexpr auto cJSON - = R"({"messageType":"unitStatus","correlationId":"id","isDeltaInfo":false,"items":[)" - R"({"item":{"id":"itemID1"},"version":"version1","state":"downloading"},)" - R"({"item":{"id":"itemID2"},"version":"version1","state":"installed"},)" - R"({"item":{"id":"itemID3"},"version":"version1","state":"failed","errorInfo":{"aosCode":1,"exitCode":0,"message":"test error"}}]})"; + constexpr auto cJSON = R"({"messageType":"unitStatus","correlationId":"id","isDeltaInfo":false,"items":[)" + R"({"item":{"id":"itemID1","type":"service"},"version":"version1","state":"downloading"},)" + R"({"item":{"id":"itemID2","type":"service"},"version":"version1","state":"installed"},)" + R"({"item":{"id":"itemID3","type":"component"},"version":"version1","state":"failed",)" + R"("errorInfo":{"aosCode":1,"exitCode":0,"message":"test error"}}]})"; auto unitStatus = std::make_unique(); unitStatus->mCorrelationID = "id"; @@ -202,16 +202,19 @@ TEST_F(CloudProtocolUnitStatus, Items) unitStatus->mUpdateItems->EmplaceBack(); unitStatus->mUpdateItems->Back().mItemID = "itemID1"; + unitStatus->mUpdateItems->Back().mType = UpdateItemTypeEnum::eService; unitStatus->mUpdateItems->Back().mVersion = "version1"; unitStatus->mUpdateItems->Back().mState = ItemStateEnum::eDownloading; unitStatus->mUpdateItems->EmplaceBack(); unitStatus->mUpdateItems->Back().mItemID = "itemID2"; + unitStatus->mUpdateItems->Back().mType = UpdateItemTypeEnum::eService; unitStatus->mUpdateItems->Back().mVersion = "version1"; unitStatus->mUpdateItems->Back().mState = ItemStateEnum::eInstalled; unitStatus->mUpdateItems->EmplaceBack(); unitStatus->mUpdateItems->Back().mItemID = "itemID3"; + unitStatus->mUpdateItems->Back().mType = UpdateItemTypeEnum::eComponent; unitStatus->mUpdateItems->Back().mVersion = "version1"; unitStatus->mUpdateItems->Back().mState = ItemStateEnum::eFailed; unitStatus->mUpdateItems->Back().mError = Error(ErrorEnum::eFailed, "test error"); diff --git a/src/common/cloudprotocol/unitstatus.cpp b/src/common/cloudprotocol/unitstatus.cpp index 3e7fd1a13..19d8ac303 100644 --- a/src/common/cloudprotocol/unitstatus.cpp +++ b/src/common/cloudprotocol/unitstatus.cpp @@ -181,7 +181,8 @@ Poco::JSON::Object::Ptr NodeInfoToJSON(const UnitNodeInfo& nodeInfo) Poco::JSON::Object::Ptr UpdateItemToJSON(const UpdateItemStatus& status) { AosIdentity identity; - identity.mID = status.mItemID.CStr(); + identity.mID = status.mItemID.CStr(); + identity.mType = status.mType; auto json = Poco::makeShared(Poco::JSON_PRESERVE_KEY_ORDER); From 6f84be33ce10db941372cefefdea84d4ce1b2fe6 Mon Sep 17 00:00:00 2001 From: Oleksandr Grytsov Date: Tue, 17 Feb 2026 22:05:46 +0200 Subject: [PATCH 07/12] common: cloudprotocol: add update item type to instances status Signed-off-by: Oleksandr Grytsov Reviewed-by: Mykola Kobets Reviewed-by: Mykola Solianko Reviewed-by: Mykhailo Lohvynenko --- src/common/cloudprotocol/common.cpp | 8 ++-- src/common/cloudprotocol/tests/unitstatus.cpp | 40 ++++++++++++------- src/common/cloudprotocol/unitstatus.cpp | 2 + 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/common/cloudprotocol/common.cpp b/src/common/cloudprotocol/common.cpp index de5fa155e..6ad4717fc 100644 --- a/src/common/cloudprotocol/common.cpp +++ b/src/common/cloudprotocol/common.cpp @@ -42,14 +42,14 @@ Poco::JSON::Object::Ptr CreateAosIdentity(const AosIdentity& identity) json->set("id", *identity.mID); } - if (identity.mType.has_value()) { - json->set("type", identity.mType->ToString().CStr()); - } - if (identity.mCodename.has_value()) { json->set("codename", *identity.mCodename); } + if (identity.mType.has_value()) { + json->set("type", identity.mType->ToString().CStr()); + } + if (identity.mTitle.has_value()) { json->set("title", *identity.mTitle); } diff --git a/src/common/cloudprotocol/tests/unitstatus.cpp b/src/common/cloudprotocol/tests/unitstatus.cpp index 40d209e8a..aea7bd9be 100644 --- a/src/common/cloudprotocol/tests/unitstatus.cpp +++ b/src/common/cloudprotocol/tests/unitstatus.cpp @@ -229,13 +229,17 @@ TEST_F(CloudProtocolUnitStatus, Items) TEST_F(CloudProtocolUnitStatus, Instances) { - constexpr auto cJSON - = R"({"messageType":"unitStatus","correlationId":"id","isDeltaInfo":false,"instances":[)" - R"({"item":{"id":"itemID1"},"subject":{"id":"subjectID1"},"version":"version1","instances":[)" - R"({"node":{"codename":"nodeID1"},"runtime":{"codename":"runtimeID1"},"instance":1,"stateChecksum":"12345678","state":"active"},)" - R"({"node":{"codename":"nodeID1"},"runtime":{"codename":"runtimeID1"},"instance":2,"state":"failed","errorInfo":{"aosCode":1,"exitCode":0,"message":""}}]},)" - R"({"item":{"id":"itemID2"},"subject":{"id":"subjectID2"},"version":"version2","instances":[)" - R"({"node":{"codename":"nodeID2"},"runtime":{"codename":"runtimeID2"},"instance":1,"state":"activating"}]}]})"; + constexpr auto cJSON = R"({"messageType":"unitStatus","correlationId":"id","isDeltaInfo":false,"instances":[)" + R"({"item":{"id":"itemID1","type":"service"},"subject":{"id":"subjectID1"},)" + R"("version":"version1","instances":[)" + R"({"node":{"codename":"nodeID1"},"runtime":{"codename":"runtimeID1"},)" + R"("instance":1,"stateChecksum":"12345678","state":"active"},)" + R"({"node":{"codename":"nodeID1"},"runtime":{"codename":"runtimeID1"},)" + R"("instance":2,"state":"failed","errorInfo":{"aosCode":1,"exitCode":0,"message":""}}]},)" + R"({"item":{"id":"itemID2","type":"component"},"subject":{"id":"subjectID2"},)" + R"("version":"version2","instances":[)" + R"({"node":{"codename":"nodeID2"},"runtime":{"codename":"runtimeID2"},)" + R"("instance":1,"state":"activating"}]}]})"; auto unitStatus = std::make_unique(); unitStatus->mCorrelationID = "id"; @@ -244,6 +248,7 @@ TEST_F(CloudProtocolUnitStatus, Instances) unitStatus->mInstances->EmplaceBack(); unitStatus->mInstances->Back().mItemID = "itemID1"; + unitStatus->mInstances->Back().mType = UpdateItemTypeEnum::eService; unitStatus->mInstances->Back().mSubjectID = "subjectID1"; unitStatus->mInstances->Back().mVersion = "version1"; @@ -263,6 +268,7 @@ TEST_F(CloudProtocolUnitStatus, Instances) unitStatus->mInstances->EmplaceBack(); unitStatus->mInstances->Back().mItemID = "itemID2"; + unitStatus->mInstances->Back().mType = UpdateItemTypeEnum::eComponent; unitStatus->mInstances->Back().mSubjectID = "subjectID2"; unitStatus->mInstances->Back().mVersion = "version2"; @@ -282,13 +288,17 @@ TEST_F(CloudProtocolUnitStatus, Instances) TEST_F(CloudProtocolUnitStatus, PreinstalledInstances) { - constexpr auto cJSON - = R"({"messageType":"unitStatus","correlationId":"id","isDeltaInfo":false,"instances":[)" - R"({"item":{"id":"itemID1"},"subject":{"id":"subjectID1"},"version":"version1","instances":[)" - R"({"node":{"codename":"nodeID1"},"runtime":{"codename":"runtimeID1"},"instance":1,"stateChecksum":"12345678","state":"active"},)" - R"({"node":{"codename":"nodeID1"},"runtime":{"codename":"runtimeID1"},"instance":2,"state":"failed","errorInfo":{"aosCode":1,"exitCode":0,"message":""}}]},)" - R"({"item":{"codename":"itemID2"},"subject":{"codename":"subjectID2"},"version":"version2","instances":[)" - R"({"node":{"codename":"nodeID2"},"runtime":{"codename":"runtimeID2"},"instance":1,"state":"activating"}]}]})"; + constexpr auto cJSON = R"({"messageType":"unitStatus","correlationId":"id","isDeltaInfo":false,"instances":[)" + R"({"item":{"id":"itemID1","type":"service"},"subject":{"id":"subjectID1"},)" + R"("version":"version1","instances":[)" + R"({"node":{"codename":"nodeID1"},"runtime":{"codename":"runtimeID1"},)" + R"("instance":1,"stateChecksum":"12345678","state":"active"},)" + R"({"node":{"codename":"nodeID1"},"runtime":{"codename":"runtimeID1"},)" + R"("instance":2,"state":"failed","errorInfo":{"aosCode":1,"exitCode":0,"message":""}}]},)" + R"({"item":{"codename":"itemID2","type":"component"},"subject":{"codename":"subjectID2"},)" + R"("version":"version2","instances":[)" + R"({"node":{"codename":"nodeID2"},"runtime":{"codename":"runtimeID2"},)" + R"("instance":1,"state":"activating"}]}]})"; auto unitStatus = std::make_unique(); unitStatus->mCorrelationID = "id"; @@ -297,6 +307,7 @@ TEST_F(CloudProtocolUnitStatus, PreinstalledInstances) unitStatus->mInstances->EmplaceBack(); unitStatus->mInstances->Back().mItemID = "itemID1"; + unitStatus->mInstances->Back().mType = UpdateItemTypeEnum::eService; unitStatus->mInstances->Back().mSubjectID = "subjectID1"; unitStatus->mInstances->Back().mVersion = "version1"; @@ -316,6 +327,7 @@ TEST_F(CloudProtocolUnitStatus, PreinstalledInstances) unitStatus->mInstances->EmplaceBack(); unitStatus->mInstances->Back().mItemID = "itemID2"; + unitStatus->mInstances->Back().mType = UpdateItemTypeEnum::eComponent; unitStatus->mInstances->Back().mSubjectID = "subjectID2"; unitStatus->mInstances->Back().mVersion = "version2"; unitStatus->mInstances->Back().mPreinstalled = true; diff --git a/src/common/cloudprotocol/unitstatus.cpp b/src/common/cloudprotocol/unitstatus.cpp index 19d8ac303..ef2674b80 100644 --- a/src/common/cloudprotocol/unitstatus.cpp +++ b/src/common/cloudprotocol/unitstatus.cpp @@ -215,6 +215,8 @@ Poco::JSON::Object::Ptr InstanceToJSON(const UnitInstancesStatuses& statuses) identity.mID = statuses.mItemID.CStr(); } + identity.mType = statuses.mType; + json->set("item", CreateAosIdentity(identity)); } From c4795df91b5168e855ff9b87388c96fe1b80f08f Mon Sep 17 00:00:00 2001 From: Oleksandr Grytsov Date: Thu, 19 Feb 2026 20:29:01 +0200 Subject: [PATCH 08/12] common: utils: use changed param oreder in FSPlatform::SetUserQuota Signed-off-by: Oleksandr Grytsov Reviewed-by: Mykola Kobets Reviewed-by: Mykola Solianko Reviewed-by: Mykhailo Lohvynenko --- src/common/utils/fsplatform.cpp | 2 +- src/common/utils/fsplatform.hpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/utils/fsplatform.cpp b/src/common/utils/fsplatform.cpp index 7b0ab794e..f3a34ee93 100644 --- a/src/common/utils/fsplatform.cpp +++ b/src/common/utils/fsplatform.cpp @@ -56,7 +56,7 @@ RetWithError FSPlatform::GetAvailableSize(const String& dir) const return size_t(st.f_bavail) * st.f_frsize; } -Error FSPlatform::SetUserQuota(const String& path, size_t quota, size_t uid) const +Error FSPlatform::SetUserQuota(const String& path, uid_t uid, size_t quota) const { if (quota == 0) { return ErrorEnum::eNone; diff --git a/src/common/utils/fsplatform.hpp b/src/common/utils/fsplatform.hpp index 411933eb0..e002200ac 100644 --- a/src/common/utils/fsplatform.hpp +++ b/src/common/utils/fsplatform.hpp @@ -49,11 +49,11 @@ class FSPlatform : public fs::FSPlatformItf { * Sets user quota for the given path. * * @param path path to set quota for. - * @param quota quota size in bytes. * @param uid user ID. + * @param quota quota size in bytes. * @return Error. */ - Error SetUserQuota(const String& path, size_t quota, size_t uid) const override; + Error SetUserQuota(const String& path, uid_t uid, size_t quota) const override; /** * Changes the owner of a file or directory. From 61e9bddcca48fba0dfdfcff56e4177e48c174c24 Mon Sep 17 00:00:00 2001 From: Oleksandr Grytsov Date: Thu, 19 Feb 2026 20:30:05 +0200 Subject: [PATCH 09/12] common: utils: convert quota size in bytes to blocks in SetUserQuota Disk quota is set in blocks of 1024 bytes. Update SetUserQuota and runtime container monitoring accordingly. Signed-off-by: Oleksandr Grytsov Reviewed-by: Mykola Kobets Reviewed-by: Mykola Solianko Reviewed-by: Mykhailo Lohvynenko --- src/common/utils/fsplatform.cpp | 2 +- src/sm/launcher/runtimes/container/monitoring.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/utils/fsplatform.cpp b/src/common/utils/fsplatform.cpp index f3a34ee93..8f7f0c71f 100644 --- a/src/common/utils/fsplatform.cpp +++ b/src/common/utils/fsplatform.cpp @@ -72,7 +72,7 @@ Error FSPlatform::SetUserQuota(const String& path, uid_t uid, size_t quota) cons dqblk dq {}; - dq.dqb_bhardlimit = quota; + dq.dqb_bhardlimit = (quota + 1023) / 1024; dq.dqb_valid = QIF_BLIMITS; if (auto res diff --git a/src/sm/launcher/runtimes/container/monitoring.cpp b/src/sm/launcher/runtimes/container/monitoring.cpp index 71d4b9181..e77cda3b1 100644 --- a/src/sm/launcher/runtimes/container/monitoring.cpp +++ b/src/sm/launcher/runtimes/container/monitoring.cpp @@ -217,7 +217,7 @@ size_t Monitoring::GetInstanceDiskUsage(const std::string& path, uid_t uid) AOS_ERROR_THROW(ErrorEnum::eFailed, "failed to get quota"); } - return static_cast(quota.dqb_curspace); + return static_cast(quota.dqb_curspace * 1024); } }; // namespace aos::sm::launcher From 6a8012260af392ec3176b903c51ecf9172cc1040 Mon Sep 17 00:00:00 2001 From: Oleksandr Grytsov Date: Thu, 19 Feb 2026 20:31:41 +0200 Subject: [PATCH 10/12] launcher: container: change err to wrn if disk quota is not supported Signed-off-by: Oleksandr Grytsov Reviewed-by: Mykola Kobets Reviewed-by: Mykola Solianko Reviewed-by: Mykhailo Lohvynenko --- src/sm/launcher/runtimes/container/monitoring.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sm/launcher/runtimes/container/monitoring.cpp b/src/sm/launcher/runtimes/container/monitoring.cpp index e77cda3b1..2e75c27ad 100644 --- a/src/sm/launcher/runtimes/container/monitoring.cpp +++ b/src/sm/launcher/runtimes/container/monitoring.cpp @@ -205,7 +205,7 @@ size_t Monitoring::GetInstanceDiskUsage(const std::string& path, uid_t uid) } if (!QuotasSupported(devicePath)) { - LOG_ERR() << "Quotas are not supported on device" << Log::Field("devicePath", devicePath.c_str()); + LOG_WRN() << "Quotas are not supported on device" << Log::Field("devicePath", devicePath.c_str()); return 0; } From e1e8de06d765b19fba7be1959b07c6772377159a Mon Sep 17 00:00:00 2001 From: Oleksandr Grytsov Date: Thu, 19 Feb 2026 20:33:08 +0200 Subject: [PATCH 11/12] ci: move static analysis at first place Static analysis doesn't require build and could be performed first to fail as soon as any issue detected. Signed-off-by: Oleksandr Grytsov Reviewed-by: Mykola Kobets Reviewed-by: Mykola Solianko Reviewed-by: Mykhailo Lohvynenko --- .github/workflows/build-test.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index a5fa2f242..073bd557e 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -41,6 +41,10 @@ jobs: repository: ${{github.event.pull_request.head.repo.full_name}} fetch-depth: 0 + - name: Static analysis + run: | + ./build.sh lint --aos-service cm --aos-service iam --aos-service sm + - name: Install SonarQube build wrapper uses: SonarSource/sonarqube-scan-action/install-build-wrapper@v6 @@ -56,10 +60,6 @@ jobs: run: | ./build.sh coverage --aos-service cm --aos-service iam --aos-service sm - - name: Static analysis - run: | - ./build.sh lint --aos-service cm --aos-service iam --aos-service sm - - name: Upload codecov report uses: codecov/codecov-action@v4 with: From bedd21db06bfbbbe9b866ebf6d5672908afd9595 Mon Sep 17 00:00:00 2001 From: Oleksandr Grytsov Date: Fri, 20 Feb 2026 18:50:57 +0200 Subject: [PATCH 12/12] common: cloudprotocol: fix parsing default unit config values Signed-off-by: Oleksandr Grytsov Reviewed-by: Mykola Kobets Reviewed-by: Mykola Solianko Reviewed-by: Mykhailo Lohvynenko --- src/common/cloudprotocol/unitconfig.cpp | 27 ++++++++++++++++--------- src/common/pbconvert/tests/sm.cpp | 10 ++++----- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/common/cloudprotocol/unitconfig.cpp b/src/common/cloudprotocol/unitconfig.cpp index 0c5edc816..6da887665 100644 --- a/src/common/cloudprotocol/unitconfig.cpp +++ b/src/common/cloudprotocol/unitconfig.cpp @@ -207,8 +207,10 @@ Error ToJSON(const NodeConfig& nodeConfig, Poco::JSON::Object& json) AosIdentity identity; - identity.mCodename = nodeConfig.mNodeID.CStr(); - json.set("node", CreateAosIdentity(identity)); + if (!nodeConfig.mNodeID.IsEmpty()) { + identity.mCodename = nodeConfig.mNodeID.CStr(); + json.set("node", CreateAosIdentity(identity)); + } identity.mCodename = nodeConfig.mNodeType.CStr(); json.set("nodeGroupSubject", CreateAosIdentity(identity)); @@ -221,7 +223,10 @@ Error ToJSON(const NodeConfig& nodeConfig, Poco::JSON::Object& json) json.set("resourceRatios", ResourceRatiosToJSON(*nodeConfig.mResourceRatios)); } - json.set("labels", utils::ToJsonArray(nodeConfig.mLabels, utils::ToStdString)); + if (nodeConfig.mLabels.Size() > 0) { + json.set("labels", utils::ToJsonArray(nodeConfig.mLabels, utils::ToStdString)); + } + json.set("priority", nodeConfig.mPriority); return ErrorEnum::eNone; @@ -249,7 +254,7 @@ Error FromJSON(const common::utils::CaseInsensitiveObjectWrapper& json, NodeConf AOS_ERROR_CHECK_AND_THROW(err, "can't parse codename"); } - { + if (json.Has("node")) { AosIdentity identity; auto err = ParseAosIdentity(json.GetObject("node"), identity); @@ -276,7 +281,9 @@ Error FromJSON(const common::utils::CaseInsensitiveObjectWrapper& json, NodeConf AOS_ERROR_CHECK_AND_THROW(err, "can't parse labels"); } - nodeConfig.mPriority = json.GetValue("priority"); + if (json.Has("priority")) { + nodeConfig.mPriority = json.GetValue("priority"); + } return ErrorEnum::eNone; } catch (const std::exception& e) { @@ -287,8 +294,8 @@ Error FromJSON(const common::utils::CaseInsensitiveObjectWrapper& json, NodeConf Error ToJSON(const UnitConfig& unitConfig, Poco::JSON::Object& json) { try { - json.set("version", unitConfig.mVersion.CStr()); json.set("formatVersion", unitConfig.mFormatVersion.CStr()); + json.set("version", unitConfig.mVersion.CStr()); json.set("nodes", common::utils::ToJsonArray(unitConfig.mNodes, [](const auto& nodeConfig) { auto nodeJson = Poco::makeShared(Poco::JSON_PRESERVE_KEY_ORDER); @@ -307,12 +314,12 @@ Error ToJSON(const UnitConfig& unitConfig, Poco::JSON::Object& json) Error FromJSON(const utils::CaseInsensitiveObjectWrapper& json, UnitConfig& unitConfig) { try { - auto err = unitConfig.mVersion.Assign(json.GetValue("version").c_str()); - AOS_ERROR_CHECK_AND_THROW(err, "parsed version length exceeds application limit"); - - err = unitConfig.mFormatVersion.Assign(json.GetValue("formatVersion").c_str()); + auto err = unitConfig.mFormatVersion.Assign(json.GetValue("formatVersion").c_str()); AOS_ERROR_CHECK_AND_THROW(err, "parsed format version length exceeds application limit"); + err = unitConfig.mVersion.Assign(json.GetValue("version").c_str()); + AOS_ERROR_CHECK_AND_THROW(err, "parsed version length exceeds application limit"); + common::utils::ForEach(json, "nodes", [&unitConfig](const auto& value) { auto err = unitConfig.mNodes.EmplaceBack(); AOS_ERROR_CHECK_AND_THROW(err, "can't create node config"); diff --git a/src/common/pbconvert/tests/sm.cpp b/src/common/pbconvert/tests/sm.cpp index 35e43abb2..5aeb3b69c 100644 --- a/src/common/pbconvert/tests/sm.cpp +++ b/src/common/pbconvert/tests/sm.cpp @@ -692,9 +692,8 @@ TEST_F(PBConvertSMTest, ConvertSMInfoFromProto) TEST_F(PBConvertSMTest, ConvertNodeConfigToCheckNodeConfigProto) { - static constexpr auto cExpectedNodeConfigJSON - = R"({"version":"2.5.0","node":{"codename":"config-node"},)" - R"("nodeGroupSubject":{"codename":"main"},"labels":[],"priority":0})"; + static constexpr auto cExpectedNodeConfigJSON = R"({"version":"2.5.0","node":{"codename":"config-node"},)" + R"("nodeGroupSubject":{"codename":"main"},"priority":0})"; NodeConfig config; @@ -713,9 +712,8 @@ TEST_F(PBConvertSMTest, ConvertNodeConfigToCheckNodeConfigProto) TEST_F(PBConvertSMTest, ConvertNodeConfigToSetNodeConfigProto) { - static constexpr auto cExpectedNodeConfigJSON - = R"({"version":"3.0.0","node":{"codename":"config-node"},)" - R"("nodeGroupSubject":{"codename":"main"},"labels":[],"priority":0})"; + static constexpr auto cExpectedNodeConfigJSON = R"({"version":"3.0.0","node":{"codename":"config-node"},)" + R"("nodeGroupSubject":{"codename":"main"},"priority":0})"; NodeConfig config;