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 }