From 02db44f54ccff61bd7cae3a4467efcdfbd066ba7 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 26 Jan 2025 21:34:09 -0800 Subject: [PATCH] First part --- dsc/src/subcommand.rs | 20 ++++++++----- dsc_lib/src/configure/mod.rs | 9 ++++-- dsc_lib/src/discovery/command_discovery.rs | 34 +++++++++------------- dsc_lib/src/discovery/discovery_trait.rs | 4 +-- dsc_lib/src/discovery/mod.rs | 6 ++-- dsc_lib/src/lib.rs | 3 +- dsc_lib/src/util.rs | 30 +++++++++++++++++++ 7 files changed, 70 insertions(+), 36 deletions(-) diff --git a/dsc/src/subcommand.rs b/dsc/src/subcommand.rs index e9851424..e4d82ae9 100644 --- a/dsc/src/subcommand.rs +++ b/dsc/src/subcommand.rs @@ -25,6 +25,7 @@ use dsc_lib::{ }, dscresources::dscresource::{Capability, ImplementedAs, Invoke}, dscresources::resource_manifest::{import_manifest, ResourceManifest}, + util::ResourceFilter, }; use rust_i18n::t; use std::{ @@ -476,7 +477,12 @@ pub fn validate_config(config: &Configuration) -> Result<(), DscError> { resource_types.push(type_name.to_lowercase().to_string()); } - dsc.find_resources(&resource_types); + + let mut resource_filters: Vec = Vec::new(); + for a in resource_types { + resource_filters.push(ResourceFilter::new(a)); + } + dsc.find_resources(&resource_filters); for resource_block in resources { let Some(type_name) = resource_block["type"].as_str() else { @@ -541,15 +547,15 @@ pub fn resource(subcommand: &ResourceSubCommand) { list_resources(&mut dsc, resource_name.as_ref(), adapter_name.as_ref(), description.as_ref(), tags.as_ref(), output_format.as_ref()); }, ResourceSubCommand::Schema { resource , output_format } => { - dsc.find_resources(&[resource.to_string()]); + dsc.find_resources(&[ResourceFilter::new(resource.to_string())]); resource_command::schema(&dsc, resource, output_format.as_ref()); }, ResourceSubCommand::Export { resource, output_format } => { - dsc.find_resources(&[resource.to_string()]); + dsc.find_resources(&[ResourceFilter::new(resource.to_string())]); resource_command::export(&mut dsc, resource, output_format.as_ref()); }, ResourceSubCommand::Get { resource, input, file: path, all, output_format } => { - dsc.find_resources(&[resource.to_string()]); + dsc.find_resources(&[ResourceFilter::new(resource.to_string())]); if *all { resource_command::get_all(&dsc, resource, output_format.as_ref()); } else { let parsed_input = get_input(input.as_ref(), path.as_ref()); @@ -557,17 +563,17 @@ pub fn resource(subcommand: &ResourceSubCommand) { } }, ResourceSubCommand::Set { resource, input, file: path, output_format } => { - dsc.find_resources(&[resource.to_string()]); + dsc.find_resources(&[ResourceFilter::new(resource.to_string())]); let parsed_input = get_input(input.as_ref(), path.as_ref()); resource_command::set(&dsc, resource, parsed_input, output_format.as_ref()); }, ResourceSubCommand::Test { resource, input, file: path, output_format } => { - dsc.find_resources(&[resource.to_string()]); + dsc.find_resources(&[ResourceFilter::new(resource.to_string())]); let parsed_input = get_input(input.as_ref(), path.as_ref()); resource_command::test(&dsc, resource, parsed_input, output_format.as_ref()); }, ResourceSubCommand::Delete { resource, input, file: path } => { - dsc.find_resources(&[resource.to_string()]); + dsc.find_resources(&[ResourceFilter::new(resource.to_string())]); let parsed_input = get_input(input.as_ref(), path.as_ref()); resource_command::delete(&dsc, resource, parsed_input); }, diff --git a/dsc_lib/src/configure/mod.rs b/dsc_lib/src/configure/mod.rs index 6e4c5477..5714fa4e 100644 --- a/dsc_lib/src/configure/mod.rs +++ b/dsc_lib/src/configure/mod.rs @@ -17,6 +17,7 @@ use self::config_doc::{Configuration, DataType, MicrosoftDscMetadata, Operation, use self::depends_on::get_resource_invocation_order; use self::config_result::{ConfigurationExportResult, ConfigurationGetResult, ConfigurationSetResult, ConfigurationTestResult}; use self::contraints::{check_length, check_number_limits, check_allowed_values}; +use crate::util::ResourceFilter; use indicatif::ProgressStyle; use security_context_lib::{SecurityContext, get_security_context}; use serde_json::{Map, Value}; @@ -649,8 +650,12 @@ impl Configurator { check_security_context(config.metadata.as_ref())?; // Perform discovery of resources used in config - let required_resources = config.resources.iter().map(|p| p.resource_type.clone()).collect::>(); - self.discovery.find_resources(&required_resources); + let required_resource_types = config.resources.iter().map(|p| p.resource_type.clone()).collect::>(); + let mut resource_filters: Vec = Vec::new(); + for a in required_resource_types { + resource_filters.push(ResourceFilter::new(a)); + } + self.discovery.find_resources(&resource_filters); self.config = config; Ok(()) } diff --git a/dsc_lib/src/discovery/command_discovery.rs b/dsc_lib/src/discovery/command_discovery.rs index 060536e1..75d4bbb1 100644 --- a/dsc_lib/src/discovery/command_discovery.rs +++ b/dsc_lib/src/discovery/command_discovery.rs @@ -25,6 +25,7 @@ use tracing_indicatif::span_ext::IndicatifSpanExt; use crate::util::get_setting; use crate::util::get_exe_path; +use crate::util::ResourceFilter; pub struct CommandDiscovery { // use BTreeMap so that the results are sorted by the typename, the Vec is sorted by version @@ -381,34 +382,25 @@ impl ResourceDiscovery for CommandDiscovery { Ok(resources) } - // TODO: handle version requirements - fn find_resources(&mut self, required_resource_types: &[String]) -> Result, DscError> + fn find_resources(&mut self, required_resource_types: &[ResourceFilter]) -> Result, DscError> { debug!("Searching for resources: {:?}", required_resource_types); self.discover_resources("*")?; - // convert required_resource_types to lowercase to handle case-insentiive search - let mut remaining_required_resource_types = required_resource_types.iter().map(|x| x.to_lowercase()).collect::>(); - remaining_required_resource_types.sort_unstable(); - remaining_required_resource_types.dedup(); - + let mut remaining_required_resource_types = required_resource_types.to_vec(); let mut found_resources = BTreeMap::::new(); for (resource_name, resources) in &self.resources { - // TODO: handle version requirements - let Some(resource ) = resources.first() else { - // skip if no resources - continue; - }; - - if remaining_required_resource_types.contains(&resource_name.to_lowercase()) - { - // remove the resource from the list of required resources - remaining_required_resource_types.retain(|x| *x != resource_name.to_lowercase()); - found_resources.insert(resource_name.to_lowercase(), resource.clone()); - if remaining_required_resource_types.is_empty() - { - return Ok(found_resources); + for (pos, req) in remaining_required_resource_types.iter().enumerate() { + for res in resources { + if req.matches(res) { + remaining_required_resource_types.swap_remove(pos); + found_resources.insert(resource_name.to_lowercase(), res.clone()); + if remaining_required_resource_types.is_empty() + { + return Ok(found_resources); + } + } } } } diff --git a/dsc_lib/src/discovery/discovery_trait.rs b/dsc_lib/src/discovery/discovery_trait.rs index a6eb226f..b5645a85 100644 --- a/dsc_lib/src/discovery/discovery_trait.rs +++ b/dsc_lib/src/discovery/discovery_trait.rs @@ -1,12 +1,12 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::{dscresources::dscresource::DscResource, dscerror::DscError}; +use crate::{dscresources::dscresource::DscResource, dscerror::DscError, util::ResourceFilter}; use std::collections::BTreeMap; pub trait ResourceDiscovery { fn discover_resources(&mut self, filter: &str) -> Result<(), DscError>; fn discover_adapted_resources(&mut self, name_filter: &str, adapter_filter: &str) -> Result<(), DscError>; fn list_available_resources(&mut self, type_name_filter: &str, adapter_name_filter: &str) -> Result>, DscError>; - fn find_resources(&mut self, required_resource_types: &[String]) -> Result, DscError>; + fn find_resources(&mut self, required_resource_types: &[ResourceFilter]) -> Result, DscError>; } diff --git a/dsc_lib/src/discovery/mod.rs b/dsc_lib/src/discovery/mod.rs index 20921f7a..59be7ba9 100644 --- a/dsc_lib/src/discovery/mod.rs +++ b/dsc_lib/src/discovery/mod.rs @@ -5,7 +5,7 @@ mod command_discovery; mod discovery_trait; use crate::discovery::discovery_trait::ResourceDiscovery; -use crate::{dscresources::dscresource::DscResource, dscerror::DscError}; +use crate::{dscresources::dscresource::DscResource, dscerror::DscError, util::ResourceFilter}; use std::collections::BTreeMap; use tracing::error; @@ -73,7 +73,7 @@ impl Discovery { /// # Arguments /// /// * `required_resource_types` - The required resource types. - pub fn find_resources(&mut self, required_resource_types: &[String]) { + pub fn find_resources(&mut self, required_resource_types: &[ResourceFilter]) { let discovery_types: Vec> = vec![ Box::new(command_discovery::CommandDiscovery::new()), ]; @@ -90,7 +90,7 @@ impl Discovery { for resource in discovered_resources { self.resources.insert(resource.0.clone(), resource.1); - remaining_required_resource_types.retain(|x| *x != resource.0); + remaining_required_resource_types.retain(|x| *x.type_name_filter != resource.0); }; } } diff --git a/dsc_lib/src/lib.rs b/dsc_lib/src/lib.rs index a01a29f2..6420c58a 100644 --- a/dsc_lib/src/lib.rs +++ b/dsc_lib/src/lib.rs @@ -4,6 +4,7 @@ use configure::config_doc::ExecutionKind; use dscerror::DscError; use dscresources::{dscresource::{DscResource, Invoke}, invoke_result::{GetResult, SetResult, TestResult}}; +use crate::util::ResourceFilter; pub mod configure; pub mod discovery; @@ -45,7 +46,7 @@ impl DscManager { self.discovery.list_available_resources(type_name_filter, adapter_name_filter) } - pub fn find_resources(&mut self, required_resource_types: &[String]) { + pub fn find_resources(&mut self, required_resource_types: &[ResourceFilter]) { self.discovery.find_resources(required_resource_types); } /// Invoke the get operation on a resource. diff --git a/dsc_lib/src/util.rs b/dsc_lib/src/util.rs index 0ff323a9..87fdcec3 100644 --- a/dsc_lib/src/util.rs +++ b/dsc_lib/src/util.rs @@ -2,6 +2,9 @@ // Licensed under the MIT License. use crate::dscerror::DscError; +use crate::dscresources::dscresource::DscResource; +use semver::VersionReq; +use semver::Version; use serde_json::Value; use std::fs; use std::fs::File; @@ -25,6 +28,33 @@ impl Default for DscSettingValue { } } +#[derive(Debug, Clone, PartialEq)] +pub struct ResourceFilter { + pub type_name_filter: String, + pub version_filter: String, + version_req: VersionReq +} + +impl ResourceFilter { + pub fn new(type_name_filter: String) -> Self { + Self { + type_name_filter: type_name_filter.to_lowercase(), + version_filter: ">=0.0.0".to_string(), + version_req: VersionReq::parse(">=0.0.0".to_string()).expect("Failed VersionReq parsing") + } + } + + pub fn matches(self: &ResourceFilter, resource: &DscResource) -> bool { + if self.type_name_filter.eq(&resource.type_name.to_lowercase()) { + let resource_ver = Version::parse(&resource.version); + if self.version_req.matches(resource_ver) { + return true; + } + }; + + return false; + } +} /// Return JSON string whether the input is JSON or YAML /// /// # Arguments