diff --git a/dsc/tests/dsc_config_version.tests.ps1 b/dsc/tests/dsc_config_version.tests.ps1 index bd109edd7..4f61969fd 100644 --- a/dsc/tests/dsc_config_version.tests.ps1 +++ b/dsc/tests/dsc_config_version.tests.ps1 @@ -91,4 +91,26 @@ Describe 'Tests for resource versioning' { $LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.log -Raw) $out.results[0].result.actualState.version | Should -BeExactly '1.1.2' } + + It 'Skips adapter discovery when a native resource type version requirement cannot be satisfied' { + $config_yaml = @" + `$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json + resources: + - name: Test Version + type: Test/Version + requireVersion: '=99.0.0' + properties: + version: '99.0.0' +"@ + $out = dsc -l trace config get -i $config_yaml 2> $TestDrive/error.log + $LASTEXITCODE | Should -Not -Be 0 + + $traces = Get-Content $TestDrive/error.log -Raw + + $traces | Should -Match "Skipping adapter search for resource 'Test/Version'" + $traces | Should -Not -Match 'Searching for adapted resources' + $traces | Should -Not -Match 'Enumerating resources for adapter' + $traces | Should -Match "Test/Version" + $traces | Should -Match "=99.0.0" + } } diff --git a/lib/dsc-lib/locales/en-us.toml b/lib/dsc-lib/locales/en-us.toml index 81083031c..83316a729 100644 --- a/lib/dsc-lib/locales/en-us.toml +++ b/lib/dsc-lib/locales/en-us.toml @@ -132,6 +132,7 @@ importExtensionsEmpty = "Import extension '%{extension}' has no import extension searchingForResources = "Searching for resources: %{resources}" foundResourceWithVersion = "Found matching resource '%{resource}' version %{version}" foundNonAdapterResources = "Found %{count} non-adapter resources" +skipAdapterSearchForNativeType = "Skipping adapter search for resource '%{resource}' (requested version: '%{required_version}'): type is known as a native command-based resource and no matching version was found natively" resourceMissingRequireAdapter = "Resource '%{resource}' is missing 'require_adapter' field." extensionDiscoverFailed = "Extension '%{extension}' failed to discover resources: %{error}" conditionNotBoolean = "Condition '%{condition}' did not evaluate to a boolean" diff --git a/lib/dsc-lib/src/discovery/command_discovery.rs b/lib/dsc-lib/src/discovery/command_discovery.rs index 4da974bc7..7c891af61 100644 --- a/lib/dsc-lib/src/discovery/command_discovery.rs +++ b/lib/dsc-lib/src/discovery/command_discovery.rs @@ -504,11 +504,46 @@ impl ResourceDiscovery for CommandDiscovery { return Ok(found_resources); } + // Determine which still-unsatisfied filters actually require adapter discovery. + // + // If a filter's resource type is already known to native discovery (present in + // RESOURCES or ADAPTERS) and the user did not pin an adapter via `requireAdapter`, + // then any unsatisfied state is necessarily a version-requirement mismatch + let adapter_filter_candidates: Vec<&DiscoveryFilter> = required_resource_types + .iter() + .filter(|filter| { + if required_resources.get(*filter).copied().unwrap_or(false) { + return false; + } + if filter.require_adapter().is_some() { + return true; + } + let type_known_natively = locked_get!(RESOURCES, filter.resource_type()).is_some() + || locked_get!(ADAPTERS, filter.resource_type()).is_some(); + if type_known_natively { + let required_version = filter.require_version() + .map_or_else(|| "*".to_string(), std::string::ToString::to_string); + debug!( + "{}", + t!("discovery.commandDiscovery.skipAdapterSearchForNativeType", + resource = filter.resource_type(), + required_version = required_version) + ); + return false; + } + true + }) + .collect(); + + if adapter_filter_candidates.is_empty() { + return Ok(found_resources); + } + // store the keys of the ADAPTERS into a vec let mut adapters: Vec = locked_clone!(ADAPTERS).keys().cloned().collect(); // sort the adapters by ones specified in the required resources first - for filter in required_resource_types { + for filter in &adapter_filter_candidates { if let Some(required_adapter) = filter.require_adapter() { if !adapters.contains(required_adapter) { return Err(DscError::AdapterNotFound(required_adapter.to_string())); @@ -522,15 +557,15 @@ impl ResourceDiscovery for CommandDiscovery { for adapter_name in &adapters { self.discover_adapted_resources(&TypeNameFilter::default(), &adapter_name.clone().into())?; add_resources_to_lookup_table(&locked_clone!(ADAPTED_RESOURCES)); - for filter in required_resource_types { + for filter in &adapter_filter_candidates { if let Some(adapted_resources) = locked_get!(ADAPTED_RESOURCES, filter.resource_type()) { filter_resources(&mut found_resources, &mut required_resources, &adapted_resources, filter); } - if required_resources.values().all(|&v| v) { + if adapter_filter_candidates.iter().all(|f| required_resources.get(*f).copied().unwrap_or(false)) { break; } } - if required_resources.values().all(|&v| v) { + if adapter_filter_candidates.iter().all(|f| required_resources.get(*f).copied().unwrap_or(false)) { break; } }