Skip to content

Commit 6b181cd

Browse files
authored
Merge branch 'PowerShell:main' into main
2 parents 51e2ddf + 92a40d9 commit 6b181cd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1979
-485
lines changed

README.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,11 @@ With DSCv3, you can:
1818

1919
### Differences from PowerShell DSC
2020

21-
This project is the next generation of DSC and leverages the
22-
[PSDesiredStateConfiguration module][00] to maintain compatibility with existing PowerShell based
23-
resources.
24-
2521
DSCv3 differs from PowerShell DSC in a few important ways:
2622

2723
- DSCv3 doesn't depend on PowerShell. You can use DSCv3 without PowerShell installed and manage
2824
resources written in bash, python, C#, Go, or any other language.
25+
- DSCv3 use of PowerShell based resources does not depend on PSDesiredStateConfiguration module
2926
- DSCv3 doesn't include a local configuration manager. DSCv3 is invoked as a command. It doesn't
3027
run as a service.
3128
- Non-PowerShell resources define their schemas with JSON files, not MOF files.

dsc/Cargo.lock

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dsc/examples/brew_uninstall.dsc.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ resources:
99
- name: os_check
1010
type: Microsoft/OSInfo
1111
properties:
12-
family: MacOS
12+
family: macOS
1313
- name: brew
1414
type: DSC.PackageManagement/Brew
1515
properties:

dsc/examples/osinfo.parameters.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"parameters": {
3+
"osFamily": "macOS"
4+
}
5+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json",
3+
"parameters": {
4+
"osFamily": {
5+
"type": "string",
6+
"defaultValue": "[concat('Win','dows')]",
7+
"allowedValues": [
8+
"Windows",
9+
"Linux",
10+
"macOS"
11+
]
12+
}
13+
},
14+
"resources": [
15+
{
16+
"name": "os",
17+
"type": "Microsoft/OSInfo",
18+
"properties": {
19+
"family": "[parameters('osFamily')]"
20+
}
21+
},
22+
{
23+
"name": "another os instance",
24+
"type": "Microsoft/OSInfo",
25+
"properties": {
26+
"family": "macOS"
27+
}
28+
},
29+
{
30+
"name": "path",
31+
"type": "Microsoft.DSC.Debug/Echo",
32+
"properties": {
33+
"output": "[envvar('PATH')]"
34+
}
35+
}
36+
]
37+
}

dsc/locales/en-us.toml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ _version = 1
44
about = "Apply configuration or invoke specific DSC resources"
55
traceFormat = "Trace format to use"
66
traceLevel = "Trace level to use"
7+
progressFormat = "Progress format to use"
78
completer = "Generate a shell completion script"
89
configAbout = "Apply a configuration document"
910
parameters = "Parameters to pass to the configuration as JSON or YAML"
@@ -41,7 +42,7 @@ foundProcesses = "Found processes"
4142
failedToGetPid = "Could not get current process id"
4243
currentPid = "Current process id"
4344
failedToGetProcess = "Could not get current process"
44-
terminatingSubprocess ="Terminating subprocesses of process"
45+
terminatingSubprocess = "Terminating subprocesses of process"
4546
terminatingProcess = "Terminating process"
4647
failedTerminatingProcess = "Failed to terminate process"
4748
storeMessage = """DSC.exe is a command-line tool and cannot be run directly from the Windows Store or Explorer.
@@ -57,11 +58,11 @@ failedToOpenFile = "Failed to open included file"
5758
invalidFileContent = "Invalid UTF-8 sequence in included file"
5859
invalidFile = "Failed to read the configuration file as YAML or JSON"
5960
resolvingParameters = "Resolving parameters from file"
60-
failedParseParametersFile = "Failed to parse parameters file to JSON"
61-
failedResolveParametersFile = "Failed to resolve parameters file"
62-
noParametersFile = "No parameters file found"
61+
failedParseParametersFile = "Failed to parse parameters file or conetnt to JSON"
62+
couldNotReadParametersFile = "Could not read parameters file"
6363
invalidPath = "Include path must not contain '..'"
6464
failedGetCurrentDirectory = "Failed to get current directory"
65+
noParameters = "No parameters specified"
6566

6667
[resource_command]
6768
implementedAs = "implemented as"
@@ -99,7 +100,7 @@ noManifest = "Resource does not have a manifest"
99100
tableHeader_type = "Type"
100101
tableHeader_kind = "Kind"
101102
tableHeader_version = "Version"
102-
tableheader_capabilities = "Capabilities"
103+
tableHeader_capabilities = "Capabilities"
103104
tableHeader_adapter = "RequireAdapter"
104105
tableHeader_description = "Description"
105106
invalidManifest = "Error in manifest for"

dsc/src/args.rs

Lines changed: 3 additions & 0 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 dsc_lib::util::ProgressFormat;
78
use rust_i18n::t;
89
use serde::Deserialize;
910

@@ -33,6 +34,8 @@ pub struct Args {
3334
pub trace_level: Option<TraceLevel>,
3435
#[clap(short = 't', long, help = t!("args.traceFormat").to_string(), value_enum)]
3536
pub trace_format: Option<TraceFormat>,
37+
#[clap(short = 'p', long, help = t!("args.progressFormat").to_string(), value_enum)]
38+
pub progress_format: Option<ProgressFormat>,
3639
}
3740

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

dsc/src/main.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use rust_i18n::{i18n, t};
88
use std::{io, process::exit};
99
use sysinfo::{Process, RefreshKind, System, get_current_pid, ProcessRefreshKind};
1010
use tracing::{error, info, warn, debug};
11+
use dsc_lib::util::ProgressFormat;
1112

1213
#[cfg(debug_assertions)]
1314
use crossterm::event;
@@ -40,6 +41,8 @@ fn main() {
4041

4142
debug!("{}: {}", t!("main.usingDscVersion"), env!("CARGO_PKG_VERSION"));
4243

44+
let progress_format = args.progress_format.unwrap_or( ProgressFormat::Default );
45+
4346
match args.subcommand {
4447
SubCommand::Completer { shell } => {
4548
info!("{} {:?}", t!("main.generatingCompleter"), shell);
@@ -50,19 +53,19 @@ fn main() {
5053
if let Some(file_name) = parameters_file {
5154
info!("{}: {file_name}", t!("main.readingParametersFile"));
5255
match std::fs::read_to_string(&file_name) {
53-
Ok(parameters) => subcommand::config(&subcommand, &Some(parameters), system_root.as_ref(), &as_group, &as_include),
56+
Ok(parameters) => subcommand::config(&subcommand, &Some(parameters), system_root.as_ref(), &as_group, &as_include, progress_format),
5457
Err(err) => {
55-
error!("{} '{file_name}': {err}", t!("main.failedToReadParametersFile"));
58+
error!("{} '{file_name}': {err}", t!("main.failedReadingParametersFile"));
5659
exit(util::EXIT_INVALID_INPUT);
5760
}
5861
}
5962
}
6063
else {
61-
subcommand::config(&subcommand, &parameters, system_root.as_ref(), &as_group, &as_include);
64+
subcommand::config(&subcommand, &parameters, system_root.as_ref(), &as_group, &as_include, progress_format);
6265
}
6366
},
6467
SubCommand::Resource { subcommand } => {
65-
subcommand::resource(&subcommand);
68+
subcommand::resource(&subcommand, progress_format);
6669
},
6770
SubCommand::Schema { dsc_type , output_format } => {
6871
let schema = util::get_schema(dsc_type);
@@ -102,13 +105,13 @@ fn ctrlc_handler() {
102105

103106
fn terminate_subprocesses(sys: &System, process: &Process) {
104107
info!("{}: {:?} {}", t!("main.terminatingSubprocess"), process.name(), process.pid());
105-
for subprocess in sys.processes().values().filter(|p| p.parent().map_or(false, |parent| parent == process.pid())) {
108+
for subprocess in sys.processes().values().filter(|p| p.parent().is_some_and(|parent| parent == process.pid())) {
106109
terminate_subprocesses(sys, subprocess);
107110
}
108111

109112
info!("{}: {:?} {}", t!("main.terminatingProcess"), process.name(), process.pid());
110113
if !process.kill() {
111-
error!("{}: {:?} {}", t!("main.failedTerminateProcess"), process.name(), process.pid());
114+
error!("{}: {:?} {}", t!("main.failedTerminatingProcess"), process.name(), process.pid());
112115
}
113116
}
114117

@@ -157,7 +160,7 @@ fn check_store() {
157160
};
158161

159162
// MS Store runs app using `sihost.exe`
160-
if parent_process.name().to_ascii_lowercase() == "sihost.exe" || parent_process.name().to_ascii_lowercase() == "explorer.exe"{
163+
if parent_process.name().eq_ignore_ascii_case("sihost.exe") || parent_process.name().eq_ignore_ascii_case("explorer.exe") {
161164
eprintln!("{}", t!("main.storeMessage"));
162165
// wait for keypress
163166
let _ = io::stdin().read(&mut [0u8]).unwrap();

dsc/src/resolve.rs

Lines changed: 82 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4-
use dsc_lib::configure::config_doc::Configuration;
54
use dsc_lib::util::parse_input_to_json;
65
use rust_i18n::t;
76
use schemars::JsonSchema;
@@ -14,14 +13,30 @@ use tracing::{debug, info};
1413
use crate::util::DSC_CONFIG_ROOT;
1514

1615
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
17-
pub struct Include {
16+
pub enum IncludeKind {
1817
/// The path to the file to include. Path is relative to the file containing the include
1918
/// and not allowed to reference parent directories. If a configuration document is used
2019
/// instead of a file, then the path is relative to the current working directory.
2120
#[serde(rename = "configurationFile")]
22-
pub configuration_file: String,
21+
ConfigurationFile(String),
22+
#[serde(rename = "configurationContent")]
23+
ConfigurationContent(String),
24+
}
25+
26+
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
27+
pub enum IncludeParametersKind {
2328
#[serde(rename = "parametersFile")]
24-
pub parameters_file: Option<String>,
29+
ParametersFile(String),
30+
#[serde(rename = "parametersContent")]
31+
ParametersContent(String),
32+
}
33+
34+
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
35+
pub struct Include {
36+
#[serde(flatten)]
37+
pub configuration: IncludeKind,
38+
#[serde(flatten)]
39+
pub parameters: Option<IncludeParametersKind>,
2540
}
2641

2742
/// Read the file specified in the Include input and return the content as a JSON string.
@@ -51,74 +66,83 @@ pub fn get_contents(input: &str) -> Result<(Option<String>, String), String> {
5166
}
5267
};
5368

54-
let include_path = normalize_path(Path::new(&include.configuration_file))?;
69+
let config_json = match include.configuration {
70+
IncludeKind::ConfigurationFile(file_path) => {
71+
let include_path = normalize_path(Path::new(&file_path))?;
5572

56-
// read the file specified in the Include input
57-
let mut buffer: Vec<u8> = Vec::new();
58-
match File::open(&include_path) {
59-
Ok(mut file) => {
60-
match file.read_to_end(&mut buffer) {
61-
Ok(_) => (),
73+
// read the file specified in the Include input
74+
let mut buffer: Vec<u8> = Vec::new();
75+
match File::open(&include_path) {
76+
Ok(mut file) => {
77+
match file.read_to_end(&mut buffer) {
78+
Ok(_) => (),
79+
Err(err) => {
80+
return Err(format!("{} '{include_path:?}': {err}", t!("resolve.failedToReadFile")));
81+
}
82+
}
83+
},
6284
Err(err) => {
63-
return Err(format!("{} '{include_path:?}': {err}", t!("resolve.failedToReadFile")));
85+
return Err(format!("{} '{include_path:?}': {err}", t!("resolve.failedToOpenFile")));
6486
}
6587
}
66-
},
67-
Err(err) => {
68-
return Err(format!("{} '{include_path:?}': {err}", t!("resolve.failedToOpenFile")));
69-
}
70-
}
71-
// convert the buffer to a string
72-
let include_content = match String::from_utf8(buffer) {
73-
Ok(input) => input,
74-
Err(err) => {
75-
return Err(format!("{} '{include_path:?}': {err}", t!("resolve.invalidFileContent")));
76-
}
77-
};
88+
// convert the buffer to a string
89+
let include_content = match String::from_utf8(buffer) {
90+
Ok(input) => input,
91+
Err(err) => {
92+
return Err(format!("{} '{include_path:?}': {err}", t!("resolve.invalidFileContent")));
93+
}
94+
};
7895

79-
// try to deserialize the Include content as YAML first
80-
let configuration: Configuration = match serde_yaml::from_str(&include_content) {
81-
Ok(configuration) => configuration,
82-
Err(_err) => {
83-
// if that fails, try to deserialize it as JSON
84-
match serde_json::from_str(&include_content) {
85-
Ok(configuration) => configuration,
96+
match parse_input_to_json(&include_content) {
97+
Ok(json) => json,
8698
Err(err) => {
8799
return Err(format!("{} '{include_path:?}': {err}", t!("resolve.invalidFile")));
88100
}
89101
}
102+
},
103+
IncludeKind::ConfigurationContent(text) => {
104+
match parse_input_to_json(&text) {
105+
Ok(json) => json,
106+
Err(err) => {
107+
return Err(format!("{}: {err}", t!("resolve.invalidFile")));
108+
}
109+
}
90110
}
91111
};
92112

93-
// serialize the Configuration as JSON
94-
let config_json = match serde_json::to_string(&configuration) {
95-
Ok(json) => json,
96-
Err(err) => {
97-
return Err(format!("JSON: {err}"));
98-
}
99-
};
100-
101-
let parameters = if let Some(parameters_file) = include.parameters_file {
102-
// combine the path with DSC_CONFIG_ROOT
103-
let parameters_file = normalize_path(Path::new(&parameters_file))?;
104-
info!("{} '{parameters_file:?}'", t!("resolve.resolvingParameters"));
105-
match std::fs::read_to_string(&parameters_file) {
106-
Ok(parameters) => {
107-
let parameters_json = match parse_input_to_json(&parameters) {
108-
Ok(json) => json,
109-
Err(err) => {
110-
return Err(format!("{} '{parameters_file:?}': {err}", t!("resolve.failedParseParametersFile")));
111-
}
112-
};
113-
Some(parameters_json)
114-
},
115-
Err(err) => {
116-
return Err(format!("{} '{parameters_file:?}': {err}", t!("resolve.failedResolveParametersFile")));
113+
let parameters = match include.parameters {
114+
Some(IncludeParametersKind::ParametersFile(file_path)) => {
115+
// combine the path with DSC_CONFIG_ROOT
116+
let parameters_file = normalize_path(Path::new(&file_path))?;
117+
info!("{} '{parameters_file:?}'", t!("resolve.resolvingParameters"));
118+
match std::fs::read_to_string(&parameters_file) {
119+
Ok(parameters) => {
120+
let parameters_json = match parse_input_to_json(&parameters) {
121+
Ok(json) => json,
122+
Err(err) => {
123+
return Err(format!("{} '{parameters_file:?}': {err}", t!("resolve.failedParseParametersFile")));
124+
}
125+
};
126+
Some(parameters_json)
127+
},
128+
Err(err) => {
129+
return Err(format!("{} '{parameters_file:?}': {err}", t!("resolve.couldNotReadParametersFile")));
130+
}
117131
}
132+
},
133+
Some(IncludeParametersKind::ParametersContent(text)) => {
134+
let parameters_json = match parse_input_to_json(&text) {
135+
Ok(json) => json,
136+
Err(err) => {
137+
return Err(format!("{}: {err}", t!("resolve.failedParseParametersFile")));
138+
}
139+
};
140+
Some(parameters_json)
141+
},
142+
None => {
143+
debug!("{}", t!("resolve.noParameters"));
144+
None
118145
}
119-
} else {
120-
debug!("{}", t!("resolve.noParametersFile"));
121-
None
122146
};
123147

124148
Ok((parameters, config_json))

0 commit comments

Comments
 (0)