Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add progress bar #323

Merged
merged 7 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions dsc/tests/dsc_export.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Describe 'resource export tests' {
$set_results.results.count | Should -BeGreaterThan 1
}

It 'Duplicate resource types in Configuration Export should result in error' {
It 'Duplicate resource types in Configuration Export should not result in error' {

$yaml = @'
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/10/config/document.json
Expand All @@ -71,8 +71,7 @@ Describe 'resource export tests' {
pid: 0
'@
$out = $yaml | dsc config export 2>&1
$LASTEXITCODE | Should -Be 2
$out | out-string | Should -BeLike '*specified multiple times*'
$LASTEXITCODE | Should -Be 0
}

It 'Export can be called on individual resource with the use of --format as a subcommand' {
Expand Down
5 changes: 3 additions & 2 deletions dsc_lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ edition = "2021"

[dependencies]
base64 = "0.21"
derive_builder ="0.12"
derive_builder ="0.20"
indicatif = { version = "0.17" }
jsonschema = "0.17"
regex = "1.7"
reqwest = { version = "0.11", features = ["blocking"] }
Expand All @@ -16,7 +17,7 @@ serde_yaml = { version = "0.9.3" }
thiserror = "1.0"
chrono = "0.4.26"
tracing = "0.1.37"
tree-sitter = "~0.20.10"
tree-sitter = "0.20"
tree-sitter-dscexpression = { path = "../tree-sitter-dscexpression" }

[dev-dependencies]
Expand Down
70 changes: 35 additions & 35 deletions dsc_lib/src/configure/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ use self::config_doc::{Configuration, DataType};
use self::depends_on::get_resource_invocation_order;
use self::config_result::{ConfigurationGetResult, ConfigurationSetResult, ConfigurationTestResult, ConfigurationExportResult};
use self::contraints::{check_length, check_number_limits, check_allowed_values};
use indicatif::{ProgressBar, ProgressStyle};
use serde_json::{Map, Value};
use std::collections::{HashMap, HashSet};
use std::collections::HashMap;
use std::time::Duration;
use tracing::{debug, trace};

pub mod context;
Expand Down Expand Up @@ -131,6 +133,15 @@ fn escape_property_values(properties: &Map<String, Value>) -> Result<Option<Map<
Ok(Some(result))
}

fn get_progress_bar(len: u64) -> Result<ProgressBar, DscError> {
let pb = ProgressBar::new(len);
pb.enable_steady_tick(Duration::from_millis(120));
pb.set_style(ProgressStyle::with_template(
"{spinner:.green} [{elapsed_precise:.cyan}] [{bar:40.cyan/blue}] {pos:>7}/{len:7} {msg:.yellow}"
)?);
Ok(pb)
}

impl Configurator {
/// Create a new `Configurator` instance.
///
Expand Down Expand Up @@ -164,8 +175,11 @@ impl Configurator {
pub fn invoke_get(&mut self, _error_action: ErrorAction, _progress_callback: impl Fn() + 'static) -> Result<ConfigurationGetResult, DscError> {
let config = self.validate_config()?;
let mut result = ConfigurationGetResult::new();
for resource in get_resource_invocation_order(&config, &mut self.statement_parser, &self.context)? {
trace!("Get resource '{}' named: {}", resource.resource_type, resource.name);
let resources = get_resource_invocation_order(&config, &mut self.statement_parser, &self.context)?;
let pb = get_progress_bar(resources.len() as u64)?;
for resource in resources {
pb.inc(1);
pb.set_message(format!("Get '{}'", resource.name));
let properties = self.invoke_property_expressions(&resource.properties)?;
let Some(dsc_resource) = self.discovery.find_resource(&resource.resource_type.to_lowercase()) else {
return Err(DscError::ResourceNotFound(resource.resource_type));
Expand All @@ -181,6 +195,7 @@ impl Configurator {
result.results.push(resource_result);
}

pb.finish_with_message("Get configuration completed");
Ok(result)
}

Expand All @@ -197,7 +212,11 @@ impl Configurator {
pub fn invoke_set(&mut self, skip_test: bool, _error_action: ErrorAction, _progress_callback: impl Fn() + 'static) -> Result<ConfigurationSetResult, DscError> {
let config = self.validate_config()?;
let mut result = ConfigurationSetResult::new();
for resource in get_resource_invocation_order(&config, &mut self.statement_parser, &self.context)? {
let resources = get_resource_invocation_order(&config, &mut self.statement_parser, &self.context)?;
let pb = get_progress_bar(resources.len() as u64)?;
for resource in resources {
pb.inc(1);
pb.set_message(format!("Set '{}'", resource.name));
let properties = self.invoke_property_expressions(&resource.properties)?;
let Some(dsc_resource) = self.discovery.find_resource(&resource.resource_type.to_lowercase()) else {
return Err(DscError::ResourceNotFound(resource.resource_type));
Expand All @@ -213,6 +232,7 @@ impl Configurator {
result.results.push(resource_result);
}

pb.finish_with_message("Set configuration completed");
Ok(result)
}

Expand All @@ -229,7 +249,11 @@ impl Configurator {
pub fn invoke_test(&mut self, _error_action: ErrorAction, _progress_callback: impl Fn() + 'static) -> Result<ConfigurationTestResult, DscError> {
let config = self.validate_config()?;
let mut result = ConfigurationTestResult::new();
for resource in get_resource_invocation_order(&config, &mut self.statement_parser, &self.context)? {
let resources = get_resource_invocation_order(&config, &mut self.statement_parser, &self.context)?;
let pb = get_progress_bar(resources.len() as u64)?;
for resource in resources {
pb.inc(1);
pb.set_message(format!("Test '{}'", resource.name));
let properties = self.invoke_property_expressions(&resource.properties)?;
let Some(dsc_resource) = self.discovery.find_resource(&resource.resource_type.to_lowercase()) else {
return Err(DscError::ResourceNotFound(resource.resource_type));
Expand All @@ -245,6 +269,7 @@ impl Configurator {
result.results.push(resource_result);
}

pb.finish_with_message("Test configuration completed");
Ok(result)
}

Expand All @@ -265,17 +290,13 @@ impl Configurator {
pub fn invoke_export(&mut self, _error_action: ErrorAction, _progress_callback: impl Fn() + 'static) -> Result<ConfigurationExportResult, DscError> {
let config = self.validate_config()?;

let duplicates = Self::find_duplicate_resource_types(&config);
if !duplicates.is_empty()
{
let duplicates_string = &duplicates.join(",");
return Err(DscError::Validation(format!("Resource(s) {duplicates_string} specified multiple times")));
}

let mut result = ConfigurationExportResult::new();
let mut conf = config_doc::Configuration::new();

for resource in get_resource_invocation_order(&config, &mut self.statement_parser, &self.context)? {
let pb = get_progress_bar(config.resources.len() as u64)?;
for resource in &config.resources {
pb.inc(1);
pb.set_message(format!("Export '{}'", resource.name));
let Some(dsc_resource) = self.discovery.find_resource(&resource.resource_type.to_lowercase()) else {
return Err(DscError::ResourceNotFound(resource.resource_type.clone()));
};
Expand All @@ -285,7 +306,7 @@ impl Configurator {
}

result.result = Some(conf);

pb.finish_with_message("Export configuration completed");
Ok(result)
}

Expand Down Expand Up @@ -369,27 +390,6 @@ impl Configurator {
Ok(())
}

fn find_duplicate_resource_types(config: &Configuration) -> Vec<String>
{
let mut map: HashMap<&String, i32> = HashMap::new();
let mut result: HashSet<String> = HashSet::new();
let resource_list = &config.resources;
if resource_list.is_empty() {
return Vec::new();
}

for r in resource_list
{
let v = map.entry(&r.resource_type).or_insert(0);
*v += 1;
if *v > 1 {
result.insert(r.resource_type.clone());
}
}

result.into_iter().collect()
}

fn validate_config(&mut self) -> Result<Configuration, DscError> {
let config: Configuration = serde_json::from_str(self.config.as_str())?;

Expand Down
31 changes: 31 additions & 0 deletions dsc_lib/src/discovery/command_discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ use crate::dscresources::dscresource::{DscResource, ImplementedAs};
use crate::dscresources::resource_manifest::{ResourceManifest, import_manifest};
use crate::dscresources::command_resource::invoke_command;
use crate::dscerror::DscError;
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use std::collections::BTreeMap;
use std::env;
use std::ffi::OsStr;
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
use std::time::Duration;
use tracing::{debug, error, warn};

pub struct CommandDiscovery {
Expand All @@ -28,6 +30,26 @@ impl CommandDiscovery {
{
let return_all_resources = required_resource_types.len() == 1 && required_resource_types[0] == "*";

let multi_progress_bar = MultiProgress::new();
let pb = multi_progress_bar.add(
if return_all_resources {
let pb = ProgressBar::new(1);
pb.enable_steady_tick(Duration::from_millis(120));
pb.set_style(ProgressStyle::with_template(
"{spinner:.green} [{elapsed_precise:.cyan}] {msg:.yellow}"
)?);
pb
} else {
let pb = ProgressBar::new(required_resource_types.len() as u64);
pb.enable_steady_tick(Duration::from_millis(120));
pb.set_style(ProgressStyle::with_template(
"{spinner:.green} [{elapsed_precise:.cyan}] [{bar:40.cyan/blue}] {pos:>7}/{len:7} {msg:.yellow}"
)?);
pb
}
);
pb.set_message("Searching for resources");

let mut resources: BTreeMap<String, DscResource> = BTreeMap::new();
let mut provider_resources: Vec<String> = Vec::new();
let mut remaining_required_resource_types = required_resource_types.to_owned();
Expand Down Expand Up @@ -88,6 +110,7 @@ impl CommandDiscovery {
{
remaining_required_resource_types.retain(|x| *x != resource.type_name.to_lowercase());
debug!("Found {} in {}", &resource.type_name, path.display());
pb.inc(1);
resources.insert(resource.type_name.to_lowercase(), resource);
if remaining_required_resource_types.is_empty()
{
Expand All @@ -105,6 +128,12 @@ impl CommandDiscovery {
// now go through the provider resources and add them to the list of resources
for provider in provider_resources {
debug!("Enumerating resources for provider {}", provider);
let pb_adapter = multi_progress_bar.add(ProgressBar::new(1));
pb_adapter.enable_steady_tick(Duration::from_millis(120));
pb_adapter.set_style(ProgressStyle::with_template(
"{spinner:.green} [{elapsed_precise:.cyan}] {msg:.white}"
)?);
pb_adapter.set_message(format!("Enumerating resources for adapter {provider}"));
let provider_resource = resources.get(&provider).unwrap();
let provider_type_name = provider_resource.type_name.clone();
let manifest = import_manifest(provider_resource.manifest.clone().unwrap())?;
Expand Down Expand Up @@ -175,10 +204,12 @@ impl CommandDiscovery {
}
};
}
pb_adapter.finish_with_message(format!("Done with {provider}"));

debug!("Provider {} listed {} matching resources", provider_type_name, provider_resources_count);
}

pb.finish_with_message("Discovery complete");
Ok(resources)
}
}
Expand Down
2 changes: 1 addition & 1 deletion dsc_lib/src/discovery/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ impl Discovery {

let mut regex: Option<Box<Regex>> = None;
let mut resources: Vec<DscResource> = Vec::new();
if !type_name_filter.is_empty()
if !type_name_filter.is_empty()
{
let regex_str = convert_wildcard_to_regex(type_name_filter);
let mut regex_builder = RegexBuilder::new(regex_str.as_str());
Expand Down
4 changes: 4 additions & 0 deletions dsc_lib/src/dscerror.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

use std::str::Utf8Error;

use indicatif::style::TemplateError;
use reqwest::StatusCode;
use thiserror::Error;
use tracing::error;
Expand Down Expand Up @@ -76,6 +77,9 @@ pub enum DscError {
#[error("Parser: {0}")]
Parser(String),

#[error("Progress: {0}")]
Progress(#[from] TemplateError),

#[error("Resource not found: {0}")]
ResourceNotFound(String),

Expand Down
2 changes: 1 addition & 1 deletion tree-sitter-dscexpression/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ include = [
path = "bindings/rust/lib.rs"

[dependencies]
tree-sitter = "~0.20.10"
tree-sitter = "0.20"

[build-dependencies]
cc = "1.0"
Loading