Skip to content

Commit 3e3d473

Browse files
authored
Merge branch 'main' into mounted-path
2 parents 677b5ec + 71fac96 commit 3e3d473

File tree

12 files changed

+455
-45
lines changed

12 files changed

+455
-45
lines changed

build.ps1

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ $filesForWindowsPackage = @(
4848
'wmi.resource.ps1',
4949
'windows_baseline.dsc.yaml',
5050
'windows_inventory.dsc.yaml'
51+
'dsc_default.settings.json',
52+
'dsc.settings.json'
5153
)
5254

5355
$filesForLinuxPackage = @(
@@ -62,7 +64,9 @@ $filesForLinuxPackage = @(
6264
'powershell.dsc.resource.json',
6365
'psDscAdapter/',
6466
'RunCommandOnSet.dsc.resource.json',
65-
'runcommandonset'
67+
'runcommandonset',
68+
'dsc_default.settings.json',
69+
'dsc.settings.json'
6670
)
6771

6872
$filesForMacPackage = @(
@@ -77,7 +81,9 @@ $filesForMacPackage = @(
7781
'powershell.dsc.resource.json',
7882
'psDscAdapter/',
7983
'RunCommandOnSet.dsc.resource.json',
80-
'runcommandonset'
84+
'runcommandonset',
85+
'dsc_default.settings.json',
86+
'dsc.settings.json'
8187
)
8288

8389
# the list of files other than the binaries which need to be executable

dsc/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dsc/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "dsc"
3-
version = "3.0.0-rc.1"
3+
version = "3.1.0-preview.1"
44
edition = "2021"
55

66
[profile.release]

dsc/copy_files.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
dsc.settings.json
2+
dsc_default.settings.json

dsc/dsc.settings.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"resourcePath": {
3+
"allowEnvOverride": true,
4+
"appendEnvPath": true,
5+
"directories": []
6+
},
7+
"tracing": {
8+
"level": "WARN",
9+
"format": "Default",
10+
"allowOverride": true
11+
}
12+
}

dsc/dsc_default.settings.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"1": {
3+
"resourcePath": {
4+
"allowEnvOverride": true,
5+
"appendEnvPath": true,
6+
"directories": []
7+
},
8+
"tracing": {
9+
"level": "WARN",
10+
"format": "Default",
11+
"allowOverride": true
12+
}
13+
}
14+
}

dsc/src/args.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use clap::{Parser, Subcommand, ValueEnum};
55
use clap_complete::Shell;
66
use dsc_lib::dscresources::command_resource::TraceLevel;
7+
use serde::Deserialize;
78

89
#[derive(Debug, Clone, PartialEq, Eq, ValueEnum)]
910
pub enum OutputFormat {
@@ -12,7 +13,7 @@ pub enum OutputFormat {
1213
Yaml,
1314
}
1415

15-
#[derive(Debug, Clone, PartialEq, Eq, ValueEnum)]
16+
#[derive(Debug, Clone, PartialEq, Eq, ValueEnum, Deserialize)]
1617
pub enum TraceFormat {
1718
Default,
1819
Plaintext,
@@ -29,8 +30,8 @@ pub struct Args {
2930
pub subcommand: SubCommand,
3031
#[clap(short = 'l', long, help = "Trace level to use", value_enum)]
3132
pub trace_level: Option<TraceLevel>,
32-
#[clap(short = 'f', long, help = "Trace format to use", value_enum, default_value = "default")]
33-
pub trace_format: TraceFormat,
33+
#[clap(short = 'f', long, help = "Trace format to use", value_enum)]
34+
pub trace_format: Option<TraceFormat>,
3435
}
3536

3637
#[derive(Debug, PartialEq, Eq, Subcommand)]

dsc/src/util.rs

Lines changed: 94 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@ use dsc_lib::{
2323
}, resource_manifest::ResourceManifest
2424
},
2525
util::parse_input_to_json,
26+
util::get_setting,
2627
};
2728
use jsonschema::Validator;
2829
use path_absolutize::Absolutize;
2930
use schemars::{schema_for, schema::RootSchema};
31+
use serde::Deserialize;
3032
use serde_json::Value;
3133
use std::collections::HashMap;
3234
use std::env;
@@ -39,7 +41,7 @@ use syntect::{
3941
parsing::SyntaxSet,
4042
util::{as_24_bit_terminal_escaped, LinesWithEndings}
4143
};
42-
use tracing::{Level, debug, error, warn, trace};
44+
use tracing::{Level, debug, error, info, warn, trace};
4345
use tracing_subscriber::{filter::EnvFilter, layer::SubscriberExt, Layer};
4446
use tracing_indicatif::IndicatifLayer;
4547

@@ -55,6 +57,27 @@ pub const EXIT_DSC_RESOURCE_NOT_FOUND: i32 = 7;
5557
pub const DSC_CONFIG_ROOT: &str = "DSC_CONFIG_ROOT";
5658
pub const DSC_TRACE_LEVEL: &str = "DSC_TRACE_LEVEL";
5759

60+
#[derive(Deserialize)]
61+
pub struct TracingSetting {
62+
/// Trace level to use - see pub enum `TraceLevel` in `dsc_lib\src\dscresources\command_resource.rs`
63+
level: TraceLevel,
64+
/// Trace format to use - see pub enum `TraceFormat` in `dsc\src\args.rs`
65+
format: TraceFormat,
66+
/// Whether the 'level' can be overrridden by `DSC_TRACE_LEVEL` environment variable
67+
#[serde(rename = "allowOverride")]
68+
allow_override: bool
69+
}
70+
71+
impl Default for TracingSetting {
72+
fn default() -> TracingSetting {
73+
TracingSetting {
74+
level: TraceLevel::Warn,
75+
format: TraceFormat::Default,
76+
allow_override: true,
77+
}
78+
}
79+
}
80+
5881
/// Get string representation of JSON value.
5982
///
6083
/// # Arguments
@@ -268,46 +291,92 @@ pub fn write_output(json: &str, format: &Option<OutputFormat>) {
268291
}
269292
}
270293

271-
pub fn enable_tracing(trace_level: &Option<TraceLevel>, trace_format: &TraceFormat) {
272-
let tracing_level = match trace_level {
273-
Some(level) => level,
274-
None => {
275-
// use DSC_TRACE_LEVEL env var if set
276-
match env::var(DSC_TRACE_LEVEL) {
277-
Ok(level) => {
278-
match level.to_ascii_uppercase().as_str() {
279-
"ERROR" => &TraceLevel::Error,
280-
"WARN" => &TraceLevel::Warn,
281-
"INFO" => &TraceLevel::Info,
282-
"DEBUG" => &TraceLevel::Debug,
283-
"TRACE" => &TraceLevel::Trace,
284-
_ => {
285-
warn!("Invalid DSC_TRACE_LEVEL value '{level}', defaulting to 'warn'");
286-
&TraceLevel::Warn
287-
},
288-
}
294+
#[allow(clippy::too_many_lines)]
295+
pub fn enable_tracing(trace_level_arg: &Option<TraceLevel>, trace_format_arg: &Option<TraceFormat>) {
296+
297+
let mut policy_is_used = false;
298+
let mut tracing_setting = TracingSetting::default();
299+
300+
let default_filter = EnvFilter::try_from_default_env()
301+
.or_else(|_| EnvFilter::try_new("warn"))
302+
.unwrap_or_default()
303+
.add_directive(Level::WARN.into());
304+
let default_indicatif_layer = IndicatifLayer::new();
305+
let default_layer = tracing_subscriber::fmt::Layer::default().with_writer(default_indicatif_layer.get_stderr_writer());
306+
let default_fmt = default_layer
307+
.with_ansi(true)
308+
.with_level(true)
309+
.boxed();
310+
let default_subscriber = tracing_subscriber::Registry::default().with(default_fmt).with(default_filter).with(default_indicatif_layer);
311+
let default_guard = tracing::subscriber::set_default(default_subscriber);
312+
313+
// read setting/policy from files
314+
if let Ok(v) = get_setting("tracing") {
315+
if v.policy != serde_json::Value::Null {
316+
match serde_json::from_value::<TracingSetting>(v.policy) {
317+
Ok(v) => {
318+
tracing_setting = v;
319+
policy_is_used = true;
320+
},
321+
Err(e) => { error!("{e}"); }
322+
}
323+
} else if v.setting != serde_json::Value::Null {
324+
match serde_json::from_value::<TracingSetting>(v.setting) {
325+
Ok(v) => {
326+
tracing_setting = v;
289327
},
290-
Err(_) => &TraceLevel::Warn,
328+
Err(e) => { error!("{e}"); }
291329
}
292330
}
331+
} else {
332+
error!("Could not read 'tracing' setting");
333+
}
334+
335+
// override with DSC_TRACE_LEVEL env var if permitted
336+
if tracing_setting.allow_override {
337+
if let Ok(level) = env::var(DSC_TRACE_LEVEL) {
338+
tracing_setting.level = match level.to_ascii_uppercase().as_str() {
339+
"ERROR" => TraceLevel::Error,
340+
"WARN" => TraceLevel::Warn,
341+
"INFO" => TraceLevel::Info,
342+
"DEBUG" => TraceLevel::Debug,
343+
"TRACE" => TraceLevel::Trace,
344+
_ => {
345+
warn!("Invalid DSC_TRACE_LEVEL value '{level}', defaulting to 'warn'");
346+
TraceLevel::Warn
347+
}
348+
}
349+
}
350+
}
351+
352+
// command-line args override setting value, but not policy
353+
if !policy_is_used {
354+
if let Some(v) = trace_level_arg {
355+
tracing_setting.level = v.clone();
356+
};
357+
if let Some(v) = trace_format_arg {
358+
tracing_setting.format = v.clone();
359+
};
293360
};
294361

295-
let tracing_level = match tracing_level {
362+
// convert to 'tracing' crate type
363+
let tracing_level = match tracing_setting.level {
296364
TraceLevel::Error => Level::ERROR,
297365
TraceLevel::Warn => Level::WARN,
298366
TraceLevel::Info => Level::INFO,
299367
TraceLevel::Debug => Level::DEBUG,
300368
TraceLevel::Trace => Level::TRACE,
301369
};
302370

371+
// enable tracing
303372
let filter = EnvFilter::try_from_default_env()
304-
.or_else(|_| EnvFilter::try_new("warning"))
373+
.or_else(|_| EnvFilter::try_new("warn"))
305374
.unwrap_or_default()
306375
.add_directive(tracing_level.into());
307376
let indicatif_layer = IndicatifLayer::new();
308377
let layer = tracing_subscriber::fmt::Layer::default().with_writer(indicatif_layer.get_stderr_writer());
309378
let with_source = tracing_level == Level::DEBUG || tracing_level == Level::TRACE;
310-
let fmt = match trace_format {
379+
let fmt = match tracing_setting.format {
311380
TraceFormat::Default => {
312381
layer
313382
.with_ansi(true)
@@ -337,12 +406,14 @@ pub fn enable_tracing(trace_level: &Option<TraceLevel>, trace_format: &TraceForm
337406

338407
let subscriber = tracing_subscriber::Registry::default().with(fmt).with(filter).with(indicatif_layer);
339408

409+
drop(default_guard);
340410
if tracing::subscriber::set_global_default(subscriber).is_err() {
341411
eprintln!("Unable to set global default tracing subscriber. Tracing is diabled.");
342412
}
343413

344414
// set DSC_TRACE_LEVEL for child processes
345415
env::set_var(DSC_TRACE_LEVEL, tracing_level.to_string().to_ascii_lowercase());
416+
info!("Trace-level is {:?}", tracing_setting.level);
346417
}
347418

348419
/// Validate the JSON against the schema.

dsc/tests/dsc_settings.tests.ps1

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
Describe 'tests for dsc settings' {
5+
BeforeAll {
6+
7+
$script:policyFilePath = if ($IsWindows) {
8+
Join-Path $env:ProgramData "dsc" "dsc.settings.json"
9+
} else {
10+
"/etc/dsc/dsc.settings.json"
11+
}
12+
13+
$script:dscHome = (Get-Command dsc).Path | Split-Path
14+
$script:dscSettingsFilePath = Join-Path $script:dscHome "dsc.settings.json"
15+
$script:dscDefaultSettingsFilePath = Join-Path $script:dscHome "dsc_default.settings.json"
16+
17+
if ($IsWindows) { #"Setting policy on Linux requires sudo"
18+
$script:policyDirPath = $script:policyFilePath | Split-Path
19+
New-Item -ItemType Directory -Path $script:policyDirPath | Out-Null
20+
}
21+
22+
#create backups of settings files
23+
$script:dscSettingsFilePath_backup = Join-Path $script:dscHome "dsc.settings.json.backup"
24+
$script:dscDefaultSettingsFilePath_backup = Join-Path $script:dscHome "dsc_default.settings.json.backup"
25+
Copy-Item -Force -Path $script:dscSettingsFilePath -Destination $script:dscSettingsFilePath_backup
26+
Copy-Item -Force -Path $script:dscDefaultSettingsFilePath -Destination $script:dscDefaultSettingsFilePath_backup
27+
}
28+
29+
AfterAll {
30+
Remove-Item -Force -Path $script:dscSettingsFilePath_backup
31+
Remove-Item -Force -Path $script:dscDefaultSettingsFilePath_backup
32+
if ($IsWindows) { #"Setting policy on Linux requires sudo"
33+
Remove-Item -Recurse -Force -Path $script:policyDirPath
34+
}
35+
}
36+
37+
BeforeEach {
38+
$script:dscDefaultSettings = Get-Content -Raw -Path $script:dscDefaultSettingsFilePath_backup | ConvertFrom-Json
39+
$script:dscDefaultv1Settings = (Get-Content -Raw -Path $script:dscDefaultSettingsFilePath_backup | ConvertFrom-Json)."1"
40+
}
41+
42+
AfterEach {
43+
Copy-Item -Force -Path $script:dscSettingsFilePath_backup -Destination $script:dscSettingsFilePath
44+
Copy-Item -Force -Path $script:dscDefaultSettingsFilePath_backup -Destination $script:dscDefaultSettingsFilePath
45+
if ($IsWindows) { #"Setting policy on Linux requires sudo"
46+
Remove-Item -Path $script:policyFilePath -ErrorAction SilentlyContinue
47+
}
48+
}
49+
50+
It 'ensure a new tracing value in settings has effect' {
51+
52+
$script:dscDefaultv1Settings."tracing"."level" = "TRACE"
53+
$script:dscDefaultv1Settings | ConvertTo-Json -Depth 90 | Set-Content -Force -Path $script:dscSettingsFilePath
54+
55+
dsc resource list 2> $TestDrive/tracing.txt
56+
"$TestDrive/tracing.txt" | Should -FileContentMatchExactly "Trace-level is Trace"
57+
}
58+
59+
It 'ensure a new resource_path value in settings has effect' {
60+
61+
$script:dscDefaultv1Settings."resourcePath"."directories" = @("TestDir1","TestDir2")
62+
$script:dscDefaultv1Settings | ConvertTo-Json -Depth 90 | Set-Content -Force -Path $script:dscSettingsFilePath
63+
dsc -l debug resource list 2> $TestDrive/tracing.txt
64+
$expectedString = 'Using Resource Path: "TestDir1'+[System.IO.Path]::PathSeparator+'TestDir2'
65+
"$TestDrive/tracing.txt" | Should -FileContentMatchExactly $expectedString
66+
}
67+
68+
It 'Confirm settings override priorities' {
69+
70+
if (! $IsWindows) {
71+
Set-ItResult -Skip -Because "Setting policy requires sudo"
72+
return
73+
}
74+
75+
$script:dscDefaultv1Settings."tracing"."level" = "TRACE"
76+
$script:dscDefaultv1Settings."resourcePath"."directories" = @("PolicyDir")
77+
$script:dscDefaultv1Settings | ConvertTo-Json -Depth 90 | Set-Content -Force -Path $script:policyFilePath
78+
79+
$script:dscDefaultv1Settings."tracing"."level" = "TRACE"
80+
$script:dscDefaultv1Settings."resourcePath"."directories" = @("SettingsDir")
81+
$script:dscDefaultv1Settings | ConvertTo-Json -Depth 90 | Set-Content -Force -Path $script:dscSettingsFilePath
82+
83+
$script:dscDefaultSettings."1"."tracing"."level" = "TRACE"
84+
$script:dscDefaultSettings."1"."resourcePath"."directories" = @("Defaultv1SettingsDir")
85+
$script:dscDefaultSettings | ConvertTo-Json -Depth 90 | Set-Content -Force -Path $script:dscDefaultSettingsFilePath
86+
87+
# ensure policy overrides everything
88+
dsc -l debug resource list 2> $TestDrive/tracing.txt
89+
"$TestDrive/tracing.txt" | Should -FileContentMatchExactly "Trace-level is Trace"
90+
"$TestDrive/tracing.txt" | Should -FileContentMatchExactly 'Using Resource Path: "PolicyDir'
91+
92+
# without policy, command-line args have priority
93+
Remove-Item -Path $script:policyFilePath
94+
dsc -l debug resource list 2> $TestDrive/tracing.txt
95+
"$TestDrive/tracing.txt" | Should -FileContentMatchExactly "Trace-level is Debug"
96+
"$TestDrive/tracing.txt" | Should -FileContentMatchExactly 'Using Resource Path: "SettingsDir'
97+
98+
# without policy and command-line args, settings file is used
99+
dsc resource list 2> $TestDrive/tracing.txt
100+
"$TestDrive/tracing.txt" | Should -FileContentMatchExactly "Trace-level is Trace"
101+
"$TestDrive/tracing.txt" | Should -FileContentMatchExactly 'Using Resource Path: "SettingsDir'
102+
103+
# without policy and command-line args and settings file, the default settings file is used
104+
Remove-Item -Path $script:dscSettingsFilePath
105+
dsc resource list 2> $TestDrive/tracing.txt
106+
"$TestDrive/tracing.txt" | Should -FileContentMatchExactly "Trace-level is Trace"
107+
"$TestDrive/tracing.txt" | Should -FileContentMatchExactly 'Using Resource Path: "Defaultv1SettingsDir'
108+
}
109+
}

0 commit comments

Comments
 (0)