Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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