winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

ConfigurationSetApplyProcessor.cpp (21226B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "ConfigurationSetApplyProcessor.h"
      5 #include "ConfigurationSetChangeData.h"
      6 #include "ExceptionResultHelpers.h"
      7 
      8 #include <AppInstallerErrors.h>
      9 #include <AppInstallerLogging.h>
     10 #include <AppInstallerStrings.h>
     11 
     12 namespace winrt::Microsoft::Management::Configuration::implementation
     13 {
     14     namespace
     15     {
     16         constexpr std::wstring_view s_ResourceType_RunCommandOnSet = L"Microsoft.DSC.Transitional/RunCommandOnSet";
     17 
     18         std::string GetNormalizedIdentifier(hstring identifier)
     19         {
     20             using namespace AppInstaller::Utility;
     21             return FoldCase(NormalizedString{ identifier });
     22         }
     23 
     24         bool AssertFilter(ConfigurationUnitIntent intent)
     25         {
     26             return intent == ConfigurationUnitIntent::Assert;
     27         }
     28 
     29         bool InformFilter(ConfigurationUnitIntent intent)
     30         {
     31             return intent == ConfigurationUnitIntent::Inform;
     32         }
     33 
     34         bool ApplyFilter(ConfigurationUnitIntent intent)
     35         {
     36             return intent == ConfigurationUnitIntent::Apply || intent == ConfigurationUnitIntent::Unknown;
     37         }
     38 
     39         // Check if a unit should always be applied. No TestSettings is needed.
     40         bool ShouldApplyAlways(const Configuration::ConfigurationUnit& unit)
     41         {
     42             if (AppInstaller::Utility::CaseInsensitiveEquals(s_ResourceType_RunCommandOnSet, unit.Type()))
     43             {
     44                 return true;
     45             }
     46 
     47             return false;
     48         }
     49     }
     50 
     51     ConfigurationSetApplyProcessor::ConfigurationSetApplyProcessor(
     52         const Configuration::ConfigurationSet& configurationSet,
     53         IConfigurationSetProcessor setProcessor,
     54         progress_type&& progress) :
     55             m_configurationSet(configurationSet),
     56             m_setProcessor(std::move(setProcessor)),
     57             m_result(make_self<wil::details::module_count_wrapper<implementation::ApplyGroupSettingsResult>>()),
     58             m_progress(std::move(progress))
     59     {
     60         // Create a copy of the set of configuration units
     61         auto unitsView = configurationSet.Units();
     62         std::vector<ConfigurationUnit> unitsToProcess{ unitsView.Size() };
     63         unitsView.GetMany(0, unitsToProcess);
     64 
     65         // Create the unit info vector from these units
     66         for (const auto& unit : unitsToProcess)
     67         {
     68             m_unitInfo.emplace_back(unit);
     69             m_result->UnitResults().Append(*m_unitInfo.back().Result);
     70         }
     71 
     72         m_progress.Result(*m_result);
     73     }
     74 
     75     void ConfigurationSetApplyProcessor::Process(bool preProcessOnly)
     76     {
     77         if (PreProcess() && !preProcessOnly)
     78         {
     79             ProcessInternal(HasProcessedSuccessfully, &ConfigurationSetApplyProcessor::ProcessUnit, true);
     80         }
     81     }
     82 
     83     IApplyGroupSettingsResult ConfigurationSetApplyProcessor::Result() const
     84     {
     85         return *m_result;
     86     }
     87 
     88     ConfigurationSetApplyProcessor::UnitInfo::UnitInfo(const Configuration::ConfigurationUnit& unit) :
     89         Unit(unit), Result(make_self<wil::details::module_count_wrapper<implementation::ApplyConfigurationUnitResult>>())
     90     {
     91         Result->Unit(unit);
     92         ResultInformation = Result->ResultInformationInternal();
     93     }
     94 
     95     bool ConfigurationSetApplyProcessor::PreProcess()
     96     {
     97         bool result = true;
     98 
     99         for (size_t i = 0; i < m_unitInfo.size(); ++i)
    100         {
    101             if (!AddUnitToMap(m_unitInfo[i], i))
    102             {
    103                 result = false;
    104             }
    105         }
    106 
    107         if (!result)
    108         {
    109             // This is the only error that adding to the map can produce
    110             m_result->ResultInformationInternal()->ResultCode(WINGET_CONFIG_ERROR_DUPLICATE_IDENTIFIER);
    111             return false;
    112         }
    113 
    114         for (UnitInfo& unitInfo : m_unitInfo)
    115         {
    116             for (hstring dependencyHstring : unitInfo.Unit.Dependencies())
    117             {
    118                 // Throw out empty dependency strings
    119                 if (dependencyHstring.empty())
    120                 {
    121                     continue;
    122                 }
    123 
    124                 std::string dependency = GetNormalizedIdentifier(dependencyHstring);
    125                 auto itr = m_idToUnitInfoIndex.find(dependency);
    126                 if (itr == m_idToUnitInfoIndex.end())
    127                 {
    128                     AICLI_LOG(Config, Error, << "Found missing dependency: " << dependency);
    129                     unitInfo.ResultInformation->Initialize(WINGET_CONFIG_ERROR_MISSING_DEPENDENCY, ConfigurationUnitResultSource::ConfigurationSet);
    130                     unitInfo.ResultInformation->Details(dependencyHstring);
    131                     SendProgress(ConfigurationUnitState::Completed, unitInfo);
    132                     result = false;
    133                     // TODO: Consider collecting all missing dependencies, for now just the first
    134                     break;
    135                 }
    136                 else
    137                 {
    138                     unitInfo.DependencyIndices.emplace_back(itr->second);
    139                 }
    140             }
    141         }
    142 
    143         if (!result)
    144         {
    145             // This is the only error that adding to the map can produce
    146             m_result->ResultInformationInternal()->ResultCode(WINGET_CONFIG_ERROR_MISSING_DEPENDENCY);
    147             return false;
    148         }
    149 
    150         if (!ProcessInternal(HasPreprocessed, &ConfigurationSetApplyProcessor::MarkPreprocessed))
    151         {
    152             // The preprocessing simulates processing as if every unit run was successful.
    153             // If it fails, this means that there are unit definitions whose dependencies cannot be satisfied.
    154             // The only reason for that is a cycle in the dependency graph somewhere.
    155             m_result->ResultInformationInternal()->ResultCode(WINGET_CONFIG_ERROR_SET_DEPENDENCY_CYCLE);
    156             return false;
    157         }
    158 
    159         return true;
    160     }
    161 
    162     bool ConfigurationSetApplyProcessor::AddUnitToMap(UnitInfo& unitInfo, size_t unitInfoIndex)
    163     {
    164         hstring originalIdentifier = unitInfo.Unit.Identifier();
    165         if (originalIdentifier.empty())
    166         {
    167             return true;
    168         }
    169 
    170         std::string identifier = GetNormalizedIdentifier(originalIdentifier);
    171 
    172         auto itr = m_idToUnitInfoIndex.find(identifier);
    173         if (itr != m_idToUnitInfoIndex.end())
    174         {
    175             AICLI_LOG(Config, Error, << "Found duplicate identifier: " << identifier);
    176             // Found a duplicate identifier, mark both as such
    177             m_unitInfo[itr->second].ResultInformation->Initialize(WINGET_CONFIG_ERROR_DUPLICATE_IDENTIFIER, ConfigurationUnitResultSource::ConfigurationSet);
    178             SendProgressIfNotComplete(ConfigurationUnitState::Completed, m_unitInfo[itr->second]);
    179             unitInfo.ResultInformation->Initialize(WINGET_CONFIG_ERROR_DUPLICATE_IDENTIFIER, ConfigurationUnitResultSource::ConfigurationSet);
    180             SendProgress(ConfigurationUnitState::Completed, unitInfo);
    181             return false;
    182         }
    183         else
    184         {
    185             m_idToUnitInfoIndex.emplace(std::move(identifier), unitInfoIndex);
    186             return true;
    187         }
    188     }
    189 
    190     bool ConfigurationSetApplyProcessor::ProcessInternal(CheckDependencyPtr checkDependencyFunction, ProcessUnitPtr processUnitFunction, bool sendProgress)
    191     {
    192         // Create the set of units that need to be processed
    193         std::vector<size_t> unitsToProcess;
    194         for (size_t i = 0, size = m_unitInfo.size(); i < size; ++i)
    195         {
    196             unitsToProcess.emplace_back(i);
    197         }
    198 
    199         // Always process all ConfigurationUnitIntent::Assert first
    200         if (!ProcessIntentInternal(
    201             unitsToProcess,
    202             checkDependencyFunction,
    203             processUnitFunction,
    204             AssertFilter,
    205             WINGET_CONFIG_ERROR_ASSERTION_FAILED,
    206             WINGET_CONFIG_ERROR_ASSERTION_FAILED,
    207             sendProgress))
    208         {
    209             return false;
    210         }
    211 
    212         // Then all ConfigurationUnitIntent::Inform
    213         if (!ProcessIntentInternal(
    214             unitsToProcess,
    215             checkDependencyFunction,
    216             processUnitFunction,
    217             InformFilter,
    218             WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED,
    219             WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED,
    220             sendProgress))
    221         {
    222             return false;
    223         }
    224 
    225         // Then all ConfigurationUnitIntent::Apply
    226         return ProcessIntentInternal(
    227             unitsToProcess,
    228             checkDependencyFunction,
    229             processUnitFunction,
    230             ApplyFilter,
    231             E_FAIL, // This should not happen as there are no other intents left
    232             WINGET_CONFIG_ERROR_SET_APPLY_FAILED,
    233             sendProgress);
    234     }
    235 
    236     bool ConfigurationSetApplyProcessor::ProcessIntentInternal(
    237         std::vector<size_t>& unitsToProcess,
    238         CheckDependencyPtr checkDependencyFunction,
    239         ProcessUnitPtr processUnitFunction,
    240         IntentFilterPtr intentFilter,
    241         hresult errorForOtherIntents,
    242         hresult errorForFailures,
    243         bool sendProgress)
    244     {
    245         // Always process the first item in the list that is available to be processed
    246         bool hasProcessed = true;
    247         bool hasFailure = false;
    248         while (hasProcessed)
    249         {
    250             hasProcessed = false;
    251             for (auto itr = unitsToProcess.begin(), end = unitsToProcess.end(); itr != end; ++itr)
    252             {
    253                 UnitInfo& unitInfo = m_unitInfo[*itr];
    254                 if (HasIntentAndSatisfiedDependencies(unitInfo, intentFilter, checkDependencyFunction))
    255                 {
    256                     if (!(this->*processUnitFunction)(unitInfo))
    257                     {
    258                         hasFailure = true;
    259                     }
    260                     unitsToProcess.erase(itr);
    261                     hasProcessed = true;
    262                     break;
    263                 }
    264             }
    265         }
    266 
    267         // Mark all remaining items with intent as failed due to dependency
    268         bool hasRemainingDependencies = false;
    269         for (size_t index : unitsToProcess)
    270         {
    271             UnitInfo& unitInfo = m_unitInfo[index];
    272             if (intentFilter(unitInfo.Unit.Intent()))
    273             {
    274                 hasRemainingDependencies = true;
    275                 unitInfo.ResultInformation->Initialize(WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED, ConfigurationUnitResultSource::Precondition);
    276                 if (sendProgress)
    277                 {
    278                     SendProgress(ConfigurationUnitState::Skipped, unitInfo);
    279                 }
    280             }
    281         }
    282 
    283         // Any failures are fatal, mark all other units as failed due to that
    284         if (hasFailure || hasRemainingDependencies)
    285         {
    286             for (size_t index : unitsToProcess)
    287             {
    288                 UnitInfo& unitInfo = m_unitInfo[index];
    289                 if (!intentFilter(unitInfo.Unit.Intent()))
    290                 {
    291                     unitInfo.ResultInformation->Initialize(errorForOtherIntents, ConfigurationUnitResultSource::Precondition);
    292                     if (sendProgress)
    293                     {
    294                         SendProgress(ConfigurationUnitState::Skipped, unitInfo);
    295                     }
    296                 }
    297             }
    298 
    299             if (hasFailure)
    300             {
    301                 m_result->ResultInformationInternal()->ResultCode(errorForFailures);
    302             }
    303             else // hasRemainingDependencies
    304             {
    305                 m_result->ResultInformationInternal()->ResultCode(WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED);
    306             }
    307             return false;
    308         }
    309 
    310         return true;
    311     }
    312 
    313     bool ConfigurationSetApplyProcessor::HasIntentAndSatisfiedDependencies(
    314         const UnitInfo& unitInfo,
    315         IntentFilterPtr intentFilter,
    316         CheckDependencyPtr checkDependencyFunction) const
    317     {
    318         bool result = false;
    319 
    320         if (intentFilter(unitInfo.Unit.Intent()))
    321         {
    322             result = true;
    323             for (size_t dependencyIndex : unitInfo.DependencyIndices)
    324             {
    325                 if (!checkDependencyFunction(m_unitInfo[dependencyIndex]))
    326                 {
    327                     result = false;
    328                     break;
    329                 }
    330             }
    331         }
    332 
    333         return result;
    334     }
    335 
    336     bool ConfigurationSetApplyProcessor::HasPreprocessed(const UnitInfo& unitInfo)
    337     {
    338         return unitInfo.PreProcessed;
    339     }
    340 
    341     bool ConfigurationSetApplyProcessor::MarkPreprocessed(UnitInfo& unitInfo)
    342     {
    343         unitInfo.PreProcessed = true;
    344         return true;
    345     }
    346 
    347     bool ConfigurationSetApplyProcessor::HasProcessedSuccessfully(const UnitInfo& unitInfo)
    348     {
    349         return unitInfo.Processed && SUCCEEDED(unitInfo.ResultInformation->ResultCode());
    350     }
    351 
    352     bool ConfigurationSetApplyProcessor::ProcessUnit(UnitInfo& unitInfo)
    353     {
    354         m_progress.ThrowIfCancelled();
    355 
    356         IConfigurationUnitProcessor unitProcessor;
    357 
    358         // Once we get this far, consider the unit processed even if we fail to create the actual processor.
    359         unitInfo.Processed = true;
    360 
    361         if (!unitInfo.Unit.IsActive())
    362         {
    363             // If the unit is requested to be skipped, we mark it with a failure to prevent any dependency from running.
    364             // But we return true from this function to indicate a successful "processing".
    365             unitInfo.ResultInformation->Initialize(WINGET_CONFIG_ERROR_MANUALLY_SKIPPED, ConfigurationUnitResultSource::Precondition);
    366             SendProgress(ConfigurationUnitState::Skipped, unitInfo);
    367             return true;
    368         }
    369 
    370         // Send a progress event that we are starting, and prepare one for completion when we exit the function
    371         SendProgress(ConfigurationUnitState::InProgress, unitInfo);
    372         auto sendCompletedProgress = wil::scope_exit([this, &unitInfo]() { SendProgress(ConfigurationUnitState::Completed, unitInfo); });
    373 
    374         try
    375         {
    376             unitProcessor = m_setProcessor.CreateUnitProcessor(unitInfo.Unit);
    377         }
    378         catch (...)
    379         {
    380             ExtractUnitResultInformation(std::current_exception(), unitInfo.ResultInformation);
    381             return false;
    382         }
    383 
    384         // As the process of creating the unit processor could take a while, check for cancellation again
    385         m_progress.ThrowIfCancelled();
    386 
    387         bool result = false;
    388 
    389         try
    390         {
    391             switch (unitInfo.Unit.Intent())
    392             {
    393             case ConfigurationUnitIntent::Assert:
    394             {
    395                 ITestSettingsResult settingsResult = unitProcessor.TestSettings();
    396 
    397                 if (settingsResult.TestResult() == ConfigurationTestResult::Positive)
    398                 {
    399                     result = true;
    400                 }
    401                 else if (settingsResult.TestResult() == ConfigurationTestResult::Negative)
    402                 {
    403                     unitInfo.ResultInformation->Initialize(WINGET_CONFIG_ERROR_ASSERTION_FAILED, ConfigurationUnitResultSource::Precondition);
    404                 }
    405                 else if (settingsResult.TestResult() == ConfigurationTestResult::Failed)
    406                 {
    407                     unitInfo.ResultInformation->Initialize(settingsResult.ResultInformation());
    408                 }
    409                 else
    410                 {
    411                     unitInfo.ResultInformation->Initialize(E_UNEXPECTED, ConfigurationUnitResultSource::Internal);
    412                 }
    413             }
    414                 break;
    415 
    416             case ConfigurationUnitIntent::Inform:
    417             {
    418                 // Force the processor to retrieve the settings
    419                 IGetSettingsResult settingsResult = unitProcessor.GetSettings();
    420                 if (SUCCEEDED(settingsResult.ResultInformation().ResultCode()))
    421                 {
    422                     result = true;
    423                 }
    424                 else
    425                 {
    426                     unitInfo.ResultInformation->Initialize(settingsResult.ResultInformation());
    427                 }
    428             }
    429                 break;
    430 
    431             case ConfigurationUnitIntent::Apply:
    432             case ConfigurationUnitIntent::Unknown:
    433             {
    434                 // Check for a group processor and let it do the work if present
    435                 IConfigurationGroupProcessor groupProcessor = unitProcessor.try_as<IConfigurationGroupProcessor>();
    436 
    437                 if (groupProcessor)
    438                 {
    439                     auto applyOperation = groupProcessor.ApplyGroupSettingsAsync([&](const auto&, const IApplyGroupMemberSettingsResult& unitResult)
    440                         {
    441                             m_progress.Progress(unitResult);
    442                         });
    443 
    444                     // Cancel the inner operation if we are cancelled
    445                     m_progress.Callback([applyOperation]() { applyOperation.Cancel(); });
    446 
    447                     IApplyGroupSettingsResult groupResult = applyOperation.get();
    448 
    449                     // Put all of the group's unit results in our unit results
    450                     bool groupPreviouslyInDesiredState = true;
    451 
    452                     for (const auto& groupUnitResult : groupResult.UnitResults())
    453                     {
    454                         m_result->UnitResults().Append(groupUnitResult);
    455                         groupPreviouslyInDesiredState = groupPreviouslyInDesiredState && groupUnitResult.PreviouslyInDesiredState();
    456                     }
    457 
    458                     // Copy the group result into the existing unit result for the group
    459                     unitInfo.Result->PreviouslyInDesiredState(groupPreviouslyInDesiredState);
    460                     unitInfo.ResultInformation->Initialize(groupResult.ResultInformation());
    461 
    462                     if (SUCCEEDED(unitInfo.ResultInformation->ResultCode()))
    463                     {
    464                         unitInfo.Result->RebootRequired(groupResult.RebootRequired());
    465                         result = true;
    466                     }
    467                 }
    468                 else
    469                 {
    470                     ITestSettingsResult testSettingsResult = nullptr;
    471                     bool applyAlways = ShouldApplyAlways(unitProcessor.Unit());
    472 
    473                     if (!applyAlways)
    474                     {
    475                         testSettingsResult = unitProcessor.TestSettings();
    476                     }
    477 
    478                     if (applyAlways || testSettingsResult.TestResult() == ConfigurationTestResult::Negative)
    479                     {
    480                         // Just in case testing took a while, check for cancellation before moving on to applying
    481                         m_progress.ThrowIfCancelled();
    482 
    483                         IApplySettingsResult applySettingsResult = unitProcessor.ApplySettings();
    484                         if (SUCCEEDED(applySettingsResult.ResultInformation().ResultCode()))
    485                         {
    486                             unitInfo.Result->RebootRequired(applySettingsResult.RebootRequired());
    487                             result = true;
    488                         }
    489                         else
    490                         {
    491                             unitInfo.ResultInformation->Initialize(applySettingsResult.ResultInformation());
    492                         }
    493                     }
    494                     else if (testSettingsResult.TestResult() == ConfigurationTestResult::Positive)
    495                     {
    496                         unitInfo.Result->PreviouslyInDesiredState(true);
    497                         result = true;
    498                     }
    499                     else if (testSettingsResult.TestResult() == ConfigurationTestResult::Failed)
    500                     {
    501                         unitInfo.ResultInformation->Initialize(testSettingsResult.ResultInformation());
    502                     }
    503                     else
    504                     {
    505                         unitInfo.ResultInformation->Initialize(E_UNEXPECTED, ConfigurationUnitResultSource::Internal);
    506                     }
    507                 }
    508             }
    509                 break;
    510 
    511             default:
    512                 unitInfo.ResultInformation->Initialize(E_UNEXPECTED, ConfigurationUnitResultSource::Internal);
    513                 break;
    514             }
    515         }
    516         catch (...)
    517         {
    518             ExtractUnitResultInformation(std::current_exception(), unitInfo.ResultInformation);
    519         }
    520 
    521         return result;
    522     }
    523 
    524     void ConfigurationSetApplyProcessor::SendProgress(ConfigurationUnitState state, const UnitInfo& unitInfo)
    525     {
    526         unitInfo.Result->State(state);
    527 
    528         try
    529         {
    530             m_progress.Progress(*unitInfo.Result);
    531         }
    532         CATCH_LOG();
    533     }
    534 
    535     void ConfigurationSetApplyProcessor::SendProgressIfNotComplete(ConfigurationUnitState state, const UnitInfo& unitInfo)
    536     {
    537         if (unitInfo.Result->State() != ConfigurationUnitState::Completed)
    538         {
    539             SendProgress(state, unitInfo);
    540         }
    541     }
    542 }