diff --git a/.github/workflows/build-release.yaml b/.github/workflows/build-release.yaml index 445742aa0..353260574 100644 --- a/.github/workflows/build-release.yaml +++ b/.github/workflows/build-release.yaml @@ -35,7 +35,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: fetch-depth: 0 diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index f2ac69c37..8c1211e78 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -36,11 +36,12 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: ref: ${{github.event.pull_request.head.ref}} repository: ${{github.event.pull_request.head.repo.full_name}} fetch-depth: 0 + allow-unsafe-pr-checkout: true - name: Install build wrapper uses: SonarSource/sonarqube-scan-action/install-build-wrapper@v7 @@ -54,6 +55,7 @@ jobs: ./build.sh coverage - name: Upload codecov report + if: github.event_name == 'push' uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/check-format.yaml b/.github/workflows/check-format.yaml index f6ad6298f..194d6ffbc 100644 --- a/.github/workflows/check-format.yaml +++ b/.github/workflows/check-format.yaml @@ -18,7 +18,7 @@ jobs: name: Formatting Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index cd2a7cab0..377c23322 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Log in to github container registry uses: docker/login-action@v3 diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 2672e7ca4..860f44ecf 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -35,7 +35,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: fetch-depth: 0 diff --git a/README.md b/README.md index 959b49306..e907893d8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,14 @@ -[![ci](https://github.com/aosedge/aos_core_lib_cpp/actions/workflows/build_test.yaml/badge.svg)](https://github.com/aosedge/aos_core_lib_cpp/actions/workflows/build_test.yaml) -[![codecov](https://codecov.io/gh/aosedge/aos_core_lib_cpp/graph/badge.svg?token=kg8h7ATd9S)](https://codecov.io/gh/aosedge/aos_core_lib_cpp) -[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=aosedge_aos_core_lib_cpp&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=aosedge_aos_core_lib_cpp) +[![ci][ci-dev]][ci-dev-link] +[![coverage][cov-dev]][cov-dev-link] +[![Quality Gate][qg-dev]][qg-dev-link] + +[ci-dev]: https://github.com/aosedge/aos_core_lib_cpp/actions/workflows/build-test.yaml/badge.svg?branch=develop +[ci-dev-link]: https://github.com/aosedge/aos_core_lib_cpp/actions/workflows/build-test.yaml?query=branch%3Adevelop +[cov-dev]: https://sonarcloud.io/api/project_badges/measure?project=aosedge_aos_core_lib_cpp&metric=coverage&branch=develop +[cov-dev-link]: https://sonarcloud.io/summary/new_code?id=aosedge_aos_core_lib_cpp&branch=develop +[qg-dev]: https://sonarcloud.io/api/project_badges/measure?project=aosedge_aos_core_lib_cpp&metric=alert_status&branch=develop +[qg-dev-link]: https://sonarcloud.io/summary/new_code?id=aosedge_aos_core_lib_cpp&branch=develop # Aos core cpp libraries diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 000000000..c4d7e1e48 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,21 @@ +codecov: + branch: main + +coverage: + status: + project: + default: + target: auto + threshold: 1% + informational: true + patch: + default: + target: 80% + informational: true + +ignore: + - "**/tests/**" + +branches: + - main + - develop diff --git a/src/core/cm/alerts/alerts.cpp b/src/core/cm/alerts/alerts.cpp index 54fa21cc2..2242134a4 100644 --- a/src/core/cm/alerts/alerts.cpp +++ b/src/core/cm/alerts/alerts.cpp @@ -44,11 +44,12 @@ class SetTimestamp : public StaticVisitor { * Public **********************************************************************************************************************/ -Error Alerts::Init( - const alerts::Config& config, cm::alerts::SenderItf& sender, cloudconnection::CloudConnectionItf& cloudConnection) +Error Alerts::Init(AllocatorItf& allocator, const alerts::Config& config, cm::alerts::SenderItf& sender, + cloudconnection::CloudConnectionItf& cloudConnection) { LOG_DBG() << "Init alerts" << Log::Field("sendPeriod", config.mSendPeriod); + mAllocator = &allocator; mConfig = config; mSender = &sender; mCloudConnection = &cloudConnection; @@ -102,7 +103,7 @@ Error Alerts::Stop() err = AOS_ERROR_WRAP(unsubscribeErr); } - if (auto stopErr = mSendTimer.Stop(); !stopErr.IsNone()) { + if (auto stopErr = mSendTimer.Stop(Timer::StopMode::WaitForCallbacks); !stopErr.IsNone()) { LOG_ERR() << "Failed to stop alerts send timer" << Log::Field(stopErr); if (err.IsNone()) { @@ -186,8 +187,8 @@ Error Alerts::UnsubscribeListener(AlertsListenerItf& listener) size_t removed = 0; - for (auto& [tag, listeners] : mListeners) { - removed += listeners.Remove(&listener); + for (auto& item : mListeners) { + removed += item.mSecond.Remove(&listener); } return removed > 0 ? ErrorEnum::eNone : ErrorEnum::eNotFound; @@ -242,6 +243,9 @@ Error Alerts::SendAlerts() while (!mAlerts.IsEmpty()) { auto package = CreatePackage(); + if (!package) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } LOG_INF() << "Send alerts" << Log::Field("alertsCount", package->mItems.Size()); @@ -257,7 +261,12 @@ Error Alerts::SendAlerts() bool Alerts::IsDuplicated(const AlertVariant& alert) { - auto alertCopy = MakeUnique(&mAllocator, alert); + auto alertCopy = MakeUnique(mAllocator, alert); + if (!alertCopy) { + LOG_ERR() << "Can't allocate alert copy" << Log::Field(ErrorEnum::eNoMemory); + + return false; + } return mAlerts.FindIf([&alertCopy](const AlertVariant& item) { alertCopy->ApplyVisitor(SetTimestamp(item.ApplyVisitor(GetTimestamp()))); @@ -268,7 +277,12 @@ bool Alerts::IsDuplicated(const AlertVariant& alert) UniquePtr Alerts::CreatePackage() { - auto package = MakeUnique(&mAllocator); + auto package = MakeUnique(mAllocator); + if (!package) { + LOG_ERR() << "Can't allocate alerts package" << Log::Field(ErrorEnum::eNoMemory); + + return package; + } const auto count = Min(cAlertItemsCount, mAlerts.Size()); diff --git a/src/core/cm/alerts/alerts.hpp b/src/core/cm/alerts/alerts.hpp index 67c02d33a..177a4dbeb 100644 --- a/src/core/cm/alerts/alerts.hpp +++ b/src/core/cm/alerts/alerts.hpp @@ -40,12 +40,13 @@ class Alerts : public ReceiverItf, /** * Initializes alerts. * + * @param allocator allocator to use for temporary objects. * @param config configuration object. * @param sender alerts sender object. * @param cloudConnection cloud connection. * @return Error. */ - Error Init(const alerts::Config& config, cm::alerts::SenderItf& sender, + Error Init(AllocatorItf& allocator, const alerts::Config& config, cm::alerts::SenderItf& sender, cloudconnection::CloudConnectionItf& cloudConnection); /** @@ -96,7 +97,6 @@ class Alerts : public ReceiverItf, Error UnsubscribeListener(AlertsListenerItf& listener) override; private: - static constexpr auto cAllocatorSize = sizeof(AlertVariant) + sizeof(aos::Alerts); static constexpr auto cListenersMaxCount = 4; static constexpr auto cAlertTagsCount = static_cast(AlertTagEnum::eNumAlertTags); @@ -111,7 +111,7 @@ class Alerts : public ReceiverItf, void ShrinkCache(size_t count); void NotifyListeners(const AlertVariant& alert); - StaticAllocator mAllocator; + AllocatorItf* mAllocator {}; alerts::Config mConfig; cm::alerts::SenderItf* mSender {}; cloudconnection::CloudConnectionItf* mCloudConnection {}; diff --git a/src/core/cm/alerts/tests/alerts.cpp b/src/core/cm/alerts/tests/alerts.cpp index d5c42fd12..64a3f3ebe 100644 --- a/src/core/cm/alerts/tests/alerts.cpp +++ b/src/core/cm/alerts/tests/alerts.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -125,6 +126,10 @@ class AlertsTest : public Test { protected: void SetUp() override { tests::utils::InitLog(); } + // mAllocator must be declared (and therefore destroyed) after any member that allocates from it, since + // members are destroyed in reverse declaration order. + HeapAllocator mAllocator; + alerts::Config mConfig {Time::cSeconds * 1}; SenderStub mCommunication; cloudconnection::CloudConnectionMock mCloudConnection; @@ -159,7 +164,7 @@ TEST_F(AlertsTest, DuplicatesAreSkipped) return ErrorEnum::eNone; })); - auto err = mAlerts->Init(mConfig, mCommunication, mCloudConnection); + auto err = mAlerts->Init(mAllocator, mConfig, mCommunication, mCloudConnection); ASSERT_TRUE(err.IsNone()) << tests::utils::ErrorToStr(err); err = mAlerts->Start(); @@ -198,16 +203,16 @@ TEST_F(AlertsTest, AlertIsSkippedIfBufferIsFull) std::string message; + auto err = mAlerts->Init(mAllocator, mConfig, mCommunication, mCloudConnection); + ASSERT_TRUE(err.IsNone()) << tests::utils::ErrorToStr(err); + for (size_t i = 0; i < cAlertsCacheSize; ++i) { auto alert = CreateCoreAlert(cTime, "node1", std::to_string(i)); - auto err = mAlerts->OnAlertReceived(*alert); + err = mAlerts->OnAlertReceived(*alert); EXPECT_TRUE(err.IsNone()) << tests::utils::ErrorToStr(err); } - auto err = mAlerts->Init(mConfig, mCommunication, mCloudConnection); - ASSERT_TRUE(err.IsNone()) << tests::utils::ErrorToStr(err); - err = mAlerts->OnAlertReceived(*CreateCoreAlert(cTime, "node1", "skipped alert")); EXPECT_TRUE(err.IsNone()) << tests::utils::ErrorToStr(err); @@ -264,7 +269,7 @@ TEST_F(AlertsTest, PackagesAreSent) mConfig.mSendPeriod = Time::cSeconds * 3; - auto err = mAlerts->Init(mConfig, mCommunication, mCloudConnection); + auto err = mAlerts->Init(mAllocator, mConfig, mCommunication, mCloudConnection); ASSERT_TRUE(err.IsNone()) << tests::utils::ErrorToStr(err); err = mAlerts->Start(); @@ -311,7 +316,7 @@ TEST_F(AlertsTest, PackagesAreSentOnReconnect) std::vector receivedPackages; - auto err = mAlerts->Init(mConfig, mCommunication, mCloudConnection); + auto err = mAlerts->Init(mAllocator, mConfig, mCommunication, mCloudConnection); ASSERT_TRUE(err.IsNone()) << tests::utils::ErrorToStr(err); err = mAlerts->Start(); @@ -353,7 +358,7 @@ TEST_F(AlertsTest, ListenersAreNotified) AlertTagEnum::eCoreAlert, }; - auto err = mAlerts->Init(mConfig, mCommunication, mCloudConnection); + auto err = mAlerts->Init(mAllocator, mConfig, mCommunication, mCloudConnection); ASSERT_TRUE(err.IsNone()) << tests::utils::ErrorToStr(err); err = mAlerts->SubscribeListener( diff --git a/src/core/cm/config.hpp b/src/core/cm/config.hpp index bfe1e7faa..ffb2677de 100644 --- a/src/core/cm/config.hpp +++ b/src/core/cm/config.hpp @@ -25,7 +25,7 @@ * Alerts cache size. */ #ifndef AOS_CONFIG_CM_ALERTS_CACHE_SIZE -#define AOS_CONFIG_CM_ALERTS_CACHE_SIZE 32 +#define AOS_CONFIG_CM_ALERTS_CACHE_SIZE 64 #endif /** diff --git a/src/core/cm/imagemanager/imagemanager.cpp b/src/core/cm/imagemanager/imagemanager.cpp index b66eb8146..9ec61bdbe 100644 --- a/src/core/cm/imagemanager/imagemanager.cpp +++ b/src/core/cm/imagemanager/imagemanager.cpp @@ -14,14 +14,15 @@ namespace aos::cm::imagemanager { * Public **********************************************************************************************************************/ -Error ImageManager::Init(const Config& config, StorageItf& storage, BlobInfoProviderItf& blobInfoProvider, - spaceallocator::SpaceAllocatorItf& downloadingSpaceAllocator, +Error ImageManager::Init(AllocatorItf& allocator, const Config& config, StorageItf& storage, + BlobInfoProviderItf& blobInfoProvider, spaceallocator::SpaceAllocatorItf& downloadingSpaceAllocator, spaceallocator::SpaceAllocatorItf& installSpaceAllocator, downloader::DownloaderItf& downloader, fileserver::FileServerItf& fileserver, crypto::CryptoHelperItf& cryptoHelper, fs::FileInfoProviderItf& fileInfoProvider, oci::OCISpecItf& ociSpec) { LOG_DBG() << "Init image manager"; + mAllocator = &allocator; mConfig = config; mStorage = &storage; mBlobInfoProvider = &blobInfoProvider; @@ -33,7 +34,7 @@ Error ImageManager::Init(const Config& config, StorageItf& storage, BlobInfoProv mFileInfoProvider = &fileInfoProvider; mOCISpec = &ociSpec; - auto items = MakeUnique>(&mAllocator); + auto items = MakeUnique>(mAllocator); if (!items) { return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); } @@ -97,7 +98,7 @@ Error ImageManager::Stop() { LOG_DBG() << "Stop image manager"; - return mTimer.Stop(); + return mTimer.Stop(Timer::StopMode::WaitForCallbacks); } Error ImageManager::DownloadUpdateItems(const Array& itemsInfo, @@ -130,7 +131,7 @@ Error ImageManager::DownloadUpdateItems(const Array& itemsInfo, statuses[i].mError = ErrorEnum::eNone; } - auto storedItems = MakeUnique>(&mAllocator); + auto storedItems = MakeUnique>(mAllocator); if (!storedItems) { return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); } @@ -220,7 +221,7 @@ Error ImageManager::InstallUpdateItems(const Array& itemsInfo, A statuses[i].mError = ErrorEnum::eNone; } - auto storedItems = MakeUnique>(&mAllocator); + auto storedItems = MakeUnique>(mAllocator); if (!storedItems) { return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); } @@ -300,7 +301,7 @@ Error ImageManager::GetUpdateItemsStatuses(Array& statuses) LOG_DBG() << "Get update items statuses"; - auto items = MakeUnique>(&mAllocator); + auto items = MakeUnique>(mAllocator); if (!items) { return ErrorEnum::eNoMemory; } @@ -358,7 +359,7 @@ Error ImageManager::GetIndexDigest(const String& itemID, const String& version, LOG_DBG() << "Get index digest" << Log::Field("itemID", itemID) << Log::Field("version", version); - auto items = MakeUnique>(&mAllocator); + auto items = MakeUnique>(mAllocator); if (!items) { return ErrorEnum::eNoMemory; } @@ -429,7 +430,7 @@ Error ImageManager::GetItemCurrentVersion(const String& itemID, String& version) LOG_DBG() << "Get item current version" << Log::Field("itemID", itemID); - auto items = MakeUnique>(&mAllocator); + auto items = MakeUnique>(mAllocator); if (!items) { return ErrorEnum::eNoMemory; } @@ -458,7 +459,7 @@ RetWithError ImageManager::RemoveItem(const String& id, const String& ve LOG_DBG() << "Remove item" << Log::Field("id", id) << Log::Field("version", version); - auto storedItems = MakeUnique>(&mAllocator); + auto storedItems = MakeUnique>(mAllocator); if (!storedItems) { return {0, AOS_ERROR_WRAP(ErrorEnum::eNoMemory)}; } @@ -508,7 +509,7 @@ Error ImageManager::RemoveOutdatedItems() LOG_DBG() << "Remove outdated items"; - auto items = MakeUnique>(&mAllocator); + auto items = MakeUnique>(mAllocator); if (!items) { return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); } @@ -586,7 +587,7 @@ Error ImageManager::AllocateSpaceForPartialDownloads() auto fileName = fileIterator->mPath; auto filePath = fs::JoinPath(algorithmDir, fileName); - auto [fileSize, sizeErr] = fs::CalculateSize(filePath); + auto [fileSize, sizeErr] = fs::CalculateSize(*mAllocator, filePath); if (!sizeErr.IsNone()) { LOG_WRN() << "Failed to get size for partial download" << Log::Field("path", filePath) << Log::Field(sizeErr); @@ -884,7 +885,7 @@ Error ImageManager::DownloadItem(const UpdateItemInfo& itemInfo, const Array(&mAllocator); + auto imageIndex = MakeUnique(mAllocator); if (!imageIndex) { return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); } @@ -898,7 +899,7 @@ Error ImageManager::DownloadItem(const UpdateItemInfo& itemInfo, const ArraymManifests.Size()); for (const auto& manifestDescriptor : imageIndex->mManifests) { - auto manifest = MakeUnique(&mAllocator); + auto manifest = MakeUnique(mAllocator); if (!manifest) { return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); } @@ -1080,7 +1081,11 @@ Error ImageManager::EnsureBlob(const String& digest, const String& downloadPath, { LOG_DBG() << "Ensure blob" << Log::Field("digest", digest); - auto blobInfo = MakeUnique(&mAllocator); + auto blobInfo = MakeUnique(mAllocator); + if (!blobInfo) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } + UniquePtr downloadingSpace; do { @@ -1132,7 +1137,7 @@ Error ImageManager::GetBlobInfo(const String& digest, BlobInfo& blobInfo) return AOS_ERROR_WRAP(err); } - auto blobsInfo = MakeUnique>(&mAllocator); + auto blobsInfo = MakeUnique>(mAllocator); if (!blobsInfo) { return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); } @@ -1190,7 +1195,7 @@ Error ImageManager::CheckExistingBlob(const String& installPath) return AOS_ERROR_WRAP(err); } - auto expectedSHA256 = MakeUnique>(&mAllocator); + auto expectedSHA256 = MakeUnique>(mAllocator); if (!expectedSHA256) { return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); } @@ -1223,7 +1228,7 @@ Error ImageManager::PrepareDownloadSpace(const String& downloadPath, const BlobI partialDownloadSize = 0; if (downloadExists) { - auto [dirSize, getSizeErr] = fs::CalculateSize(downloadPath); + auto [dirSize, getSizeErr] = fs::CalculateSize(*mAllocator, downloadPath); if (!getSizeErr.IsNone()) { return AOS_ERROR_WRAP(getSizeErr); } @@ -1275,7 +1280,7 @@ Error ImageManager::PerformDownload(const BlobInfo& blobInfo, const String& down << Log::Field("path", downloadPath) << Log::Field(AOS_ERROR_WRAP(err)); if (err = WaitForStop(); !err.IsNone()) { - auto [newPartialSize, retrySizeErr] = fs::CalculateSize(downloadPath); + auto [newPartialSize, retrySizeErr] = fs::CalculateSize(*mAllocator, downloadPath); if (!retrySizeErr.IsNone()) { LOG_WRN() << "Failed to get partial download size" << Log::Field("path", downloadPath) << Log::Field(retrySizeErr); @@ -1417,9 +1422,11 @@ bool ImageManager::StartAction() mCondVar.Wait(lock, [this]() { return !mInProgress || mCancel; }); - if (mCancel) { - mCancel = false; + const bool cancelledWhileRunning = mCancel && mInProgress; + + mCancel = false; + if (cancelledWhileRunning) { return false; } @@ -1512,7 +1519,7 @@ Error ImageManager::VerifyBlobChecksum(const String& digest, const fs::FileInfo& return AOS_ERROR_WRAP(err); } - auto expectedSHA256 = MakeUnique>(&mAllocator); + auto expectedSHA256 = MakeUnique>(mAllocator); if (!expectedSHA256) { return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); } @@ -1541,7 +1548,7 @@ Error ImageManager::VerifyItemBlobs(const String& indexDigest) return AOS_ERROR_WRAP(err); } - auto imageIndex = MakeUnique(&mAllocator); + auto imageIndex = MakeUnique(mAllocator); if (!imageIndex) { return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); } @@ -1560,7 +1567,7 @@ Error ImageManager::VerifyItemBlobs(const String& indexDigest) return AOS_ERROR_WRAP(err); } - auto manifest = MakeUnique(&mAllocator); + auto manifest = MakeUnique(mAllocator); if (!manifest) { return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); } @@ -1603,7 +1610,7 @@ bool ImageManager::IsBlobUsedByItems(const String& blobDigest, const Array(&mAllocator); + auto imageIndex = MakeUnique(mAllocator); if (!imageIndex) { continue; } @@ -1623,7 +1630,7 @@ bool ImageManager::IsBlobUsedByItems(const String& blobDigest, const Array(&mAllocator); + auto manifest = MakeUnique(mAllocator); if (!manifest) { continue; } @@ -1657,7 +1664,7 @@ RetWithError ImageManager::CleanupOrphanedBlobs() size_t totalSize = 0; - auto storedItems = MakeUnique>(&mAllocator); + auto storedItems = MakeUnique>(mAllocator); if (!storedItems) { return {0, AOS_ERROR_WRAP(ErrorEnum::eNoMemory)}; } @@ -1683,7 +1690,7 @@ RetWithError ImageManager::CleanupOrphanedBlobs() if (!IsBlobUsedByItems(blobDigest, *storedItems)) { auto filePath = fs::JoinPath(algorithmDir, hash); - auto [blobSize, sizeErr] = fs::CalculateSize(filePath); + auto [blobSize, sizeErr] = fs::CalculateSize(*mAllocator, filePath); if (!sizeErr.IsNone()) { LOG_WRN() << "Failed to get blob size" << Log::Field("path", filePath) << Log::Field(sizeErr); } else { diff --git a/src/core/cm/imagemanager/imagemanager.hpp b/src/core/cm/imagemanager/imagemanager.hpp index f72698ba6..0f8271f41 100644 --- a/src/core/cm/imagemanager/imagemanager.hpp +++ b/src/core/cm/imagemanager/imagemanager.hpp @@ -36,6 +36,7 @@ class ImageManager : public ImageManagerItf, public ItemInfoProviderItf, public /** * Initializes image manager. * + * @param allocator allocator to use for temporary objects. * @param config image manager config. * @param storage stores internal persistent data. * @param blobInfoProvider retrieves blobs info. @@ -48,8 +49,8 @@ class ImageManager : public ImageManagerItf, public ItemInfoProviderItf, public * @param ociSpec parses OCI spec files. * @return Error. */ - Error Init(const Config& config, StorageItf& storage, BlobInfoProviderItf& blobInfoProvider, - spaceallocator::SpaceAllocatorItf& downloadingSpaceAllocator, + Error Init(AllocatorItf& allocator, const Config& config, StorageItf& storage, + BlobInfoProviderItf& blobInfoProvider, spaceallocator::SpaceAllocatorItf& downloadingSpaceAllocator, spaceallocator::SpaceAllocatorItf& installSpaceAllocator, downloader::DownloaderItf& downloader, fileserver::FileServerItf& fileserver, crypto::CryptoHelperItf& cryptoHelper, fs::FileInfoProviderItf& fileInfoProvider, oci::OCISpecItf& ociSpec); @@ -253,10 +254,8 @@ class ImageManager : public ImageManagerItf, public ItemInfoProviderItf, public ConditionalVariable mCondVar; bool mCancel {}; bool mInProgress {}; - mutable StaticAllocator<(sizeof(StaticArray) * 2) + sizeof(oci::ImageIndex) - + sizeof(oci::ImageManifest) + sizeof(StaticArray) - + sizeof(StaticArray) + sizeof(BlobInfo)> - mAllocator; + + AllocatorItf* mAllocator {}; }; } // namespace aos::cm::imagemanager diff --git a/src/core/cm/imagemanager/tests/imagemanager.cpp b/src/core/cm/imagemanager/tests/imagemanager.cpp index 283890ada..95245bab2 100644 --- a/src/core/cm/imagemanager/tests/imagemanager.cpp +++ b/src/core/cm/imagemanager/tests/imagemanager.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "mocks/blobinfoprovidermock.hpp" #include "mocks/itemstatuslistenermock.hpp" @@ -50,7 +51,7 @@ class ImageManagerTest : public testing::Test { EXPECT_CALL(mStorageMock, GetAllItemsInfos(_)).WillRepeatedly(Return(ErrorEnum::eNone)); EXPECT_TRUE(mImageManager - .Init(mConfig, mStorageMock, mBlobInfoProviderMock, mDownloadingSpaceAllocatorMock, + .Init(mAllocator, mConfig, mStorageMock, mBlobInfoProviderMock, mDownloadingSpaceAllocatorMock, mInstallSpaceAllocatorMock, mDownloaderMock, mFileServerMock, mCryptoHelperMock, mFileInfoProviderMock, mOCISpecMock) .IsNone()); @@ -62,9 +63,12 @@ class ImageManagerTest : public testing::Test { fs::RemoveAll(mConfig.mDownloadPath); } + // mAllocator must be declared (and therefore destroyed) after any member that allocates from it, since + // members are destroyed in reverse declaration order. + HeapAllocator mAllocator; + Config mConfig; ImageManager mImageManager; - StaticAllocator<1024 * 5, 20> mAllocator; StrictMock mStorageMock; StrictMock mBlobInfoProviderMock; StrictMock mDownloadingSpaceAllocatorMock; @@ -213,6 +217,8 @@ TEST_F(ImageManagerTest, DownloadUpdateItems_Success_NewItem) EXPECT_CALL(mDownloadingSpaceAllocatorMock, AllocateSpace(_)) .WillRepeatedly(Invoke([this](size_t) -> RetWithError> { auto space = MakeUnique(&mAllocator); + EXPECT_TRUE(space); + EXPECT_CALL(*space, Accept()).Times(AtLeast(0)); EXPECT_CALL(*space, Release()).Times(AtLeast(0)); @@ -222,6 +228,8 @@ TEST_F(ImageManagerTest, DownloadUpdateItems_Success_NewItem) EXPECT_CALL(mInstallSpaceAllocatorMock, AllocateSpace(_)) .WillRepeatedly(Invoke([this](size_t) -> RetWithError> { auto space = MakeUnique(&mAllocator); + EXPECT_TRUE(space); + EXPECT_CALL(*space, Accept()).Times(AtLeast(0)); EXPECT_CALL(*space, Release()).Times(AtLeast(0)); @@ -339,6 +347,8 @@ TEST_F(ImageManagerTest, DownloadUpdateItems_AlreadyInstalled) EXPECT_CALL(mDownloadingSpaceAllocatorMock, AllocateSpace(_)) .WillRepeatedly(Invoke([this](size_t) -> RetWithError> { auto space = MakeUnique(&mAllocator); + EXPECT_TRUE(space); + EXPECT_CALL(*space, Accept()).Times(AtLeast(0)); EXPECT_CALL(*space, Release()).Times(AtLeast(0)); @@ -348,6 +358,8 @@ TEST_F(ImageManagerTest, DownloadUpdateItems_AlreadyInstalled) EXPECT_CALL(mInstallSpaceAllocatorMock, AllocateSpace(_)) .WillRepeatedly(Invoke([this](size_t) -> RetWithError> { auto space = MakeUnique(&mAllocator); + EXPECT_TRUE(space); + EXPECT_CALL(*space, Accept()).Times(AtLeast(0)); EXPECT_CALL(*space, Release()).Times(AtLeast(0)); @@ -522,6 +534,8 @@ TEST_F(ImageManagerTest, DownloadUpdateItems_MultipleItems_Success) EXPECT_CALL(mDownloadingSpaceAllocatorMock, AllocateSpace(_)) .WillRepeatedly(Invoke([this](size_t) -> RetWithError> { auto space = MakeUnique(&mAllocator); + EXPECT_TRUE(space); + EXPECT_CALL(*space, Accept()).Times(AtLeast(0)); EXPECT_CALL(*space, Release()).Times(AtLeast(0)); @@ -531,6 +545,8 @@ TEST_F(ImageManagerTest, DownloadUpdateItems_MultipleItems_Success) EXPECT_CALL(mInstallSpaceAllocatorMock, AllocateSpace(_)) .WillRepeatedly(Invoke([this](size_t) -> RetWithError> { auto space = MakeUnique(&mAllocator); + EXPECT_TRUE(space); + EXPECT_CALL(*space, Accept()).Times(AtLeast(0)); EXPECT_CALL(*space, Release()).Times(AtLeast(0)); @@ -670,6 +686,8 @@ TEST_F(ImageManagerTest, DownloadUpdateItems_Cancel_DownloadFailed) EXPECT_CALL(mDownloadingSpaceAllocatorMock, AllocateSpace(_)) .WillRepeatedly(Invoke([this](size_t) -> RetWithError> { auto space = MakeUnique(&mAllocator); + EXPECT_TRUE(space); + EXPECT_CALL(*space, Accept()).Times(AtLeast(0)); EXPECT_CALL(*space, Release()).Times(AtLeast(0)); testing::Mock::AllowLeak(space.Get()); @@ -759,6 +777,8 @@ TEST_F(ImageManagerTest, DownloadUpdateItems_RemovesOldPendingVersion) EXPECT_CALL(mDownloadingSpaceAllocatorMock, AllocateSpace(_)) .WillRepeatedly(Invoke([this](size_t) -> RetWithError> { auto space = MakeUnique(&mAllocator); + EXPECT_TRUE(space); + EXPECT_CALL(*space, Accept()).Times(AtLeast(0)); EXPECT_CALL(*space, Release()).Times(AtLeast(0)); @@ -768,6 +788,8 @@ TEST_F(ImageManagerTest, DownloadUpdateItems_RemovesOldPendingVersion) EXPECT_CALL(mInstallSpaceAllocatorMock, AllocateSpace(_)) .WillRepeatedly(Invoke([this](size_t) -> RetWithError> { auto space = MakeUnique(&mAllocator); + EXPECT_TRUE(space); + EXPECT_CALL(*space, Accept()).Times(AtLeast(0)); EXPECT_CALL(*space, Release()).Times(AtLeast(0)); @@ -876,6 +898,8 @@ TEST_F(ImageManagerTest, DownloadUpdateItems_RemovesOldFailedVersion) EXPECT_CALL(mDownloadingSpaceAllocatorMock, AllocateSpace(_)) .WillRepeatedly(Invoke([this](size_t) -> RetWithError> { auto space = MakeUnique(&mAllocator); + EXPECT_TRUE(space); + EXPECT_CALL(*space, Accept()).Times(AtLeast(0)); EXPECT_CALL(*space, Release()).Times(AtLeast(0)); @@ -885,6 +909,8 @@ TEST_F(ImageManagerTest, DownloadUpdateItems_RemovesOldFailedVersion) EXPECT_CALL(mInstallSpaceAllocatorMock, AllocateSpace(_)) .WillRepeatedly(Invoke([this](size_t) -> RetWithError> { auto space = MakeUnique(&mAllocator); + EXPECT_TRUE(space); + EXPECT_CALL(*space, Accept()).Times(AtLeast(0)); EXPECT_CALL(*space, Release()).Times(AtLeast(0)); diff --git a/src/core/cm/launcher/CMakeLists.txt b/src/core/cm/launcher/CMakeLists.txt index a463c90a8..26d1f67ce 100644 --- a/src/core/cm/launcher/CMakeLists.txt +++ b/src/core/cm/launcher/CMakeLists.txt @@ -16,13 +16,13 @@ set(TARGET_NAME launcher) set(SOURCES balancer.cpp - gidpool.cpp imageinfoprovider.cpp instance.cpp instancemanager.cpp launcher.cpp node.cpp nodemanager.cpp + overrideenvvarsprocessor.cpp runrequestsloader.cpp storagestate.cpp ) @@ -32,7 +32,7 @@ set(SOURCES # ###################################################################################################################### set(HEADERS - gidpool.hpp + idpool.hpp imageinfoprovider.hpp runrequestsloader.hpp itf/instancerunner.hpp diff --git a/src/core/cm/launcher/balancer.cpp b/src/core/cm/launcher/balancer.cpp index 6db0bb865..1e5ea27db 100644 --- a/src/core/cm/launcher/balancer.cpp +++ b/src/core/cm/launcher/balancer.cpp @@ -14,9 +14,10 @@ namespace aos::cm::launcher { * Public **********************************************************************************************************************/ -void Balancer::Init(InstanceManager& instanceManager, ImageInfoProvider& imageInfoProvider, NodeManager& nodeManager, - MonitoringProviderItf& monitorProvider, InstanceRunnerItf& runner) +void Balancer::Init(AllocatorItf& allocator, InstanceManager& instanceManager, ImageInfoProvider& imageInfoProvider, + NodeManager& nodeManager, MonitoringProviderItf& monitorProvider, InstanceRunnerItf& runner) { + mAllocator = &allocator; mInstanceManager = &instanceManager; mImageInfoProvider = &imageInfoProvider; mNodeManager = &nodeManager; @@ -91,12 +92,20 @@ Error Balancer::PerformNodeBalancing(Array>& instances) continue; } - auto imageIndex = MakeUnique(&mAllocator); + auto imageIndex = MakeUnique(mAllocator); + if (!imageIndex) { + LOG_ERR() << "Can't allocate image index" << Log::Field("instance", id) << Log::Field(ErrorEnum::eNoMemory); + + mInstanceManager->ScheduleInstance(instance, AOS_ERROR_WRAP(ErrorEnum::eNoMemory)); + + continue; + } if (auto err = mImageInfoProvider->GetImageIndex(id.mItemID, info.mVersion, *imageIndex); !err.IsNone()) { LOG_ERR() << "Can't get images" << Log::Field("instance", id) << Log::Field(err); mInstanceManager->ScheduleInstance(instance, AOS_ERROR_WRAP(err)); + continue; } @@ -106,8 +115,7 @@ Error Balancer::PerformNodeBalancing(Array>& instances) LOG_DBG() << "Try to schedule instance" << Log::Field("instance", id) << Log::Field("manifest", manifest.mDigest); - scheduleErr = ScheduleInstance(instance, manifest); - if (scheduleErr.IsNone()) { + if (scheduleErr = ScheduleInstance(instance, manifest); scheduleErr.IsNone()) { LOG_DBG() << "Instance scheduled successfully" << Log::Field("nodeID", info.mNodeID); break; @@ -126,7 +134,10 @@ Error Balancer::PerformNodeBalancing(Array>& instances) Error Balancer::ScheduleInstance(SharedPtr& instance, const oci::IndexContentDescriptor& imageDescriptor) { - auto nodes = MakeUnique>(&mAllocator); + auto nodes = MakeUnique>(mAllocator); + if (!nodes) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } auto releaseConfigs = DeferRelease(reinterpret_cast(1), [&](int*) { instance->ResetConfigs(); }); @@ -149,7 +160,7 @@ Error Balancer::ScheduleInstance(SharedPtr& instance, const oci::Index } // Schedule instance - auto& node = nodeRuntime.mFirst; + const auto& node = nodeRuntime.mFirst; const auto& runtime = nodeRuntime.mSecond; if (auto err = mInstanceManager->ScheduleInstance(instance, *node, runtime->mRuntimeID); !err.IsNone()) { @@ -194,9 +205,12 @@ void Balancer::FilterNodesByResources(Instance& instance, Array& nodes) nodes.RemoveIf([&instance](const Node* node) { return !instance.AreNodeResourcesOk(*node); }); } -RetWithError> Balancer::SelectRuntime(Instance& instance, Array& nodes) +RetWithError> Balancer::SelectRuntime(Instance& instance, const Array& nodes) { - auto nodeRuntimes = MakeUnique(&mAllocator); + auto nodeRuntimes = MakeUnique(mAllocator); + if (!nodeRuntimes) { + return {nullptr, AOS_ERROR_WRAP(ErrorEnum::eNoMemory)}; + } if (auto err = CreateRuntimes(nodes, *nodeRuntimes); !err.IsNone()) { return {nullptr, AOS_ERROR_WRAP(err)}; @@ -265,7 +279,7 @@ RetWithError> Balancer::SelectRuntime(Instance& return {result, ErrorEnum::eNone}; } -Error Balancer::CreateRuntimes(Array& nodes, NodeRuntimes& runtimes) +Error Balancer::CreateRuntimes(const Array& nodes, NodeRuntimes& runtimes) { for (auto node : nodes) { if (auto err = runtimes.Emplace(node); !err.IsNone()) { @@ -339,7 +353,7 @@ void Balancer::FilterByCPU(Instance& instance, NodeRuntimes& nodes) auto filter = [&instance](Node* node, const RuntimeInfo* runtime) { auto availCPU = node->GetAvailableCPU(runtime->mRuntimeID); - return instance.IsAvailableCpuOk(availCPU, node->GetConfig(), node->NeedBalancing()); + return instance.IsAvailableCpuOk(availCPU, *node); }; FilterRuntimes(nodes, filter); @@ -350,7 +364,7 @@ void Balancer::FilterByRAM(Instance& instance, NodeRuntimes& nodes) auto filter = [&instance](Node* node, const RuntimeInfo* runtime) { auto availRAM = node->GetAvailableRAM(runtime->mRuntimeID); - return instance.IsAvailableRamOk(availRAM, node->GetConfig(), node->NeedBalancing()); + return instance.IsAvailableRamOk(availRAM, *node); }; FilterRuntimes(nodes, filter); @@ -384,7 +398,10 @@ void Balancer::FilterTopPriorityNodes(NodeRuntimes& nodes) Error Balancer::PerformPolicyBalancing(Array>& instances) { - auto imageIndex = MakeUnique(&mAllocator); + auto imageIndex = MakeUnique(mAllocator); + if (!imageIndex) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } for (auto& instance : instances) { const auto& info = instance->GetInfo(); @@ -444,6 +461,12 @@ Error Balancer::PerformPolicyBalancing(Array>& instances) continue; } + if (!node->IsConnected() || node->GetInfo().mState != NodeStateEnum::eProvisioned) { + LOG_WRN() << "Node is skipped from balancing" << Log::Field("nodeID", info.mNodeID); + + continue; + } + if (auto err = mInstanceManager->ScheduleInstance(instance, *node, info.mRuntimeID); !err.IsNone()) { LOG_WRN() << "Can't schedule instance" << Log::Field("instance", id) << Log::Field(AOS_ERROR_WRAP(err)); @@ -459,7 +482,10 @@ Error Balancer::UpdateMonitoringData(bool isInitialUpdate) for (auto& node : mNodeManager->GetNodes()) { const auto& nodeID = node.GetInfo().mNodeID; - auto nodeMonitoring = MakeUnique(&mAllocator); + auto nodeMonitoring = MakeUnique(mAllocator); + if (!nodeMonitoring) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } // Monitoring data immediately after startup is not availble. // Assign zero consumption on start. diff --git a/src/core/cm/launcher/balancer.hpp b/src/core/cm/launcher/balancer.hpp index 6a4b21d28..bf22bf06e 100644 --- a/src/core/cm/launcher/balancer.hpp +++ b/src/core/cm/launcher/balancer.hpp @@ -7,6 +7,8 @@ #ifndef AOS_CORE_CM_LAUNCHER_BALANCER_HPP_ #define AOS_CORE_CM_LAUNCHER_BALANCER_HPP_ +#include + #include "itf/instancerunner.hpp" #include "itf/launcher.hpp" #include "itf/monitoringprovider.hpp" @@ -29,14 +31,15 @@ class Balancer { /** * Initializes runner with required managers and providers. * + * @param allocator allocator to use for temporary objects. * @param instanceManager instance manager. * @param imageInfoProvider image info provider. * @param nodeManager node manager. * @param monitorProvider monitoring provider. * @param runner instance runner interface. */ - void Init(InstanceManager& instanceManager, ImageInfoProvider& imageInfoProvider, NodeManager& nodeManager, - MonitoringProviderItf& monitorProvider, InstanceRunnerItf& runner); + void Init(AllocatorItf& allocator, InstanceManager& instanceManager, ImageInfoProvider& imageInfoProvider, + NodeManager& nodeManager, MonitoringProviderItf& monitorProvider, InstanceRunnerItf& runner); /** * Runs instances. @@ -57,13 +60,6 @@ class Balancer { private: using NodeRuntimes = StaticMap, cMaxNumInstances>; - static constexpr size_t cScheduleInstanceSize - = sizeof(oci::ImageIndex) + sizeof(StaticArray) + sizeof(NodeRuntimes); - static constexpr size_t cPolicyBalancingSize = sizeof(oci::ImageIndex); - static constexpr size_t cMonitoringSize = sizeof(monitoring::NodeMonitoringData); - - static constexpr size_t cAllocatorSize = Max(cScheduleInstanceSize, cPolicyBalancingSize, cMonitoringSize); - Error PerformNodeBalancing(Array>& instances); Error ScheduleInstance(SharedPtr& instance, const oci::IndexContentDescriptor& imageDescriptor); @@ -75,9 +71,9 @@ class Balancer { void FilterNodesByResources(Instance& instance, Array& nodes); // Selects runtime - RetWithError> SelectRuntime(Instance& instance, Array& nodes); + RetWithError> SelectRuntime(Instance& instance, const Array& nodes); - Error CreateRuntimes(Array& nodes, NodeRuntimes& runtimes); + Error CreateRuntimes(const Array& nodes, NodeRuntimes& runtimes); template void FilterRuntimes(NodeRuntimes& runtimes, Filter& filter); @@ -99,7 +95,7 @@ class Balancer { InstanceRunnerItf* mRunner {}; SubjectArray mSubjects; - StaticAllocator mAllocator; + AllocatorItf* mAllocator {}; }; /** @}*/ diff --git a/src/core/cm/launcher/gidpool.cpp b/src/core/cm/launcher/gidpool.cpp deleted file mode 100644 index 8b2b463a6..000000000 --- a/src/core/cm/launcher/gidpool.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2025 EPAM Systems, Inc. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "gidpool.hpp" - -namespace aos::cm::launcher { - -RetWithError GIDPool::GetGID(const String& itemID, gid_t gid) -{ - if (auto existing = mItemGIDs.Find(itemID); existing != mItemGIDs.end()) { - if (gid != 0 && existing->mSecond.mGID != gid) { - return {0, AOS_ERROR_WRAP(ErrorEnum::eInvalidArgument)}; - } - - existing->mSecond.mRefCount++; - - return {existing->mSecond.mGID, ErrorEnum::eNone}; - } - - gid_t assigned = gid; - - if (gid != 0) { - if (auto err = mPool.TryAcquire(gid); !err.IsNone()) { - return {0, AOS_ERROR_WRAP(err)}; - } - } else { - auto [autoGID, acquireErr] = mPool.Acquire(); - if (!acquireErr.IsNone()) { - return {0, AOS_ERROR_WRAP(acquireErr)}; - } - - assigned = static_cast(autoGID); - } - - ItemEntry entry {assigned, 1}; - - if (auto err = mItemGIDs.Emplace(itemID, entry); !err.IsNone()) { - mPool.Release(assigned); - - return {0, AOS_ERROR_WRAP(err)}; - } - - return {assigned, ErrorEnum::eNone}; -} - -Error GIDPool::Release(const String& itemID) -{ - auto existing = mItemGIDs.Find(itemID); - if (existing == mItemGIDs.end()) { - return ErrorEnum::eNotFound; - } - - auto& entry = existing->mSecond; - - if (entry.mRefCount > 1) { - entry.mRefCount--; - - return ErrorEnum::eNone; - } - - if (auto err = mPool.Release(entry.mGID); !err.IsNone()) { - return AOS_ERROR_WRAP(err); - } - - return mItemGIDs.Remove(itemID); -} - -Error GIDPool::Clear() -{ - if (auto err = mPool.Clear(); !err.IsNone()) { - return AOS_ERROR_WRAP(err); - } - - mItemGIDs.Clear(); - - return ErrorEnum::eNone; -} - -} // namespace aos::cm::launcher diff --git a/src/core/cm/launcher/gidpool.hpp b/src/core/cm/launcher/gidpool.hpp deleted file mode 100644 index 53549d3df..000000000 --- a/src/core/cm/launcher/gidpool.hpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2025 EPAM Systems, Inc. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef AOS_CORE_CM_LAUNCHER_GIDPOOL_HPP_ -#define AOS_CORE_CM_LAUNCHER_GIDPOOL_HPP_ - -#include - -#include -#include -#include -#include - -namespace aos::cm::launcher { - -/** - * GID range start. - */ -static constexpr auto cGIDRangeBegin = 5000; - -/** - * GID range end. - */ -static constexpr auto cGIDRangeEnd = 10000; - -/** - * Max number of locked IDs simultaneously. - */ -static constexpr auto cMaxNumLockedGIDs = cMaxNumUpdateItems; - -/** - * Pool that manages group identifiers for update items. - */ -class GIDPool { -public: - /** - * Initializes the underlying identifier pool. - * - * @param validator validator callback. - * @return Error. - */ - Error Init(IdentifierPoolValidator validator) { return mPool.Init(validator); } - - /** - * Returns a GID for an update item. - * - * @param itemID item ID. - * @param gid requested GID. If 0, a new GID will be generated. - * @return RetWithError. - */ - RetWithError GetGID(const String& itemID, gid_t gid = 0); - - /** - * Releases a reference for the update item GID. - * - * @param itemID item ID. - * @return Error. - */ - Error Release(const String& itemID); - - /** - * Clears allocated GIDs. - * - * @return Error. - */ - Error Clear(); - -private: - struct ItemEntry { - gid_t mGID {}; - size_t mRefCount {}; - }; - - IdentifierRangePool mPool; - StaticMap, ItemEntry, cMaxNumUpdateItems> mItemGIDs; -}; - -} // namespace aos::cm::launcher - -#endif diff --git a/src/core/cm/launcher/idpool.hpp b/src/core/cm/launcher/idpool.hpp new file mode 100644 index 000000000..d2cb5868a --- /dev/null +++ b/src/core/cm/launcher/idpool.hpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2025 EPAM Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef AOS_CORE_CM_LAUNCHER_IDPOOL_HPP_ +#define AOS_CORE_CM_LAUNCHER_IDPOOL_HPP_ + +#include + +#include +#include +#include +#include + +namespace aos::cm::launcher { + +/** + * GID range start. + */ +static constexpr auto cGIDRangeBegin = 5000; + +/** + * GID range end. + */ +static constexpr auto cGIDRangeEnd = 10000; + +/** + * Max number of locked GIDs simultaneously. + * Double the number of update items to avoid exhausting the range when removing instances. + */ +static constexpr auto cMaxNumLockedGIDs = 2 * cMaxNumUpdateItems; + +/** + * UID range start. + */ +static constexpr auto cUIDRangeBegin = 5000; + +/** + * UID range end. + */ +static constexpr auto cUIDRangeEnd = 10000; + +/** + * Max number of locked UIDs simultaneously. + * Double the number of update items to avoid exhausting the range when removing instances. + */ +static constexpr auto cMaxNumLockedUIDs = 2 * cMaxNumInstances; + +/** + * Pool that manages identifiers with reference counting per key. + * + * @tparam K key type. + * @tparam I identifier type. + * @tparam cRangeBegin identifier range start. + * @tparam cRangeEnd identifier range end. + * @tparam cMaxNumLocked max number of locked IDs simultaneously. + * @tparam cMaxNumItems max number of tracked keys. + */ +template +class IDPool { +public: + /** + * Initializes the underlying identifier pool. + * + * @param validator validator callback. + * @return Error. + */ + Error Init(IdentifierPoolValidator validator) { return mPool.Init(validator); } + + /** + * Returns an identifier for a key. + * + * @param key key. + * @param defaultID requested identifier. If 0, a new identifier will be generated. + * @return RetWithError. + */ + RetWithError Acquire(const K& key, I defaultID = 0) + { + if (auto existing = mItems.Find(key); existing != mItems.end()) { + if (defaultID != 0 && existing->mSecond.mID != defaultID) { + return {0, AOS_ERROR_WRAP(ErrorEnum::eInvalidArgument)}; + } + + existing->mSecond.mRefCount++; + + return {existing->mSecond.mID, ErrorEnum::eNone}; + } + + I assigned = defaultID; + + if (defaultID != 0) { + if (auto err = mPool.TryAcquire(defaultID); !err.IsNone()) { + return {0, AOS_ERROR_WRAP(err)}; + } + } else { + auto [autoID, acquireErr] = mPool.Acquire(); + if (!acquireErr.IsNone()) { + return {0, AOS_ERROR_WRAP(acquireErr)}; + } + + assigned = static_cast(autoID); + } + + ItemEntry entry {assigned, 1}; + + if (auto err = mItems.Emplace(key, entry); !err.IsNone()) { + mPool.Release(assigned); + + return {0, AOS_ERROR_WRAP(err)}; + } + + return {assigned, ErrorEnum::eNone}; + } + + /** + * Releases a reference for the key identifier. + * + * @param key key. + * @return Error. + */ + Error Release(const K& key) + { + auto existing = mItems.Find(key); + if (existing == mItems.end()) { + return ErrorEnum::eNotFound; + } + + auto& entry = existing->mSecond; + + if (entry.mRefCount > 1) { + entry.mRefCount--; + + return ErrorEnum::eNone; + } + + if (auto err = mPool.Release(entry.mID); !err.IsNone()) { + return AOS_ERROR_WRAP(err); + } + + return mItems.Remove(key); + } + + /** + * Clears allocated identifiers. + * + * @return Error. + */ + Error Clear() + { + if (auto err = mPool.Clear(); !err.IsNone()) { + return AOS_ERROR_WRAP(err); + } + + mItems.Clear(); + + return ErrorEnum::eNone; + } + +private: + struct ItemEntry { + I mID {}; + size_t mRefCount {}; + }; + + IdentifierRangePool mPool; + StaticMap mItems; +}; + +using GIDPool = IDPool, gid_t, cGIDRangeBegin, cGIDRangeEnd, cMaxNumLockedGIDs, cMaxNumLockedGIDs>; + +using UIDPool = IDPool; + +} // namespace aos::cm::launcher + +#endif diff --git a/src/core/cm/launcher/imageinfoprovider.cpp b/src/core/cm/launcher/imageinfoprovider.cpp index 7198363e0..d45b4c355 100644 --- a/src/core/cm/launcher/imageinfoprovider.cpp +++ b/src/core/cm/launcher/imageinfoprovider.cpp @@ -11,17 +11,30 @@ namespace aos::cm::launcher { -void ImageInfoProvider::Init(imagemanager::ItemInfoProviderItf& itemInfoProvider, oci::OCISpecItf& ociSpec) +void ImageInfoProvider::Init( + AllocatorItf& allocator, imagemanager::ItemInfoProviderItf& itemInfoProvider, oci::OCISpecItf& ociSpec) { + mAllocator = &allocator; mItemInfoProvider = &itemInfoProvider; mOCISpec = &ociSpec; } Error ImageInfoProvider::GetImageConfig(const oci::IndexContentDescriptor& imageDescriptor, oci::ImageConfig& config) { - auto manifestPath = MakeUnique>(&mAllocator); - auto manifest = MakeUnique(&mAllocator); - auto configPath = MakeUnique>(&mAllocator); + auto manifestPath = MakeUnique>(mAllocator); + if (!manifestPath) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } + + auto manifest = MakeUnique(mAllocator); + if (!manifest) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } + + auto configPath = MakeUnique>(mAllocator); + if (!configPath) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = mItemInfoProvider->GetBlobPath(imageDescriptor.mDigest, *manifestPath); !err.IsNone()) { return AOS_ERROR_WRAP(err); @@ -44,9 +57,20 @@ Error ImageInfoProvider::GetImageConfig(const oci::IndexContentDescriptor& image Error ImageInfoProvider::GetItemConfig(const oci::IndexContentDescriptor& imageDescriptor, oci::ItemConfig& itemConfig) { - auto manifestPath = MakeUnique>(&mAllocator); - auto manifest = MakeUnique(&mAllocator); - auto servicePath = MakeUnique>(&mAllocator); + auto manifestPath = MakeUnique>(mAllocator); + if (!manifestPath) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } + + auto manifest = MakeUnique(mAllocator); + if (!manifest) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } + + auto servicePath = MakeUnique>(mAllocator); + if (!servicePath) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = mItemInfoProvider->GetBlobPath(imageDescriptor.mDigest, *manifestPath); !err.IsNone()) { return AOS_ERROR_WRAP(err); @@ -73,8 +97,15 @@ Error ImageInfoProvider::GetItemConfig(const oci::IndexContentDescriptor& imageD Error ImageInfoProvider::GetImageIndex(const String& itemID, const String& version, oci::ImageIndex& imageIndex) { - auto indexDigest = MakeUnique>(&mAllocator); - auto indexPath = MakeUnique>(&mAllocator); + auto indexDigest = MakeUnique>(mAllocator); + if (!indexDigest) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } + + auto indexPath = MakeUnique>(mAllocator); + if (!indexPath) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = mItemInfoProvider->GetIndexDigest(itemID, version, *indexDigest); !err.IsNone()) { return AOS_ERROR_WRAP(err); diff --git a/src/core/cm/launcher/imageinfoprovider.hpp b/src/core/cm/launcher/imageinfoprovider.hpp index 0d54417eb..37dbb2878 100644 --- a/src/core/cm/launcher/imageinfoprovider.hpp +++ b/src/core/cm/launcher/imageinfoprovider.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -25,10 +26,11 @@ class ImageInfoProvider { /** * @brief Initializes image info provider. * + * @param allocator allocator to use for temporary objects. * @param itemInfoProvider item info provider. * @param ociSpec OCI spec. */ - void Init(imagemanager::ItemInfoProviderItf& itemInfoProvider, oci::OCISpecItf& ociSpec); + void Init(AllocatorItf& allocator, imagemanager::ItemInfoProviderItf& itemInfoProvider, oci::OCISpecItf& ociSpec); /** * Returns OCI image config for the specified instance/image identifiers. @@ -59,13 +61,10 @@ class ImageInfoProvider { Error GetImageIndex(const String& itemID, const String& version, oci::ImageIndex& imageIndex); private: - static constexpr auto cAllocatorSize = Max(sizeof(oci::ImageManifest) + sizeof(StaticString) * 3, - sizeof(StaticString) + sizeof(StaticString)); - imagemanager::ItemInfoProviderItf* mItemInfoProvider {}; oci::OCISpecItf* mOCISpec {}; - StaticAllocator mAllocator; + AllocatorItf* mAllocator {}; }; } // namespace aos::cm::launcher diff --git a/src/core/cm/launcher/instance.cpp b/src/core/cm/launcher/instance.cpp index 964405927..a360a65c0 100644 --- a/src/core/cm/launcher/instance.cpp +++ b/src/core/cm/launcher/instance.cpp @@ -16,7 +16,7 @@ namespace aos::cm::launcher { **********************************************************************************************************************/ Instance::Instance( - const InstanceInfo& info, StorageItf& storage, ImageInfoProvider& imageInfoProvider, Allocator& allocator) + AllocatorItf& allocator, const InstanceInfo& info, StorageItf& storage, ImageInfoProvider& imageInfoProvider) : mInfo(info) , mStorage(storage) , mImageInfoProvider(imageInfoProvider) @@ -37,8 +37,15 @@ Instance::Instance( Error Instance::LoadConfigs(const oci::IndexContentDescriptor& imageDescriptor) { - mItemConfig = MakeUnique(&mAllocator); + mItemConfig = MakeUnique(&mAllocator); + if (!mItemConfig) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } + mImageConfig = MakeUnique(&mAllocator); + if (!mImageConfig) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } auto releaseConfigs = DeferRelease(reinterpret_cast(1), [&](int*) { ResetConfigs(); }); if (auto err = mImageInfoProvider.GetItemConfig(imageDescriptor, *mItemConfig); !err.IsNone()) { @@ -74,6 +81,11 @@ bool Instance::IsImageValid() } auto imageIndex = MakeUnique(&mAllocator); + if (!imageIndex) { + LOG_ERR() << "Can't allocate image index" << Log::Field(ErrorEnum::eNoMemory); + + return false; + } auto err = mImageInfoProvider.GetImageIndex(mInfo.mInstanceIdent.mItemID, mInfo.mVersion, *imageIndex); if (!err.IsNone()) { @@ -196,6 +208,9 @@ bool Instance::AreNodeLabelsOk(const LabelsArray& nodeLabels) RetWithError Instance::OverrideEnvVars(const OverrideEnvVarsRequest& envVars) { auto newEnvVars = MakeUnique(&mAllocator); + if (!newEnvVars) { + return {false, AOS_ERROR_WRAP(ErrorEnum::eNoMemory)}; + } for (const auto& item : envVars.mItems) { if (!item.Match(mInfo.mInstanceIdent)) { @@ -280,8 +295,8 @@ Error Instance::SetDefaultRuntimes() **********************************************************************************************************************/ ComponentInstance::ComponentInstance( - const InstanceInfo& info, StorageItf& storage, ImageInfoProvider& imageInfoProvider, Allocator& allocator) - : Instance(info, storage, imageInfoProvider, allocator) + AllocatorItf& allocator, const InstanceInfo& info, StorageItf& storage, ImageInfoProvider& imageInfoProvider) + : Instance(allocator, info, storage, imageInfoProvider) { } @@ -316,20 +331,18 @@ Error ComponentInstance::Cache(bool disable) return ErrorEnum::eNone; } -bool ComponentInstance::IsAvailableCpuOk(size_t availableCPU, const NodeConfig& nodeConfig, bool useMonitoringData) +bool ComponentInstance::IsAvailableCpuOk(size_t availableCPU, const NodeItf& node) { (void)availableCPU; - (void)nodeConfig; - (void)useMonitoringData; + (void)node; return true; } -bool ComponentInstance::IsAvailableRamOk(size_t availableRAM, const NodeConfig& nodeConfig, bool useMonitoringData) +bool ComponentInstance::IsAvailableRamOk(size_t availableRAM, const NodeItf& node) { (void)availableRAM; - (void)nodeConfig; - (void)useMonitoringData; + (void)node; return true; } @@ -348,8 +361,11 @@ oci::BalancingPolicyEnum ComponentInstance::GetBalancingPolicy() Error ComponentInstance::Schedule(NodeItf& node, const String& runtimeID) { - auto releaseConfig = DeferRelease(reinterpret_cast(1), [&](int*) { mImageConfig = nullptr; }); + return LoadSMInfo(node, runtimeID); +} +Error ComponentInstance::LoadSMInfo(NodeItf& node, const String& runtimeID) +{ static_cast(mSMInfo) = mInfo.mInstanceIdent; mSMInfo.mVersion = mInfo.mVersion; mSMInfo.mManifestDigest = mInfo.mManifestDigest; @@ -376,9 +392,9 @@ Error ComponentInstance::Schedule(NodeItf& node, const String& runtimeID) * ServiceInstance implementation **********************************************************************************************************************/ -ServiceInstance::ServiceInstance(const InstanceInfo& info, UIDPool& uidPool, GIDPool& gidPool, StorageItf& storage, - StorageState& storageState, ImageInfoProvider& imageInfoProvider, Allocator& allocator) - : Instance(info, storage, imageInfoProvider, allocator) +ServiceInstance::ServiceInstance(AllocatorItf& allocator, const InstanceInfo& info, UIDPool& uidPool, GIDPool& gidPool, + StorageItf& storage, StorageState& storageState, ImageInfoProvider& imageInfoProvider) + : Instance(allocator, info, storage, imageInfoProvider) , mUIDPool(uidPool) , mGIDPool(gidPool) , mStorageState(storageState) @@ -387,29 +403,19 @@ ServiceInstance::ServiceInstance(const InstanceInfo& info, UIDPool& uidPool, GID Error ServiceInstance::Init() { - if (mInfo.mUID != 0) { - if (auto err = mUIDPool.TryAcquire(mInfo.mUID); !err.IsNone()) { - LOG_WRN() << "Can't add UID to pool" << Log::Field(err); - } - } else { - Error err; + Error uidErr; + Error gidErr; - Tie(mInfo.mUID, err) = mUIDPool.Acquire(); - if (!err.IsNone()) { - LOG_WRN() << "Can't add UID to pool" << Log::Field(err); - } + Tie(mInfo.mUID, uidErr) = mUIDPool.Acquire(mInfo.mInstanceIdent, mInfo.mUID); + if (!uidErr.IsNone()) { + return AOS_ERROR_WRAP(uidErr); } - gid_t gid; - Error gidErr; - - Tie(gid, gidErr) = mGIDPool.GetGID(mInfo.mInstanceIdent.mItemID, mInfo.mGID); + Tie(mInfo.mGID, gidErr) = mGIDPool.Acquire(mInfo.mInstanceIdent.mItemID, mInfo.mGID); if (!gidErr.IsNone()) { return AOS_ERROR_WRAP(gidErr); } - mInfo.mGID = gid; - return ErrorEnum::eNone; } @@ -417,24 +423,32 @@ Error ServiceInstance::Remove() { LOG_DBG() << "Remove instance" << Log::Field("instanceID", mInfo.mInstanceIdent); + Error firstErr = ErrorEnum::eNone; + if (auto err = mStorageState.Remove(mInfo.mInstanceIdent); !err.IsNone() && !err.Is(ErrorEnum::eNotFound)) { - return AOS_ERROR_WRAP(err); + firstErr = AOS_ERROR_WRAP(err); } if (auto err = mStorage.RemoveInstance(mInfo.mInstanceIdent, mInfo.mVersion); !err.IsNone() && !err.Is(ErrorEnum::eNotFound)) { - return AOS_ERROR_WRAP(err); + if (firstErr.IsNone()) { + firstErr = AOS_ERROR_WRAP(err); + } } - if (auto err = mUIDPool.Release(mInfo.mUID); !err.IsNone() && !err.Is(ErrorEnum::eNotFound)) { - return AOS_ERROR_WRAP(err); + if (auto err = mUIDPool.Release(mInfo.mInstanceIdent); !err.IsNone() && !err.Is(ErrorEnum::eNotFound)) { + if (firstErr.IsNone()) { + firstErr = AOS_ERROR_WRAP(err); + } } if (auto err = mGIDPool.Release(mInfo.mInstanceIdent.mItemID); !err.IsNone() && !err.Is(ErrorEnum::eNotFound)) { - return AOS_ERROR_WRAP(err); + if (firstErr.IsNone()) { + firstErr = AOS_ERROR_WRAP(err); + } } - return ErrorEnum::eNone; + return firstErr; } Error ServiceInstance::Cache(bool disable) @@ -455,29 +469,29 @@ Error ServiceInstance::Cache(bool disable) return ErrorEnum::eNone; } -bool ServiceInstance::IsAvailableCpuOk(size_t availableCPU, const NodeConfig& nodeConfig, bool useMonitoringData) +bool ServiceInstance::IsAvailableCpuOk(size_t availableCPU, const NodeItf& node) { assert(mItemConfig); - auto requestedCPU = GetRequestedCPU(nodeConfig, useMonitoringData); + auto requestedCPU = GetRequestedCPU(node); bool ok = availableCPU >= requestedCPU; - LOG_DBG() << "Available CPU " << (ok ? "enough" : "not enough") << Log::Field("nodeID", nodeConfig.mNodeID) + LOG_DBG() << "Available CPU " << (ok ? "enough" : "not enough") << Log::Field("nodeID", node.GetConfig().mNodeID) << Log::Field("availableCPU", availableCPU) << Log::Field("requestedCPU", requestedCPU); return ok; } -bool ServiceInstance::IsAvailableRamOk(size_t availableRAM, const NodeConfig& nodeConfig, bool useMonitoringData) +bool ServiceInstance::IsAvailableRamOk(size_t availableRAM, const NodeItf& node) { assert(mItemConfig); - auto requestedRAM = GetRequestedRAM(nodeConfig, useMonitoringData); + auto requestedRAM = GetRequestedRAM(node); bool ok = availableRAM >= requestedRAM; - LOG_DBG() << "Available RAM " << (ok ? "enough" : "not enough") << Log::Field("nodeID", nodeConfig.mNodeID) + LOG_DBG() << "Available RAM " << (ok ? "enough" : "not enough") << Log::Field("nodeID", node.GetConfig().mNodeID) << Log::Field("availableRAM", availableRAM) << Log::Field("requestedRAM", requestedRAM); return ok; @@ -507,10 +521,20 @@ Error ServiceInstance::Schedule(NodeItf& node, const String& runtimeID) { assert(mItemConfig); - auto releaseConfigs = DeferRelease(reinterpret_cast(1), [&](int*) { - mItemConfig.Reset(); - mImageConfig.Reset(); - }); + if (auto err = ReserveRuntimeResources(node, runtimeID); !err.IsNone()) { + return AOS_ERROR_WRAP(err); + } + + if (auto err = LoadSMInfo(node, runtimeID); !err.IsNone()) { + return AOS_ERROR_WRAP(err); + } + + return ErrorEnum::eNone; +} + +Error ServiceInstance::LoadSMInfo(NodeItf& node, const String& runtimeID) +{ + assert(mItemConfig); static_cast(mSMInfo) = mInfo.mInstanceIdent; mSMInfo.mVersion = mInfo.mVersion; @@ -533,10 +557,6 @@ Error ServiceInstance::Schedule(NodeItf& node, const String& runtimeID) mSMInfo.mMonitoringParams.GetValue().mAlertRules = mItemConfig->mAlertRules.GetValue(); } - if (auto err = ReserveRuntimeResources(node, runtimeID); !err.IsNone()) { - return AOS_ERROR_WRAP(err); - } - if (auto err = SetActive(node.GetConfig().mNodeID, runtimeID); !err.IsNone()) { return AOS_ERROR_WRAP(err); } @@ -544,7 +564,7 @@ Error ServiceInstance::Schedule(NodeItf& node, const String& runtimeID) return ErrorEnum::eNone; } -size_t ServiceInstance::GetRequestedCPU(const NodeConfig& nodeConfig, bool useMonitoringData) +size_t ServiceInstance::GetRequestedCPU(const NodeItf& node) { assert(mItemConfig); @@ -552,6 +572,8 @@ size_t ServiceInstance::GetRequestedCPU(const NodeConfig& nodeConfig, bool useMo return 0; } + const auto& nodeConfig = node.GetConfig(); + size_t requestedCPU = 0; auto quota = mItemConfig->mQuotas.mCPUDMIPSLimit; @@ -561,7 +583,7 @@ size_t ServiceInstance::GetRequestedCPU(const NodeConfig& nodeConfig, bool useMo requestedCPU = GetReqCPUFromNodeConfig(quota, nodeConfig.mResourceRatios); } - if (useMonitoringData) { + if (node.NeedBalancing()) { if (mMonitoringData.mCPU > requestedCPU) { return mMonitoringData.mCPU; } @@ -570,7 +592,7 @@ size_t ServiceInstance::GetRequestedCPU(const NodeConfig& nodeConfig, bool useMo return requestedCPU; } -size_t ServiceInstance::GetRequestedRAM(const NodeConfig& nodeConfig, bool useMonitoringData) +size_t ServiceInstance::GetRequestedRAM(const NodeItf& node) { assert(mItemConfig); @@ -578,6 +600,8 @@ size_t ServiceInstance::GetRequestedRAM(const NodeConfig& nodeConfig, bool useMo return 0; } + const auto& nodeConfig = node.GetConfig(); + size_t requestedRAM = 0; auto quota = mItemConfig->mQuotas.mRAMLimit; @@ -587,7 +611,7 @@ size_t ServiceInstance::GetRequestedRAM(const NodeConfig& nodeConfig, bool useMo requestedRAM = GetReqRAMFromNodeConfig(quota, nodeConfig.mResourceRatios); } - if (useMonitoringData) { + if (node.NeedBalancing()) { if (mMonitoringData.mRAM > requestedRAM) { return mMonitoringData.mRAM; } @@ -747,8 +771,8 @@ Error ServiceInstance::SetupStateStorage(const NodeConfig& nodeConfig, String& s Error ServiceInstance::ReserveRuntimeResources(NodeItf& node, const String& runtimeID) { - auto requestedCPU = mItemConfig->mSkipResourceLimits ? 0 : GetRequestedCPU(node.GetConfig(), false); - auto requestedRAM = mItemConfig->mSkipResourceLimits ? 0 : GetRequestedRAM(node.GetConfig(), false); + auto requestedCPU = mItemConfig->mSkipResourceLimits ? 0 : GetRequestedCPU(node); + auto requestedRAM = mItemConfig->mSkipResourceLimits ? 0 : GetRequestedRAM(node); Array requestedResources = mItemConfig->mSkipResourceLimits ? Array() : mItemConfig->mResources; diff --git a/src/core/cm/launcher/instance.hpp b/src/core/cm/launcher/instance.hpp index 73cef93f0..46893231e 100644 --- a/src/core/cm/launcher/instance.hpp +++ b/src/core/cm/launcher/instance.hpp @@ -11,13 +11,12 @@ #include #include -#include #include #include #include "itf/storage.hpp" -#include "gidpool.hpp" +#include "idpool.hpp" #include "imageinfoprovider.hpp" #include "nodeitf.hpp" #include "storagestate.hpp" @@ -28,26 +27,6 @@ namespace aos::cm::launcher { * @{ */ -/** - * UID range start. - */ -static constexpr auto cUIDRangeBegin = 5000; - -/** - * UID range end. - */ -static constexpr auto cUIDRangeEnd = 10000; - -/** - * Max number of locked IDs simultaneously. - */ -static constexpr auto cMaxNumLockedUIDs = cMaxNumInstances; - -/** - * User ID pool - */ -using UIDPool = IdentifierRangePool; - /** * Base instance class. */ @@ -56,12 +35,13 @@ class Instance { /** * Constructs instance. * + * @param allocator instance allocator. * @param info instance information. * @param storage interface to persistent storage. * @param imageInfoProvider interface for retrieving service information from image. - * @param allocator instance allocator. */ - Instance(const InstanceInfo& info, StorageItf& storage, ImageInfoProvider& imageInfoProvider, Allocator& allocator); + Instance( + AllocatorItf& allocator, const InstanceInfo& info, StorageItf& storage, ImageInfoProvider& imageInfoProvider); /** * Destructor. @@ -158,21 +138,19 @@ class Instance { * Checks whether available CPU fits instance requirements. * * @param availableCPU available CPU. - * @param nodeConfig node configuration. - * @param useMonitoringData whether to use monitoring data. + * @param node node. * @return bool. */ - virtual bool IsAvailableCpuOk(size_t availableCPU, const NodeConfig& nodeConfig, bool useMonitoringData) = 0; + virtual bool IsAvailableCpuOk(size_t availableCPU, const NodeItf& node) = 0; /** * Checks whether available RAM fits instance requirements. * * @param availableRAM available RAM. - * @param nodeConfig node configuration. - * @param useMonitoringData whether to use monitoring data. + * @param node node. * @return bool. */ - virtual bool IsAvailableRamOk(size_t availableRAM, const NodeConfig& nodeConfig, bool useMonitoringData) = 0; + virtual bool IsAvailableRamOk(size_t availableRAM, const NodeItf& node) = 0; /** * Checks whether runtime type fits instance requirements. @@ -232,6 +210,15 @@ class Instance { */ virtual Error Schedule(NodeItf& node, const String& runtimeID) = 0; + /** + * Loads SM instance info. + * + * @param node node interface. + * @param runtimeID runtime identifier. + * @return Error. + */ + virtual Error LoadSMInfo(NodeItf& node, const String& runtimeID) = 0; + /** * Overrides environment variables. * @@ -250,7 +237,7 @@ class Instance { StorageItf& mStorage; ImageInfoProvider& mImageInfoProvider; - Allocator& mAllocator; + AllocatorItf& mAllocator; MonitoringData mMonitoringData; @@ -266,13 +253,13 @@ class ComponentInstance : public Instance { /** * Constructs component instance. * + * @param allocator instance allocator. * @param info instance information. * @param storage interface to persistent storage. * @param imageInfoProvider interface for retrieving service information from image. - * @param allocator instance allocator. */ ComponentInstance( - const InstanceInfo& info, StorageItf& storage, ImageInfoProvider& imageInfoProvider, Allocator& allocator); + AllocatorItf& allocator, const InstanceInfo& info, StorageItf& storage, ImageInfoProvider& imageInfoProvider); /** * Initializes component instance. @@ -300,21 +287,19 @@ class ComponentInstance : public Instance { * Checks whether available CPU fits instance requirements. * * @param availableCPU available CPU. - * @param nodeConfig node configuration. - * @param useMonitoringData whether to use monitoring data. + * @param node node. * @return bool. */ - bool IsAvailableCpuOk(size_t availableCPU, const NodeConfig& nodeConfig, bool useMonitoringData) override; + bool IsAvailableCpuOk(size_t availableCPU, const NodeItf& node) override; /** * Checks whether available RAM fits instance requirements. * * @param availableRAM available RAM. - * @param nodeConfig node configuration. - * @param useMonitoringData whether to use monitoring data. + * @param node node. * @return bool. */ - bool IsAvailableRamOk(size_t availableRAM, const NodeConfig& nodeConfig, bool useMonitoringData) override; + bool IsAvailableRamOk(size_t availableRAM, const NodeItf& node) override; /** * Checks whether node resources fit instance requirements. @@ -340,6 +325,15 @@ class ComponentInstance : public Instance { * @return Error. */ Error Schedule(NodeItf& node, const String& runtimeID) override; + + /** + * Loads SM instance info + * + * @param node node interface. + * @param runtimeID runtime identifier. + * @return Error. + */ + Error LoadSMInfo(NodeItf& node, const String& runtimeID) override; }; /** @@ -350,14 +344,14 @@ class ServiceInstance : public Instance { /** * Constructs service instance. * + * @param allocator instance allocator. * @param info instance information. * @param uidPool pool for managing user identifiers. * @param storage interface to persistent storage. * @param storageState interface for managing storage and state partitions. - * @param allocator instance allocator. */ - ServiceInstance(const InstanceInfo& info, UIDPool& uidPool, GIDPool& gidPool, StorageItf& storage, - StorageState& storageState, ImageInfoProvider& imageInfoProvider, Allocator& allocator); + ServiceInstance(AllocatorItf& allocator, const InstanceInfo& info, UIDPool& uidPool, GIDPool& gidPool, + StorageItf& storage, StorageState& storageState, ImageInfoProvider& imageInfoProvider); /** * Initializes service instance. @@ -385,21 +379,19 @@ class ServiceInstance : public Instance { * Checks whether available CPU fits instance requirements. * * @param availableCPU available CPU. - * @param nodeConfig node configuration. - * @param useMonitoringData whether to use monitoring data. + * @param node node. * @return bool. */ - bool IsAvailableCpuOk(size_t availableCPU, const NodeConfig& nodeConfig, bool useMonitoringData) override; + bool IsAvailableCpuOk(size_t availableCPU, const NodeItf& node) override; /** * Checks whether available RAM fits instance requirements. * * @param availableRAM available RAM. - * @param nodeConfig node configuration. - * @param useMonitoringData whether to use monitoring data. + * @param node node. * @return bool. */ - bool IsAvailableRamOk(size_t availableRAM, const NodeConfig& nodeConfig, bool useMonitoringData) override; + bool IsAvailableRamOk(size_t availableRAM, const NodeItf& node) override; /** * Checks whether node resources fit instance requirements. @@ -426,11 +418,20 @@ class ServiceInstance : public Instance { */ Error Schedule(NodeItf& node, const String& runtimeID) override; + /** + * Loads SM instance info. + * + * @param node node interface. + * @param runtimeID runtime identifier. + * @return Error. + */ + Error LoadSMInfo(NodeItf& node, const String& runtimeID) override; + private: static constexpr auto cDefaultResourceRation = 50.0; - size_t GetRequestedCPU(const NodeConfig& nodeConfig, bool useMonitoringData); - size_t GetRequestedRAM(const NodeConfig& nodeConfig, bool useMonitoringData); + size_t GetRequestedCPU(const NodeItf& node); + size_t GetRequestedRAM(const NodeItf& node); size_t GetReqStateSize(const NodeConfig& nodeConfig); size_t GetReqStorageSize(const NodeConfig& nodeConfig); diff --git a/src/core/cm/launcher/instancemanager.cpp b/src/core/cm/launcher/instancemanager.cpp index 885d98a1d..b042b89f8 100644 --- a/src/core/cm/launcher/instancemanager.cpp +++ b/src/core/cm/launcher/instancemanager.cpp @@ -38,15 +38,17 @@ Error InstanceManager::RemoveInstances(Array>& instances, Pr * Public **********************************************************************************************************************/ -Error InstanceManager::Init(const Config& config, imagemanager::ItemInfoProviderItf& itemInfoProvider, - storagestate::StorageStateItf& storageState, oci::OCISpecItf& ociSpec, IdentifierPoolValidator gidValidator, - IdentifierPoolValidator uidValidator, StorageItf& storage) +Error InstanceManager::Init(AllocatorItf& allocator, const Config& config, + imagemanager::ItemInfoProviderItf& itemInfoProvider, storagestate::StorageStateItf& storageState, + oci::OCISpecItf& ociSpec, IdentifierPoolValidator gidValidator, IdentifierPoolValidator uidValidator, + StorageItf& storage) { - mConfig = config; - mStorage = &storage; + mAllocator = &allocator; + mConfig = config; + mStorage = &storage; - mImageInfoProvider.Init(itemInfoProvider, ociSpec); - mStorageState.Init(storageState); + mImageInfoProvider.Init(allocator, itemInfoProvider, ociSpec); + mStorageState.Init(allocator, storageState); if (auto err = mUIDPool.Init(uidValidator); !err.IsNone()) { return err; @@ -66,7 +68,7 @@ Error InstanceManager::Start() } if (auto err = LoadInstancesFromStorage(); !err.IsNone()) { - LOG_ERR() << "Can't load instances from storage " << Log::Field(err); + LOG_ERR() << "Can't load instances from storage" << Log::Field(err); return err; } @@ -104,11 +106,11 @@ Error InstanceManager::Start() Error InstanceManager::Stop() { - if (auto err = mCleanInstancesTimer.Stop(); !err.IsNone()) { + if (auto err = mCleanInstancesTimer.Stop(Timer::StopMode::WaitForCallbacks); !err.IsNone()) { return AOS_ERROR_WRAP(err); } - if (auto err = mInitTimer.Stop(); !err.IsNone()) { + if (auto err = mInitTimer.Stop(Timer::StopMode::WaitForCallbacks); !err.IsNone()) { return AOS_ERROR_WRAP(err); } @@ -171,34 +173,43 @@ Array& InstanceManager::GetRunningInstances() Error InstanceManager::UpdateStatus(const InstanceStatus& status) { - if (status.mPreinstalled) { - auto preinstalledComponent - = FindPreinstalledComponent(static_cast(status), status.mVersion); - if (preinstalledComponent == nullptr) { - if (auto err = mPreinstalledComponents.EmplaceBack(status); !err.IsNone()) { - return AOS_ERROR_WRAP(err); - } + Error firstErr = ErrorEnum::eNone; - return ErrorEnum::eNone; - } + auto& statuses = status.mPreinstalled ? mPreinstalledComponents : mRunningInstances; - *preinstalledComponent = status; + auto existing = statuses.FindIf([&status](const InstanceStatus& item) { + return static_cast(item) == static_cast(status) + && item.mVersion == status.mVersion; + }); - return ErrorEnum::eNone; + bool missingStatus = existing == statuses.end(); + + if (!missingStatus) { + *existing = status; } - auto instance = FindActiveInstance(static_cast(status), status.mVersion); - if (!instance) { - // Ignore inactive instance, SM sometimes sends inactive status for stopped instances. - if (status.mState == aos::InstanceStateEnum::eInactive) { - return ErrorEnum::eNone; + bool missingActiveInstance = false; + + if (!status.mPreinstalled) { + auto instance = FindActiveInstance(static_cast(status), status.mVersion); + if (instance) { + if (auto err = instance->UpdateStatus(status); !err.IsNone()) { + firstErr = err; + } + } else { + missingActiveInstance = true; } + } - // Not expected instance received from SM. - return AOS_ERROR_WRAP(ErrorEnum::eNotFound); + if (missingActiveInstance || missingStatus) { + LOG_WRN() << "Received status for instance missing in" + << (missingActiveInstance ? " \'active instance list\'" : "") + << (missingStatus ? " \'status list\'" : "") + << Log::Field("instance", static_cast(status)) + << Log::Field("version", status.mVersion); } - return instance->UpdateStatus(status); + return firstErr; } RetWithError> InstanceManager::CreateInstance(const RunInstanceRequest& request, uint64_t index) @@ -209,6 +220,9 @@ RetWithError> InstanceManager::CreateInstance(const RunInsta } auto instanceInfo = CreateInfo(id, "", "", request); + if (!instanceInfo) { + return {nullptr, AOS_ERROR_WRAP(ErrorEnum::eNoMemory)}; + } if (auto err = mStorage->AddInstance(*instanceInfo); !err.IsNone()) { return {nullptr, AOS_ERROR_WRAP(err)}; @@ -232,6 +246,9 @@ RetWithError> InstanceManager::CreateInstance(const RunInsta auto id = InstanceIdent {request.mItemID, request.mSubjectInfo.mSubjectID, index, request.mUpdateItemType}; auto instanceInfo = CreateInfo(id, nodeID, runtimeID, request); + if (!instanceInfo) { + return {nullptr, AOS_ERROR_WRAP(ErrorEnum::eNoMemory)}; + } if (auto err = mStorage->AddInstance(*instanceInfo); !err.IsNone()) { return {nullptr, AOS_ERROR_WRAP(err)}; @@ -291,10 +308,12 @@ Error InstanceManager::SubmitScheduledInstances() mScheduledInstances.Clear(); + ClearCacheIfLimitReached(); + return ErrorEnum::eNone; } -Error InstanceManager::DisableInstance(SharedPtr& instance) +void InstanceManager::DisableInstance(SharedPtr& instance) { if (auto err = instance->Cache(true); !err.IsNone()) { const auto& id = instance->GetInfo().mInstanceIdent; @@ -307,7 +326,29 @@ Error InstanceManager::DisableInstance(SharedPtr& instance) mScheduledInstances.Remove(instance); mActiveInstances.Remove(instance); - return ErrorEnum::eNone; + ClearCacheIfLimitReached(); +} + +void InstanceManager::ClearCacheIfLimitReached() +{ + // Cache shares the allocator budget with active/scheduled instances. Drop the whole cache once the + // allowed number of instances is reached. + if (mScheduledInstances.Size() + mActiveInstances.Size() + mCachedInstances.Size() < 2 * cMaxNumInstances - 1) { + // Storage can hold at most cMaxNumInstances instances (active + cached are persisted), so keep their + // total within that limit and drop the cache once it is reached. + if (mActiveInstances.Size() + mCachedInstances.Size() <= cMaxNumInstances) { + return; + } + } + + for (auto& instance : mCachedInstances) { + if (auto err = instance->Remove(); !err.IsNone()) { + LOG_ERR() << "Remove cached instance failed" << Log::Field("instanceID", instance->GetInfo().mInstanceIdent) + << AOS_ERROR_WRAP(err); + } + } + + mCachedInstances.Clear(); } SharedPtr InstanceManager::FindActiveInstance(const InstanceIdent& id, const String& version) @@ -357,12 +398,36 @@ void InstanceManager::UpdateMonitoringData(const Array& statuses, const InstanceStatus& status) +{ + auto existing = statuses.FindIf([&status](const InstanceStatus& item) { + return static_cast(item) == static_cast(status) + && item.mVersion == status.mVersion; + }); + + if (existing != statuses.end()) { + *existing = status; + + return ErrorEnum::eNone; + } + + if (auto err = statuses.EmplaceBack(status); !err.IsNone()) { + return AOS_ERROR_WRAP(err); + } + + return ErrorEnum::eNone; +} + Error InstanceManager::LoadInstancesFromStorage() { mActiveInstances.Clear(); mCachedInstances.Clear(); - auto instances = MakeUnique>(&mAllocator); + auto instances = MakeUnique>(mAllocator); + if (!instances) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } + if (auto err = mStorage->LoadActiveInstances(*instances); !err.IsNone()) { return AOS_ERROR_WRAP(err); } @@ -371,7 +436,10 @@ Error InstanceManager::LoadInstancesFromStorage() for (const auto& instance : *instances) { if (auto err = LoadInstanceFromStorage(instance); !err.IsNone()) { - return AOS_ERROR_WRAP(err); + LOG_ERR() << "Can't load instance from storage" << Log::Field("instance", instance.mInstanceIdent) + << Log::Field(err); + + continue; } } @@ -397,8 +465,7 @@ Error InstanceManager::LoadInstanceFromStorage(const InstanceInfo& info) return AOS_ERROR_WRAP(err); } } else { - LOG_DBG() << "Load cached instance" << Log::Field("instanceID", instance->GetInfo().mInstanceIdent) - << Log::Field("nodeID", instance->GetStatus().mNodeID); + LOG_DBG() << "Load cached instance" << Log::Field("instanceID", instance->GetInfo().mInstanceIdent); if (auto err = mCachedInstances.EmplaceBack(instance); !err.IsNone()) { return AOS_ERROR_WRAP(err); @@ -487,29 +554,35 @@ Error InstanceManager::ClearInstancesWithDeletedImages() RetWithError> InstanceManager::CreateInstance(const InstanceInfo& info) { + ClearCacheIfLimitReached(); + SharedPtr newInstance; switch (info.mInstanceIdent.mType.GetValue()) { case UpdateItemTypeEnum::eService: newInstance = MakeShared( - &mAllocator, info, mUIDPool, mGIDPool, *mStorage, mStorageState, mImageInfoProvider, mInstanceAllocator); + mAllocator, *mAllocator, info, mUIDPool, mGIDPool, *mStorage, mStorageState, mImageInfoProvider); break; case UpdateItemTypeEnum::eComponent: - newInstance - = MakeShared(&mAllocator, info, *mStorage, mImageInfoProvider, mInstanceAllocator); + newInstance = MakeShared(mAllocator, *mAllocator, info, *mStorage, mImageInfoProvider); break; default: return {{}, AOS_ERROR_WRAP(ErrorEnum::eNotSupported)}; } - if (auto err = newInstance->Init(); !err.IsNone()) { - return {{}, AOS_ERROR_WRAP(err)}; + if (!newInstance) { + return {nullptr, AOS_ERROR_WRAP(ErrorEnum::eNoMemory)}; } - if (auto [_, err] = newInstance->OverrideEnvVars(mEnvVarsOverrides); !err.IsNone()) { - return {{}, AOS_ERROR_WRAP(err)}; + if (auto err = newInstance->Init(); !err.IsNone()) { + // Do not leave invalid instance in storage. + if (auto rmErr = newInstance->Remove(); !rmErr.IsNone()) { + LOG_ERR() << "Can't remove instance" << Log::Field(AOS_ERROR_WRAP(rmErr)); + } + + return {nullptr, err}; } return newInstance; @@ -551,26 +624,37 @@ Error InstanceManager::UpdateRunningInstances(const String& nodeID, const Array< mRunningInstances.RemoveIf([&nodeID](const InstanceStatus& status) { return status.mNodeID == nodeID; }); mPreinstalledComponents.RemoveIf([&nodeID](const InstanceStatus& status) { return status.mNodeID == nodeID; }); + Error firstErr = ErrorEnum::eNone; + for (const auto& status : statuses) { - if (status.mNodeID == nodeID) { - if (status.mPreinstalled) { - if (auto err = mPreinstalledComponents.EmplaceBack(status); !err.IsNone()) { - return AOS_ERROR_WRAP(err); - } - } else { - if (auto err = mRunningInstances.EmplaceBack(status); !err.IsNone()) { - return AOS_ERROR_WRAP(err); - } - } + if (auto err = SetStatus(status); !err.IsNone() && firstErr.IsNone()) { + firstErr = err; } } + return firstErr; +} + +Error InstanceManager::SetStatus(const InstanceStatus& status) +{ + if (status.mPreinstalled) { + return SetStatus(mPreinstalledComponents, status); + } + Error firstErr = ErrorEnum::eNone; + if (auto err = SetStatus(mRunningInstances, status); !err.IsNone()) { + firstErr = err; + } - for (const auto& status : statuses) { - if (auto err = UpdateStatus(status); !err.IsNone() && firstErr.IsNone()) { + auto instance = FindActiveInstance(static_cast(status), status.mVersion); + if (instance) { + if (auto err = instance->UpdateStatus(status); !err.IsNone() && firstErr.IsNone()) { firstErr = err; } + } else { + LOG_WRN() << "Received node instance status for not active instance" + << Log::Field("instance", static_cast(status)) + << Log::Field("version", status.mVersion); } return firstErr; @@ -578,10 +662,6 @@ Error InstanceManager::UpdateRunningInstances(const String& nodeID, const Array< Error InstanceManager::ScheduleInstance(SharedPtr& instance, NodeItf& node, const String& runtimeID) { - if (auto [_, overrideErr] = instance->OverrideEnvVars(mEnvVarsOverrides); !overrideErr.IsNone()) { - return AOS_ERROR_WRAP(overrideErr); - } - if (auto err = instance->Schedule(node, runtimeID); !err.IsNone()) { return AOS_ERROR_WRAP(err); } @@ -604,17 +684,6 @@ Error InstanceManager::ScheduleInstance(SharedPtr& instance, const Err return ErrorEnum::eNone; } -bool InstanceManager::OverrideEnvVars(const OverrideEnvVarsRequest& envVars) -{ - if (mEnvVarsOverrides.mItems == envVars.mItems) { - return false; - } - - mEnvVarsOverrides = envVars; - - return true; -} - SharedPtr InstanceManager::FindReadyInstance(const InstanceIdent& id, const String& version) { auto instance = FindScheduledInstance(id, version); @@ -668,7 +737,12 @@ uint64_t InstanceManager::FindIndexForNewInstance(const String& itemID, const St UniquePtr InstanceManager::CreateInfo( const InstanceIdent& id, const String& nodeID, const String& runtimeID, const RunInstanceRequest& request) { - auto info = MakeUnique(&mAllocator); + auto info = MakeUnique(mAllocator); + if (!info) { + LOG_ERR() << "Can't allocate instance info" << Log::Field(ErrorEnum::eNoMemory); + + return info; + } info->mInstanceIdent = id; info->mManifestDigest = ""; diff --git a/src/core/cm/launcher/instancemanager.hpp b/src/core/cm/launcher/instancemanager.hpp index b1f97c7b6..25187f88e 100644 --- a/src/core/cm/launcher/instancemanager.hpp +++ b/src/core/cm/launcher/instancemanager.hpp @@ -34,6 +34,7 @@ class InstanceManager { /** * Initializes the instance manager with configuration and required interfaces. * + * @param allocator allocator to use for temporary objects. * @param config Configuration object. * @param imageInfoProvider Interface for retrieving service information from images. * @param storageState Interface for managing storage and state partitions. @@ -42,7 +43,7 @@ class InstanceManager { * @param storage Interface to persistent storage. * @return Error. */ - Error Init(const Config& config, imagemanager::ItemInfoProviderItf& itemInfoProvider, + Error Init(AllocatorItf& allocator, const Config& config, imagemanager::ItemInfoProviderItf& itemInfoProvider, storagestate::StorageStateItf& storageState, oci::OCISpecItf& ociSpec, IdentifierPoolValidator gidValidator, IdentifierPoolValidator uidValidator, StorageItf& storage); @@ -186,9 +187,8 @@ class InstanceManager { * Disables instance. * * @param instance instance. - * @return Error. */ - Error DisableInstance(SharedPtr& instance); + void DisableInstance(SharedPtr& instance); /** * Updates monitoring data for active instances. @@ -249,20 +249,11 @@ class InstanceManager { */ Error ScheduleInstance(SharedPtr& instance, const Error& error); - /** - * Overrides environment variables. - * - * @param envVars environment variables. - * @return bool true if env vars changed, false otherwise. - */ - bool OverrideEnvVars(const OverrideEnvVarsRequest& envVars); - private: - static constexpr auto cRemovePeriod = Time::cDay; - static constexpr auto cAllocatorSize = Max(sizeof(ComponentInstance), sizeof(ServiceInstance)) * cMaxNumInstances - + sizeof(InstanceInfo) * cMaxNumInstances + sizeof(InstanceInfo) + sizeof(oci::ImageIndex); - static constexpr auto cInstanceAllocatorSize = sizeof(oci::ImageConfig) + sizeof(oci::ItemConfig) - + sizeof(InstanceStatus) + sizeof(oci::ImageIndex) + sizeof(EnvVarArray); + static constexpr auto cRemovePeriod = Time::cDay; + + Error SetStatus(const InstanceStatus& status); + Error SetStatus(Array& statuses, const InstanceStatus& status); Error LoadInstancesFromStorage(); Error LoadInstanceFromStorage(const InstanceInfo& info); @@ -274,6 +265,8 @@ class InstanceManager { template Error RemoveInstances(Array>& instances, Predicate predicate) const; + void ClearCacheIfLimitReached(); + RetWithError> CreateInstance(const InstanceInfo& info); SharedPtr FindReadyInstance(const InstanceIdent& id, const String& version); @@ -304,8 +297,7 @@ class InstanceManager { Timer mCleanInstancesTimer; Timer mInitTimer; - StaticAllocator mAllocator; - StaticAllocator mInstanceAllocator; + AllocatorItf* mAllocator {}; StaticArray, cMaxNumInstances> mActiveInstances; StaticArray, cMaxNumInstances> mScheduledInstances; @@ -314,8 +306,7 @@ class InstanceManager { StaticArray mPreinstalledComponents; StaticArray mRunningInstances; - SubjectArray mSubjects; - OverrideEnvVarsRequest mEnvVarsOverrides; + SubjectArray mSubjects; }; /** diff --git a/src/core/cm/launcher/launcher.cpp b/src/core/cm/launcher/launcher.cpp index c1ed01a8e..98f4c9f0b 100644 --- a/src/core/cm/launcher/launcher.cpp +++ b/src/core/cm/launcher/launcher.cpp @@ -33,15 +33,17 @@ class ShouldRebalanceVisitor : public StaticVisitor { * Public **********************************************************************************************************************/ -Error Launcher::Init(const Config& config, nodeinfoprovider::NodeInfoProviderItf& nodeInfoProvider, - InstanceRunnerItf& runner, imagemanager::ItemInfoProviderItf& itemInfoProvider, oci::OCISpecItf& ociSpec, +Error Launcher::Init(AllocatorItf& allocator, const Config& config, + nodeinfoprovider::NodeInfoProviderItf& nodeInfoProvider, InstanceRunnerItf& runner, + imagemanager::ItemInfoProviderItf& itemInfoProvider, oci::OCISpecItf& ociSpec, unitconfig::NodeConfigProviderItf& nodeConfigProvider, storagestate::StorageStateItf& storageState, MonitoringProviderItf& monitorProvider, alerts::AlertsProviderItf& alertsProvider, iamclient::IdentProviderItf& identProvider, IdentifierPoolValidator gidValidator, - IdentifierPoolValidator uidValidator, StorageItf& storage) + IdentifierPoolValidator uidValidator, StorageItf& storage, SenderItf& sender) { LOG_DBG() << "Init Launcher"; + mAllocator = &allocator; mConfig = config; mStorage = &storage; mNodeInfoProvider = &nodeInfoProvider; @@ -51,18 +53,23 @@ Error Launcher::Init(const Config& config, nodeinfoprovider::NodeInfoProviderItf mMonitorProvider = &monitorProvider; mAlertsProvider = &alertsProvider; mIdentProvider = &identProvider; + mSender = &sender; - auto err - = mInstanceManager.Init(config, itemInfoProvider, storageState, ociSpec, gidValidator, uidValidator, storage); + auto err = mInstanceManager.Init( + allocator, config, itemInfoProvider, storageState, ociSpec, gidValidator, uidValidator, storage); if (!err.IsNone()) { return AOS_ERROR_WRAP(err); } - mImageInfoProvider.Init(itemInfoProvider, ociSpec); + mImageInfoProvider.Init(allocator, itemInfoProvider, ociSpec); - mRunRequestsLoader.Init(storage, mInstanceManager, mImageInfoProvider); - mNodeManager.Init(*mNodeInfoProvider, *mNodeConfigProvider, *mRunner); - mBalancer.Init(mInstanceManager, mImageInfoProvider, mNodeManager, *mMonitorProvider, *mRunner); + mRunRequestsLoader.Init(allocator, storage, mInstanceManager, mImageInfoProvider); + mNodeManager.Init(allocator, *mNodeInfoProvider, *mNodeConfigProvider, *mRunner, mOverrideEnvVarsProcessor); + mBalancer.Init(allocator, mInstanceManager, mImageInfoProvider, mNodeManager, *mMonitorProvider, *mRunner); + + if (err = mOverrideEnvVarsProcessor.Init(config, storage, sender, *this); !err.IsNone()) { + return AOS_ERROR_WRAP(err); + } return ErrorEnum::eNone; } @@ -104,7 +111,10 @@ Error Launcher::Start() } // Set initial subjects list. - auto subjects = MakeUnique(&mAllocator); + auto subjects = MakeUnique(mAllocator); + if (!subjects) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = mIdentProvider->GetSubjects(*subjects); !err.IsNone()) { return AOS_ERROR_WRAP(err); @@ -119,41 +129,24 @@ Error Launcher::Start() return AOS_ERROR_WRAP(err); } - // Load env vars overrides. - if (auto err = LoadEnvVarsOverrides(); !err.IsNone()) { - return AOS_ERROR_WRAP(err); - } - - auto onEnvVarsTTLTimerTick = [this](void*) { - UniqueLock updateLock {mUpdateMutex}; - - if (auto err = ProcessOverrideEnvVars(mOverrideEnvVars); !err.IsNone()) { - LOG_ERR() << "Update override env vars failed" << Log::Field(err); - } - }; - - if (auto err = mEnvVarsTTLTimer.Start(mConfig.mCheckOverrideEnvVarsPeriod, onEnvVarsTTLTimerTick, false); - !err.IsNone()) { - return AOS_ERROR_WRAP(err); - } - // Load SM data for active instances. if (auto err = mBalancer.LoadSMDataForActiveInstances(); !err.IsNone()) { LOG_ERR() << "Can't load SM data for active instances" << Log::Field(err); } + // Load env vars overrides and start TTL check timer; flag an update if some expired while offline. + if (auto [changed, err] = mOverrideEnvVarsProcessor.Start(); !err.IsNone()) { + return AOS_ERROR_WRAP(err); + } else { + mIsOverrideEnvVarsChanged = changed; + } + // Start process updates thread. mDisableProcessUpdates = false; mUpdatedNodes.Clear(); mNewSubjects.SetValue(*subjects); // Check subjects after startup. UpdateInstanceStatuses(); - - // Check for override env var TTL and setup update if needed. - if (auto err = ProcessOverrideEnvVars(mOverrideEnvVars); !err.IsNone()) { - return AOS_ERROR_WRAP(err); - } - ProcessNotScheduledInstances(); if (auto err = mWorkerThread.Run([this](void*) { ProcessUpdate(); }); !err.IsNone()) { @@ -202,7 +195,7 @@ Error Launcher::Stop() return err; } - if (auto err = mEnvVarsTTLTimer.Stop(); !err.IsNone()) { + if (auto err = mOverrideEnvVarsProcessor.Stop(); !err.IsNone()) { return AOS_ERROR_WRAP(err); } @@ -295,9 +288,10 @@ Error Launcher::OverrideEnvVars(const OverrideEnvVarsRequest& envVars) { LOG_DBG() << "Override env vars"; - LockGuard updateLock {mUpdateMutex}; + // Don't hold mUpdateMutex: the listener takes it, which would deadlock. + auto [_, err] = mOverrideEnvVarsProcessor.OverrideEnvVars(envVars); - return ProcessOverrideEnvVars(envVars); + return err; } /*********************************************************************************************************************** @@ -311,7 +305,13 @@ void Launcher::UpdateInstanceStatuses() const auto totalSize = activeInstances.Size() + preinstalledComponents.Size(); // Copy old statuses. - auto oldInstanceStatuses = MakeUnique>(&mAllocator); + auto oldInstanceStatuses = MakeUnique>(mAllocator); + if (!oldInstanceStatuses) { + LOG_ERR() << "Failed to allocate old instance statuses" << Log::Field(AOS_ERROR_WRAP(ErrorEnum::eNoMemory)); + + return; + } + if (auto err = oldInstanceStatuses->Assign(mInstanceStatuses); !err.IsNone()) { LOG_ERR() << "Failed to copy old instance statuses" << Log::Field(AOS_ERROR_WRAP(err)); @@ -351,7 +351,12 @@ void Launcher::UpdateInstanceStatuses() } // Find new statuses. - auto changedStatuses = MakeUnique>(&mAllocator); + auto changedStatuses = MakeUnique>(mAllocator); + if (!changedStatuses) { + LOG_ERR() << "Failed to allocate changed statuses" << Log::Field(AOS_ERROR_WRAP(ErrorEnum::eNoMemory)); + + return; + } for (size_t i = 0; i < mInstanceStatuses.Size(); ++i) { auto newStatus = !oldInstanceStatuses->Contains(mInstanceStatuses[i]); @@ -399,7 +404,11 @@ Error Launcher::BalanceInstances(UniqueLock& lock, bool rebalance) LOG_DBG() << "Balance instances" << Log::Field("rebalance", rebalance); // Create instances from run requests. - auto instances = MakeUnique, cMaxNumInstances>>(&mAllocator); + auto instances = MakeUnique, cMaxNumInstances>>(mAllocator); + if (!instances) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } + mRunRequestsLoader.CreateInstances(mNodeManager.GetNodes(), *instances); auto runErr = mBalancer.RunInstances(lock, *instances, rebalance); @@ -461,29 +470,16 @@ void Launcher::ProcessUpdate() doRebalance = true; } - // Process override environment variables changed. + // On override env vars change resend all nodes; bool forceRestart = false; if (mIsOverrideEnvVarsChanged) { mIsOverrideEnvVarsChanged = false; - mUpdatedNodes.Clear(); + forceRestart = true; - for (auto& instance : mInstanceManager.GetActiveInstances()) { - if (auto [changed, overrideErr] = instance->OverrideEnvVars(mOverrideEnvVars); !overrideErr.IsNone()) { - LOG_ERR() << "Failed to override env vars" << Log::Field(AOS_ERROR_WRAP(overrideErr)); - - continue; - } else { - if (changed) { - err = PushUnique(mUpdatedNodes, instance->GetInfo().mNodeID); - if (!err.IsNone()) { - LOG_ERR() << "Failed to add node ID to updated nodes" << Log::Field(AOS_ERROR_WRAP(err)); - - continue; - } - - forceRestart = true; - } + for (const auto& node : mNodeManager.GetNodes()) { + if (auto pushErr = PushUnique(mUpdatedNodes, node.GetInfo().mNodeID); !pushErr.IsNone()) { + LOG_ERR() << "Failed to add node to updated nodes" << Log::Field(AOS_ERROR_WRAP(pushErr)); } } } @@ -517,7 +513,9 @@ void Launcher::ProcessUpdate() void Launcher::WaitAllNodesConnected(UniqueLock& lock) { auto allNodesConnected = [this]() { - auto notConnected = [](const Node& node) { return !node.IsConnected(); }; + auto notConnected = [](const Node& node) { + return !node.IsConnected() && node.GetInfo().mState == NodeStateEnum::eProvisioned; + }; return !mNodeManager.GetNodes().ContainsIf(notConnected) || !mIsRunning; }; @@ -525,49 +523,6 @@ void Launcher::WaitAllNodesConnected(UniqueLock& lock) mAllNodesConnectedCondVar.Wait(lock, allNodesConnected); } -Error Launcher::LoadEnvVarsOverrides() -{ - // Restore override environment variables without TTL check, so we can detect changes in ProcessOverrideEnvVars(). - if (auto err = mStorage->LoadOverrideEnvVars(mOverrideEnvVars); !err.IsNone()) { - return AOS_ERROR_WRAP(err); - } - - mInstanceManager.OverrideEnvVars(mOverrideEnvVars); - - return ErrorEnum::eNone; -} - -Error Launcher::ProcessOverrideEnvVars(const OverrideEnvVarsRequest& envVars) -{ - mOverrideEnvVars = envVars; - - // Remove variables with expired TTLs. - auto now = Time::Now(); - - for (auto& item : mOverrideEnvVars.mItems) { - item.mVariables.RemoveIf([&now](const EnvVarInfo& envVarInfo) { - return envVarInfo.mTTL.HasValue() && envVarInfo.mTTL.GetValue() < now; - }); - } - - mOverrideEnvVars.mItems.RemoveIf([](const EnvVarsInstanceInfo& item) { return item.mVariables.IsEmpty(); }); - - // Save override environment variables. - if (!mInstanceManager.OverrideEnvVars(mOverrideEnvVars)) { - return ErrorEnum::eNone; - } - - if (auto err = mStorage->SaveOverrideEnvVars(mOverrideEnvVars); !err.IsNone()) { - return AOS_ERROR_WRAP(err); - } - - mIsOverrideEnvVarsChanged = true; - - mProcessUpdatesCondVar.NotifyAll(); - - return ErrorEnum::eNone; -} - void Launcher::ProcessNotScheduledInstances() { bool hasNotScheduledInstance = mInstanceManager.GetActiveInstances().ContainsIf( @@ -624,6 +579,10 @@ Error Launcher::OnNodeInstancesStatusesReceived(const String& nodeID, const Arra firstErr = err; } + if (auto err = mOverrideEnvVarsProcessor.AddStatuses(statuses); !err.IsNone() && firstErr.IsNone()) { + firstErr = err; + } + if (!firstErr.IsNone()) { return firstErr; } @@ -685,4 +644,13 @@ void Launcher::SubjectsChanged(const Array>& subjects) mProcessUpdatesCondVar.NotifyAll(); } +void Launcher::OnOverrideEnvVarsChanged() +{ + LockGuard updateLock {mUpdateMutex}; + + mIsOverrideEnvVarsChanged = true; + + mProcessUpdatesCondVar.NotifyAll(); +} + } // namespace aos::cm::launcher diff --git a/src/core/cm/launcher/launcher.hpp b/src/core/cm/launcher/launcher.hpp index 6969dbeaf..b82383a63 100644 --- a/src/core/cm/launcher/launcher.hpp +++ b/src/core/cm/launcher/launcher.hpp @@ -18,11 +18,13 @@ #include "itf/envvarhandler.hpp" #include "itf/instancestatusreceiver.hpp" #include "itf/launcher.hpp" +#include "itf/sender.hpp" #include "itf/storage.hpp" #include "balancer.hpp" #include "instancemanager.hpp" #include "nodemanager.hpp" +#include "overrideenvvarsprocessor.hpp" #include "runrequestsloader.hpp" namespace aos::cm::launcher { @@ -39,11 +41,13 @@ class Launcher : public LauncherItf, public EnvVarHandlerItf, private nodeinfoprovider::NodeInfoListenerItf, private alerts::AlertsListenerItf, - private iamclient::SubjectsListenerItf { + private iamclient::SubjectsListenerItf, + private OverrideEnvVarsListenerItf { public: /** * Initializes launcher object instance. * + * @param allocator allocator to use for temporary objects. * @param config configuration. * @param nodeInfoProvider interface providing information about all unit nodes. * @param runner instance runner interface. @@ -57,14 +61,15 @@ class Launcher : public LauncherItf, * @param gidValidator GID validator. * @param uidValidator UID validator. * @param storage storage interface. + * @param sender sender interface. * @return Error. */ - Error Init(const Config& config, nodeinfoprovider::NodeInfoProviderItf& nodeInfoProvider, InstanceRunnerItf& runner, - imagemanager::ItemInfoProviderItf& itemInfoProvider, oci::OCISpecItf& ociSpec, + Error Init(AllocatorItf& allocator, const Config& config, nodeinfoprovider::NodeInfoProviderItf& nodeInfoProvider, + InstanceRunnerItf& runner, imagemanager::ItemInfoProviderItf& itemInfoProvider, oci::OCISpecItf& ociSpec, unitconfig::NodeConfigProviderItf& nodeConfigProvider, storagestate::StorageStateItf& storageState, MonitoringProviderItf& monitorProvider, alerts::AlertsProviderItf& alertsProvider, iamclient::IdentProviderItf& identProvider, IdentifierPoolValidator gidValidator, - IdentifierPoolValidator uidValidator, StorageItf& storage); + IdentifierPoolValidator uidValidator, StorageItf& storage, SenderItf& sender); /** * Starts launcher instance. @@ -133,8 +138,6 @@ class Launcher : public LauncherItf, private: static constexpr auto cMaxNumInstanceStatusListeners = 8; - static constexpr auto cAllocatorSize = 2 * sizeof(StaticArray) - + sizeof(StaticArray, cMaxNumInstances>); void SendRunStatus(); @@ -146,9 +149,7 @@ class Launcher : public LauncherItf, void ProcessUpdate(); void WaitAllNodesConnected(UniqueLock& lock); - Error LoadEnvVarsOverrides(); - Error ProcessOverrideEnvVars(const OverrideEnvVarsRequest& envVars); - void ProcessNotScheduledInstances(); + void ProcessNotScheduledInstances(); // InstanceStatusReceiverItf implementation Error OnInstanceStatusReceived(const InstanceStatus& status) override; @@ -163,7 +164,11 @@ class Launcher : public LauncherItf, // iamclient::SubjectsListenerItf implementation void SubjectsChanged(const Array>& subjects) override; + // OverrideEnvVarsListenerItf implementation + void OnOverrideEnvVarsChanged() override; + // External dependencies + AllocatorItf* mAllocator {}; Config mConfig; StorageItf* mStorage {}; nodeinfoprovider::NodeInfoProviderItf* mNodeInfoProvider {}; @@ -173,14 +178,16 @@ class Launcher : public LauncherItf, storagestate::StorageStateItf* mStorageState {}; MonitoringProviderItf* mMonitorProvider {}; alerts::AlertsProviderItf* mAlertsProvider {}; + SenderItf* mSender {}; StaticArray mInstanceStatusListeners; // Managers - RunRequestsLoader mRunRequestsLoader {}; - InstanceManager mInstanceManager {}; - NodeManager mNodeManager {}; - ImageInfoProvider mImageInfoProvider {}; - Balancer mBalancer {}; + RunRequestsLoader mRunRequestsLoader {}; + InstanceManager mInstanceManager {}; + NodeManager mNodeManager {}; + ImageInfoProvider mImageInfoProvider {}; + Balancer mBalancer {}; + OverrideEnvVarsProcessor mOverrideEnvVarsProcessor {}; // Process update thread Thread<> mWorkerThread; @@ -194,16 +201,13 @@ class Launcher : public LauncherItf, bool mForceRebalance {}; // Override environment variables - OverrideEnvVarsRequest mOverrideEnvVars; - Timer mEnvVarsTTLTimer; - bool mIsOverrideEnvVarsChanged {}; + bool mIsOverrideEnvVarsChanged {}; // Misc StaticArray mInstanceStatuses; Mutex mBalancingMutex; ConditionalVariable mAllNodesConnectedCondVar; bool mIsRunning {}; - StaticAllocator mAllocator; }; /** @}*/ diff --git a/src/core/cm/launcher/node.cpp b/src/core/cm/launcher/node.cpp index f92be7725..c4be15573 100644 --- a/src/core/cm/launcher/node.cpp +++ b/src/core/cm/launcher/node.cpp @@ -7,100 +7,36 @@ #include #include "node.hpp" +#include "utils.hpp" namespace aos::cm::launcher { -template -class Filter { -public: - class Iterator { - public: - Iterator(typename Array::ConstIterator it, typename Array::ConstIterator end, Cmp cmp) - : mIt(it) - , mEnd(end) - , mCmp(cmp) - { - while (mIt != mEnd && !mCmp(*mIt)) { - ++mIt; - } - } - - Iterator& operator++() - { - assert(mIt != mEnd); - - ++mIt; - - while (mIt != mEnd && !mCmp(*mIt)) { - ++mIt; - } - - return *this; - } - - Iterator operator++(int) - { - assert(mIt != mEnd); - - Iterator tmp = *this; - - ++(*this); - - return tmp; - } - - bool operator==(const Iterator& other) const { return mIt == other.mIt; } - bool operator!=(const Iterator& other) const { return mIt != other.mIt; } - - const T& operator*() const { return *mIt; } - const T* operator->() const { return mIt; } - - private: - typename Array::ConstIterator mIt; - typename Array::ConstIterator mEnd; - Cmp mCmp; - }; - - Filter(const Array& array, Cmp cmp) - : mArray(&array) - , mCmp(cmp) - { - } - - Iterator begin() const { return Iterator(mArray->begin(), mArray->end(), mCmp); } - Iterator end() const { return Iterator(mArray->end(), mArray->end(), mCmp); } - -private: - const Array* mArray; - Cmp mCmp; -}; - auto FilterActiveNodeInstances(const Array& array, const String& nodeID) { auto cmp = [nodeID](const InstanceStatus& status) { return status.mNodeID == nodeID && status.mState != aos::InstanceStateEnum::eInactive; }; - return Filter(array, cmp); + return Filter(array, cmp); } auto FilterByNode(const Array>& array, const String& nodeID) { auto cmp = [nodeID](const SharedPtr& instance) { return instance->GetInfo().mNodeID == nodeID; }; - return Filter, decltype(cmp)>(array, cmp); + return Filter(array, cmp); } /*********************************************************************************************************************** * Public **********************************************************************************************************************/ -void Node::Init(const String& id, unitconfig::NodeConfigProviderItf& nodeConfigProvider, - InstanceRunnerItf& instanceRunner, Allocator* allocator) +void Node::Init(AllocatorItf& allocator, const String& id, unitconfig::NodeConfigProviderItf& nodeConfigProvider, + InstanceRunnerItf& instanceRunner) { mNodeConfigProvider = &nodeConfigProvider; mInstanceRunner = &instanceRunner; - mAllocator = allocator; + mAllocator = &allocator; mInfo.mNodeID = id; mInfo.mState = NodeStateEnum::eUnprovisioned; @@ -120,9 +56,8 @@ void Node::PrepareForBalancing(bool rebalancing) const auto& alertRules = mConfig.mAlertRules.GetValue(); if (alertRules.mCPU.HasValue() || alertRules.mRAM.HasValue()) { if (alertRules.mCPU.HasValue()) { - const auto usedCPU = mTotalCPUUsage; - const auto maxTreshold - = mInfo.mMaxDMIPS * static_cast(alertRules.mCPU.GetValue().mMaxThreshold / 100.0); + const auto usedCPU = mTotalCPUUsage; + const auto maxTreshold = mInfo.mMaxDMIPS * alertRules.mCPU.GetValue().mMaxThreshold / 100.0; if (usedCPU > maxTreshold) { mNeedBalancing = true; @@ -130,9 +65,8 @@ void Node::PrepareForBalancing(bool rebalancing) } if (alertRules.mRAM.HasValue()) { - const auto usedRAM = mTotalRAMUsage; - const auto maxTreshold - = mInfo.mMaxDMIPS * static_cast(alertRules.mRAM.GetValue().mMaxThreshold / 100.0); + const auto usedRAM = mTotalRAMUsage; + const auto maxTreshold = mInfo.mTotalRAM * alertRules.mRAM.GetValue().mMaxThreshold / 100.0; if (usedRAM > maxTreshold) { mNeedBalancing = true; @@ -334,8 +268,15 @@ Error Node::ReserveResources(const InstanceIdent& instanceIdent, const String& r Error Node::SendScheduledInstances( const Array>& scheduledInstances, const Array& runningInstances) { - auto stopInstances = MakeUnique>(mAllocator); + auto stopInstances = MakeUnique>(mAllocator); + if (!stopInstances) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } + auto startInstances = MakeUnique>(mAllocator); + if (!startInstances) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } for (const auto& status : FilterActiveNodeInstances(runningInstances, mInfo.mNodeID)) { // Check if the instance is scheduled on this node (ident, runtime, node, and service version must match). @@ -384,8 +325,16 @@ Error Node::SendScheduledInstances( RetWithError Node::ResendInstances( const Array>& activeInstances, const Array& runningInstances, bool forceRestart) { - auto stopInstances = MakeUnique>(mAllocator); - auto startInstances = MakeUnique>(mAllocator); + auto stopInstances = MakeUnique>(mAllocator); + if (!stopInstances) { + return {false, AOS_ERROR_WRAP(ErrorEnum::eNoMemory)}; + } + + auto startInstances = MakeUnique>(mAllocator); + if (!startInstances) { + return {false, AOS_ERROR_WRAP(ErrorEnum::eNoMemory)}; + } + size_t runningNodeInstances = 0; for (const auto& status : FilterActiveNodeInstances(runningInstances, mInfo.mNodeID)) { diff --git a/src/core/cm/launcher/node.hpp b/src/core/cm/launcher/node.hpp index a0b25a403..aae04776f 100644 --- a/src/core/cm/launcher/node.hpp +++ b/src/core/cm/launcher/node.hpp @@ -30,13 +30,13 @@ class Node : public NodeItf { /** * Initializes node. * + * @param allocator allocator. * @param info node information. * @param nodeConfigProvider node config provider. * @param instanceRunner instance runner interface. - * @param allocator allocator. */ - void Init(const String& id, unitconfig::NodeConfigProviderItf& nodeConfigProvider, - InstanceRunnerItf& instanceRunner, Allocator* allocator); + void Init(AllocatorItf& allocator, const String& id, unitconfig::NodeConfigProviderItf& nodeConfigProvider, + InstanceRunnerItf& instanceRunner); /** * Prepares node for balancing. @@ -60,11 +60,6 @@ class Node : public NodeItf { */ const UnitNodeInfo& GetInfo() const { return mInfo; } - /** - * Indicates whether node requires rebalancing. - */ - bool NeedBalancing() const { return mNeedBalancing; } - /** * Updates node information. * @@ -188,8 +183,6 @@ class Node : public NodeItf { UnitNodeInfo mInfo {}; bool mIsNodeStatusReceived {}; - bool mNeedBalancing {}; - size_t mTotalCPUUsage {}; size_t mTotalRAMUsage {}; size_t mSystemCPUUsage {}; @@ -203,7 +196,7 @@ class Node : public NodeItf { StaticMap, size_t, cMaxNumNodeRuntimes> mRuntimeAvailableCPU; StaticMap, size_t, cMaxNumNodeResources> mMaxInstances; - Allocator* mAllocator {}; + AllocatorItf* mAllocator {}; }; /** @}*/ diff --git a/src/core/cm/launcher/nodeitf.hpp b/src/core/cm/launcher/nodeitf.hpp index a9db3cb48..a7ee0e1f3 100644 --- a/src/core/cm/launcher/nodeitf.hpp +++ b/src/core/cm/launcher/nodeitf.hpp @@ -55,8 +55,16 @@ class NodeItf { */ const NodeConfig& GetConfig() const { return mConfig; } + /** + * Indicates whether node requires rebalancing. + * + * @return bool. + */ + bool NeedBalancing() const { return mNeedBalancing; } + protected: NodeConfig mConfig {}; + bool mNeedBalancing {}; }; /** @}*/ diff --git a/src/core/cm/launcher/nodemanager.cpp b/src/core/cm/launcher/nodemanager.cpp index 74591d184..a6dfefe07 100644 --- a/src/core/cm/launcher/nodemanager.cpp +++ b/src/core/cm/launcher/nodemanager.cpp @@ -4,28 +4,42 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "nodemanager.hpp" - #include -#include + +#include "nodemanager.hpp" +#include "utils.hpp" namespace aos::cm::launcher { +auto FilterActiveNodes(Array& array) +{ + auto cmp + = [](const Node& node) { return node.IsConnected() && node.GetInfo().mState == NodeStateEnum::eProvisioned; }; + + return Filter(array, cmp); +} + /*********************************************************************************************************************** * Public **********************************************************************************************************************/ -void NodeManager::Init(nodeinfoprovider::NodeInfoProviderItf& nodeInfoProvider, - unitconfig::NodeConfigProviderItf& nodeConfigProvider, InstanceRunnerItf& runner) +void NodeManager::Init(AllocatorItf& allocator, nodeinfoprovider::NodeInfoProviderItf& nodeInfoProvider, + unitconfig::NodeConfigProviderItf& nodeConfigProvider, InstanceRunnerItf& runner, + OverrideEnvVarsProcessor& overrideEnvVarsProcessor) { - mNodeInfoProvider = &nodeInfoProvider; - mNodeConfigProvider = &nodeConfigProvider; - mRunner = &runner; + mAllocator = &allocator; + mNodeInfoProvider = &nodeInfoProvider; + mNodeConfigProvider = &nodeConfigProvider; + mRunner = &runner; + mOverrideEnvVarsProcessor = &overrideEnvVarsProcessor; } Error NodeManager::Start() { - auto nodes = MakeUnique, cMaxNumNodes>>(&mAllocator); + auto nodes = MakeUnique, cMaxNumNodes>>(mAllocator); + if (!nodes) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = mNodeInfoProvider->GetAllNodeIDs(*nodes); !err.IsNone()) { return AOS_ERROR_WRAP(err); @@ -33,7 +47,10 @@ Error NodeManager::Start() LOG_DBG() << "Start node manager" << Log::Field("nodes", nodes->Size()); - auto nodeInfo = MakeUnique(&mAllocator); + auto nodeInfo = MakeUnique(mAllocator); + if (!nodeInfo) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } for (const auto& nodeID : *nodes) { if (auto err = mNodeInfoProvider->GetNodeInfo(nodeID, *nodeInfo); !err.IsNone()) { @@ -49,7 +66,7 @@ Error NodeManager::Start() // Add online provisioned node mNodes.EmplaceBack(); - mNodes.Back().Init(nodeInfo->mNodeID, *mNodeConfigProvider, *mRunner, &mNodeAllocator); + mNodes.Back().Init(*mAllocator, nodeInfo->mNodeID, *mNodeConfigProvider, *mRunner); mNodes.Back().UpdateInfo(*nodeInfo); } @@ -69,6 +86,8 @@ Error NodeManager::Stop() Error NodeManager::PrepareForBalancing(bool rebalancing) { + // Launcher utilizes scheduling implementation to load SM data for active instances on startup + // so we need to prepare for balancing all nodes. for (auto& node : mNodes) { node.PrepareForBalancing(rebalancing); } @@ -93,11 +112,19 @@ Error NodeManager::LoadSMDataForActiveInstances( if (node == nullptr) { LOG_ERR() << "Can't find node" << Log::Field("instanceID", instanceID) << Log::Field("nodeID", nodeID) << Log::Field(AOS_ERROR_WRAP(ErrorEnum::eNotFound)); + continue; } - auto imageDescriptor = MakeUnique(&mAllocator); - auto findDescErr = FindImageDescriptor( + auto imageDescriptor = MakeUnique(mAllocator); + if (!imageDescriptor) { + LOG_ERR() << "Can't allocate image descriptor" << Log::Field("instanceID", instanceID) + << Log::Field(AOS_ERROR_WRAP(ErrorEnum::eNoMemory)); + + continue; + } + + auto findDescErr = FindImageDescriptor( instanceID.mItemID, instance->GetInfo().mVersion, manifestDigest, imageInfoProvider, *imageDescriptor); if (!findDescErr.IsNone()) { LOG_ERR() << "Can't find image descriptor" << Log::Field("instanceID", instanceID) @@ -114,7 +141,7 @@ Error NodeManager::LoadSMDataForActiveInstances( continue; } - if (auto err = instance->Schedule(*node, runtimeID); !err.IsNone()) { + if (auto err = instance->LoadSMInfo(*node, runtimeID); !err.IsNone()) { LOG_ERR() << "Can't load instance" << Log::Field("nodeID", nodeID) << Log::Field("instanceID", instanceID) << Log::Field(AOS_ERROR_WRAP(err)); @@ -134,7 +161,7 @@ Error NodeManager::NotifyNodeStatusReceived(const String& nodeID) return AOS_ERROR_WRAP(err); } - mNodes.Back().Init(nodeID, *mNodeConfigProvider, *mRunner, &mNodeAllocator); + mNodes.Back().Init(*mAllocator, nodeID, *mNodeConfigProvider, *mRunner); node = FindNode(nodeID); } @@ -158,7 +185,7 @@ Error NodeManager::GetConnectedNodes(Array& nodes) { nodes.Clear(); - for (auto& node : mNodes) { + for (auto& node : FilterActiveNodes(mNodes)) { if (auto err = nodes.PushBack(&node); !err.IsNone()) { return AOS_ERROR_WRAP(err); } @@ -187,12 +214,36 @@ Array& NodeManager::GetNodes() return mNodes; } +Error NodeManager::ApplyOverrideEnvVars(const Array>& instances) +{ + Error firstErr = ErrorEnum::eNone; + + auto overrideEnvVars = mOverrideEnvVarsProcessor->GetOverrideEnvVars(); + + for (auto& instance : instances) { + if (auto [changed, err] = instance->OverrideEnvVars(*overrideEnvVars); !err.IsNone()) { + LOG_ERR() << "Can't override env vars" << Log::Field("instance", instance->GetInfo().mInstanceIdent) + << Log::Field(err); + + if (firstErr.IsNone()) { + firstErr = err; + } + } + } + + return firstErr; +} + Error NodeManager::SendScheduledInstances(UniqueLock& lock, const Array>& scheduledInstances, const Array& runningInstances) { Error firstErr = ErrorEnum::eNone; - for (auto& node : mNodes) { + if (auto err = ApplyOverrideEnvVars(scheduledInstances); !err.IsNone()) { + return err; + } + + for (auto& node : FilterActiveNodes(mNodes)) { auto err = node.SendScheduledInstances(scheduledInstances, runningInstances); if (!err.IsNone()) { LOG_ERR() << "Can't send instance update" << Log::Field("nodeID", node.GetInfo().mNodeID) @@ -211,7 +262,7 @@ Error NodeManager::SendScheduledInstances(UniqueLock& lock, const Array& lock, const ArraySendStatuses(); + return ErrorEnum::eNone; } @@ -231,9 +284,13 @@ Error NodeManager::ResendInstances(UniqueLock& lock, const Array& lock, const ArraySendStatuses(); + return ErrorEnum::eNone; } @@ -281,12 +340,6 @@ bool NodeManager::UpdateNodeInfo(const UnitNodeInfo& info) auto* node = FindNode(info.mNodeID); if (node != nullptr) { - if (info.mState != NodeStateEnum::eProvisioned) { - mNodes.Erase(node); - - return true; - } - return node->UpdateInfo(info); } else { if (info.mState != NodeStateEnum::eProvisioned) { @@ -299,7 +352,7 @@ bool NodeManager::UpdateNodeInfo(const UnitNodeInfo& info) return false; } - mNodes.Back().Init(info.mNodeID, *mNodeConfigProvider, *mRunner, &mNodeAllocator); + mNodes.Back().Init(*mAllocator, info.mNodeID, *mNodeConfigProvider, *mRunner); mNodes.Back().UpdateInfo(info); return true; @@ -313,7 +366,10 @@ bool NodeManager::UpdateNodeInfo(const UnitNodeInfo& info) Error NodeManager::FindImageDescriptor(const String& itemID, const String& version, const String& manifestDigest, ImageInfoProvider& imageInfoProvider, oci::IndexContentDescriptor& imageDescriptor) { - auto imageIndex = MakeUnique(&mAllocator); + auto imageIndex = MakeUnique(mAllocator); + if (!imageIndex) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = imageInfoProvider.GetImageIndex(itemID, version, *imageIndex); !err.IsNone()) { return AOS_ERROR_WRAP(err); diff --git a/src/core/cm/launcher/nodemanager.hpp b/src/core/cm/launcher/nodemanager.hpp index 83ba69dc5..441fe7384 100644 --- a/src/core/cm/launcher/nodemanager.hpp +++ b/src/core/cm/launcher/nodemanager.hpp @@ -9,10 +9,11 @@ #include #include -#include +#include #include #include "node.hpp" +#include "overrideenvvarsprocessor.hpp" namespace aos::cm::launcher { @@ -28,13 +29,15 @@ class NodeManager { /** * Initializes node manager. * + * @param allocator allocator to use for temporary objects. * @param nodeInfoProvider node info provider. * @param nodeConfigProvider node config provider. - * @param storageState storage state interface. * @param runner instance runner interface. + * @param overrideEnvVarsProcessor override env vars processor. */ - void Init(nodeinfoprovider::NodeInfoProviderItf& nodeInfoProvider, - unitconfig::NodeConfigProviderItf& nodeConfigProvider, InstanceRunnerItf& runner); + void Init(AllocatorItf& allocator, nodeinfoprovider::NodeInfoProviderItf& nodeInfoProvider, + unitconfig::NodeConfigProviderItf& nodeConfigProvider, InstanceRunnerItf& runner, + OverrideEnvVarsProcessor& overrideEnvVarsProcessor); /** * Starts node manager. @@ -142,20 +145,17 @@ class NodeManager { private: static constexpr auto cStatusUpdateTimeout = Time::cMinutes * 10; - static constexpr auto cAllocatorSize - = sizeof(StaticArray, cMaxNumNodes>) + sizeof(UnitNodeInfo); - - static constexpr auto cNodeAllocatorSize = sizeof(StaticArray) * 2; - Error FindImageDescriptor(const String& itemID, const String& version, const String& manifestDigest, ImageInfoProvider& imageInfoProvider, oci::IndexContentDescriptor& imageDescriptor); + Error ApplyOverrideEnvVars(const Array>& instances); + nodeinfoprovider::NodeInfoProviderItf* mNodeInfoProvider {}; unitconfig::NodeConfigProviderItf* mNodeConfigProvider {}; InstanceRunnerItf* mRunner {}; + OverrideEnvVarsProcessor* mOverrideEnvVarsProcessor {}; - StaticAllocator mAllocator; - StaticAllocator mNodeAllocator; + AllocatorItf* mAllocator {}; StaticArray mNodes; diff --git a/src/core/cm/launcher/overrideenvvarsprocessor.cpp b/src/core/cm/launcher/overrideenvvarsprocessor.cpp new file mode 100644 index 000000000..49405428e --- /dev/null +++ b/src/core/cm/launcher/overrideenvvarsprocessor.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2026 EPAM Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "overrideenvvarsprocessor.hpp" + +namespace aos::cm::launcher { + +/*********************************************************************************************************************** + * Public + **********************************************************************************************************************/ + +Error OverrideEnvVarsProcessor::Init( + const Config& config, StorageItf& storage, SenderItf& envVarStatusSender, OverrideEnvVarsListenerItf& listener) +{ + LOG_DBG() << "Init override env vars processor"; + + mCheckPeriod = config.mCheckOverrideEnvVarsPeriod; + mStorage = &storage; + mEnvVarStatusSender = &envVarStatusSender; + mListener = &listener; + + return ErrorEnum::eNone; +} + +RetWithError OverrideEnvVarsProcessor::Start() +{ + LOG_DBG() << "Start override env vars processor"; + + bool changed = false; + Error err; + + { + LockGuard lock {mMutex}; + + if (auto loadErr = mStorage->LoadOverrideEnvVars(mOverrideEnvVars); !loadErr.IsNone()) { + return {false, AOS_ERROR_WRAP(loadErr)}; + } + + // Drop variables that expired while offline. + Tie(changed, err) = ProcessOverrideEnvVars(mOverrideEnvVars); + if (!err.IsNone()) { + return {changed, err}; + } + } + + if (auto timerErr = mTimer.Start( + mCheckPeriod, [this](void*) { OnTTLTimerTick(); }, false); + !timerErr.IsNone()) { + return {changed, AOS_ERROR_WRAP(timerErr)}; + } + + return {changed, ErrorEnum::eNone}; +} + +Error OverrideEnvVarsProcessor::Stop() +{ + LOG_DBG() << "Stop override env vars processor"; + + if (auto err = mTimer.Stop(Timer::StopMode::WaitForCallbacks); !err.IsNone()) { + return AOS_ERROR_WRAP(err); + } + + return ErrorEnum::eNone; +} + +RetWithError OverrideEnvVarsProcessor::OverrideEnvVars(const OverrideEnvVarsRequest& envVars) +{ + bool changed = false; + Error err; + + { + LockGuard lock {mMutex}; + + Tie(changed, err) = ProcessOverrideEnvVars(envVars); + } + + // Notify outside mMutex to avoid lock order inversion. + if (changed && err.IsNone()) { + mListener->OnOverrideEnvVarsChanged(); + } + + return {changed, err}; +} + +Error OverrideEnvVarsProcessor::AddStatuses(const Array& statuses) +{ + LockGuard lock {mMutex}; + + for (const auto& status : statuses) { + if (status.mEnvVarsStatuses.IsEmpty()) { + continue; + } + + if (auto err = mNewEnvVarStatuses.mStatuses.EmplaceBack(); !err.IsNone()) { + return AOS_ERROR_WRAP(err); + } + + auto& item = mNewEnvVarStatuses.mStatuses.Back(); + + static_cast(item) = static_cast(status); + item.mStatuses = status.mEnvVarsStatuses; + } + + return ErrorEnum::eNone; +} + +void OverrideEnvVarsProcessor::SendStatuses() +{ + LockGuard lock {mMutex}; + + if (mNewEnvVarStatuses != mEnvVarStatuses) { + if (auto err = mEnvVarStatusSender->SendOverrideEnvsStatuses(mNewEnvVarStatuses); !err.IsNone()) { + LOG_ERR() << "Can't send override env vars statuses" << Log::Field(AOS_ERROR_WRAP(err)); + } + } + + mEnvVarStatuses = mNewEnvVarStatuses; + mNewEnvVarStatuses.mStatuses.Clear(); +} + +/*********************************************************************************************************************** + * Private + **********************************************************************************************************************/ + +void OverrideEnvVarsProcessor::OnTTLTimerTick() +{ + if (auto [_, err] = OverrideEnvVars(mOverrideEnvVars); !err.IsNone()) { + LOG_ERR() << "Update override env vars failed" << Log::Field(err); + } +} + +RetWithError OverrideEnvVarsProcessor::ProcessOverrideEnvVars(const OverrideEnvVarsRequest& envVars) +{ + auto now = Time::Now(); + bool changed = mOverrideEnvVars.mItems != envVars.mItems || HasExpiredVariables(envVars, now); + + LOG_DBG() << "Process override env vars" << Log::Field("changed", changed) + << Log::Field("count", envVars.mItems.Size()); + + if (changed) { + // envVars may alias mOverrideEnvVars on TTL recheck. + if (&mOverrideEnvVars != &envVars) { + mOverrideEnvVars = envVars; + } + + RemoveExpiredVariables(mOverrideEnvVars, now); + + if (auto err = mStorage->SaveOverrideEnvVars(mOverrideEnvVars); !err.IsNone()) { + return {changed, AOS_ERROR_WRAP(err)}; + } + } + + return {changed, ErrorEnum::eNone}; +} + +bool OverrideEnvVarsProcessor::HasExpiredVariables(const OverrideEnvVarsRequest& envVars, const Time& now) +{ + for (const auto& item : envVars.mItems) { + for (const auto& envVar : item.mVariables) { + if (envVar.mTTL.HasValue() && envVar.mTTL.GetValue() < now) { + return true; + } + } + } + + return false; +} + +void OverrideEnvVarsProcessor::RemoveExpiredVariables(OverrideEnvVarsRequest& envVars, const Time& now) +{ + for (auto& item : envVars.mItems) { + item.mVariables.RemoveIf([&now](const EnvVarInfo& envVarInfo) { + return envVarInfo.mTTL.HasValue() && envVarInfo.mTTL.GetValue() < now; + }); + } + + envVars.mItems.RemoveIf([](const EnvVarsInstanceInfo& item) { return item.mVariables.IsEmpty(); }); +} + +} // namespace aos::cm::launcher diff --git a/src/core/cm/launcher/overrideenvvarsprocessor.hpp b/src/core/cm/launcher/overrideenvvarsprocessor.hpp new file mode 100644 index 000000000..b48cbebe7 --- /dev/null +++ b/src/core/cm/launcher/overrideenvvarsprocessor.hpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2026 EPAM Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef AOS_CORE_CM_LAUNCHER_OVERRIDEENVVARSPROCESSOR_HPP_ +#define AOS_CORE_CM_LAUNCHER_OVERRIDEENVVARSPROCESSOR_HPP_ + +#include +#include +#include +#include + +#include "itf/sender.hpp" +#include "itf/storage.hpp" + +#include "config.hpp" + +namespace aos::cm::launcher { + +/** @addtogroup cm Communication Manager + * @{ + */ + +/** + * Override environment variables listener interface. + */ +class OverrideEnvVarsListenerItf { +public: + /** + * Destructor. + */ + virtual ~OverrideEnvVarsListenerItf() = default; + + /** + * Notifies that the override env vars has changed (new request or TTL expiry). + */ + virtual void OnOverrideEnvVarsChanged() = 0; +}; + +/** + * RAII accessor that keeps the processor mutex locked while the override env vars are accessed. + */ +class OverrideEnvVarsAccessor { +public: + /** + * Locks the mutex and binds the override env vars for the accessor lifetime. + * + * @param mutex processor mutex. + * @param envVars override environment variables. + */ + OverrideEnvVarsAccessor(Mutex& mutex, const OverrideEnvVarsRequest& envVars) + : mLock(mutex) + , mEnvVars(envVars) + { + } + + /** + * Returns the locked override environment variables. + * + * @return const OverrideEnvVarsRequest&. + */ + const OverrideEnvVarsRequest& operator*() const { return mEnvVars; } + +private: + LockGuard mLock; + const OverrideEnvVarsRequest& mEnvVars; +}; + +/** + * Processes override environment variables requests. + */ +class OverrideEnvVarsProcessor { +public: + /** + * Initializes override env vars processor. + * + * @param config launcher configuration. + * @param storage storage interface. + * @param envVarStatusSender override env vars status sender interface. + * @param listener override env vars change listener. + * @return Error. + */ + Error Init( + const Config& config, StorageItf& storage, SenderItf& envVarStatusSender, OverrideEnvVarsListenerItf& listener); + + /** + * Starts override env vars processor. + * + * The change is reported via the return value instead of the listener: on start the caller holds the launcher + * mutex (which the listener also takes, so notifying would deadlock) and the worker thread is not running yet. + * + * @return RetWithError true if the restored override set changed due to expired variables. + */ + RetWithError Start(); + + /** + * Stops override env vars processor. + * + * @return Error. + */ + Error Stop(); + + /** + * Overrides environment variables. + * + * @param envVars requested override environment variables. + * @return RetWithError true if the override env vars changed. + */ + RetWithError OverrideEnvVars(const OverrideEnvVarsRequest& envVars); + + /** + * Returns an RAII accessor that keeps the mutex locked while the current override env vars are read. + * + * @return OverrideEnvVarsAccessor. + */ + OverrideEnvVarsAccessor GetOverrideEnvVars() { return OverrideEnvVarsAccessor(mMutex, mOverrideEnvVars); } + + /** + * Adds instance env var statuses to the new instance pool. + * + * @param statuses instance statuses. + * @return Error. + */ + Error AddStatuses(const Array& statuses); + + /** + * Sends the accumulated env var statuses to the listener if they changed since the last send. + */ + void SendStatuses(); + +private: + static void RemoveExpiredVariables(OverrideEnvVarsRequest& envVars, const Time& now); + static bool HasExpiredVariables(const OverrideEnvVarsRequest& envVars, const Time& now); + + void OnTTLTimerTick(); + RetWithError ProcessOverrideEnvVars(const OverrideEnvVarsRequest& envVars); + + Duration mCheckPeriod {}; + StorageItf* mStorage {}; + SenderItf* mEnvVarStatusSender {}; + OverrideEnvVarsListenerItf* mListener {}; + + Mutex mMutex; + Timer mTimer; + + OverrideEnvVarsRequest mOverrideEnvVars; + OverrideEnvVarsStatuses mEnvVarStatuses; + OverrideEnvVarsStatuses mNewEnvVarStatuses; +}; + +/** @}*/ + +} // namespace aos::cm::launcher + +#endif diff --git a/src/core/cm/launcher/runrequestsloader.cpp b/src/core/cm/launcher/runrequestsloader.cpp index cf865001c..49752ac24 100644 --- a/src/core/cm/launcher/runrequestsloader.cpp +++ b/src/core/cm/launcher/runrequestsloader.cpp @@ -10,9 +10,10 @@ namespace aos::cm::launcher { -void RunRequestsLoader::Init( - StorageItf& storage, InstanceManager& instanceManager, ImageInfoProvider& imageInfoProvider) +void RunRequestsLoader::Init(AllocatorItf& allocator, StorageItf& storage, InstanceManager& instanceManager, + ImageInfoProvider& imageInfoProvider) { + mAllocator = &allocator; mStorage = &storage; mInstanceManager = &instanceManager; mImageInfoProvider = &imageInfoProvider; @@ -88,14 +89,24 @@ void RunRequestsLoader::CreateInstances(const Array& nodes, Array& nodes, Array>& instances) { - auto imageIndex = MakeUnique(&mAllocator); + auto imageIndex = MakeUnique(mAllocator); + if (!imageIndex) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = mImageInfoProvider->GetImageIndex(request.mItemID, request.mVersion, *imageIndex); !err.IsNone()) { return AOS_ERROR_WRAP(err); } - auto combinedRuntimes = MakeUnique(&mAllocator); - auto itemConfig = MakeUnique(&mAllocator); + auto combinedRuntimes = MakeUnique(mAllocator); + if (!combinedRuntimes) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } + + auto itemConfig = MakeUnique(mAllocator); + if (!itemConfig) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = CombinedRuntimes(*imageIndex, *combinedRuntimes, *itemConfig); !err.IsNone()) { return AOS_ERROR_WRAP(err); diff --git a/src/core/cm/launcher/runrequestsloader.hpp b/src/core/cm/launcher/runrequestsloader.hpp index 5211fe372..56b3cd4b2 100644 --- a/src/core/cm/launcher/runrequestsloader.hpp +++ b/src/core/cm/launcher/runrequestsloader.hpp @@ -7,6 +7,8 @@ #ifndef AOS_CORE_CM_LAUNCHER_RUNREQUESTSLOADER_HPP_ #define AOS_CORE_CM_LAUNCHER_RUNREQUESTSLOADER_HPP_ +#include + #include "instancemanager.hpp" #include "itf/storage.hpp" #include "node.hpp" @@ -21,11 +23,13 @@ class RunRequestsLoader { /** * Initializes the loader. * + * @param allocator allocator to use for temporary objects. * @param storage storage interface. * @param instanceManager instance manager. * @param imageInfoProvider image info provider. */ - void Init(StorageItf& storage, InstanceManager& instanceManager, ImageInfoProvider& imageInfoProvider); + void Init(AllocatorItf& allocator, StorageItf& storage, InstanceManager& instanceManager, + ImageInfoProvider& imageInfoProvider); /** * Saves run requests to internal buffer and to storage. @@ -55,9 +59,6 @@ class RunRequestsLoader { using CombinedRuntimesArray = StaticArray, oci::cMaxNumManifests * oci::cMaxNumRunners>; - static constexpr auto cCombinedRuntimesSize = sizeof(CombinedRuntimesArray); - static constexpr auto cAllocatorSize = sizeof(oci::ImageIndex) + cCombinedRuntimesSize + sizeof(oci::ItemConfig); - Error GenerateInstances( const RunInstanceRequest& request, const Array& nodes, Array>& instances); Error CombinedRuntimes( @@ -71,7 +72,7 @@ class RunRequestsLoader { StaticArray mRunRequests; - StaticAllocator mAllocator; + AllocatorItf* mAllocator {}; }; } // namespace aos::cm::launcher diff --git a/src/core/cm/launcher/storagestate.cpp b/src/core/cm/launcher/storagestate.cpp index a3f1f259f..f19ef0037 100644 --- a/src/core/cm/launcher/storagestate.cpp +++ b/src/core/cm/launcher/storagestate.cpp @@ -14,8 +14,9 @@ namespace aos::cm::launcher { * Public **********************************************************************************************************************/ -void StorageState::Init(storagestate::StorageStateItf& storageState) +void StorageState::Init(AllocatorItf& allocator, storagestate::StorageStateItf& storageState) { + mAllocator = &allocator; mStorageStateManager = &storageState; } @@ -38,10 +39,14 @@ Error StorageState::PrepareForBalancing() mAvailableStorage.Reset(); if (mStorageStateManager->IsSamePartition()) { - mAvailableState = mAvailableStorage = MakeShared(&mAllocator, 0); + mAvailableState = mAvailableStorage = MakeShared(mAllocator, 0); } else { - mAvailableState = MakeShared(&mAllocator, 0); - mAvailableStorage = MakeShared(&mAllocator, 0); + mAvailableState = MakeShared(mAllocator, 0); + mAvailableStorage = MakeShared(mAllocator, 0); + } + + if (!mAvailableState || !mAvailableStorage) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); } const auto& [stateSize, stateErr] = mStorageStateManager->GetTotalStateSize(); diff --git a/src/core/cm/launcher/storagestate.hpp b/src/core/cm/launcher/storagestate.hpp index 70556a094..dec07d82c 100644 --- a/src/core/cm/launcher/storagestate.hpp +++ b/src/core/cm/launcher/storagestate.hpp @@ -8,7 +8,7 @@ #define AOS_CORE_CM_LAUNCHER_STORAGESTATE_HPP_ #include -#include +#include namespace aos::cm::launcher { @@ -21,9 +21,10 @@ class StorageState { /** * Initializes storage state. * + * @param allocator allocator to use for temporary objects. * @param storageState storage state interface. */ - void Init(storagestate::StorageStateItf& storageState); + void Init(AllocatorItf& allocator, storagestate::StorageStateItf& storageState); /** * Starts storage state. @@ -77,13 +78,11 @@ class StorageState { size_t requestedStorageSize, size_t requestedStateSize, String& storagePath, String& statePath); private: - static constexpr auto cAllocatorSize = sizeof(size_t) * 2; - storagestate::StorageStateItf* mStorageStateManager {}; - StaticAllocator mAllocator; - SharedPtr mAvailableState; - SharedPtr mAvailableStorage; + AllocatorItf* mAllocator {}; + SharedPtr mAvailableState; + SharedPtr mAvailableStorage; }; } // namespace aos::cm::launcher diff --git a/src/core/cm/launcher/tests/launcher.cpp b/src/core/cm/launcher/tests/launcher.cpp index 219ca4cd9..6ddefae17 100644 --- a/src/core/cm/launcher/tests/launcher.cpp +++ b/src/core/cm/launcher/tests/launcher.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "stubs/alertsproviderstub.hpp" #include "stubs/identproviderstub.hpp" @@ -26,6 +27,7 @@ #include "stubs/monitoringproviderstub.hpp" #include "stubs/nodeinfoproviderstub.hpp" #include "stubs/resourcemanagerstub.hpp" +#include "stubs/senderstub.hpp" #include "stubs/storagestatestub.hpp" #include "stubs/storagestub.hpp" @@ -142,6 +144,9 @@ class CMLauncherTest : public testing::Test { return mImageStore.GetManifestDigest(id.c_str(), imageID.c_str()); } + // Allocator must be declared before any object allocated from it to ensure correct destruction order. + HeapAllocator mAllocator; + // Stub objects alerts::AlertsProviderStub mAlertsProvider; imagemanager::ImageStoreStub mImageStore; @@ -153,6 +158,7 @@ class CMLauncherTest : public testing::Test { resourcemanager::ResourceManagerStub mResourceManager; StorageStub mStorage; storagestate::StorageStateStub mStorageState; + SenderStub mSender; Launcher mLauncher; }; @@ -564,6 +570,30 @@ EnvVar CreateEnvVar(const std::string& name, const std::string& value) return var; } +EnvVarsInstanceStatus CreateEnvVarsInstanceStatus(const InstanceIdent& ident, const std::vector& statuses) +{ + EnvVarsInstanceStatus result; + + static_cast(result) = ident; + + for (const auto& status : statuses) { + result.mStatuses.PushBack(status); + } + + return result; +} + +OverrideEnvVarsStatuses CreateOverrideEnvVarsStatuses(const std::vector& statuses) +{ + OverrideEnvVarsStatuses result; + + for (const auto& status : statuses) { + result.mStatuses.PushBack(status); + } + + return result; +} + /*********************************************************************************************************************** * Tests **********************************************************************************************************************/ @@ -580,11 +610,11 @@ TEST_F(CMLauncherTest, InstancesWithInvalidImageAreRemovedOnStart) mInstanceRunner.Init(mLauncher); // Init launcher - ASSERT_TRUE( - mLauncher - .Init(CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, mResourceManager, - mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, ValidateGID, ValidateUID, mStorage) - .IsNone()); + ASSERT_TRUE(mLauncher + .Init(mAllocator, CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, + mResourceManager, mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, + ValidateGID, ValidateUID, mStorage, mSender) + .IsNone()); ASSERT_TRUE(mLauncher.Start().IsNone()); @@ -626,11 +656,11 @@ TEST_F(CMLauncherTest, InstancesWithOutdatedTTLRemovedOnStart) mInstanceRunner.Init(mLauncher); // Init launcher - ASSERT_TRUE( - mLauncher - .Init(CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, mResourceManager, - mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, ValidateGID, ValidateUID, mStorage) - .IsNone()); + ASSERT_TRUE(mLauncher + .Init(mAllocator, CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, + mResourceManager, mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, + ValidateGID, ValidateUID, mStorage, mSender) + .IsNone()); ASSERT_TRUE(mLauncher.Start().IsNone()); @@ -691,11 +721,11 @@ TEST_F(CMLauncherTest, CacheInstances) mInstanceRunner.Init(mLauncher); // Init launcher - ASSERT_TRUE( - mLauncher - .Init(CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, mResourceManager, - mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, ValidateGID, ValidateUID, mStorage) - .IsNone()); + ASSERT_TRUE(mLauncher + .Init(mAllocator, CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, + mResourceManager, mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, + ValidateGID, ValidateUID, mStorage, mSender) + .IsNone()); ASSERT_TRUE(mLauncher.Start().IsNone()); @@ -789,11 +819,11 @@ TEST_F(CMLauncherTest, Components) mInstanceRunner.Init(mLauncher); // Init launcher - ASSERT_TRUE( - mLauncher - .Init(CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, mResourceManager, - mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, ValidateGID, ValidateUID, mStorage) - .IsNone()); + ASSERT_TRUE(mLauncher + .Init(mAllocator, CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, + mResourceManager, mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, + ValidateGID, ValidateUID, mStorage, mSender) + .IsNone()); ASSERT_TRUE(mLauncher.Start().IsNone()); @@ -1479,9 +1509,9 @@ TEST_F(CMLauncherTest, Balancing) // Init launcher ASSERT_TRUE(mLauncher - .Init(CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, + .Init(mAllocator, CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, mResourceManager, mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, - ValidateGID, ValidateUID, mStorage) + ValidateGID, ValidateUID, mStorage, mSender) .IsNone()); InstanceStatusListenerStub instanceStatusListener; @@ -1583,11 +1613,11 @@ TEST_F(CMLauncherTest, PlatformFiltering) AddItem(cService3, cImageID1, *itemConfig3, CreateImageConfig("x86_64", "generic", "linux", "5.4.0", "feature1")); // Init launcher - ASSERT_TRUE( - mLauncher - .Init(CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, mResourceManager, - mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, ValidateGID, ValidateUID, mStorage) - .IsNone()); + ASSERT_TRUE(mLauncher + .Init(mAllocator, CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, + mResourceManager, mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, + ValidateGID, ValidateUID, mStorage, mSender) + .IsNone()); ASSERT_TRUE(mLauncher.Start().IsNone()); @@ -1676,11 +1706,11 @@ TEST_F(CMLauncherTest, ResendInstancesOnMismatchedNodeStatus) }); // Init launcher - ASSERT_TRUE( - mLauncher - .Init(CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, mResourceManager, - mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, ValidateGID, ValidateUID, mStorage) - .IsNone()); + ASSERT_TRUE(mLauncher + .Init(mAllocator, CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, + mResourceManager, mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, + ValidateGID, ValidateUID, mStorage, mSender) + .IsNone()); ASSERT_TRUE(mLauncher.Start().IsNone()); @@ -1751,11 +1781,11 @@ TEST_F(CMLauncherTest, SubjectChanged) mInstanceRunner.Init(mLauncher); // Init launcher - ASSERT_TRUE( - mLauncher - .Init(CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, mResourceManager, - mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, ValidateGID, ValidateUID, mStorage) - .IsNone()); + ASSERT_TRUE(mLauncher + .Init(mAllocator, CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, + mResourceManager, mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, + ValidateGID, ValidateUID, mStorage, mSender) + .IsNone()); InstanceStatusListenerStub instanceStatusListener; mLauncher.SubscribeListener(instanceStatusListener); @@ -1824,11 +1854,11 @@ TEST_F(CMLauncherTest, TestSentInstanceInfo) mInstanceRunner.Init(mLauncher, true, aos::InstanceStateEnum::eActive); // Init launcher - ASSERT_TRUE( - mLauncher - .Init(CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, mResourceManager, - mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, ValidateGID, ValidateUID, mStorage) - .IsNone()); + ASSERT_TRUE(mLauncher + .Init(mAllocator, CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, + mResourceManager, mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, + ValidateGID, ValidateUID, mStorage, mSender) + .IsNone()); ASSERT_TRUE(mLauncher.Start().IsNone()); @@ -1910,11 +1940,11 @@ TEST_F(CMLauncherTest, PreinstalledComponents) mInstanceRunner.SetPreinstalledComponents({preinstalledStatus}); // Init launcher - ASSERT_TRUE( - mLauncher - .Init(CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, mResourceManager, - mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, ValidateGID, ValidateUID, mStorage) - .IsNone()); + ASSERT_TRUE(mLauncher + .Init(mAllocator, CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, + mResourceManager, mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, + ValidateGID, ValidateUID, mStorage, mSender) + .IsNone()); ASSERT_TRUE(mLauncher.Start().IsNone()); @@ -1988,7 +2018,7 @@ TEST_F(CMLauncherTest, SetStatusOnStart) auto manifestDigest = BuildManifestDigest(cService1, cImageID1); auto instance1 = CreateInstanceInfo(CreateInstanceIdent(cService1, cSubject1, 0), manifestDigest, cRunnerRunc, cNodeIDLocalSM, - InstanceStateEnum::eActive, 5001, 0, Time::Now(), "1.0.0", false, "", SubjectTypeEnum::eGroup, 100); + InstanceStateEnum::eActive, 5000, 0, Time::Now(), "1.0.0", false, "", SubjectTypeEnum::eGroup, 100); auto instance2 = CreateInstanceInfo(CreateInstanceIdent(cService1, cSubject1, 1), manifestDigest, cRunnerRunc, cNodeIDLocalSM, @@ -1998,11 +2028,11 @@ TEST_F(CMLauncherTest, SetStatusOnStart) ASSERT_TRUE(mStorage.AddInstance(instance2).IsNone()); // Init launcher - ASSERT_TRUE( - mLauncher - .Init(CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, mResourceManager, - mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, ValidateGID, ValidateUID, mStorage) - .IsNone()); + ASSERT_TRUE(mLauncher + .Init(mAllocator, CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, + mResourceManager, mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, + ValidateGID, ValidateUID, mStorage, mSender) + .IsNone()); InstanceStatusListenerStub instanceStatusListener; mLauncher.SubscribeListener(instanceStatusListener); @@ -2065,11 +2095,11 @@ TEST_F(CMLauncherTest, OverrideEnvVars) mInstanceRunner.Init(mLauncher, true, aos::InstanceStateEnum::eActive); // Init launcher - ASSERT_TRUE( - mLauncher - .Init(CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, mResourceManager, - mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, ValidateGID, ValidateUID, mStorage) - .IsNone()); + ASSERT_TRUE(mLauncher + .Init(mAllocator, CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, + mResourceManager, mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, + ValidateGID, ValidateUID, mStorage, mSender) + .IsNone()); InstanceStatusListenerStub instanceStatusListener; mLauncher.SubscribeListener(instanceStatusListener); @@ -2133,6 +2163,14 @@ TEST_F(CMLauncherTest, OverrideEnvVars) EXPECT_EQ(mInstanceRunner.GetRunRequests(), expectedRunRequests); + // 5) Check override env vars statuses are reported back to the sender. + ASSERT_TRUE(mSender.WaitForSendCount(1, 2s)); + + auto expectedInstanceStatus = CreateEnvVarsInstanceStatus(CreateInstanceIdent(cService1, cSubject1, 0), + {EnvVarStatus {"OVERRIDE_VAR2", ErrorEnum::eNone}, EnvVarStatus {"OVERRIDE_VAR3", ErrorEnum::eNone}}); + + EXPECT_EQ(mSender.GetOverrideEnvVarsStatuses(), CreateOverrideEnvVarsStatuses({expectedInstanceStatus})); + ASSERT_TRUE(mLauncher.Stop().IsNone()); } @@ -2180,11 +2218,11 @@ TEST_F(CMLauncherTest, MultiNodeInstance) AddItem(cComponent1, cImageID1, *componentConfig, CreateImageConfig()); // Init launcher - ASSERT_TRUE( - mLauncher - .Init(CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, mResourceManager, - mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, ValidateGID, ValidateUID, mStorage) - .IsNone()); + ASSERT_TRUE(mLauncher + .Init(mAllocator, CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, + mResourceManager, mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, + ValidateGID, ValidateUID, mStorage, mSender) + .IsNone()); ASSERT_TRUE(mLauncher.Start().IsNone()); @@ -2310,11 +2348,11 @@ TEST_F(CMLauncherTest, RebalancingWithStoredNotScheduledInstances) mMonitoringProvider.SetAverageMonitoring(cNodeIDRemoteSM1, *remoteMonitoring); // Init launcher. - ASSERT_TRUE( - mLauncher - .Init(CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, mResourceManager, - mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, ValidateGID, ValidateUID, mStorage) - .IsNone()); + ASSERT_TRUE(mLauncher + .Init(mAllocator, CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, + mResourceManager, mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, + ValidateGID, ValidateUID, mStorage, mSender) + .IsNone()); InstanceStatusListenerStub instanceStatusListener; mLauncher.SubscribeListener(instanceStatusListener); @@ -2407,11 +2445,11 @@ TEST_F(CMLauncherTest, CpuAlertRebalancingMovesLowerPriorityService) } // Init launcher. - ASSERT_TRUE( - mLauncher - .Init(CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, mResourceManager, - mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, ValidateGID, ValidateUID, mStorage) - .IsNone()); + ASSERT_TRUE(mLauncher + .Init(mAllocator, CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, + mResourceManager, mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, + ValidateGID, ValidateUID, mStorage, mSender) + .IsNone()); InstanceStatusListenerStub instanceStatusListener; mLauncher.SubscribeListener(instanceStatusListener); @@ -2511,11 +2549,11 @@ TEST_F(CMLauncherTest, ServiceUpdate) mInstanceRunner.Init(mLauncher, true, aos::InstanceStateEnum::eActive); // Init launcher - ASSERT_TRUE( - mLauncher - .Init(CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, mResourceManager, - mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, ValidateGID, ValidateUID, mStorage) - .IsNone()); + ASSERT_TRUE(mLauncher + .Init(mAllocator, CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, + mResourceManager, mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, + ValidateGID, ValidateUID, mStorage, mSender) + .IsNone()); InstanceStatusListenerStub instanceStatusListener; mLauncher.SubscribeListener(instanceStatusListener); @@ -2552,7 +2590,7 @@ TEST_F(CMLauncherTest, ServiceUpdate) EXPECT_TRUE(instanceStatusListener.WaitForNotifyCount(2, 2s)); // New version replaces the old one on the node: SM is told to stop 1.0.0 instances, then start 1.0.1. - // Cached v1.0.0 instances still hold the first UID pool allocation; v1.0.1 gets the next UIDs and IPs. + // Cached v1.0.0 instances still hold UID pool references; v1.0.1 reuses the same UIDs per InstanceIdent. auto stopV100Inst0 = CreateAosStopInstanceInfo(CreateInstanceIdent(cService1, cSubject1, 0), cRunnerRunc); stopV100Inst0.mVersion = "1.0.0"; auto stopV100Inst1 = CreateAosStopInstanceInfo(CreateInstanceIdent(cService1, cSubject1, 1), cRunnerRunc); @@ -2561,9 +2599,9 @@ TEST_F(CMLauncherTest, ServiceUpdate) std::map expectedAfterV101 = {{cNodeIDLocalSM, {{stopV100Inst0, stopV100Inst1}, {CreateServiceRunInfo( - CreateInstanceIdent(cService1, cSubject1, 0), cImageID1, cRunnerRunc, 5002, 5000, 50, "1.0.1"), + CreateInstanceIdent(cService1, cSubject1, 0), cImageID1, cRunnerRunc, 5000, 5000, 50, "1.0.1"), CreateServiceRunInfo( - CreateInstanceIdent(cService1, cSubject1, 1), cImageID1, cRunnerRunc, 5003, 5000, 50, "1.0.1")}}}}; + CreateInstanceIdent(cService1, cSubject1, 1), cImageID1, cRunnerRunc, 5001, 5000, 50, "1.0.1")}}}}; EXPECT_EQ(mInstanceRunner.GetRunRequests(), expectedAfterV101); @@ -2611,11 +2649,11 @@ TEST_F(CMLauncherTest, UnlimitedSharedResource) mInstanceRunner.Init(mLauncher, true, aos::InstanceStateEnum::eActive); - ASSERT_TRUE( - mLauncher - .Init(CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, mResourceManager, - mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, ValidateGID, ValidateUID, mStorage) - .IsNone()); + ASSERT_TRUE(mLauncher + .Init(mAllocator, CreateConfig(), mNodeInfoProvider, mInstanceRunner, mImageStore, mImageStore, + mResourceManager, mStorageState, mMonitoringProvider, mAlertsProvider, mIdentProvider, + ValidateGID, ValidateUID, mStorage, mSender) + .IsNone()); ASSERT_TRUE(mLauncher.Start().IsNone()); diff --git a/src/core/cm/launcher/tests/stubs/instancerunnerstub.hpp b/src/core/cm/launcher/tests/stubs/instancerunnerstub.hpp index a9defbb52..80d3d1fa2 100644 --- a/src/core/cm/launcher/tests/stubs/instancerunnerstub.hpp +++ b/src/core/cm/launcher/tests/stubs/instancerunnerstub.hpp @@ -152,7 +152,10 @@ class InstanceRunnerStub : public InstanceRunnerItf { envVarStatus.mName = envVar.mName; envVarStatus.mError = ErrorEnum::eNone; - assert(envVarsStatuses.PushBack(envVarStatus).IsNone()); + + auto err = envVarsStatuses.PushBack(envVarStatus); + assert(err.IsNone()); + (void)err; } } diff --git a/src/core/cm/launcher/tests/stubs/senderstub.hpp b/src/core/cm/launcher/tests/stubs/senderstub.hpp new file mode 100644 index 000000000..920d2f094 --- /dev/null +++ b/src/core/cm/launcher/tests/stubs/senderstub.hpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2026 EPAM Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef AOS_CM_LAUNCHER_STUBS_SENDERSTUB_HPP_ +#define AOS_CM_LAUNCHER_STUBS_SENDERSTUB_HPP_ + +#include +#include +#include + +#include + +namespace aos::cm::launcher { + +class SenderStub : public SenderItf { +public: + Error SendOverrideEnvsStatuses(const OverrideEnvVarsStatuses& statuses) override + { + std::lock_guard lock {mMutex}; + + mStatuses = statuses; + ++mSendCount; + + mCondVar.notify_all(); + + return ErrorEnum::eNone; + } + + OverrideEnvVarsStatuses GetOverrideEnvVarsStatuses() const + { + std::lock_guard lock {mMutex}; + + return mStatuses; + } + + bool WaitForSendCount(size_t expectedCount, std::chrono::milliseconds timeout) const + { + std::unique_lock lock {mMutex}; + + return mCondVar.wait_for(lock, timeout, [&]() { return mSendCount >= expectedCount; }); + } + +private: + mutable std::mutex mMutex; + mutable std::condition_variable mCondVar; + OverrideEnvVarsStatuses mStatuses; + size_t mSendCount {}; +}; + +} // namespace aos::cm::launcher + +#endif diff --git a/src/core/cm/launcher/utils.hpp b/src/core/cm/launcher/utils.hpp new file mode 100644 index 000000000..65acd5b15 --- /dev/null +++ b/src/core/cm/launcher/utils.hpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2026 EPAM Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef AOS_CORE_CM_LAUNCHER_UTILS_HPP_ +#define AOS_CORE_CM_LAUNCHER_UTILS_HPP_ + +#include + +namespace aos::cm::launcher { + +template +class FilterIterator { +public: + FilterIterator(It it, It end, Cmp cmp) + : mIt(it) + , mEnd(end) + , mCmp(cmp) + { + while (mIt != mEnd && !mCmp(*mIt)) { + ++mIt; + } + } + + FilterIterator& operator++() + { + assert(mIt != mEnd); + + ++mIt; + + while (mIt != mEnd && !mCmp(*mIt)) { + ++mIt; + } + + return *this; + } + + FilterIterator operator++(int) + { + assert(mIt != mEnd); + + FilterIterator tmp = *this; + + ++(*this); + + return tmp; + } + + bool operator==(const FilterIterator& other) const { return mIt == other.mIt; } + bool operator!=(const FilterIterator& other) const { return mIt != other.mIt; } + + auto& operator*() const { return *mIt; } + auto operator->() const { return mIt; } + +private: + It mIt; + It mEnd; + Cmp mCmp; +}; + +template +class Filter { +public: + Filter(It begin, It end, Cmp cmp) + : mBegin(begin) + , mEnd(end) + , mCmp(cmp) + { + } + + template + Filter(Array& array, Cmp cmp) + : Filter(array.begin(), array.end(), cmp) + { + } + + template + Filter(const Array& array, Cmp cmp) + : Filter(array.begin(), array.end(), cmp) + { + } + + FilterIterator begin() const { return FilterIterator(mBegin, mEnd, mCmp); } + FilterIterator end() const { return FilterIterator(mEnd, mEnd, mCmp); } + +private: + It mBegin; + It mEnd; + Cmp mCmp; +}; + +template +Filter(Array&, Cmp) -> Filter::Iterator, Cmp>; + +template +Filter(const Array&, Cmp) -> Filter::ConstIterator, Cmp>; + +} // namespace aos::cm::launcher + +#endif diff --git a/src/core/cm/monitoring/monitoring.cpp b/src/core/cm/monitoring/monitoring.cpp index d3220b9b4..f6be59c77 100644 --- a/src/core/cm/monitoring/monitoring.cpp +++ b/src/core/cm/monitoring/monitoring.cpp @@ -97,7 +97,7 @@ Error Monitoring::Stop() mIsRunning = false; - return mSendTimer.Stop(); + return mSendTimer.Stop(Timer::StopMode::WaitForCallbacks); } Error Monitoring::OnMonitoringReceived(const aos::monitoring::NodeMonitoringData& monitoring) diff --git a/src/core/cm/nodeinfoprovider/nodeinfoprovider.cpp b/src/core/cm/nodeinfoprovider/nodeinfoprovider.cpp index e29e2cd6d..4acbda10f 100644 --- a/src/core/cm/nodeinfoprovider/nodeinfoprovider.cpp +++ b/src/core/cm/nodeinfoprovider/nodeinfoprovider.cpp @@ -14,10 +14,12 @@ namespace aos::cm::nodeinfoprovider { * Public **********************************************************************************************************************/ -Error NodeInfoProvider::Init(const Config& config, iamclient::NodeInfoProviderItf& nodeInfoProvider) +Error NodeInfoProvider::Init( + AllocatorItf& allocator, const Config& config, iamclient::NodeInfoProviderItf& nodeInfoProvider) { LOG_DBG() << "Init node info provider"; + mAllocator = &allocator; mNodeInfoProvider = &nodeInfoProvider; mConfig = config; @@ -35,14 +37,20 @@ Error NodeInfoProvider::Start() return ErrorEnum::eWrongState; } - auto ids = MakeUnique, cMaxNumNodes>>(&mAllocator); + auto ids = MakeUnique, cMaxNumNodes>>(mAllocator); + if (!ids) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = mNodeInfoProvider->GetAllNodeIDs(*ids); !err.IsNone()) { return err; } for (const auto& id : *ids) { - auto nodeInfo = MakeUnique(&mAllocator); + auto nodeInfo = MakeUnique(mAllocator); + if (!nodeInfo) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = mNodeInfoProvider->GetNodeInfo(id, *nodeInfo); !err.IsNone()) { return AOS_ERROR_WRAP(err); @@ -249,7 +257,12 @@ NodeInfoCache* NodeInfoProvider::AddOrGetCacheItem(const String& nodeID) void NodeInfoProvider::NotifyListeners(const NodeInfoCache& info) { - auto unitNodeInfo = MakeUnique(&mAllocator); + auto unitNodeInfo = MakeUnique(mAllocator); + if (!unitNodeInfo) { + LOG_ERR() << "Can't allocate unit node info" << Log::Field(ErrorEnum::eNoMemory); + + return; + } info.GetUnitNodeInfo(*unitNodeInfo); diff --git a/src/core/cm/nodeinfoprovider/nodeinfoprovider.hpp b/src/core/cm/nodeinfoprovider/nodeinfoprovider.hpp index 4f869b49f..e30ae6347 100644 --- a/src/core/cm/nodeinfoprovider/nodeinfoprovider.hpp +++ b/src/core/cm/nodeinfoprovider/nodeinfoprovider.hpp @@ -7,7 +7,6 @@ #ifndef AOS_CORE_CM_NODEINFOPROVIDER_NODEINFOPROVIDER_HPP_ #define AOS_CORE_CM_NODEINFOPROVIDER_NODEINFOPROVIDER_HPP_ -#include #include #include #include @@ -34,11 +33,12 @@ class NodeInfoProvider : public NodeInfoProviderItf, public SMInfoReceiverItf, p /** * Initializes node info provider. * + * @param allocator allocator to use for temporary objects. * @param config configuration. * @param nodeInfoProvider IAM client node info provider. * @return Error. */ - Error Init(const Config& config, iamclient::NodeInfoProviderItf& nodeInfoProvider); + Error Init(AllocatorItf& allocator, const Config& config, iamclient::NodeInfoProviderItf& nodeInfoProvider); /** * Starts node info provider. @@ -112,8 +112,6 @@ class NodeInfoProvider : public NodeInfoProviderItf, public SMInfoReceiverItf, p private: static constexpr auto cListenersSize = 4; - static constexpr auto cAllocatorSize - = sizeof(UnitNodeInfo) + sizeof(StaticArray, cMaxNumNodes>); void OnNodeInfoChanged(const NodeInfo& info) override; @@ -124,7 +122,7 @@ class NodeInfoProvider : public NodeInfoProviderItf, public SMInfoReceiverItf, p void Run(); mutable Mutex mMutex; - StaticAllocator mAllocator; + AllocatorItf* mAllocator {}; Thread<> mThread; ConditionalVariable mCondVar; bool mRunning {}; diff --git a/src/core/cm/nodeinfoprovider/tests/nodeinfoprovider.cpp b/src/core/cm/nodeinfoprovider/tests/nodeinfoprovider.cpp index fb24bb6d8..3582188e5 100644 --- a/src/core/cm/nodeinfoprovider/tests/nodeinfoprovider.cpp +++ b/src/core/cm/nodeinfoprovider/tests/nodeinfoprovider.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -93,6 +94,10 @@ class CMNodeInfoProviderTest : public Test { protected: void SetUp() override { tests::utils::InitLog(); } + // mAllocator must be declared (and therefore destroyed) after any member that allocates from it, since + // members are destroyed in reverse declaration order. + HeapAllocator mAllocator; + iamclient::NodeInfoProviderStub mIAMNodeInfoProvider; NodeInfoListenerStub mListener; Config mConfig {Time::cMilliseconds * 100}; @@ -116,7 +121,7 @@ TEST_F(CMNodeInfoProviderTest, KnownInstancesAreProcessedOnStart) mIAMNodeInfoProvider.SetNodeInfo(*node); } - auto err = mNodeInfoProvider.Init(mConfig, mIAMNodeInfoProvider); + auto err = mNodeInfoProvider.Init(mAllocator, mConfig, mIAMNodeInfoProvider); ASSERT_TRUE(err.IsNone()) << tests::utils::ErrorToStr(err); err = mNodeInfoProvider.SubscribeListener(mListener); @@ -155,7 +160,7 @@ TEST_F(CMNodeInfoProviderTest, GetAllNodeIDs) mIAMNodeInfoProvider.SetNodeInfo(*CreateNodeInfo("node1", NodeStateEnum::eProvisioned, true)); mIAMNodeInfoProvider.SetNodeInfo(*CreateNodeInfo("node2", NodeStateEnum::eProvisioned, true)); - err = mNodeInfoProvider.Init(mConfig, mIAMNodeInfoProvider); + err = mNodeInfoProvider.Init(mAllocator, mConfig, mIAMNodeInfoProvider); ASSERT_TRUE(err.IsNone()) << tests::utils::ErrorToStr(err); err = mNodeInfoProvider.Start(); @@ -177,7 +182,7 @@ TEST_F(CMNodeInfoProviderTest, GetNodeInfo) mIAMNodeInfoProvider.SetNodeInfo(*CreateNodeInfo("node1", NodeStateEnum::eProvisioned, true)); mIAMNodeInfoProvider.SetNodeInfo(*CreateNodeInfo("node2", NodeStateEnum::eProvisioned, true)); - auto err = mNodeInfoProvider.Init(Config {Time::cDay}, mIAMNodeInfoProvider); + auto err = mNodeInfoProvider.Init(mAllocator, Config {Time::cDay}, mIAMNodeInfoProvider); ASSERT_TRUE(err.IsNone()) << tests::utils::ErrorToStr(err); err = mNodeInfoProvider.Start(); @@ -208,7 +213,7 @@ TEST_F(CMNodeInfoProviderTest, NodeWithoutSMComponent) { auto nodeInfo = CreateNodeInfo("node1", NodeStateEnum::eProvisioned, true, false); - auto err = mNodeInfoProvider.Init(mConfig, mIAMNodeInfoProvider); + auto err = mNodeInfoProvider.Init(mAllocator, mConfig, mIAMNodeInfoProvider); ASSERT_TRUE(err.IsNone()) << tests::utils::ErrorToStr(err); err = mNodeInfoProvider.Start(); @@ -237,7 +242,7 @@ TEST_F(CMNodeInfoProviderTest, NodeWithSMComponent) CreateNodeInfo("node2", NodeStateEnum::eError, false), }; - auto err = mNodeInfoProvider.Init(mConfig, mIAMNodeInfoProvider); + auto err = mNodeInfoProvider.Init(mAllocator, mConfig, mIAMNodeInfoProvider); ASSERT_TRUE(err.IsNone()) << tests::utils::ErrorToStr(err); err = mNodeInfoProvider.Start(); @@ -300,7 +305,7 @@ TEST_F(CMNodeInfoProviderTest, NodeSetToOnlineIfBothIAMAndSMAreReceived) auto cNodeInfo = CreateNodeInfo("node1", NodeStateEnum::eProvisioned, false); - auto err = mNodeInfoProvider.Init(mConfig, mIAMNodeInfoProvider); + auto err = mNodeInfoProvider.Init(mAllocator, mConfig, mIAMNodeInfoProvider); ASSERT_TRUE(err.IsNone()) << tests::utils::ErrorToStr(err); err = mNodeInfoProvider.Start(); @@ -340,7 +345,7 @@ TEST_F(CMNodeInfoProviderTest, NodeSetToUnprovisionedIfSMNotConnectedButNodeInfo auto cNodeInfo = CreateNodeInfo("node1", NodeStateEnum::eUnprovisioned, true); - auto err = mNodeInfoProvider.Init(mConfig, mIAMNodeInfoProvider); + auto err = mNodeInfoProvider.Init(mAllocator, mConfig, mIAMNodeInfoProvider); ASSERT_TRUE(err.IsNone()) << tests::utils::ErrorToStr(err); err = mNodeInfoProvider.Start(); @@ -367,7 +372,7 @@ TEST_F(CMNodeInfoProviderTest, NotifySubscribersOnceSMInfoReceived) { const auto cNodeInfo = CreateNodeInfo("node1", NodeStateEnum::eProvisioned, false); - auto err = mNodeInfoProvider.Init(mConfig, mIAMNodeInfoProvider); + auto err = mNodeInfoProvider.Init(mAllocator, mConfig, mIAMNodeInfoProvider); ASSERT_TRUE(err.IsNone()) << tests::utils::ErrorToStr(err); err = mNodeInfoProvider.Start(); diff --git a/src/core/cm/storagestate/storagestate.cpp b/src/core/cm/storagestate/storagestate.cpp index c9f43117b..f09670780 100644 --- a/src/core/cm/storagestate/storagestate.cpp +++ b/src/core/cm/storagestate/storagestate.cpp @@ -44,11 +44,12 @@ Error ToRelativePath(const String& base, const String& full, String& result) * Public **********************************************************************************************************************/ -Error StorageState::Init(const Config& config, StorageItf& storage, SenderItf& sender, fs::FSPlatformItf& fsPlatform, - fs::FSWatcherItf& fsWatcher, crypto::HasherItf& hasher) +Error StorageState::Init(AllocatorItf& allocator, const Config& config, StorageItf& storage, SenderItf& sender, + fs::FSPlatformItf& fsPlatform, fs::FSWatcherItf& fsWatcher, crypto::HasherItf& hasher) { LOG_DBG() << "Init storage state"; + mAllocator = &allocator; mConfig = config; mStorage = &storage; mMessageSender = &sender; @@ -109,7 +110,11 @@ Error StorageState::Stop() } } - return mThreadPool.Shutdown(); + if (auto err = mThreadPool.Shutdown(); !err.IsNone()) { + return AOS_ERROR_WRAP(err); + } + + return ErrorEnum::eNone; } Error StorageState::UpdateState(const aos::UpdateState& state) @@ -132,7 +137,10 @@ Error StorageState::UpdateState(const aos::UpdateState& state) return AOS_ERROR_WRAP(err); } - auto storageStateInfo = MakeUnique(&mAllocator); + auto storageStateInfo = MakeUnique(mAllocator); + if (!storageStateInfo) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = mStorage->GetStorageStateInfo(state, *storageStateInfo); !err.IsNone()) { return AOS_ERROR_WRAP(err); @@ -184,7 +192,10 @@ Error StorageState::AcceptState(const StateAcceptance& state) return mMessageSender->SendStateRequest(request); } - auto storageStateInfo = MakeUnique(&mAllocator); + auto storageStateInfo = MakeUnique(mAllocator); + if (!storageStateInfo) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = mStorage->GetStorageStateInfo(state, *storageStateInfo); !err.IsNone()) { return AOS_ERROR_WRAP(err); @@ -208,11 +219,17 @@ Error StorageState::Setup( LOG_DBG() << "Setup storage and state" << setupParams; - auto storageData = MakeUnique(&mAllocator); + auto storageData = MakeUnique(mAllocator); + if (!storageData) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } auto err = mStorage->GetStorageStateInfo(instanceIdent, *storageData); if (err.Is(ErrorEnum::eNotFound)) { - storageData = MakeUnique(&mAllocator); + storageData = MakeUnique(mAllocator); + if (!storageData) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } storageData->mInstanceIdent = instanceIdent; @@ -348,7 +365,10 @@ Error StorageState::InitStateWatching() { LOG_DBG() << "Init state watching"; - auto infos = MakeUnique(&mAllocator); + auto infos = MakeUnique(mAllocator); + if (!infos) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = mStorage->GetAllStorageStateInfo(*infos); !err.IsNone()) { return AOS_ERROR_WRAP(err); @@ -469,7 +489,10 @@ Error StorageState::CheckChecksumAndSendUpdateRequest(const State& state) { LOG_DBG() << "Check checksum and send update request" << state; - auto stateContent = MakeUnique>(&mAllocator); + auto stateContent = MakeUnique>(mAllocator); + if (!stateContent) { + return ErrorEnum::eNoMemory; + } if (auto err = fs::ReadFileToString(state.mFilePath, *stateContent); !err.IsNone()) { return err; @@ -570,7 +593,10 @@ Error StorageState::SetQuotas(const SetupParams& setupParams) Error StorageState::SendNewStateIfFileChanged(State& state) { - auto newState = MakeUnique(&mAllocator); + auto newState = MakeUnique(mAllocator); + if (!newState) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } static_cast(*newState) = state.mInstanceIdent; diff --git a/src/core/cm/storagestate/storagestate.hpp b/src/core/cm/storagestate/storagestate.hpp index 906ae936c..8cb0ed09e 100644 --- a/src/core/cm/storagestate/storagestate.hpp +++ b/src/core/cm/storagestate/storagestate.hpp @@ -35,6 +35,7 @@ class StorageState : public StateHandlerItf, /** * Initializes storage state instance. * + * @param allocator allocator to use for temporary objects. * @param config config object. * @param storage storage instance. * @param sender sender instance. @@ -43,8 +44,8 @@ class StorageState : public StateHandlerItf, * @param cryptoProvider crypto provider instance. * @return Error. */ - Error Init(const Config& config, StorageItf& storage, SenderItf& sender, fs::FSPlatformItf& fsPlatform, - fs::FSWatcherItf& fsWatcher, crypto::HasherItf& hasher); + Error Init(AllocatorItf& allocator, const Config& config, StorageItf& storage, SenderItf& sender, + fs::FSPlatformItf& fsPlatform, fs::FSWatcherItf& fsWatcher, crypto::HasherItf& hasher); /** * Starts storage state instance. @@ -145,7 +146,6 @@ class StorageState : public StateHandlerItf, static constexpr auto cHashAlgorithm = crypto::HashEnum::eSHA3_224; static constexpr auto cNumSendNewStateThreads = 1; static constexpr auto cInstanceStringLen = 8; - static constexpr auto cAllocatorSize = sizeof(InstanceInfoArray) + sizeof(NewState); struct State { State(const InstanceIdent& instanceIdent, const String& filePath, size_t quota) @@ -189,7 +189,7 @@ class StorageState : public StateHandlerItf, StaticString GetStoragePath(const InstanceIdent& instanceIdent) const; Error CalculateChecksum(const String& data, Array& checksum); - StaticAllocator mAllocator; + AllocatorItf* mAllocator {}; ThreadPool mThreadPool; Mutex mMutex; Config mConfig; diff --git a/src/core/cm/storagestate/tests/storagestate.cpp b/src/core/cm/storagestate/tests/storagestate.cpp index 051620036..c5c0c0ab4 100644 --- a/src/core/cm/storagestate/tests/storagestate.cpp +++ b/src/core/cm/storagestate/tests/storagestate.cpp @@ -17,6 +17,7 @@ #include #include #include +#include using namespace testing; @@ -203,7 +204,7 @@ class StorageStateTests : public Test { tests::utils::InitLog(); - EXPECT_TRUE(mCryptoProvider.Init().IsNone()) << "Failed to initialize crypto provider"; + EXPECT_TRUE(mCryptoProvider.Init(mAllocator).IsNone()) << "Failed to initialize crypto provider"; EXPECT_CALL(mFSPlatformMock, GetMountPoint) .WillRepeatedly(Return(RetWithError>(cTestDir.c_str()))); @@ -254,6 +255,10 @@ class StorageStateTests : public Test { return ErrorEnum::eNone; } + // mAllocator must be declared (and therefore destroyed) after any member that allocates from it, since + // members are destroyed in reverse declaration order. + HeapAllocator mAllocator; + crypto::DefaultCryptoProvider mCryptoProvider; StorageStub mStorageStub; StrictMock mFSPlatformMock; @@ -269,7 +274,8 @@ class StorageStateTests : public Test { TEST_F(StorageStateTests, StartStop) { - auto err = mStorageState.Init(mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); + auto err = mStorageState.Init( + mAllocator, mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); EXPECT_TRUE(err.IsNone()) << "Failed to initialize storage state: " << tests::utils::ErrorToStr(err); err = mStorageState.Start(); @@ -291,7 +297,8 @@ TEST_F(StorageStateTests, StorageQuotaNotSet) EXPECT_CALL(mFSPlatformMock, SetUserQuota(_, setupParams.mUID, setupParams.mStateQuota)).Times(1); - auto err = mStorageState.Init(mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); + auto err = mStorageState.Init( + mAllocator, mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); EXPECT_TRUE(err.IsNone()) << "Failed to initialize storage state: " << tests::utils::ErrorToStr(err); EXPECT_CALL(mSenderMock, SendStateRequest).WillOnce(Return(ErrorEnum::eNone)); @@ -317,7 +324,8 @@ TEST_F(StorageStateTests, StateQuotaNotSet) StaticString storagePath, statePath; - auto err = mStorageState.Init(mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); + auto err = mStorageState.Init( + mAllocator, mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); EXPECT_TRUE(err.IsNone()) << "Failed to initialize storage state: " << tests::utils::ErrorToStr(err); err = mStorageState.Start(); @@ -352,7 +360,8 @@ TEST_F(StorageStateTests, StorageAndStateQuotaNotSet) StaticString storagePath, statePath; - auto err = mStorageState.Init(mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); + auto err = mStorageState.Init( + mAllocator, mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); EXPECT_TRUE(err.IsNone()) << "Failed to initialize storage state: " << tests::utils::ErrorToStr(err); EXPECT_CALL(mFSPlatformMock, SetUserQuota).Times(0); @@ -375,7 +384,8 @@ TEST_F(StorageStateTests, SetupOnDifferentPartitions) .WillOnce(Return(RetWithError>("partition1"))) .WillOnce(Return(RetWithError>("partition2"))); - auto err = mStorageState.Init(mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); + auto err = mStorageState.Init( + mAllocator, mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); EXPECT_TRUE(err.IsNone()) << "Failed to initialize storage state: " << tests::utils::ErrorToStr(err); err = mStorageState.Start(); @@ -406,7 +416,8 @@ TEST_F(StorageStateTests, SetupFailsOnSetUserQuotaError) { constexpr auto cSetQuotaError = ErrorEnum::eOutOfRange; - auto err = mStorageState.Init(mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); + auto err = mStorageState.Init( + mAllocator, mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); EXPECT_TRUE(err.IsNone()) << "Failed to initialize storage state: " << tests::utils::ErrorToStr(err); err = mStorageState.Start(); @@ -426,7 +437,8 @@ TEST_F(StorageStateTests, SetupFailsOnSetUserQuotaError) TEST_F(StorageStateTests, SetupSameInstance) { - auto err = mStorageState.Init(mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); + auto err = mStorageState.Init( + mAllocator, mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); EXPECT_TRUE(err.IsNone()) << "Failed to initialize storage state: " << tests::utils::ErrorToStr(err); err = mStorageState.Start(); @@ -494,7 +506,8 @@ TEST_F(StorageStateTests, GetInstanceCheckSum) auto err = AddInstanceIdent(cInstanceIdent, "getchecksum-content"); EXPECT_TRUE(err.IsNone()) << "Failed to add instance ident: " << tests::utils::ErrorToStr(err); - err = mStorageState.Init(mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); + err = mStorageState.Init( + mAllocator, mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); EXPECT_TRUE(err.IsNone()) << "Failed to initialize storage state: " << tests::utils::ErrorToStr(err); EXPECT_CALL(mFSWatcherMock, Subscribe).WillOnce(Return(ErrorEnum::eNone)); @@ -521,7 +534,8 @@ TEST_F(StorageStateTests, Cleanup) { auto err = AddInstanceIdent(cInstanceIdent, "cleanup-content"); - err = mStorageState.Init(mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); + err = mStorageState.Init( + mAllocator, mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); EXPECT_TRUE(err.IsNone()) << "Failed to initialize storage state: " << tests::utils::ErrorToStr(err); EXPECT_CALL(mFSWatcherMock, Subscribe).WillOnce(Return(ErrorEnum::eNone)); @@ -553,7 +567,8 @@ TEST_F(StorageStateTests, Remove) { auto err = AddInstanceIdent(cInstanceIdent, "remove-content"); - err = mStorageState.Init(mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); + err = mStorageState.Init( + mAllocator, mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); EXPECT_TRUE(err.IsNone()) << "Failed to initialize storage state: " << tests::utils::ErrorToStr(err); EXPECT_CALL(mFSWatcherMock, Subscribe).WillOnce(Return(ErrorEnum::eNone)); @@ -585,7 +600,8 @@ TEST_F(StorageStateTests, UpdateState) auto err = AddInstanceIdent(cInstanceIdent, "outdated state content"); - err = mStorageState.Init(mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); + err = mStorageState.Init( + mAllocator, mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); EXPECT_TRUE(err.IsNone()) << "Failed to initialize storage state: " << tests::utils::ErrorToStr(err); EXPECT_CALL(mFSWatcherMock, Subscribe).WillOnce(Return(ErrorEnum::eNone)); @@ -624,7 +640,8 @@ TEST_F(StorageStateTests, UpdateState) TEST_F(StorageStateTests, AcceptStateUnknownInstance) { - auto err = mStorageState.Init(mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); + auto err = mStorageState.Init( + mAllocator, mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); EXPECT_TRUE(err.IsNone()) << "Failed to initialize storage state: " << tests::utils::ErrorToStr(err); err = mStorageState.Start(); @@ -647,7 +664,8 @@ TEST_F(StorageStateTests, AcceptStateChecksumMismatch) { auto err = AddInstanceIdent(cInstanceIdent, "initial state content"); - err = mStorageState.Init(mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); + err = mStorageState.Init( + mAllocator, mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); EXPECT_TRUE(err.IsNone()) << "Failed to initialize storage state: " << tests::utils::ErrorToStr(err); EXPECT_CALL(mFSWatcherMock, Subscribe).WillOnce(Return(ErrorEnum::eNone)); @@ -674,7 +692,8 @@ TEST_F(StorageStateTests, AcceptStateChecksumMismatch) TEST_F(StorageStateTests, AcceptStateWithRejectedStatus) { - auto err = mStorageState.Init(mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); + auto err = mStorageState.Init( + mAllocator, mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); EXPECT_TRUE(err.IsNone()) << "Failed to initialize storage state: " << tests::utils::ErrorToStr(err); err = mStorageState.Start(); @@ -733,7 +752,8 @@ TEST_F(StorageStateTests, UpdateAndAcceptStateFlow) err = CalculateChecksum(cUpdateStateContent, updateStateContentChecksum); EXPECT_TRUE(err.IsNone()) << "Failed to calculate checksum: " << tests::utils::ErrorToStr(err); - err = mStorageState.Init(mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); + err = mStorageState.Init( + mAllocator, mConfig, mStorageStub, mSenderMock, mFSPlatformMock, mFSWatcherMock, mCryptoProvider); EXPECT_TRUE(err.IsNone()) << "Failed to initialize storage state: " << tests::utils::ErrorToStr(err); err = mStorageState.Start(); diff --git a/src/core/cm/unitconfig/tests/unitconfig.cpp b/src/core/cm/unitconfig/tests/unitconfig.cpp index 9e98b52cc..13d0fab83 100644 --- a/src/core/cm/unitconfig/tests/unitconfig.cpp +++ b/src/core/cm/unitconfig/tests/unitconfig.cpp @@ -13,6 +13,7 @@ #include #include #include +#include using namespace testing; @@ -119,6 +120,10 @@ class UnitConfigTest : public Test { ASSERT_TRUE(err.IsNone()) << "Failed to create test config file"; } + // mAllocator must be declared (and therefore destroyed) after any member that allocates from it, since + // members are destroyed in reverse declaration order. + HeapAllocator mAllocator; + StrictMock mNodeInfoProvider; StrictMock mNodeConfigHandler; StrictMock mJSONProvider; @@ -135,7 +140,7 @@ TEST_F(UnitConfigTest, InitWithValidConfig) SetupValidUnitConfig(); - auto err = mUnitConfig.Init({cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider); + auto err = mUnitConfig.Init(mAllocator, {cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider); EXPECT_TRUE(err.IsNone()); } @@ -146,14 +151,14 @@ TEST_F(UnitConfigTest, InitWithInvalidConfig) EXPECT_CALL(mJSONProvider, UnitConfigFromJSON(_, _)).WillOnce(Return(ErrorEnum::eInvalidArgument)); - auto err = mUnitConfig.Init({cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider); + auto err = mUnitConfig.Init(mAllocator, {cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider); EXPECT_TRUE(err.IsNone()); } TEST_F(UnitConfigTest, InitWithMissingConfigFile) { - auto err = mUnitConfig.Init({cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider); + auto err = mUnitConfig.Init(mAllocator, {cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider); EXPECT_TRUE(err.IsNone()); @@ -171,7 +176,8 @@ TEST_F(UnitConfigTest, GetUnitConfigStatusValid) SetupValidUnitConfig(); - ASSERT_TRUE(mUnitConfig.Init({cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); + ASSERT_TRUE( + mUnitConfig.Init(mAllocator, {cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); UnitConfigStatus status; auto err = mUnitConfig.GetUnitConfigStatus(status); @@ -188,7 +194,7 @@ TEST_F(UnitConfigTest, GetUnitConfigStatusWithError) EXPECT_CALL(mJSONProvider, UnitConfigFromJSON(_, _)).WillOnce(Return(ErrorEnum::eInvalidArgument)); - auto err = mUnitConfig.Init({cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider); + auto err = mUnitConfig.Init(mAllocator, {cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider); EXPECT_TRUE(err.IsNone()); @@ -206,7 +212,8 @@ TEST_F(UnitConfigTest, GetNodeConfigByType) SetupValidUnitConfig(); - ASSERT_TRUE(mUnitConfig.Init({cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); + ASSERT_TRUE( + mUnitConfig.Init(mAllocator, {cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); NodeConfig nodeConfig; auto err = mUnitConfig.GetNodeConfig("", cTestNodeType, nodeConfig); @@ -226,7 +233,8 @@ TEST_F(UnitConfigTest, GetNodeConfigByID) EXPECT_CALL(mJSONProvider, UnitConfigFromJSON(_, _)) .WillOnce(DoAll(SetArgReferee<1>(config), Return(ErrorEnum::eNone))); - ASSERT_TRUE(mUnitConfig.Init({cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); + ASSERT_TRUE( + mUnitConfig.Init(mAllocator, {cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); NodeConfig nodeConfig; auto err = mUnitConfig.GetNodeConfig(cTestNodeID, "", nodeConfig); @@ -243,7 +251,8 @@ TEST_F(UnitConfigTest, GetNodeConfigNotFound) SetupValidUnitConfig(); - ASSERT_TRUE(mUnitConfig.Init({cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); + ASSERT_TRUE( + mUnitConfig.Init(mAllocator, {cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); NodeConfig nodeConfig; auto err = mUnitConfig.GetNodeConfig("nonexistent", "unknown", nodeConfig); @@ -263,7 +272,8 @@ TEST_F(UnitConfigTest, CheckUnitConfigValidVersion) EXPECT_CALL(mJSONProvider, UnitConfigFromJSON(_, _)) .WillOnce(DoAll(SetArgReferee<1>(config), Return(ErrorEnum::eNone))); - ASSERT_TRUE(mUnitConfig.Init({cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); + ASSERT_TRUE( + mUnitConfig.Init(mAllocator, {cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); auto newUnitConfig = CreateTestUnitConfigWithNodeID("2.0.0"); @@ -293,7 +303,8 @@ TEST_F(UnitConfigTest, CheckUnitConfigSameVersion) SetupValidUnitConfig(); - ASSERT_TRUE(mUnitConfig.Init({cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); + ASSERT_TRUE( + mUnitConfig.Init(mAllocator, {cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); auto sameVersionConfig = CreateTestUnitConfig("1.0.0"); @@ -307,7 +318,8 @@ TEST_F(UnitConfigTest, CheckUnitConfigLowerVersion) SetupValidUnitConfig(); - ASSERT_TRUE(mUnitConfig.Init({cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); + ASSERT_TRUE( + mUnitConfig.Init(mAllocator, {cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); auto lowerVersionConfig = CreateTestUnitConfig("0.9.0"); @@ -321,7 +333,8 @@ TEST_F(UnitConfigTest, UpdateUnitConfigSuccess) SetupValidUnitConfig(); - ASSERT_TRUE(mUnitConfig.Init({cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); + ASSERT_TRUE( + mUnitConfig.Init(mAllocator, {cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); auto newUnitConfig = CreateTestUnitConfig("2.0.0"); @@ -347,7 +360,8 @@ TEST_F(UnitConfigTest, UpdateUnitConfigSameVersion) SetupValidUnitConfig(); - ASSERT_TRUE(mUnitConfig.Init({cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); + ASSERT_TRUE( + mUnitConfig.Init(mAllocator, {cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); auto sameVersionConfig = CreateTestUnitConfig("1.0.0"); @@ -361,7 +375,8 @@ TEST_F(UnitConfigTest, OnNodeInfoChangedUpdatesConfig) SetupValidUnitConfig(); - ASSERT_TRUE(mUnitConfig.Init({cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); + ASSERT_TRUE( + mUnitConfig.Init(mAllocator, {cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); UnitNodeInfo nodeInfo = CreateTestNodeInfo(); @@ -382,7 +397,8 @@ TEST_F(UnitConfigTest, OnNodeInfoChangedSkipsIfVersionMatches) SetupValidUnitConfig(); - ASSERT_TRUE(mUnitConfig.Init({cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); + ASSERT_TRUE( + mUnitConfig.Init(mAllocator, {cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); UnitNodeInfo nodeInfo = CreateTestNodeInfo(); @@ -395,13 +411,29 @@ TEST_F(UnitConfigTest, OnNodeInfoChangedSkipsIfVersionMatches) mUnitConfig.OnNodeInfoChanged(nodeInfo); } +TEST_F(UnitConfigTest, OnNodeInfoChangedSkipsWhenUnitConfigAbsent) +{ + ASSERT_TRUE( + mUnitConfig.Init(mAllocator, {cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); + + UnitConfigStatus status; + ASSERT_TRUE(mUnitConfig.GetUnitConfigStatus(status).IsNone()); + ASSERT_EQ(status.mState, UnitConfigStateEnum::eAbsent); + + UnitNodeInfo nodeInfo = CreateTestNodeInfo(); + + // Strict mocks: neither GetNodeConfigStatus nor UpdateNodeConfig must be called when unit config is absent. + mUnitConfig.OnNodeInfoChanged(nodeInfo); +} + TEST_F(UnitConfigTest, OnNodeInfoChangedWithUnitConfigError) { CreateTestConfigFile(cInvalidTestUnitConfig); EXPECT_CALL(mJSONProvider, UnitConfigFromJSON(_, _)).WillOnce(Return(ErrorEnum::eInvalidArgument)); - ASSERT_TRUE(mUnitConfig.Init({cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); + ASSERT_TRUE( + mUnitConfig.Init(mAllocator, {cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); UnitNodeInfo nodeInfo = CreateTestNodeInfo(); @@ -414,7 +446,8 @@ TEST_F(UnitConfigTest, VersionComparisonPrerelease) SetupValidUnitConfig(); - ASSERT_TRUE(mUnitConfig.Init({cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); + ASSERT_TRUE( + mUnitConfig.Init(mAllocator, {cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); auto prereleaseVersion = CreateTestUnitConfig("1.0.0-alpha"); auto err = mUnitConfig.CheckUnitConfig(prereleaseVersion); @@ -431,7 +464,8 @@ TEST_F(UnitConfigTest, UpdateUnitConfigSkipsOfflineNodeThenSendsOnConnect) SetupValidUnitConfig(); - ASSERT_TRUE(mUnitConfig.Init({cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); + ASSERT_TRUE( + mUnitConfig.Init(mAllocator, {cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); auto newUnitConfig = CreateTestUnitConfig("2.0.0"); @@ -490,7 +524,8 @@ TEST_F(UnitConfigTest, CheckUnitConfigMultipleNodes) EXPECT_CALL(mJSONProvider, UnitConfigFromJSON(_, _)) .WillOnce(DoAll(SetArgReferee<1>(config), Return(ErrorEnum::eNone))); - ASSERT_TRUE(mUnitConfig.Init({cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); + ASSERT_TRUE( + mUnitConfig.Init(mAllocator, {cTestConfigFile}, mNodeInfoProvider, mNodeConfigHandler, mJSONProvider).IsNone()); aos::UnitConfig newUnitConfig; newUnitConfig.mVersion = "2.0.0"; diff --git a/src/core/cm/unitconfig/unitconfig.cpp b/src/core/cm/unitconfig/unitconfig.cpp index f2ba9aa2d..937179637 100644 --- a/src/core/cm/unitconfig/unitconfig.cpp +++ b/src/core/cm/unitconfig/unitconfig.cpp @@ -16,11 +16,13 @@ namespace aos::cm::unitconfig { * Public **********************************************************************************************************************/ -Error UnitConfig::Init(const Config& config, nodeinfoprovider::NodeInfoProviderItf& nodeInfoProvider, - NodeConfigHandlerItf& nodeConfigHandler, JSONProviderItf& jsonProvider) +Error UnitConfig::Init(AllocatorItf& allocator, const Config& config, + nodeinfoprovider::NodeInfoProviderItf& nodeInfoProvider, NodeConfigHandlerItf& nodeConfigHandler, + JSONProviderItf& jsonProvider) { LOG_DBG() << "Init unit config"; + mAllocator = &allocator; mUnitConfigFile = config.mUnitConfigFile; mNodeInfoProvider = &nodeInfoProvider; mNodeConfigHandler = &nodeConfigHandler; @@ -83,7 +85,10 @@ Error UnitConfig::CheckUnitConfig(const aos::UnitConfig& config) } for (const auto& id : nodeIds) { - auto nodeInfo = MakeUnique(&mAllocator); + auto nodeInfo = MakeUnique(mAllocator); + if (!nodeInfo) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = mNodeInfoProvider->GetNodeInfo(id, *nodeInfo); !err.IsNone()) { return AOS_ERROR_WRAP(err); @@ -102,7 +107,10 @@ Error UnitConfig::CheckUnitConfig(const aos::UnitConfig& config) } if (nodeConfigStatus.mVersion != config.mVersion || !nodeConfigStatus.mError.IsNone()) { - auto nodeConfig = MakeUnique(&mAllocator); + auto nodeConfig = MakeUnique(mAllocator); + if (!nodeConfig) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = FindNodeConfig(nodeInfo->mNodeID, nodeInfo->mNodeType, config, *nodeConfig); !err.IsNone()) { return err; @@ -143,7 +151,10 @@ Error UnitConfig::UpdateUnitConfig(const aos::UnitConfig& unitConfig) mUnitConfig = unitConfig; - auto unitConfigJSON = MakeUnique>(&mAllocator); + auto unitConfigJSON = MakeUnique>(mAllocator); + if (!unitConfigJSON) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = mJSONProvider->UnitConfigToJSON(unitConfig, *unitConfigJSON); !err.IsNone()) { return AOS_ERROR_WRAP(err); @@ -163,8 +174,15 @@ Error UnitConfig::UpdateUnitConfig(const aos::UnitConfig& unitConfig) } for (const auto& id : nodeIds) { - auto nodeConfig = MakeUnique(&mAllocator); - auto nodeInfo = MakeUnique(&mAllocator); + auto nodeConfig = MakeUnique(mAllocator); + if (!nodeConfig) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } + + auto nodeInfo = MakeUnique(mAllocator); + if (!nodeInfo) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = mNodeInfoProvider->GetNodeInfo(id, *nodeInfo); !err.IsNone()) { return AOS_ERROR_WRAP(err); @@ -196,6 +214,12 @@ void UnitConfig::OnNodeInfoChanged(const UnitNodeInfo& info) << Log::Field("state", info.mState) << Log::Field("isConnected", info.mIsConnected) << Log::Field(info.mError); + if (mUnitConfigState == UnitConfigStateEnum::eAbsent) { + LOG_DBG() << "Skip node config update due to unit config is absent" << Log::Field("nodeID", info.mNodeID); + + return; + } + if (mUnitConfigState != UnitConfigStateEnum::eInstalled) { LOG_WRN() << "Can't update node config due to state" << Log::Field("nodeID", info.mNodeID) << Log::Field("state", mUnitConfigState) << Log::Field(mUnitConfigError); @@ -221,7 +245,12 @@ void UnitConfig::OnNodeInfoChanged(const UnitNodeInfo& info) return; } - auto nodeConfig = MakeUnique(&mAllocator); + auto nodeConfig = MakeUnique(mAllocator); + if (!nodeConfig) { + LOG_ERR() << "Can't allocate node config" << Log::Field(ErrorEnum::eNoMemory); + + return; + } if (auto err = FindNodeConfig(info.mNodeID, info.mNodeType, mUnitConfig, *nodeConfig); !err.IsNone()) { LOG_ERR() << "Error finding node config" << Log::Field(err); @@ -244,7 +273,10 @@ Error UnitConfig::LoadConfig() { LOG_DBG() << "Load config"; - auto unitConfig = MakeUnique>(&mAllocator); + auto unitConfig = MakeUnique>(mAllocator); + if (!unitConfig) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } auto err = fs::ReadFileToString(mUnitConfigFile, *unitConfig); if (!err.IsNone()) { diff --git a/src/core/cm/unitconfig/unitconfig.hpp b/src/core/cm/unitconfig/unitconfig.hpp index 0d1c02622..16ad28965 100644 --- a/src/core/cm/unitconfig/unitconfig.hpp +++ b/src/core/cm/unitconfig/unitconfig.hpp @@ -33,13 +33,14 @@ class UnitConfig : public UnitConfigItf, public NodeConfigProviderItf, public no /** * Initializes unit config. * + * @param allocator allocator to use for temporary objects. * @param config configuration. * @param nodeInfoProvider node info provider. * @param nodeConfigHandler node config handler. * @param jsonProvider JSON provider. * @return Error. */ - Error Init(const Config& config, nodeinfoprovider::NodeInfoProviderItf& nodeInfoProvider, + Error Init(AllocatorItf& allocator, const Config& config, nodeinfoprovider::NodeInfoProviderItf& nodeInfoProvider, NodeConfigHandlerItf& nodeConfigHandler, JSONProviderItf& jsonProvider); /** @@ -114,9 +115,7 @@ class UnitConfig : public UnitConfigItf, public NodeConfigProviderItf, public no Error mUnitConfigError; UnitConfigState mUnitConfigState {UnitConfigStateEnum::eAbsent}; - StaticAllocator) + sizeof(NodeConfig) - + sizeof(UnitNodeInfo)> - mAllocator; + AllocatorItf* mAllocator {}; StaticArray mNodeConfigStatuses; diff --git a/src/core/cm/updatemanager/desiredstatushandler.cpp b/src/core/cm/updatemanager/desiredstatushandler.cpp index 7c8ce5f33..de5feab69 100644 --- a/src/core/cm/updatemanager/desiredstatushandler.cpp +++ b/src/core/cm/updatemanager/desiredstatushandler.cpp @@ -14,12 +14,13 @@ namespace aos::cm::updatemanager { * Public **********************************************************************************************************************/ -Error DesiredStatusHandler::Init(iamclient::NodeHandlerItf& nodeHandler, unitconfig::UnitConfigItf& unitConfig, - imagemanager::ImageManagerItf& imageManager, launcher::LauncherItf& launcher, UnitStatusHandler& unitStatusHandler, - StorageItf& storage) +Error DesiredStatusHandler::Init(AllocatorItf& allocator, iamclient::NodeHandlerItf& nodeHandler, + unitconfig::UnitConfigItf& unitConfig, imagemanager::ImageManagerItf& imageManager, launcher::LauncherItf& launcher, + UnitStatusHandler& unitStatusHandler, StorageItf& storage) { LOG_DBG() << "Init desired status handler"; + mAllocator = &allocator; mNodeHandler = &nodeHandler; mUnitConfig = &unitConfig; mUnitStatusHandler = &unitStatusHandler; @@ -331,7 +332,10 @@ void DesiredStatusHandler::SetState(UpdateState state) Error DesiredStatusHandler::DownloadUpdateItems() { - auto itemsStatuses = MakeUnique>(&mAllocator); + auto itemsStatuses = MakeUnique>(mAllocator); + if (!itemsStatuses) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } LOG_DBG() << "Download update items" << Log::Field("count", mCurrentDesiredStatus.mUpdateItems.Size()); @@ -402,8 +406,15 @@ Error DesiredStatusHandler::InstallDesiredStatus() Error DesiredStatusHandler::LaunchInstances() { - auto runRequest = MakeUnique>(&mAllocator); - auto instancesStatuses = MakeUnique>(&mAllocator); + auto runRequest = MakeUnique>(mAllocator); + if (!runRequest) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } + + auto instancesStatuses = MakeUnique>(mAllocator); + if (!instancesStatuses) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } LOG_DBG() << "Launch instances" << Log::Field("count", mCurrentDesiredStatus.mInstances.Size()); @@ -458,7 +469,10 @@ Error DesiredStatusHandler::LaunchInstances() Error DesiredStatusHandler::WaitInstancesActive() { - auto instancesStatuses = MakeUnique>(&mAllocator); + auto instancesStatuses = MakeUnique>(mAllocator); + if (!instancesStatuses) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } while (mIsRunning) { if (auto err = mLauncher->GetInstancesStatuses(*instancesStatuses); !err.IsNone()) { @@ -491,7 +505,10 @@ Error DesiredStatusHandler::WaitInstancesActive() Error DesiredStatusHandler::FinalizeUpdate() { - auto itemsStatuses = MakeUnique>(&mAllocator); + auto itemsStatuses = MakeUnique>(mAllocator); + if (!itemsStatuses) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } LOG_DBG() << "Install update items" << Log::Field("count", mCurrentDesiredStatus.mUpdateItems.Size()); @@ -534,7 +551,12 @@ bool DesiredStatusHandler::IsUpdateRequired(const DesiredStatus& desiredStatus) bool DesiredStatusHandler::IsUpdateItemsRequired(const DesiredStatus& desiredStatus) const { - auto itemsStatuses = MakeUnique>(&mAllocator); + auto itemsStatuses = MakeUnique>(mAllocator); + if (!itemsStatuses) { + LOG_ERR() << "Failed to allocate update items statuses" << Log::Field(ErrorEnum::eNoMemory); + + return true; + } if (auto err = mImageManager->GetUpdateItemsStatuses(*itemsStatuses); !err.IsNone()) { LOG_ERR() << "Failed to get update items statuses" << Log::Field(err); @@ -594,7 +616,12 @@ bool DesiredStatusHandler::IsSameUpdate(const DesiredStatus& desiredStatus) cons bool DesiredStatusHandler::IsUpdateInstancesRequired(const DesiredStatus& desiredStatus) const { - auto instancesStatuses = MakeUnique>(&mAllocator); + auto instancesStatuses = MakeUnique>(mAllocator); + if (!instancesStatuses) { + LOG_ERR() << "Failed to allocate instances statuses" << Log::Field(ErrorEnum::eNoMemory); + + return true; + } if (auto err = mLauncher->GetInstancesStatuses(*instancesStatuses); !err.IsNone()) { LOG_ERR() << "Failed to get instances statuses" << Log::Field(err); diff --git a/src/core/cm/updatemanager/desiredstatushandler.hpp b/src/core/cm/updatemanager/desiredstatushandler.hpp index 7fbc831de..eaba0dcc4 100644 --- a/src/core/cm/updatemanager/desiredstatushandler.hpp +++ b/src/core/cm/updatemanager/desiredstatushandler.hpp @@ -31,6 +31,7 @@ class DesiredStatusHandler : private instancestatusprovider::ListenerItf { /** * Initializes desired status handler. * + * @param allocator allocator to use for temporary objects. * @param nodeHandler node handler. * @param unitConfig unit config interface. * @param imageManager image manager. @@ -39,7 +40,7 @@ class DesiredStatusHandler : private instancestatusprovider::ListenerItf { * @param storage storage interface. * @return Error. */ - Error Init(iamclient::NodeHandlerItf& nodeHandler, unitconfig::UnitConfigItf& unitConfig, + Error Init(AllocatorItf& allocator, iamclient::NodeHandlerItf& nodeHandler, unitconfig::UnitConfigItf& unitConfig, imagemanager::ImageManagerItf& imageManager, launcher::LauncherItf& launcher, UnitStatusHandler& unitStatusHandler, StorageItf& storage); @@ -67,11 +68,6 @@ class DesiredStatusHandler : private instancestatusprovider::ListenerItf { private: static constexpr auto cWaitActiveTimeout = Time::cMinutes * 10; - static constexpr auto cAllocatorSize = Max(sizeof(StaticArray), - sizeof(StaticArray) - + sizeof(StaticArray)) - + Max(sizeof(StaticArray), - sizeof(StaticArray)); // instancestatusprovider::ListenerItf implementation void OnInstancesStatusesChanged(const Array& statuses) override; @@ -109,7 +105,7 @@ class DesiredStatusHandler : private instancestatusprovider::ListenerItf { bool mCancelCurrentUpdate {}; UpdateState mUpdateState {}; - mutable StaticAllocator mAllocator {}; + AllocatorItf* mAllocator {}; }; /** @}*/ diff --git a/src/core/cm/updatemanager/tests/updatemanager.cpp b/src/core/cm/updatemanager/tests/updatemanager.cpp index 6e551220a..4969ccccb 100644 --- a/src/core/cm/updatemanager/tests/updatemanager.cpp +++ b/src/core/cm/updatemanager/tests/updatemanager.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -41,6 +42,13 @@ const auto cCVTimeout = std::chrono::seconds(5); * Static **********************************************************************************************************************/ +static StaticArray sInstanceStatusStorage; + +void ResetInstanceStatusStorage() +{ + sInstanceStatusStorage.Clear(); +} + void SetNodeInfo(UnitNodeInfo& nodeInfo, const String& nodeID, const String& nodeType, const NodeState& state = NodeStateEnum::eProvisioned, bool isConnected = true, Error error = ErrorEnum::eNone) { @@ -151,15 +159,19 @@ void CreateInstancesStatuses(UnitStatus& unitStatus, const String& itemID, const instancesStatuses.mPreinstalled = preinstalled; for (size_t i = 0; i < numInstances; i++) { - UnitInstanceStatus instanceStatus; + auto err = sInstanceStatusStorage.EmplaceBack(); + EXPECT_TRUE(err.IsNone()); + auto& instanceStatus = sInstanceStatusStorage.Back(); + + instanceStatus = {}; instanceStatus.mInstance = i; instanceStatus.mManifestDigest = "digest1"; instanceStatus.mNodeID = "node1"; instanceStatus.mRuntimeID = "runtime1"; instanceStatus.mState = state; - auto err = instancesStatuses.mInstances.PushBack(instanceStatus); + err = instancesStatuses.mInstances.PushBack(&instanceStatus); EXPECT_TRUE(err.IsNone()); } } @@ -179,15 +191,19 @@ void ChangeInstancesStatuses(UnitStatus& unitStatus, const String& itemID, const instancesStatuses.mInstances.Clear(); for (size_t i = 0; i < numInstances; i++) { - UnitInstanceStatus instanceStatus; + auto err = sInstanceStatusStorage.EmplaceBack(); + EXPECT_TRUE(err.IsNone()); + + auto& instanceStatus = sInstanceStatusStorage.Back(); + instanceStatus = {}; instanceStatus.mInstance = i; instanceStatus.mManifestDigest = "digest1"; instanceStatus.mNodeID = "node1"; instanceStatus.mRuntimeID = "runtime1"; instanceStatus.mState = state; - auto err = instancesStatuses.mInstances.PushBack(instanceStatus); + err = instancesStatuses.mInstances.PushBack(&instanceStatus); EXPECT_TRUE(err.IsNone()); } } @@ -295,11 +311,11 @@ void ConvertInstancesStatuses( status.mType = unitInstanceStatus.mType; status.mSubjectID = unitInstanceStatus.mSubjectID; status.mVersion = unitInstanceStatus.mVersion; - status.mInstance = instanceStatus.mInstance; - status.mNodeID = instanceStatus.mNodeID; - status.mRuntimeID = instanceStatus.mRuntimeID; - status.mManifestDigest = instanceStatus.mManifestDigest; - status.mState = instanceStatus.mState; + status.mInstance = instanceStatus->mInstance; + status.mNodeID = instanceStatus->mNodeID; + status.mRuntimeID = instanceStatus->mRuntimeID; + status.mManifestDigest = instanceStatus->mManifestDigest; + status.mState = instanceStatus->mState; status.mPreinstalled = unitInstanceStatus.mPreinstalled; instancesStatuses.PushBack(status); @@ -324,9 +340,11 @@ class UpdateManagerTest : public Test { void SetUp() override { + ResetInstanceStatusStorage(); + Config config {cUnitStatusSendTimeout}; - auto err = mUpdateManager.Init(config, mIdentProviderMock, mNodeHandlerMock, mUnitConfigMock, + auto err = mUpdateManager.Init(mAllocator, config, mIdentProviderMock, mNodeHandlerMock, mUnitConfigMock, mNodeInfoProviderMock, mImageManagerMock, mLauncherMock, mCloudConnectionMock, mSenderStub, mStorageStub); EXPECT_TRUE(err.IsNone()) << "Failed to initialize update manager: " << tests::utils::ErrorToStr(err); @@ -412,6 +430,10 @@ class UpdateManagerTest : public Test { EXPECT_TRUE(err.IsNone()) << "Failed to stop update manager: " << tests::utils::ErrorToStr(err); } + // mAllocator must be declared (and therefore destroyed) after any member that allocates from it, since + // members are destroyed in reverse declaration order. + HeapAllocator mAllocator; + UpdateManager mUpdateManager; NiceMock mIdentProviderMock; NiceMock mNodeHandlerMock; @@ -608,7 +630,7 @@ TEST_F(UpdateManagerTest, SendDeltaUnitStatus) EXPECT_TRUE(err.IsNone()); CreateInstanceStatus(statuses->Back(), instancesStatuses.mItemID, instancesStatuses.mSubjectID, - instanceStatus.mInstance, instancesStatuses.mVersion, instanceStatus); + instanceStatus->mInstance, instancesStatuses.mVersion, *instanceStatus); } } @@ -627,7 +649,7 @@ TEST_F(UpdateManagerTest, SendDeltaUnitStatus) EXPECT_TRUE(err.IsNone()); CreateInstanceStatus(statuses->Back(), instancesStatuses.mItemID, instancesStatuses.mSubjectID, - instanceStatus.mInstance, instancesStatuses.mVersion, instanceStatus); + instanceStatus->mInstance, instancesStatuses.mVersion, *instanceStatus); } } @@ -811,11 +833,11 @@ TEST_F(UpdateManagerTest, ProcessFullDesiredStatus) status.mItemID = instancesStatuses.mItemID; status.mSubjectID = instancesStatuses.mSubjectID; status.mVersion = instancesStatuses.mVersion; - status.mInstance = instanceStatus.mInstance; - status.mNodeID = instanceStatus.mNodeID; - status.mRuntimeID = instanceStatus.mRuntimeID; - status.mManifestDigest = instanceStatus.mManifestDigest; - status.mState = instanceStatus.mState; + status.mInstance = instanceStatus->mInstance; + status.mNodeID = instanceStatus->mNodeID; + status.mRuntimeID = instanceStatus->mRuntimeID; + status.mManifestDigest = instanceStatus->mManifestDigest; + status.mState = instanceStatus->mState; instances.PushBack(status); } diff --git a/src/core/cm/updatemanager/unitstatushandler.cpp b/src/core/cm/updatemanager/unitstatushandler.cpp index 8d04069c8..3bd0f6e65 100644 --- a/src/core/cm/updatemanager/unitstatushandler.cpp +++ b/src/core/cm/updatemanager/unitstatushandler.cpp @@ -14,12 +14,13 @@ namespace aos::cm::updatemanager { * Public **********************************************************************************************************************/ -Error UnitStatusHandler::Init(const Config& config, iamclient::IdentProviderItf& identProvider, +Error UnitStatusHandler::Init(AllocatorItf& allocator, const Config& config, iamclient::IdentProviderItf& identProvider, unitconfig::UnitConfigItf& unitConfig, nodeinfoprovider::NodeInfoProviderItf& nodeInfoProvider, imagemanager::ItemStatusProviderItf& itemStatusProvider, instancestatusprovider::ProviderItf& instanceStatusProvider, cloudconnection::CloudConnectionItf& cloudConnection, SenderItf& sender) { + mAllocator = &allocator; mIdentProvider = &identProvider; mUnitConfig = &unitConfig; mNodeInfoProvider = &nodeInfoProvider; @@ -311,20 +312,25 @@ void UnitStatusHandler::OnInstancesStatusesChanged(const Array& itemIt = &mUnitStatus.mInstances->Back(); } - auto instanceIt = itemIt->mInstances.FindIf([&status](const UnitInstanceStatus& instanceStatus) { - return instanceStatus.mInstance == status.mInstance; + auto instanceIt = itemIt->mInstances.FindIf([&status](const UnitInstanceStatus* instanceStatus) { + return instanceStatus->mInstance == status.mInstance; }); if (instanceIt == itemIt->mInstances.end()) { - if (auto err = itemIt->mInstances.EmplaceBack(); !err.IsNone()) { + if (auto err = mUnitInstancesStatuses.EmplaceBack(); !err.IsNone()) { LOG_ERR() << "Failed to emplace instance status" << Log::Field(err); return; } + if (auto err = itemIt->mInstances.PushBack(&mUnitInstancesStatuses.Back()); !err.IsNone()) { + LOG_ERR() << "Failed to push instance status pointer" << Log::Field(err); + return; + } + instanceIt = &itemIt->mInstances.Back(); } - static_cast(*instanceIt) = static_cast(status); - instanceIt->mInstance = status.mInstance; + static_cast(**instanceIt) = static_cast(status); + (*instanceIt)->mInstance = status.mInstance; } StartTimer(); @@ -438,7 +444,10 @@ Error UnitStatusHandler::SetNodesInfo() Error UnitStatusHandler::SetUpdateItemsStatus() { - auto itemsStatuses = MakeUnique(&mAllocator); + auto itemsStatuses = MakeUnique(mAllocator); + if (!itemsStatuses) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } mItemStatusProvider->GetUpdateItemsStatuses(*itemsStatuses); @@ -460,8 +469,12 @@ Error UnitStatusHandler::SetUpdateItemsStatus() Error UnitStatusHandler::SetInstancesStatus() { mUnitStatus.mInstances.EmplaceValue(); + mUnitInstancesStatuses.Clear(); - auto instancesStatuses = MakeUnique>(&mAllocator); + auto instancesStatuses = MakeUnique>(mAllocator); + if (!instancesStatuses) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = mInstanceStatusProvider->GetInstancesStatuses(*instancesStatuses); !err.IsNone()) { return AOS_ERROR_WRAP(err); @@ -482,12 +495,16 @@ Error UnitStatusHandler::SetInstancesStatus() it = &mUnitStatus.mInstances->Back(); } - UnitInstanceStatus instanceStatus {}; + if (auto err = mUnitInstancesStatuses.EmplaceBack(); !err.IsNone()) { + return AOS_ERROR_WRAP(err); + } + + auto& instanceStatus = mUnitInstancesStatuses.Back(); static_cast(instanceStatus) = static_cast(status); instanceStatus.mInstance = status.mInstance; - it->mInstances.PushBack(instanceStatus); + it->mInstances.PushBack(&instanceStatus); } return ErrorEnum::eNone; @@ -534,11 +551,11 @@ void UnitStatusHandler::LogUnitStatus() << Log::Field("version", instanceStatuses.mVersion); for (const auto& instanceStatus : instanceStatuses.mInstances) { - LOG_INF() << "Unit status instance" << Log::Field("instance", instanceStatus.mInstance) - << Log::Field("manifestDigest", instanceStatus.mManifestDigest) - << Log::Field("nodeID", instanceStatus.mNodeID) - << Log::Field("runtimeID", instanceStatus.mRuntimeID) - << Log::Field("state", instanceStatus.mState) << Log::Field(instanceStatus.mError); + LOG_INF() << "Unit status instance" << Log::Field("instance", instanceStatus->mInstance) + << Log::Field("manifestDigest", instanceStatus->mManifestDigest) + << Log::Field("nodeID", instanceStatus->mNodeID) + << Log::Field("runtimeID", instanceStatus->mRuntimeID) + << Log::Field("state", instanceStatus->mState) << Log::Field(instanceStatus->mError); } } } @@ -558,6 +575,7 @@ void UnitStatusHandler::ClearUnitStatus() mUnitStatus.mUpdateItems.Reset(); mUnitStatus.mInstances.Reset(); mUnitStatus.mUnitSubjects.Reset(); + mUnitInstancesStatuses.Clear(); }; void UnitStatusHandler::ClearUpdateStatuses() diff --git a/src/core/cm/updatemanager/unitstatushandler.hpp b/src/core/cm/updatemanager/unitstatushandler.hpp index 413d974a2..50692f498 100644 --- a/src/core/cm/updatemanager/unitstatushandler.hpp +++ b/src/core/cm/updatemanager/unitstatushandler.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,7 @@ class UnitStatusHandler : private nodeinfoprovider::NodeInfoListenerItf, /** * Initializes unit status handler. * + * @param allocator allocator to use for temporary objects. * @param config update manager configuration. * @param identProvider identity provider. * @param unitConfig unit config interface. @@ -48,10 +50,10 @@ class UnitStatusHandler : private nodeinfoprovider::NodeInfoListenerItf, * @param sender unit status sender. * @return Error. */ - Error Init(const Config& config, iamclient::IdentProviderItf& identProvider, unitconfig::UnitConfigItf& unitConfig, - nodeinfoprovider::NodeInfoProviderItf& nodeInfoProvider, - imagemanager::ItemStatusProviderItf& itemStatusProvider, - instancestatusprovider::ProviderItf& instanceStatusProvider, + Error Init(AllocatorItf& allocator, const Config& config, iamclient::IdentProviderItf& identProvider, + unitconfig::UnitConfigItf& unitConfig, nodeinfoprovider::NodeInfoProviderItf& nodeInfoProvider, + imagemanager::ItemStatusProviderItf& itemStatusProvider, + instancestatusprovider::ProviderItf& instanceStatusProvider, cloudconnection::CloudConnectionItf& cloudConnection, SenderItf& sender); /** @@ -91,9 +93,6 @@ class UnitStatusHandler : private nodeinfoprovider::NodeInfoListenerItf, Error SetUpdateNodeStatus(const String& nodeID, const Error& updateErr); private: - static constexpr auto cAllocatorSize - = Max(sizeof(StaticArray), sizeof(UpdateItemStatusArray)); - // nodeinfoprovider::NodeInfoListenerItf implementation void OnNodeInfoChanged(const UnitNodeInfo& info) override; @@ -131,11 +130,12 @@ class UnitStatusHandler : private nodeinfoprovider::NodeInfoListenerItf, cloudconnection::CloudConnectionItf* mCloudConnection {}; SenderItf* mSender {}; - Mutex mMutex; - UnitStatus mUnitStatus; - StaticAllocator mAllocator; - bool mCloudConnected {}; - bool mIsStatusProcessing {}; + Mutex mMutex; + UnitStatus mUnitStatus; + StaticArray mUnitInstancesStatuses; + AllocatorItf* mAllocator {}; + bool mCloudConnected {}; + bool mIsStatusProcessing {}; Timer mTimer; bool mTimerStarted {}; diff --git a/src/core/cm/updatemanager/updatemanager.cpp b/src/core/cm/updatemanager/updatemanager.cpp index de7b5b95d..00ad0de46 100644 --- a/src/core/cm/updatemanager/updatemanager.cpp +++ b/src/core/cm/updatemanager/updatemanager.cpp @@ -14,7 +14,7 @@ namespace aos::cm::updatemanager { * Public **********************************************************************************************************************/ -Error UpdateManager::Init(const Config& config, iamclient::IdentProviderItf& identProvider, +Error UpdateManager::Init(AllocatorItf& allocator, const Config& config, iamclient::IdentProviderItf& identProvider, iamclient::NodeHandlerItf& nodeHandler, unitconfig::UnitConfigItf& unitConfig, nodeinfoprovider::NodeInfoProviderItf& nodeInfoProvider, imagemanager::ImageManagerItf& imageManager, launcher::LauncherItf& launcher, cloudconnection::CloudConnectionItf& cloudConnection, SenderItf& sender, @@ -22,14 +22,14 @@ Error UpdateManager::Init(const Config& config, iamclient::IdentProviderItf& ide { LOG_DBG() << "Init update manager"; - if (auto err - = mDesiredStatusHandler.Init(nodeHandler, unitConfig, imageManager, launcher, mUnitStatusHandler, storage); + if (auto err = mDesiredStatusHandler.Init( + allocator, nodeHandler, unitConfig, imageManager, launcher, mUnitStatusHandler, storage); !err.IsNone()) { return AOS_ERROR_WRAP(err); } - if (auto err = mUnitStatusHandler.Init( - config, identProvider, unitConfig, nodeInfoProvider, imageManager, launcher, cloudConnection, sender); + if (auto err = mUnitStatusHandler.Init(allocator, config, identProvider, unitConfig, nodeInfoProvider, imageManager, + launcher, cloudConnection, sender); !err.IsNone()) { return AOS_ERROR_WRAP(err); } diff --git a/src/core/cm/updatemanager/updatemanager.hpp b/src/core/cm/updatemanager/updatemanager.hpp index 5f0a02e29..140efbf09 100644 --- a/src/core/cm/updatemanager/updatemanager.hpp +++ b/src/core/cm/updatemanager/updatemanager.hpp @@ -27,6 +27,7 @@ class UpdateManager : public UpdateManagerItf { /** * Initializes update manager. * + * @param allocator allocator to use for temporary objects. * @param config update manager configuration. * @param identProvider identity provider. * @param nodeHandler node handler. @@ -39,10 +40,11 @@ class UpdateManager : public UpdateManagerItf { * @param storage storage interface. * @return Error. */ - Error Init(const Config& config, iamclient::IdentProviderItf& identProvider, iamclient::NodeHandlerItf& nodeHandler, - unitconfig::UnitConfigItf& unitConfig, nodeinfoprovider::NodeInfoProviderItf& nodeInfoProvider, - imagemanager::ImageManagerItf& imageManager, launcher::LauncherItf& launcher, - cloudconnection::CloudConnectionItf& cloudConnection, SenderItf& sender, StorageItf& storage); + Error Init(AllocatorItf& allocator, const Config& config, iamclient::IdentProviderItf& identProvider, + iamclient::NodeHandlerItf& nodeHandler, unitconfig::UnitConfigItf& unitConfig, + nodeinfoprovider::NodeInfoProviderItf& nodeInfoProvider, imagemanager::ImageManagerItf& imageManager, + launcher::LauncherItf& launcher, cloudconnection::CloudConnectionItf& cloudConnection, SenderItf& sender, + StorageItf& storage); /** * Starts update manager. diff --git a/src/core/common/config.hpp b/src/core/common/config.hpp index b594527f1..51c326a9a 100644 --- a/src/core/common/config.hpp +++ b/src/core/common/config.hpp @@ -148,13 +148,6 @@ #define AOS_CONFIG_TYPES_MAX_NUM_BLOBS 16 #endif -/** - * Max number of instances per update item. - */ -#ifndef AOS_CONFIG_TYPES_MAX_NUM_UPDATE_ITEM_INSTANCES -#define AOS_CONFIG_TYPES_MAX_NUM_UPDATE_ITEM_INSTANCES 16 -#endif - /** * Error message len. */ @@ -306,7 +299,7 @@ * Max number of hosts. */ #ifndef AOS_CONFIG_TYPES_MAX_NUM_HOSTS -#define AOS_CONFIG_TYPES_MAX_NUM_HOSTS 8 +#define AOS_CONFIG_TYPES_MAX_NUM_HOSTS 10 #endif /** diff --git a/src/core/common/crypto/certloader.cpp b/src/core/common/crypto/certloader.cpp index d2cbf2627..bcfe966fe 100644 --- a/src/core/common/crypto/certloader.cpp +++ b/src/core/common/crypto/certloader.cpp @@ -24,10 +24,11 @@ constexpr auto cSchemeMaxLength = Max(sizeof(cSchemeFile), sizeof(cSchemePKCS11) * CertLoader **********************************************************************************************************************/ -Error CertLoader::Init(x509::ProviderItf& cryptoProvider, pkcs11::PKCS11Manager& pkcs11Manager) +Error CertLoader::Init(AllocatorItf& allocator, x509::ProviderItf& cryptoProvider, pkcs11::PKCS11Manager& pkcs11Manager) { LOG_DBG() << "Init cert loader"; + mAllocator = &allocator; mCryptoProvider = &cryptoProvider; mPKCS11 = &pkcs11Manager; @@ -73,7 +74,7 @@ RetWithError> CertLoader::LoadCertsChainByURL( return {nullptr, err}; } - return pkcs11::Utils(session, *mCryptoProvider, mAllocator).FindCertificateChain(id, label); + return pkcs11::Utils(*mAllocator, session, *mCryptoProvider).FindCertificateChain(id, label); } return {nullptr, ErrorEnum::eInvalidArgument}; @@ -118,7 +119,7 @@ RetWithError> CertLoader::LoadPrivKeyByURL(const String return {nullptr, err}; } - auto key = pkcs11::Utils(session, *mCryptoProvider, mAllocator).FindPrivateKey(id, label); + auto key = pkcs11::Utils(*mAllocator, session, *mCryptoProvider).FindPrivateKey(id, label); return {key.mValue.GetPrivKey(), key.mError}; } @@ -165,7 +166,11 @@ RetWithError> CertLoader::OpenSession( RetWithError CertLoader::FindToken(const pkcs11::LibraryContext& library, const String& token) { StaticArray slotList; - auto tokenInfo = MakeUnique(&mAllocator); + + auto tokenInfo = MakeUnique(mAllocator); + if (!tokenInfo) { + return {0, ErrorEnum::eNoMemory}; + } auto err = library.GetSlotList(true, slotList); if (!err.IsNone()) { @@ -190,14 +195,20 @@ RetWithError> CertLoader::LoadCertsFromFile(co { LOG_DBG() << "Load certs chain from file: fileName=" << fileName; - auto buff = MakeUnique(&mAllocator); + auto buff = MakeUnique(mAllocator); + if (!buff) { + return {nullptr, ErrorEnum::eNoMemory}; + } auto err = fs::ReadFileToString(fileName, *buff); if (!err.IsNone()) { return {nullptr, err}; } - auto certificates = MakeShared(&mAllocator); + auto certificates = MakeShared(mAllocator); + if (!certificates) { + return {nullptr, ErrorEnum::eNoMemory}; + } err = mCryptoProvider->PEMToX509Certs(*buff, *certificates); @@ -208,7 +219,10 @@ RetWithError> CertLoader::LoadPrivKeyFromFile(const Str { LOG_DBG() << "Load private key from file: fileName=" << fileName; - auto buff = MakeUnique>(&mAllocator); + auto buff = MakeUnique>(mAllocator); + if (!buff) { + return {nullptr, ErrorEnum::eNoMemory}; + } auto err = fs::ReadFileToString(fileName, *buff); if (!err.IsNone()) { @@ -355,45 +369,32 @@ Error DecodeToPKCS11ID(const String& idStr, Array& id) { id.Clear(); - auto percentDetected = false; - aos::StaticString<2> hexByte; - - for (const auto& ch : idStr) { - if (ch == '%') { - if (percentDetected || hexByte.Size()) { - return aos::ErrorEnum::eInvalidArgument; - } - - percentDetected = true; - } else if (percentDetected) { - auto err = hexByte.PushBack(ch); - if (!err.IsNone()) { - return err; - } - - if (hexByte.Size() == hexByte.MaxSize()) { - percentDetected = false; - - uint8_t byte; - - aos::Tie(byte, err) = hexByte.HexToByte(); - if (!err.IsNone()) { - return err; - } - - err = id.PushBack(byte); - if (!err.IsNone()) { - return err; - } - - hexByte.Clear(); - } - - } else { - auto err = id.PushBack(static_cast(ch)); - if (!err.IsNone()) { - return err; - } + if (idStr.Size() % 3 != 0) { + return aos::ErrorEnum::eInvalidArgument; + } + + for (size_t i = 0; i < idStr.Size(); i += 3) { + if (idStr[i] != '%') { + return ErrorEnum::eInvalidArgument; + } + + aos::StaticString<2> hexByte; + + auto err = hexByte.Insert(hexByte.end(), idStr.begin() + i + 1, idStr.begin() + i + 3); + if (!err.IsNone()) { + return err; + } + + uint8_t byte; + + aos::Tie(byte, err) = hexByte.HexToByte(); + if (!err.IsNone()) { + return err; + } + + err = id.PushBack(byte); + if (!err.IsNone()) { + return err; } } diff --git a/src/core/common/crypto/certloader.hpp b/src/core/common/crypto/certloader.hpp index fa63febe6..cd2a58d4a 100644 --- a/src/core/common/crypto/certloader.hpp +++ b/src/core/common/crypto/certloader.hpp @@ -24,11 +24,12 @@ class CertLoader : public CertLoaderItf { /** * Initializes object instance. * + * @param allocator allocator to use for certificates/keys. * @param cryptoProvider crypto provider interface. * @param pkcs11Manager PKCS11 library manager. * @return Error. */ - Error Init(x509::ProviderItf& cryptoProvider, pkcs11::PKCS11Manager& pkcs11Manager); + Error Init(AllocatorItf& allocator, x509::ProviderItf& cryptoProvider, pkcs11::PKCS11Manager& pkcs11Manager); /** * Loads certificate chain by URL. @@ -49,12 +50,6 @@ class CertLoader : public CertLoaderItf { private: using PEMCertChainBlob = StaticString; - static constexpr auto cCertAllocatorSize - = cCertChainsCount * cCertChainSize * sizeof(x509::Certificate) + sizeof(PEMCertChainBlob); - static constexpr auto cKeyAllocatorSize - = AOS_CONFIG_CRYPTO_PRIV_KEYS_COUNT * pkcs11::cPrivateKeyMaxSize + sizeof(cPrivKeyPEMLen); - static constexpr auto cNumAllocation = AOS_CONFIG_CRYPTO_NUM_ALLOCATIONS; - static constexpr auto cDefaultPKCS11Library = AOS_CONFIG_CRYPTO_DEFAULT_PKCS11_LIB; RetWithError> OpenSession( @@ -66,9 +61,7 @@ class CertLoader : public CertLoaderItf { x509::ProviderItf* mCryptoProvider = nullptr; pkcs11::PKCS11Manager* mPKCS11 = nullptr; - - StaticAllocator - mAllocator; + AllocatorItf* mAllocator {}; }; } // namespace aos::crypto diff --git a/src/core/common/crypto/cryptohelper.cpp b/src/core/common/crypto/cryptohelper.cpp index 0c1ccccd2..2372f4adc 100644 --- a/src/core/common/crypto/cryptohelper.cpp +++ b/src/core/common/crypto/cryptohelper.cpp @@ -21,15 +21,20 @@ CryptoHelper::CryptoHelper() { } -Error CryptoHelper::Init(iamclient::CertProviderItf& certProvider, CryptoProviderItf& cryptoProvider, - CertLoaderItf& certLoader, const String& serviceDiscoveryURL, const String& caCert) +Error CryptoHelper::Init(AllocatorItf& allocator, iamclient::CertProviderItf& certProvider, + CryptoProviderItf& cryptoProvider, CertLoaderItf& certLoader, const String& serviceDiscoveryURL, + const String& caCert) { + mAllocator = &allocator; mCertProvider = &certProvider; mCryptoProvider = &cryptoProvider; mCertLoader = &certLoader; mServiceDiscoveryURL = serviceDiscoveryURL; - auto caCertsPEM = MakeUnique>(&mAllocator); + auto caCertsPEM = MakeUnique>(mAllocator); + if (!caCertsPEM) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = fs::ReadFileToString(caCert, *caCertsPEM); !err.IsNone()) { return AOS_ERROR_WRAP(err); @@ -39,6 +44,12 @@ Error CryptoHelper::Init(iamclient::CertProviderItf& certProvider, CryptoProvide return AOS_ERROR_WRAP(err); } + for (const auto& caCert : mCACerts) { + if (auto err = ValidateCACert(caCert); !err.IsNone()) { + return AOS_ERROR_WRAP(err); + } + } + return ErrorEnum::eNone; } @@ -110,7 +121,10 @@ Error CryptoHelper::ValidateSigns(const String& decryptedPath, const SignInfo& s { LockGuard lock {mSemaphore}; - auto signCtx = MakeUnique(&mAllocator); + auto signCtx = MakeUnique(mAllocator); + if (!signCtx) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = AddCertificates(certs, *signCtx); !err.IsNone()) { return AOS_ERROR_WRAP(err); @@ -131,8 +145,15 @@ Error CryptoHelper::DecryptMetadata(const Array& input, Array& { LockGuard lock {mSemaphore}; - auto contentInfo = MakeUnique(&mAllocator); - auto symKey = MakeUnique>(&mAllocator); + auto contentInfo = MakeUnique(mAllocator); + if (!contentInfo) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } + + auto symKey = MakeUnique>(mAllocator); + if (!symKey) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } auto err = UnmarshalCMS(input, *contentInfo); if (!err.IsNone()) { @@ -164,7 +185,11 @@ Error CryptoHelper::DecryptMetadata(const Array& input, Array& RetWithError> CryptoHelper::GetOnlineCert() { - auto certInfo = MakeUnique(&mAllocator); + auto certInfo = MakeUnique(mAllocator); + if (!certInfo) { + return {{}, AOS_ERROR_WRAP(ErrorEnum::eNoMemory)}; + } + if (auto err = mCertProvider->GetCert(cOnlineCert, {}, {}, *certInfo); !err.IsNone()) { return {{}, AOS_ERROR_WRAP(err)}; } @@ -205,7 +230,10 @@ Error CryptoHelper::GetServiceDiscoveryFromExtensions(const x509::Certificate& c Error CryptoHelper::GetServiceDiscoveryFromOrganization( const x509::Certificate& cert, Array>& urls) { - auto subject = MakeUnique>(&mAllocator); + auto subject = MakeUnique>(mAllocator); + if (!subject) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = mCryptoProvider->ASN1DecodeDN(cert.mSubject, *subject); !err.IsNone()) { return AOS_ERROR_WRAP(err); @@ -221,8 +249,15 @@ Error CryptoHelper::GetServiceDiscoveryFromOrganization( auto valueStart = orgPos + orgKey.Size(); auto [valueEnd, _] = subject->FindSubstr(valueStart, ","); - auto orgName = MakeUnique>(&mAllocator); - auto url = MakeUnique>(&mAllocator); + auto orgName = MakeUnique>(mAllocator); + if (!orgName) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } + + auto url = MakeUnique>(mAllocator); + if (!url) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } auto assignErr = orgName->Insert(orgName->begin(), subject->begin() + valueStart, subject->begin() + valueEnd); if (!assignErr.IsNone()) { @@ -331,8 +366,15 @@ Error CryptoHelper::CheckSessionKey( Error CryptoHelper::DecodeFile(const String& encryptedFile, const String& decryptedFile, AESCipherItf& decoder) { - auto inBlock = MakeUnique>(&mAllocator); - auto outBlock = MakeUnique>(&mAllocator); + auto inBlock = MakeUnique>(mAllocator); + if (!inBlock) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } + + auto outBlock = MakeUnique>(mAllocator); + if (!outBlock) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } fs::File inputFile, outputFile; @@ -410,7 +452,10 @@ Error CryptoHelper::AddCertificates(const Array& certs, SignCon continue; } - auto cert = MakeUnique(&mAllocator); + auto cert = MakeUnique(mAllocator); + if (!cert) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = mCryptoProvider->DERToX509Cert(certInfo.mCertificate, *cert); !err.IsNone()) { return AOS_ERROR_WRAP(err); @@ -475,7 +520,10 @@ Error CryptoHelper::VerifySigns(const String& file, const SignInfo& signs, SignC } // Verify sign - auto hashSum = MakeUnique>(&mAllocator); + auto hashSum = MakeUnique>(mAllocator); + if (!hashSum) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = CalculateFileHash(file, hash, *mCryptoProvider, *hashSum); !err.IsNone()) { return AOS_ERROR_WRAP(err); @@ -500,7 +548,10 @@ Error CryptoHelper::VerifySigns(const String& file, const SignInfo& signs, SignC } // Verify certs - auto intermCertPool = MakeUnique>(&mAllocator); + auto intermCertPool = MakeUnique>(mAllocator); + if (!intermCertPool) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (auto err = CreateIntermCertPool(signCtx, *chain, *intermCertPool); !err.IsNone()) { return err; @@ -856,7 +907,10 @@ Error CryptoHelper::ParseEncryptedContentInfo(const Array& data, Encryp Error CryptoHelper::GetKeyForEnvelope(const TransRecipientInfo& info, Array& symmetricKey) { - auto certInfo = MakeUnique(&mAllocator); + auto certInfo = MakeUnique(mAllocator); + if (!certInfo) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } auto err = mCertProvider->GetCert(cOfflineCert, info.mRID.mIssuer, info.mRID.mSerial, *certInfo); if (!err.IsNone()) { @@ -920,7 +974,10 @@ Error CryptoHelper::DecryptMessage( Error CryptoHelper::DecodeMessage(AESCipherItf& decoder, const Array& input, Array& message) { - auto outBlock = MakeUnique>(&mAllocator); + auto outBlock = MakeUnique>(mAllocator); + if (!outBlock) { + return AOS_ERROR_WRAP(ErrorEnum::eNoMemory); + } if (input.Size() % AESCipherItf::cBlockSize != 0) { return AOS_ERROR_WRAP(Error(ErrorEnum::eInvalidArgument, "message should be a multiple of CBC block size")); diff --git a/src/core/common/crypto/cryptohelper.hpp b/src/core/common/crypto/cryptohelper.hpp index a569e6f5b..df8e6b4ae 100644 --- a/src/core/common/crypto/cryptohelper.hpp +++ b/src/core/common/crypto/cryptohelper.hpp @@ -8,6 +8,7 @@ #define AOS_AOS_COMMON_CRYPTO_CRYPTOHELPER_HPP_ #include +#include #include "itf/certloader.hpp" #include "itf/crypto.hpp" @@ -97,6 +98,7 @@ class CryptoHelper : public CryptoHelperItf { /** * Initializes crypto helper. * + * @param allocator allocator to use for temporary objects. * @param certProvider certificate provider interface. * @param cryptoProvider cryptographic provider interface. * @param certLoader certificate loader interface. @@ -104,8 +106,8 @@ class CryptoHelper : public CryptoHelperItf { * @param caCert root certificate path. * @return Error. */ - Error Init(iamclient::CertProviderItf& certProvider, CryptoProviderItf& cryptoProvider, CertLoaderItf& certLoader, - const String& serviceDiscoveryURL, const String& caCert); + Error Init(AllocatorItf& allocator, iamclient::CertProviderItf& certProvider, CryptoProviderItf& cryptoProvider, + CertLoaderItf& certLoader, const String& serviceDiscoveryURL, const String& caCert); /** * Retrieves available service discovery URLs. @@ -157,11 +159,6 @@ class CryptoHelper : public CryptoHelperItf { static constexpr auto cRSAEncryptionOid = "1.2.840.113549.1.1.1"; static constexpr auto cAES256CBCOid = "2.16.840.1.101.3.4.1.42"; - static constexpr auto cThreadHeapUsage = 2 * sizeof(CertInfo) + sizeof(StaticString) - + sizeof(StaticArray) + sizeof(SignContext) + sizeof(x509::Certificate) - + sizeof(StaticArray) + sizeof(StaticArray) - + sizeof(StaticArray) * 2 + sizeof(StaticString) * 2; - RetWithError> GetOnlineCert(); Error SetDefaultServiceDiscoveryURL(Array>& urls); Error GetServiceDiscoveryFromExtensions(const x509::Certificate& cert, Array>& urls); @@ -201,8 +198,8 @@ class CryptoHelper : public CryptoHelperItf { StaticString mServiceDiscoveryURL; x509::CertificateChain mCACerts; - Semaphore mSemaphore; - StaticAllocator mAllocator; + Semaphore mSemaphore; + AllocatorItf* mAllocator {}; }; } // namespace aos::crypto diff --git a/src/core/common/crypto/cryptoutils.cpp b/src/core/common/crypto/cryptoutils.cpp index be1ee173b..ab87a5859 100644 --- a/src/core/common/crypto/cryptoutils.cpp +++ b/src/core/common/crypto/cryptoutils.cpp @@ -6,6 +6,8 @@ #include #include +#include +#include #include "cryptoutils.hpp" @@ -70,4 +72,166 @@ Error CalculateFileHash(const String& path, const Hash& algorithm, HasherItf& ha return ErrorEnum::eNone; } +/*********************************************************************************************************************** + * CA certificate validation + **********************************************************************************************************************/ + +namespace { + +// Intersection of curves supported by mbedTLS and the AOS OpenSSL engine. +constexpr uint8_t cPrime192v1OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x01}; // 1.2.840.10045.3.1.1 +constexpr uint8_t cSecp224r1OID[] = {0x2B, 0x81, 0x04, 0x00, 0x21}; // 1.3.132.0.33 +constexpr uint8_t cPrime256v1OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07}; // 1.2.840.10045.3.1.7 +constexpr uint8_t cSecp384r1OID[] = {0x2B, 0x81, 0x04, 0x00, 0x22}; // 1.3.132.0.34 +constexpr uint8_t cSecp521r1OID[] = {0x2B, 0x81, 0x04, 0x00, 0x23}; // 1.3.132.0.35 +constexpr uint8_t cSecp192k1OID[] = {0x2B, 0x81, 0x04, 0x00, 0x1F}; // 1.3.132.0.31 +constexpr uint8_t cSecp224k1OID[] = {0x2B, 0x81, 0x04, 0x00, 0x20}; // 1.3.132.0.32 +constexpr uint8_t cSecp256k1OID[] = {0x2B, 0x81, 0x04, 0x00, 0x0A}; // 1.3.132.0.10 +constexpr uint8_t cX25519OID[] = {0x2B, 0x65, 0x6E}; // 1.3.101.110 +constexpr uint8_t cX448OID[] = {0x2B, 0x65, 0x6F}; // 1.3.101.111 +constexpr uint8_t cBrainpoolP256r1OID[] + = {0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07}; // 1.3.36.3.3.2.8.1.1.7 +constexpr uint8_t cBrainpoolP384r1OID[] + = {0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B}; // 1.3.36.3.3.2.8.1.1.11 +constexpr uint8_t cBrainpoolP512r1OID[] + = {0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D}; // 1.3.36.3.3.2.8.1.1.13 + +struct ECCurveOID { + const uint8_t* mOID; + size_t mOIDLen; + const char* mName; +}; + +constexpr ECCurveOID cSupportedECCurves[] = { + {cPrime192v1OID, sizeof(cPrime192v1OID), "prime192v1"}, + {cSecp224r1OID, sizeof(cSecp224r1OID), "secp224r1"}, + {cPrime256v1OID, sizeof(cPrime256v1OID), "prime256v1"}, + {cSecp384r1OID, sizeof(cSecp384r1OID), "secp384r1"}, + {cSecp521r1OID, sizeof(cSecp521r1OID), "secp521r1"}, + {cSecp192k1OID, sizeof(cSecp192k1OID), "secp192k1"}, + {cSecp224k1OID, sizeof(cSecp224k1OID), "secp224k1"}, + {cSecp256k1OID, sizeof(cSecp256k1OID), "secp256k1"}, + {cX25519OID, sizeof(cX25519OID), "X25519"}, + {cX448OID, sizeof(cX448OID), "X448"}, + {cBrainpoolP256r1OID, sizeof(cBrainpoolP256r1OID), "brainpoolP256r1"}, + {cBrainpoolP384r1OID, sizeof(cBrainpoolP384r1OID), "brainpoolP384r1"}, + {cBrainpoolP512r1OID, sizeof(cBrainpoolP512r1OID), "brainpoolP512r1"}, +}; + +const uint8_t* ECParamsOIDData(const Array& oid, size_t& len) +{ + if (oid.Size() >= 2 && oid[0] == 0x06 && static_cast(oid[1]) == oid.Size() - 2) { + len = oid.Size() - 2; + + return oid.Get() + 2; + } + + len = oid.Size(); + + return oid.Get(); +} + +bool ECParamsOIDEquals(const Array& oid, const uint8_t* expected, size_t expectedLen) +{ + size_t len = 0; + const uint8_t* data = ECParamsOIDData(oid, len); + + return len == expectedLen && memcmp(data, expected, expectedLen) == 0; +} + +const char* GetECCurveName(const Array& ecParamsOID) +{ + for (const auto& curve : cSupportedECCurves) { + if (ECParamsOIDEquals(ecParamsOID, curve.mOID, curve.mOIDLen)) { + return curve.mName; + } + } + + return "unknown"; +} + +bool IsSupportedECCurve(const Array& ecParamsOID) +{ + for (const auto& curve : cSupportedECCurves) { + if (ECParamsOIDEquals(ecParamsOID, curve.mOID, curve.mOIDLen)) { + return true; + } + } + + return false; +} + +Error CheckCAPublicKey(const x509::Certificate& cert) +{ + const auto& pubKey = GetBase(cert.mPublicKey); + + switch (pubKey.GetKeyType().GetValue()) { + case KeyTypeEnum::eRSA: { + const auto& rsa = static_cast(pubKey); + if (rsa.GetN().Size() * 8 < 2048) { + LOG_WRN() << "CA certificate RSA public key length is below 2048 bits: bits=" << rsa.GetN().Size() * 8; + } + + return ErrorEnum::eNone; + } + + case KeyTypeEnum::eECDSA: { + const auto& ecdsa = static_cast(pubKey); + if (!IsSupportedECCurve(ecdsa.GetECParamsOID())) { + LOG_ERR() << "CA certificate public key curve mismatch: unsupported curve: " + << GetECCurveName(ecdsa.GetECParamsOID()); + + return AOS_ERROR_WRAP(ErrorEnum::eNotSupported); + } + + return ErrorEnum::eNone; + } + + default: + LOG_ERR() << "CA certificate public key algorithm mismatch: expected RSA or ECDSA, actual " + << pubKey.GetKeyType(); + + return AOS_ERROR_WRAP(ErrorEnum::eNotSupported); + } +} + +} // namespace + +Error ValidateCACert(const x509::Certificate& cert) +{ + if (cert.mVersion != x509::cX509Version3) { + LOG_ERR() << "CA certificate is not X.509 v3: version=" << cert.mVersion; + + return AOS_ERROR_WRAP(ErrorEnum::eFailed); + } + + if (!cert.mIsCA) { + LOG_ERR() << "CA certificate basic constraints mismatch: expected CA:TRUE, actual CA:FALSE"; + + return AOS_ERROR_WRAP(ErrorEnum::eFailed); + } + + if (!cert.mKeyUsage.HasValue()) { + LOG_WRN() << "CA certificate key usage extension is missing"; + } else { + if ((cert.mKeyUsage.GetValue() & x509::keyusage::cKeyCertSign) == 0) { + LOG_ERR() << "CA certificate key usage mismatch: missing keyCertSign"; + + return AOS_ERROR_WRAP(ErrorEnum::eFailed); + } + + if ((cert.mKeyUsage.GetValue() & x509::keyusage::cCRLSign) == 0) { + LOG_WRN() << "CA certificate key usage mismatch: missing cRLSign"; + } + } + + if (cert.mSubjectKeyId.IsEmpty()) { + LOG_ERR() << "CA certificate subjectKeyIdentifier is missing"; + + return AOS_ERROR_WRAP(ErrorEnum::eFailed); + } + + return CheckCAPublicKey(cert); +} + } // namespace aos::crypto diff --git a/src/core/common/crypto/cryptoutils.hpp b/src/core/common/crypto/cryptoutils.hpp index 3a781b355..8be8f1647 100644 --- a/src/core/common/crypto/cryptoutils.hpp +++ b/src/core/common/crypto/cryptoutils.hpp @@ -8,6 +8,7 @@ #define AOS_AOS_COMMON_CRYPTO_CRYPTOUTILS_HPP_ #include "itf/hash.hpp" +#include "itf/x509.hpp" namespace aos::crypto { @@ -22,6 +23,14 @@ namespace aos::crypto { */ Error CalculateFileHash(const String& path, const Hash& algorithm, HasherItf& hashProvider, Array& hash); +/** + * Validates that a certificate can be used as a CA (root or intermediate issuer). + * + * @param cert certificate to validate. + * @return Error. + */ +Error ValidateCACert(const x509::Certificate& cert); + } // namespace aos::crypto #endif diff --git a/src/core/common/crypto/itf/certloader.hpp b/src/core/common/crypto/itf/certloader.hpp index 3c2246491..b3a859988 100644 --- a/src/core/common/crypto/itf/certloader.hpp +++ b/src/core/common/crypto/itf/certloader.hpp @@ -67,6 +67,7 @@ Error EncodePKCS11ID(const Array& id, String& idStr); /** * Decodes PKCS11 ID from percent-encoded string. + * Only fully percent-encoded input is supported, which is aligned with EncodePKCS11ID implementation. * * @param idStr percent-encoded string. * @param id PKCS11 ID. diff --git a/src/core/common/crypto/itf/x509.hpp b/src/core/common/crypto/itf/x509.hpp index 9e0a61d79..4b96c4e65 100644 --- a/src/core/common/crypto/itf/x509.hpp +++ b/src/core/common/crypto/itf/x509.hpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -118,6 +119,26 @@ constexpr auto cMaxNumCertificates = AOS_CONFIG_CRYPTO_MAX_NUM_CERTIFICATES; namespace x509 { +/** + * X.509 v3 version number (Certificate.mVersion). + */ +constexpr int cX509Version3 = 3; + +/** + * X.509 KeyUsage bits (RFC 5280 / OpenSSL / mbedTLS layout). + */ +namespace keyusage { +constexpr uint32_t cDigitalSignature = 0x0080; +constexpr uint32_t cNonRepudiation = 0x0040; +constexpr uint32_t cKeyEncipherment = 0x0020; +constexpr uint32_t cDataEncipherment = 0x0010; +constexpr uint32_t cKeyAgreement = 0x0008; +constexpr uint32_t cKeyCertSign = 0x0004; +constexpr uint32_t cCRLSign = 0x0002; +constexpr uint32_t cEncipherOnly = 0x0001; +constexpr uint32_t cDecipherOnly = 0x8000; +} // namespace keyusage + /** * Padding type. */ @@ -178,6 +199,18 @@ struct Certificate { * Public key. */ Variant mPublicKey; + /** + * X.509 version (1 = v1, 2 = v2, 3 = v3). + */ + int mVersion {}; + /** + * Basic Constraints CA flag (CA:TRUE). + */ + bool mIsCA {}; + /** + * KeyUsage bits. Set when the KeyUsage extension is present. + */ + Optional mKeyUsage; /** * Complete ASN.1 DER content (certificate, signature algorithm and signature). */ diff --git a/src/core/common/crypto/mbedtls/cryptoprovider.cpp b/src/core/common/crypto/mbedtls/cryptoprovider.cpp index 52e41220a..362262fa5 100644 --- a/src/core/common/crypto/mbedtls/cryptoprovider.cpp +++ b/src/core/common/crypto/mbedtls/cryptoprovider.cpp @@ -574,10 +574,12 @@ Error VerifyECDSASignature(const ECDSAPublicKey& pubKey, const Array& d * Public **********************************************************************************************************************/ -Error MbedTLSCryptoProvider::Init() +Error MbedTLSCryptoProvider::Init(AllocatorItf& allocator) { LOG_DBG() << "Init mbedTLS crypto provider"; + mAllocator = &allocator; + auto ret = psa_crypto_init(); return ret != PSA_SUCCESS ? AOS_ERROR_WRAP(ret) : ErrorEnum::eNone; @@ -827,7 +829,10 @@ RetWithError> MbedTLSCryptoProvider::PEMToX509PrivKey(c { LOG_ERR() << "Create private key from PEM"; - auto res = MakeShared(&mAllocator); + auto res = MakeShared(mAllocator); + if (!res) { + return {{}, ErrorEnum::eNoMemory}; + } auto err = res->Init(pemBlob); if (!err.IsNone()) { @@ -941,7 +946,11 @@ RetWithError> MbedTLSCryptoProvider::CreateHash(Hash algorith return {nullptr, ErrorEnum::eNotSupported}; } - auto hasher = MakeUnique(&mAllocator, alg); + auto hasher = MakeUnique(mAllocator, alg); + if (!hasher) { + return {nullptr, ErrorEnum::eNoMemory}; + } + if (auto err = hasher->Init(); !err.IsNone()) { return {nullptr, AOS_ERROR_WRAP(err)}; } @@ -1055,7 +1064,10 @@ RetWithError> MbedTLSCryptoProvider::CreateAESEncoder( return {{}, AOS_ERROR_WRAP(ErrorEnum::eNotSupported)}; } - auto cipher = MakeUnique(&mAllocator); + auto cipher = MakeUnique(mAllocator); + if (!cipher) { + return {{}, ErrorEnum::eNoMemory}; + } auto err = cipher->Init(key, iv, true); if (!err.IsNone()) { @@ -1072,7 +1084,10 @@ RetWithError> MbedTLSCryptoProvider::CreateAESDecoder( return {{}, AOS_ERROR_WRAP(ErrorEnum::eNotSupported)}; } - auto cipher = MakeUnique(&mAllocator); + auto cipher = MakeUnique(mAllocator); + if (!cipher) { + return {{}, ErrorEnum::eNoMemory}; + } auto err = cipher->Init(key, iv, false); if (!err.IsNone()) { @@ -1915,7 +1930,10 @@ Error MbedTLSCryptoProvider::ParseX509CertPublicKey(const mbedtls_pk_context* pk return ParseECKey(mbedtls_pk_ec(*pk), cert); default: - return ErrorEnum::eNotFound; + LOG_ERR() << "Unsupported certificate public key algorithm: type=" << static_cast(mbedtls_pk_get_type(pk)) + << ", only RSA and ECDSA are supported"; + + return AOS_ERROR_WRAP(ErrorEnum::eNotSupported); } } @@ -2042,6 +2060,14 @@ Error MbedTLSCryptoProvider::GetX509CertData(x509::Certificate& cert, mbedtls_x5 memcpy(cert.mRaw.Get(), crt->raw.p, crt->raw.len); + cert.mVersion = crt->version; + cert.mIsCA = mbedtls_x509_crt_get_ca_istrue(crt) > 0; + + cert.mKeyUsage.Reset(); + if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_KEY_USAGE) != 0) { + cert.mKeyUsage.SetValue(static_cast(crt->MBEDTLS_PRIVATE(key_usage))); + } + return ErrorEnum::eNone; } diff --git a/src/core/common/crypto/mbedtls/cryptoprovider.hpp b/src/core/common/crypto/mbedtls/cryptoprovider.hpp index 88ec9d269..7b1f43b61 100644 --- a/src/core/common/crypto/mbedtls/cryptoprovider.hpp +++ b/src/core/common/crypto/mbedtls/cryptoprovider.hpp @@ -27,9 +27,10 @@ class MbedTLSCryptoProvider : public CryptoProviderItf { /** * Initializes the object. * + * @param allocator allocator to use for temporary and key objects. * @result Error. */ - Error Init(); + Error Init(AllocatorItf& allocator); /** * Creates a new certificate based on a template. @@ -396,11 +397,6 @@ class MbedTLSCryptoProvider : public CryptoProviderItf { mutable mbedtls_pk_context mPrivKey; }; - static constexpr auto cAllocatorSize - = AOS_CONFIG_CRYPTO_PUB_KEYS_COUNT * Max(sizeof(RSAPublicKey), sizeof(ECDSAPublicKey)) - + AOS_CONFIG_CRYPTO_HASHER_COUNT * sizeof(MBedTLSHash) - + AOS_CONFIG_CRYPTO_PRIV_KEYS_COUNT * sizeof(MbedTLSRSAPrivKey); - static int VerifyTime(void* data, mbedtls_x509_crt* crt, int depth, uint32_t* flags); static RetWithError