Skip to content
Open
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
1 change: 0 additions & 1 deletion dsc/locales/en-us.toml
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,6 @@ jsonArrayNotSupported = "JSON array output format is only supported for `--all'"

[util]
failedToConvertJsonToString = "Failed to convert JSON to string"
failedToReadTracingSetting = "Could not read 'tracing' setting"
invalidTraceLevel = "Default to 'warn', invalid DSC_TRACE_LEVEL value"
failedToSetTracing = "Unable to set global default tracing subscriber. Tracing is disabled."
readingInput = "Reading input from command line parameter"
Expand Down
9 changes: 1 addition & 8 deletions dsc/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,7 @@ pub enum ListOutputFormat {
TableNoTruncate,
}

#[derive(Debug, Clone, PartialEq, Eq, ValueEnum, Deserialize)]
pub enum TraceFormat {
Default,
Plaintext,
Json,
#[clap(hide = true)]
PassThrough,
}
pub use dsc_lib::settings::TraceFormat;

#[derive(Debug, Parser)]
#[clap(name = "dsc", version = env!("CARGO_PKG_VERSION"), about = t!("args.about").to_string(), long_about = None)]
Expand Down
61 changes: 9 additions & 52 deletions dsc/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,12 @@ use dsc_lib::{
extension_manifest::ExtensionManifest,
},
functions::FunctionDefinition,
util::{
get_setting,
parse_input_to_json,
},
settings::get_settings,
util::parse_input_to_json,
};
use path_absolutize::Absolutize;
use rust_i18n::t;
use schemars::{Schema, schema_for};
use serde::Deserialize;
use std::collections::HashMap;
use std::env;
use std::io::{IsTerminal, Read, stdout, Write};
Expand Down Expand Up @@ -80,27 +77,6 @@ pub const EXIT_BICEP_FAILED: i32 = 10;
pub const DSC_CONFIG_ROOT: &str = "DSC_CONFIG_ROOT";
pub const DSC_TRACE_LEVEL: &str = "DSC_TRACE_LEVEL";

#[derive(Deserialize)]
pub struct TracingSetting {
/// Trace level to use - see pub enum `TraceLevel` in `dsc_lib\src\dscresources\command_resource.rs`
level: TraceLevel,
/// Trace format to use - see pub enum `TraceFormat` in `dsc\src\args.rs`
format: TraceFormat,
/// Whether the 'level' can be overrridden by `DSC_TRACE_LEVEL` environment variable
#[serde(rename = "allowOverride")]
allow_override: bool
}

impl Default for TracingSetting {
fn default() -> TracingSetting {
TracingSetting {
level: TraceLevel::Warn,
format: TraceFormat::Default,
allow_override: true,
}
}
}

/// Get string representation of JSON value.
///
/// # Arguments
Expand Down Expand Up @@ -328,9 +304,6 @@ pub fn write_object(json: &str, format: Option<&OutputFormat>, include_separator
#[allow(clippy::too_many_lines)]
pub fn enable_tracing(trace_level_arg: Option<&TraceLevel>, trace_format_arg: Option<&TraceFormat>) {

let mut policy_is_used = false;
let mut tracing_setting = TracingSetting::default();

let default_filter = EnvFilter::try_from_default_env()
.or_else(|_| EnvFilter::try_new("warn"))
.unwrap_or_default()
Expand All @@ -344,30 +317,14 @@ pub fn enable_tracing(trace_level_arg: Option<&TraceLevel>, trace_format_arg: Op
let default_subscriber = tracing_subscriber::Registry::default().with(default_fmt).with(default_filter).with(default_indicatif_layer);
let default_guard = tracing::subscriber::set_default(default_subscriber);

// read setting/policy from files
if let Ok(v) = get_setting("tracing") {
if v.policy != serde_json::Value::Null {
match serde_json::from_value::<TracingSetting>(v.policy) {
Ok(v) => {
tracing_setting = v;
policy_is_used = true;
},
Err(e) => { error!("{e}"); }
}
} else if v.setting != serde_json::Value::Null {
match serde_json::from_value::<TracingSetting>(v.setting) {
Ok(v) => {
tracing_setting = v;
},
Err(e) => { error!("{e}"); }
}
}
} else {
error!("{}", t!("util.failedToReadTracingSetting"));
}
// read resolved settings/policy from files
let resolved_settings = get_settings();
let mut tracing_setting = resolved_settings.tracing.value.clone();
let policy_is_used = resolved_settings.tracing.is_policy();

// override with DSC_TRACE_LEVEL env var if permitted
if tracing_setting.allow_override && let Ok(level) = env::var(DSC_TRACE_LEVEL) {
// override with DSC_TRACE_LEVEL env var if permitted; a policy-scoped tracing
// setting cannot be overridden by the environment variable
if !policy_is_used && tracing_setting.allow_override && let Ok(level) = env::var(DSC_TRACE_LEVEL) {
tracing_setting.level = match level.to_ascii_uppercase().as_str() {
"ERROR" => TraceLevel::Error,
"WARN" => TraceLevel::Warn,
Expand Down
88 changes: 88 additions & 0 deletions dsc/tests/dsc_settings.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ Describe 'tests for dsc settings' {
$script:dscDefaultSettingsFilePath_backup = Join-Path $script:dscHome "dsc_default.settings.json.backup"
Copy-Item -Force -Path $script:dscSettingsFilePath -Destination $script:dscSettingsFilePath_backup
Copy-Item -Force -Path $script:dscDefaultSettingsFilePath -Destination $script:dscDefaultSettingsFilePath_backup

$script:originalXdgConfigHome = $env:XDG_CONFIG_HOME
}

AfterAll {
Expand All @@ -45,6 +47,7 @@ Describe 'tests for dsc settings' {
if ($IsWindows) { #"Setting policy on Linux requires sudo"
Remove-Item -Path $script:policyFilePath -ErrorAction SilentlyContinue
}
$env:XDG_CONFIG_HOME = $script:originalXdgConfigHome
}

It 'ensure a new tracing value in settings has effect' {
Expand Down Expand Up @@ -106,4 +109,89 @@ Describe 'tests for dsc settings' {
"$TestDrive/tracing.txt" | Should -FileContentMatchExactly "Trace-level is Trace"
"$TestDrive/tracing.txt" | Should -FileContentMatchExactly 'Using Resource Path: Defaultv1SettingsDir'
}

It 'ensure a user settings file via XDG_CONFIG_HOME has effect' {

$env:XDG_CONFIG_HOME = Join-Path $TestDrive 'xdg'
$userSettingsDir = Join-Path $env:XDG_CONFIG_HOME 'dsc'
New-Item -ItemType Directory -Path $userSettingsDir -Force | Out-Null

$script:dscDefaultv1Settings."resourcePath"."directories" = @("UserDir")
$script:dscDefaultv1Settings | ConvertTo-Json -Depth 90 | Set-Content -Force -Path (Join-Path $userSettingsDir 'dsc.settings.json')

dsc -l debug resource list 2> $TestDrive/tracing.txt
"$TestDrive/tracing.txt" | Should -FileContentMatchExactly 'Using Resource Path: UserDir'
}

It 'ensure a workspace settings file has effect' {

$workspaceDir = Join-Path $TestDrive 'workspace'
New-Item -ItemType Directory -Path $workspaceDir -Force | Out-Null

$script:dscDefaultv1Settings."resourcePath"."directories" = @("WorkspaceDir")
$script:dscDefaultv1Settings | ConvertTo-Json -Depth 90 | Set-Content -Force -Path (Join-Path $workspaceDir 'dsc.settings.json')

try {
Push-Location $workspaceDir
dsc -l debug resource list 2> $TestDrive/tracing.txt
} finally {
Pop-Location
}
"$TestDrive/tracing.txt" | Should -FileContentMatchExactly 'Using Resource Path: WorkspaceDir'
}

It 'ensure workspace settings override user and install settings' {

$script:dscDefaultv1Settings."resourcePath"."directories" = @("InstallDir")
$script:dscDefaultv1Settings | ConvertTo-Json -Depth 90 | Set-Content -Force -Path $script:dscSettingsFilePath

$env:XDG_CONFIG_HOME = Join-Path $TestDrive 'xdg'
$userSettingsDir = Join-Path $env:XDG_CONFIG_HOME 'dsc'
New-Item -ItemType Directory -Path $userSettingsDir -Force | Out-Null
$script:dscDefaultv1Settings."resourcePath"."directories" = @("UserDir")
$script:dscDefaultv1Settings | ConvertTo-Json -Depth 90 | Set-Content -Force -Path (Join-Path $userSettingsDir 'dsc.settings.json')

$workspaceDir = Join-Path $TestDrive 'workspace'
New-Item -ItemType Directory -Path $workspaceDir -Force | Out-Null
$script:dscDefaultv1Settings."resourcePath"."directories" = @("WorkspaceDir")
$script:dscDefaultv1Settings | ConvertTo-Json -Depth 90 | Set-Content -Force -Path (Join-Path $workspaceDir 'dsc.settings.json')

# user settings override install settings
dsc -l debug resource list 2> $TestDrive/tracing.txt
"$TestDrive/tracing.txt" | Should -FileContentMatchExactly 'Using Resource Path: UserDir'

# workspace settings override user settings
try {
Push-Location $workspaceDir
dsc -l debug resource list 2> $TestDrive/tracing.txt
} finally {
Pop-Location
}
"$TestDrive/tracing.txt" | Should -FileContentMatchExactly 'Using Resource Path: WorkspaceDir'
}

It 'ensure policy overrides workspace settings' {

if (! $IsWindows) {
Set-ItResult -Skip -Because "Setting policy requires sudo"
return
}

$script:dscDefaultv1Settings."resourcePath"."directories" = @("PolicyDir")
# only define resourcePath as policy so the tracing field stays overridable by '-l debug'
@{ resourcePath = $script:dscDefaultv1Settings."resourcePath" } | ConvertTo-Json -Depth 90 | Set-Content -Force -Path $script:policyFilePath

$workspaceDir = Join-Path $TestDrive 'workspace'
New-Item -ItemType Directory -Path $workspaceDir -Force | Out-Null
$script:dscDefaultv1Settings."resourcePath"."directories" = @("WorkspaceDir")
$script:dscDefaultv1Settings | ConvertTo-Json -Depth 90 | Set-Content -Force -Path (Join-Path $workspaceDir 'dsc.settings.json')

try {
Push-Location $workspaceDir
dsc -l debug resource list 2> $TestDrive/tracing.txt
} finally {
Pop-Location
}
"$TestDrive/tracing.txt" | Should -FileContentMatchExactly 'Using Resource Path: PolicyDir'
}
}
11 changes: 7 additions & 4 deletions lib/dsc-lib/locales/en-us.toml
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ importingParametersFromInput = "Importing parameters from simple input"
invalidParamsFormat = "Invalid parameters format: %{error}"

[discovery.commandDiscovery]
couldNotReadSetting = "Could not read 'resourcePath' setting"
appendingEnvPath = "Appending PATH to resourcePath"
originalPath = "Original PATH: %{path}"
failedGetEnvPath = "Failed to get PATH environment variable"
Expand Down Expand Up @@ -923,11 +922,15 @@ utf16Conversion = "Failed to convert UTF-16 bytes to string"
[progress]
failedToSerialize = "Failed to serialize progress JSON: %{json}"

[settings]
failedToGetExePath = "Can't get 'dsc' executable path to locate settings files"
notFoundSettingsFile = "Settings file not found: %{path}"
loadedSettingsFile = "Loaded settings file: %{path}"
invalidSettingsFile = "Invalid settings file '%{path}': %{error}"
missingSettingsSchemaVersion = "Settings file '%{path}' is missing schema version key '%{version}'"

[util]
foundSetting = "Found setting '%{name}' in %{path}"
notFoundSetting = "Setting '%{name}' not found in %{path}"
failedToGetExePath = "Can't get 'dsc' executable path"
settingNotFound = "Setting '%{name}' not found"
failedToAbsolutizePath = "Failed to absolutize path '%{path}'"
executableNotFoundInWorkingDirectory = "Executable '%{executable}' not found with working directory '%{cwd}'"
executableNotFound = "Executable '%{executable}' not found"
Expand Down
64 changes: 6 additions & 58 deletions lib/dsc-lib/src/discovery/command_discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::extensions::dscextension::{self, DscExtension, Capability as Extensio
use crate::extensions::extension_manifest::ExtensionManifest;
use crate::progress::{ProgressBar, ProgressFormat};
use crate::schemas::transforms::idiomaticize_externally_tagged_enum;
use crate::settings::get_settings;
use rust_i18n::t;
use schemars::JsonSchema;
use serde::Deserialize;
Expand All @@ -25,7 +26,6 @@ use std::path::{Path, PathBuf};
use std::str::FromStr;
use tracing::{debug, info, trace, warn};

use crate::util::get_setting;
use crate::util::{canonicalize_which, get_exe_path};

// NOTE: if new types of file extensions are added, ensure they are added to `process_discover_args` in `lib/dsc-lib/src/extensions/discover.rs`
Expand Down Expand Up @@ -62,28 +62,6 @@ pub struct CommandDiscovery {
discovery_mode: ResourceDiscoveryMode,
}

#[derive(Deserialize)]
pub struct ResourcePathSetting {
/// whether to allow overriding with the `DSC_RESOURCE_PATH` environment variable
#[serde(rename = "allowEnvOverride")]
allow_env_override: bool,
/// whether to append the PATH environment variable to the list of resource directories
#[serde(rename = "appendEnvPath")]
append_env_path: bool,
/// array of directories that DSC should search for non-built-in resources
directories: Vec<String>
}

impl Default for ResourcePathSetting {
fn default() -> ResourcePathSetting {
ResourcePathSetting {
allow_env_override: true,
append_env_path: true,
directories: vec![],
}
}
}

impl CommandDiscovery {
#[must_use]
pub fn new(progress_format: ProgressFormat) -> CommandDiscovery {
Expand All @@ -96,48 +74,18 @@ impl CommandDiscovery {
#[must_use]
pub fn get_extensions(&self) -> DiscoveryExtensionCache { locked_clone!(EXTENSIONS) }

fn get_resource_path_setting() -> Result<ResourcePathSetting, DscError>
{
if let Ok(v) = get_setting("resourcePath") {
// if there is a policy value defined - use it; otherwise use setting value
if v.policy != serde_json::Value::Null {
match serde_json::from_value::<ResourcePathSetting>(v.policy) {
Ok(v) => {
return Ok(v);
},
Err(e) => { return Err(DscError::Setting(format!("{e}"))); }
}
} else if v.setting != serde_json::Value::Null {
match serde_json::from_value::<ResourcePathSetting>(v.setting) {
Ok(v) => {
return Ok(v);
},
Err(e) => { return Err(DscError::Setting(format!("{e}"))); }
}
}
}

Err(DscError::Setting(t!("discovery.commandDiscovery.couldNotReadSetting").to_string()))
}

fn get_resource_paths() -> Result<Vec<PathBuf>, DscError>
{
let mut resource_path_setting = ResourcePathSetting::default();

match Self::get_resource_path_setting() {
Ok(v) => {
resource_path_setting = v;
},
Err(e) => {
debug!("{e}");
}
}
let resolved_resource_path = get_settings().resource_path.clone();
// a policy-scoped resourcePath cannot be overridden by the environment variable
let allow_env_override = resolved_resource_path.value.allow_env_override && !resolved_resource_path.is_policy();
let resource_path_setting = resolved_resource_path.value;

let mut using_custom_path = false;
let mut paths: Vec<PathBuf> = vec![];

let dsc_resource_path = env::var_os("DSC_RESOURCE_PATH");
if resource_path_setting.allow_env_override && dsc_resource_path.is_some() {
if allow_env_override && dsc_resource_path.is_some() {
if let Some(value) = dsc_resource_path {
debug!("DSC_RESOURCE_PATH: {:?}", value.to_string_lossy());
using_custom_path = true;
Expand Down
4 changes: 2 additions & 2 deletions lib/dsc-lib/src/dscresources/command_resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use clap::ValueEnum;
use dsc_lib_security_context::{SecurityContext, get_security_context};
use jsonschema::Validator;
use rust_i18n::t;
use serde::Deserialize;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use std::{collections::HashMap, env, path::Path, process::Stdio};
use crate::{configure::{config_doc::{ExecutionKind, SecurityContextKind}, config_result::{ResourceGetResult, ResourceTestResult}}, dscresources::resource_manifest::{ExportSchemaKind, ExportSchemaOrFiltering, SchemaArgKind}, types::{ExitCodesMap}, util::canonicalize_which};
Expand Down Expand Up @@ -1305,7 +1305,7 @@ fn validate_security_context(required_security_context: &Option<SecurityContextK
Ok(())
}

#[derive(Clone, Debug, PartialEq, Eq, Deserialize, ValueEnum)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema, ValueEnum)]
pub enum TraceLevel {
#[serde(rename = "ERROR")]
Error,
Expand Down
1 change: 1 addition & 0 deletions lib/dsc-lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub mod extensions;
pub mod functions;
pub mod parser;
pub mod progress;
pub mod settings;
pub mod types;
pub mod util;

Expand Down
Loading
Loading