diff --git a/archive/registry/registry.dsc.resource.json b/archive/registry/registry.dsc.resource.json index 98110f41..6d819040 100644 --- a/archive/registry/registry.dsc.resource.json +++ b/archive/registry/registry.dsc.resource.json @@ -11,17 +11,23 @@ "executable": "registry", "args": [ "config", - "get" - ], - "input": "stdin" + "get", + { + "jsonInputArg": "--input", + "mandatory": true + } + ] }, "set": { "executable": "registry", "args": [ "config", - "set" + "set", + { + "jsonInputArg": "--input", + "mandatory": true + } ], - "input": "stdin", "implementsPretest": true, "return": "state" }, @@ -29,9 +35,12 @@ "executable": "registry", "args": [ "config", - "test" + "test", + { + "jsonInputArg": "--input", + "mandatory": true + } ], - "input": "stdin", "return": "state" }, "exitCodes": { diff --git a/build.ps1 b/build.ps1 index 9d1ad0a6..aed317c3 100644 --- a/build.ps1 +++ b/build.ps1 @@ -30,11 +30,15 @@ if ($GetPackageVersion) { $filesForWindowsPackage = @( 'dsc.exe', + 'dsc_default.settings.json', + 'dsc.settings.json', 'dscecho.exe', 'echo.dsc.resource.json', 'assertion.dsc.resource.json', 'group.dsc.resource.json', 'NOTICE.txt', + 'osinfo.exe', + 'osinfo.dsc.resource.json', 'powershell.dsc.resource.json', 'psDscAdapter/', 'reboot_pending.dsc.resource.json', @@ -48,12 +52,12 @@ $filesForWindowsPackage = @( 'wmi.resource.ps1', 'windows_baseline.dsc.yaml', 'windows_inventory.dsc.yaml' - 'dsc_default.settings.json', - 'dsc.settings.json' ) $filesForLinuxPackage = @( 'dsc', + 'dsc_default.settings.json', + 'dsc.settings.json' 'dscecho', 'echo.dsc.resource.json', 'assertion.dsc.resource.json', @@ -61,16 +65,18 @@ $filesForLinuxPackage = @( 'apt.dsc.resource.sh', 'group.dsc.resource.json', 'NOTICE.txt', + 'osinfo', + 'osinfo.dsc.resource.json', 'powershell.dsc.resource.json', 'psDscAdapter/', 'RunCommandOnSet.dsc.resource.json', - 'runcommandonset', - 'dsc_default.settings.json', - 'dsc.settings.json' + 'runcommandonset' ) $filesForMacPackage = @( 'dsc', + 'dsc_default.settings.json', + 'dsc.settings.json' 'dscecho', 'echo.dsc.resource.json', 'assertion.dsc.resource.json', @@ -78,12 +84,12 @@ $filesForMacPackage = @( 'brew.dsc.resource.sh', 'group.dsc.resource.json', 'NOTICE.txt', + 'osinfo', + 'osinfo.dsc.resource.json', 'powershell.dsc.resource.json', 'psDscAdapter/', 'RunCommandOnSet.dsc.resource.json', - 'runcommandonset', - 'dsc_default.settings.json', - 'dsc.settings.json' + 'runcommandonset' ) # the list of files other than the binaries which need to be executable diff --git a/dsc/Cargo.lock b/dsc/Cargo.lock index a4f4b1f1..45a56a51 100644 --- a/dsc/Cargo.lock +++ b/dsc/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -158,7 +158,16 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ - "bit-vec", + "bit-vec 0.6.3", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec 0.8.0", ] [[package]] @@ -167,6 +176,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitflags" version = "1.3.2" @@ -469,7 +484,7 @@ dependencies = [ [[package]] name = "dsc" -version = "3.1.0-preview.1" +version = "3.0.0-preview.12" dependencies = [ "clap", "clap_complete", @@ -477,7 +492,7 @@ dependencies = [ "ctrlc", "dsc_lib", "indicatif", - "jsonschema", + "jsonschema 0.26.1", "path-absolutize", "schemars", "serde", @@ -485,7 +500,7 @@ dependencies = [ "serde_yaml", "syntect", "sysinfo", - "thiserror", + "thiserror 2.0.6", "tracing", "tracing-indicatif", "tracing-subscriber", @@ -501,7 +516,7 @@ dependencies = [ "clap", "derive_builder", "indicatif", - "jsonschema", + "jsonschema 0.23.0", "linked-hash-map", "num-traits", "regex", @@ -511,7 +526,7 @@ dependencies = [ "serde", "serde_json", "serde_yaml", - "thiserror", + "thiserror 1.0.64", "tokio", "tracing", "tracing-indicatif", @@ -569,7 +584,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" dependencies = [ - "bit-set", + "bit-set 0.5.3", "regex", ] @@ -579,7 +594,18 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "531e46835a22af56d1e3b66f04844bed63158bc094a628bec1d321d9b4c44bf2" dependencies = [ - "bit-set", + "bit-set 0.5.3", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", +] + +[[package]] +name = "fancy-regex" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298" +dependencies = [ + "bit-set 0.8.0", "regex-automata 0.4.8", "regex-syntax 0.8.5", ] @@ -596,9 +622,9 @@ dependencies = [ [[package]] name = "fluent-uri" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7bd399b64ddd63a83cf40512c96007dafe9ac26cfc8c89c820a247c6f7d2376" +checksum = "1918b65d96df47d3591bed19c5cca17e3fa5d0707318e4b5ef2eae01764df7e5" dependencies = [ "borrow-or-share", "ref-cast", @@ -932,7 +958,7 @@ dependencies = [ "num-cmp", "once_cell", "percent-encoding", - "referencing", + "referencing 0.23.0", "regex-syntax 0.8.5", "serde", "serde_json", @@ -940,6 +966,30 @@ dependencies = [ "uuid-simd", ] +[[package]] +name = "jsonschema" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "893d6229c7315763ca0df9b29ab7661ee419f286577a02847c5521b462e071af" +dependencies = [ + "ahash", + "base64", + "bytecount", + "email_address", + "fancy-regex 0.14.0", + "fraction", + "idna 1.0.2", + "itoa", + "num-cmp", + "once_cell", + "percent-encoding", + "referencing 0.26.1", + "regex-syntax 0.8.5", + "serde", + "serde_json", + "uuid-simd", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -948,9 +998,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.159" +version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" [[package]] name = "linked-hash-map" @@ -1252,9 +1302,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "proc-macro2" -version = "1.0.87" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -1339,6 +1389,19 @@ dependencies = [ "serde_json", ] +[[package]] +name = "referencing" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb853437e467c693ac1dc8c1520105a31b8c2588544ff2f3cfa5a7c706c6c069" +dependencies = [ + "ahash", + "fluent-uri", + "once_cell", + "percent-encoding", + "serde_json", +] + [[package]] name = "regex" version = "1.11.0" @@ -1600,9 +1663,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.79" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -1637,16 +1700,16 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "thiserror", + "thiserror 1.0.64", "walkdir", "yaml-rust", ] [[package]] name = "sysinfo" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ae3f4f7d64646c46c4cae4e3f01d1c5d255c7406fdd7c7f999a94e488791" +checksum = "948512566b1895f93b1592c7574baeb2de842f224f2aab158799ecadb8ebbb46" dependencies = [ "core-foundation-sys", "libc", @@ -1662,7 +1725,16 @@ version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.64", +] + +[[package]] +name = "thiserror" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" +dependencies = [ + "thiserror-impl 2.0.6", ] [[package]] @@ -1676,6 +1748,17 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.1.8" diff --git a/dsc/Cargo.toml b/dsc/Cargo.toml index 8523f2e0..97c53e9b 100644 --- a/dsc/Cargo.toml +++ b/dsc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dsc" -version = "3.1.0-preview.1" +version = "3.0.0-preview.12" edition = "2021" [profile.release] @@ -17,16 +17,15 @@ crossterm = { version = "0.28" } ctrlc = { version = "3.4" } dsc_lib = { path = "../dsc_lib" } indicatif = { version = "0.17" } -jsonschema = { version = "0.23", default-features = false } +jsonschema = { version = "0.26", default-features = false } path-absolutize = { version = "3.1" } -# reqwest = { version = "0.12.8", features = ["native-tls"], default-features = false } schemars = { version = "0.8" } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["preserve_order"] } serde_yaml = { version = "0.9" } syntect = { version = "5.0", features = ["default-fancy"], default-features = false } -sysinfo = { version = "0.32" } -thiserror = "1.0" -tracing = { version = "0.1.37" } +sysinfo = { version = "0.33" } +thiserror = "2.0" +tracing = { version = "0.1" } tracing-subscriber = { version = "0.3", features = ["ansi", "env-filter", "json"] } tracing-indicatif = { version = "0.3" } diff --git a/dsc/assertion.dsc.resource.json b/dsc/assertion.dsc.resource.json index 17142f8a..2427a8d0 100644 --- a/dsc/assertion.dsc.resource.json +++ b/dsc/assertion.dsc.resource.json @@ -12,9 +12,12 @@ "config", "--as-group", "test", - "--as-get" - ], - "input": "stdin" + "--as-get", + { + "jsonInputArg": "--input", + "mandatory": true + } + ] }, "set": { "executable": "dsc", @@ -23,9 +26,12 @@ "pass-through", "config", "--as-group", - "test" + "test", + { + "jsonInputArg": "--input", + "mandatory": true + } ], - "input": "stdin", "implementsPretest": true, "return": "state" }, @@ -37,9 +43,12 @@ "config", "--as-group", "test", - "--as-config" + "--as-config", + { + "jsonInputArg": "--input", + "mandatory": true + } ], - "input": "stdin", "return": "state" }, "exitCodes": { @@ -58,8 +67,11 @@ "--trace-format", "pass-through", "config", - "validate" - ], - "input": "stdin" + "validate", + { + "jsonInputArg": "--input", + "mandatory": true + } + ] } } diff --git a/dsc/group.dsc.resource.json b/dsc/group.dsc.resource.json index b92e0473..08a920ce 100644 --- a/dsc/group.dsc.resource.json +++ b/dsc/group.dsc.resource.json @@ -11,9 +11,12 @@ "pass-through", "config", "--as-group", - "get" - ], - "input": "stdin" + "get", + { + "jsonInputArg": "--input", + "mandatory": true + } + ] }, "set": { "executable": "dsc", @@ -22,9 +25,12 @@ "pass-through", "config", "--as-group", - "set" + "set", + { + "jsonInputArg": "--input", + "mandatory": true + } ], - "input": "stdin", "implementsPretest": true, "return": "state" }, @@ -35,9 +41,12 @@ "pass-through", "config", "--as-group", - "test" + "test", + { + "jsonInputArg": "--input", + "mandatory": true + } ], - "input": "stdin", "return": "state" }, "exitCodes": { @@ -56,8 +65,11 @@ "--trace-format", "pass-through", "config", - "validate" - ], - "input": "stdin" + "validate", + { + "jsonInputArg": "--input", + "mandatory": true + } + ] } } diff --git a/dsc/include.dsc.resource.json b/dsc/include.dsc.resource.json index a2cf2d01..e25e8e1b 100644 --- a/dsc/include.dsc.resource.json +++ b/dsc/include.dsc.resource.json @@ -12,9 +12,12 @@ "config", "--as-include", "--as-group", - "get" - ], - "input": "stdin" + "get", + { + "jsonInputArg": "--input", + "mandatory": true + } + ] }, "set": { "executable": "dsc", @@ -24,9 +27,12 @@ "config", "--as-include", "--as-group", - "set" + "set", + { + "jsonInputArg": "--input", + "mandatory": true + } ], - "input": "stdin", "implementsPretest": true, "return": "state" }, @@ -38,9 +44,12 @@ "config", "--as-include", "--as-group", - "test" - ], - "input": "stdin" + "test", + { + "jsonInputArg": "--input", + "mandatory": true + } + ] }, "exitCodes": { "0": "Success", @@ -58,8 +67,11 @@ "pass-through", "config", "--as-include", - "validate" - ], - "input": "stdin" + "validate", + { + "jsonInputArg": "--input", + "mandatory": true + } + ] } } diff --git a/dsc/parallel.dsc.resource.json b/dsc/parallel.dsc.resource.json deleted file mode 100644 index 4392127c..00000000 --- a/dsc/parallel.dsc.resource.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/bundled/resource/manifest.json", - "type": "Microsoft.DSC/Parallel", - "version": "0.1.0", - "description": "All resources in the supplied configuration run concurrently.", - "kind": "Group", - "get": { - "executable": "dsc", - "args": [ - "--trace-format", - "pass-through", - "config", - "--parallel", - "--as-group", - "get" - ], - "input": "stdin" - }, - "set": { - "executable": "dsc", - "args": [ - "--trace-format", - "pass-through", - "config", - "--parallel", - "--as-group", - "set" - ], - "input": "stdin", - "implementsPretest": true, - "return": "state" - }, - "test": { - "executable": "dsc", - "args": [ - "--trace-format", - "pass-through", - "config", - "--parallel", - "--as-group", - "test" - ], - "input": "stdin", - "return": "state" - }, - "exitCodes": { - "0": "Success", - "1": "Invalid argument", - "2": "Resource error", - "3": "JSON Serialization error", - "4": "Invalid input format", - "5": "Resource instance failed schema validation", - "6": "Command cancelled", - "7": "Resource not found" - }, - "validate": { - "executable": "dsc", - "args": [ - "--trace-format", - "pass-through", - "config", - "validate" - ], - "input": "stdin" - } -} diff --git a/dsc/src/args.rs b/dsc/src/args.rs index 94c1bbcd..f99162ac 100644 --- a/dsc/src/args.rs +++ b/dsc/src/args.rs @@ -24,7 +24,7 @@ pub struct Args { pub subcommand: SubCommand, #[clap(short = 'l', long, help = "Trace level to use", value_enum)] pub trace_level: Option, - #[clap(short = 'f', long, help = "Trace format to use", value_enum)] + #[clap(short = 't', long, help = "Trace format to use", value_enum)] pub trace_format: Option, } @@ -61,8 +61,8 @@ pub enum SubCommand { Schema { #[clap(name = "type", short, long, help = "The type of DSC schema to get")] dsc_type: DscType, - #[clap(short = 'f', long, help = "The output format to use")] - format: Option, + #[clap(short = 'o', long, help = "The output format to use")] + output_format: Option, }, } @@ -70,32 +70,32 @@ pub enum SubCommand { pub enum ConfigSubCommand { #[clap(name = "get", about = "Retrieve the current configuration")] Get { - #[clap(short = 'd', long, help = "The document to pass to the configuration or resource", conflicts_with = "path")] - document: Option, - #[clap(short = 'p', long, help = "The path to a file used as input to the configuration or resource", conflicts_with = "document")] - path: Option, - #[clap(short = 'f', long, help = "The output format to use")] - format: Option, + #[clap(short = 'i', long, help = "The input document as JSON or YAML to pass to the configuration or resource", conflicts_with = "file")] + input: Option, + #[clap(short = 'f', long, help = "The path to a file used as input to the configuration or resource. Use '-' for the file to read from STDIN.", conflicts_with = "input")] + file: Option, + #[clap(short = 'o', long, help = "The output format to use")] + output_format: Option, }, #[clap(name = "set", about = "Set the current configuration")] Set { - #[clap(short = 'd', long, help = "The document to pass to the configuration or resource", conflicts_with = "path")] - document: Option, - #[clap(short = 'p', long, help = "The path to a file used as input to the configuration or resource", conflicts_with = "document")] - path: Option, - #[clap(short = 'f', long, help = "The output format to use")] - format: Option, + #[clap(short = 'i', long, help = "The input document as JSON or YAML to pass to the configuration or resource", conflicts_with = "file")] + input: Option, + #[clap(short = 'f', long, help = "The path to a file used as input to the configuration or resource. Use '-' for the file to read from STDIN.", conflicts_with = "input")] + file: Option, + #[clap(short = 'o', long, help = "The output format to use")] + output_format: Option, #[clap(short = 'w', long, help = "Run as a what-if operation instead of executing the configuration or resource")] what_if: bool, }, #[clap(name = "test", about = "Test the current configuration")] Test { - #[clap(short = 'd', long, help = "The document to pass to the configuration or resource", conflicts_with = "path")] - document: Option, - #[clap(short = 'p', long, help = "The path to a file used as input to the configuration or resource", conflicts_with = "document")] - path: Option, - #[clap(short = 'f', long, help = "The output format to use")] - format: Option, + #[clap(short = 'i', long, help = "The input document as JSON or YAML to pass to the configuration or resource", conflicts_with = "file")] + input: Option, + #[clap(short = 'f', long, help = "The path to a file used as input to the configuration or resource. Use '-' for the file to read from STDIN.", conflicts_with = "input")] + file: Option, + #[clap(short = 'o', long, help = "The output format to use")] + output_format: Option, // Used by Assertion resource to return `test` result as a `get` result #[clap(long, hide = true)] as_get: bool, @@ -105,30 +105,30 @@ pub enum ConfigSubCommand { }, #[clap(name = "validate", about = "Validate the current configuration", hide = true)] Validate { - #[clap(short = 'd', long, help = "The document to pass to the configuration or resource", conflicts_with = "path")] - document: Option, - #[clap(short = 'p', long, help = "The path to a file used as input to the configuration or resource", conflicts_with = "document")] - path: Option, - #[clap(short = 'f', long, help = "The output format to use")] - format: Option, + #[clap(short = 'i', long, help = "The document to pass to the configuration or resource", conflicts_with = "file")] + input: Option, + #[clap(short = 'f', long, help = "The path to a file used as input to the configuration or resource. Use '-' for the file to read from STDIN.", conflicts_with = "input")] + file: Option, + #[clap(short = 'o', long, help = "The output format to use")] + output_format: Option, }, #[clap(name = "export", about = "Export the current configuration")] Export { - #[clap(short = 'd', long, help = "The document to pass to the configuration or resource", conflicts_with = "path")] - document: Option, - #[clap(short = 'p', long, help = "The path to a file used as input to the configuration or resource", conflicts_with = "document")] - path: Option, - #[clap(short = 'f', long, help = "The output format to use")] - format: Option, + #[clap(short = 'i', long, help = "The document to pass to the configuration or resource", conflicts_with = "file")] + input: Option, + #[clap(short = 'f', long, help = "The path to a file used as input to the configuration or resource. Use '-' for the file to read from STDIN.", conflicts_with = "input")] + file: Option, + #[clap(short = 'o', long, help = "The output format to use")] + output_format: Option, }, #[clap(name = "resolve", about = "Resolve the current configuration", hide = true)] Resolve { - #[clap(short = 'd', long, help = "The document to pass to the configuration or resource", conflicts_with = "path")] - document: Option, - #[clap(short = 'p', long, help = "The path to a file used as input to the configuration or resource", conflicts_with = "document")] - path: Option, - #[clap(short = 'f', long, help = "The output format to use")] - format: Option, + #[clap(short = 'i', long, help = "The document to pass to the configuration or resource", conflicts_with = "file")] + input: Option, + #[clap(short = 'f', long, help = "The path to a file used as input to the configuration or resource. Use '-' for the file to read from STDIN.", conflicts_with = "input")] + file: Option, + #[clap(short = 'o', long, help = "The output format to use")] + output_format: Option, } } @@ -145,8 +145,8 @@ pub enum ResourceSubCommand { description: Option, #[clap(short, long, help = "Tag to search for in the resource tags")] tags: Option>, - #[clap(short = 'f', long, help = "The output format to use")] - format: Option, + #[clap(short = 'o', long, help = "The output format to use")] + output_format: Option, }, #[clap(name = "get", about = "Invoke the get operation to a resource", arg_required_else_help = true)] Get { @@ -154,57 +154,57 @@ pub enum ResourceSubCommand { all: bool, #[clap(short, long, help = "The name or DscResource JSON of the resource to invoke `get` on")] resource: String, - #[clap(short, long, help = "The input to pass to the resource as JSON or YAML", conflicts_with = "path")] + #[clap(short, long, help = "The input to pass to the resource as JSON or YAML", conflicts_with = "file")] input: Option, - #[clap(short = 'p', long, help = "The path to a JSON or YAML file used as input to the configuration or resource", conflicts_with = "input")] - path: Option, - #[clap(short = 'f', long, help = "The output format to use")] - format: Option, + #[clap(short = 'f', long, help = "The path to a JSON or YAML file used as input to the configuration or resource. Use '-' as the file to read from STDIN.", conflicts_with = "input")] + file: Option, + #[clap(short = 'o', long, help = "The output format to use")] + output_format: Option, }, #[clap(name = "set", about = "Invoke the set operation to a resource", arg_required_else_help = true)] Set { #[clap(short, long, help = "The name or DscResource JSON of the resource to invoke `set` on")] resource: String, - #[clap(short, long, help = "The input to pass to the resource as JSON or YAML", conflicts_with = "path")] + #[clap(short, long, help = "The input to pass to the resource as JSON or YAML", conflicts_with = "file")] input: Option, - #[clap(short = 'p', long, help = "The path to a JSON or YAML file used as input to the configuration or resource", conflicts_with = "input")] - path: Option, - #[clap(short = 'f', long, help = "The output format to use")] - format: Option, + #[clap(short = 'f', long, help = "The path to a JSON or YAML file used as input to the configuration or resource. Use '-' for the file to read from STDIN.", conflicts_with = "input")] + file: Option, + #[clap(short = 'o', long, help = "The output format to use")] + output_format: Option, }, #[clap(name = "test", about = "Invoke the test operation to a resource", arg_required_else_help = true)] Test { #[clap(short, long, help = "The name or DscResource JSON of the resource to invoke `test` on")] resource: String, - #[clap(short, long, help = "The input to pass to the resource as JSON or YAML", conflicts_with = "path")] + #[clap(short, long, help = "The input to pass to the resource as JSON or YAML", conflicts_with = "file")] input: Option, - #[clap(short = 'p', long, help = "The path to a JSON or YAML file used as input to the configuration or resource", conflicts_with = "input")] - path: Option, - #[clap(short = 'f', long, help = "The output format to use")] - format: Option, + #[clap(short = 'f', long, help = "The path to a JSON or YAML file used as input to the configuration or resource. Use '-' for the file to read from STDIN.", conflicts_with = "input")] + file: Option, + #[clap(short = 'o', long, help = "The output format to use")] + output_format: Option, }, #[clap(name = "delete", about = "Invoke the delete operation to a resource", arg_required_else_help = true)] Delete { #[clap(short, long, help = "The name or DscResource JSON of the resource to invoke `delete` on")] resource: String, - #[clap(short, long, help = "The input to pass to the resource as JSON or YAML", conflicts_with = "path")] + #[clap(short, long, help = "The input to pass to the resource as JSON or YAML", conflicts_with = "file")] input: Option, - #[clap(short = 'p', long, help = "The path to a JSON or YAML file used as input to the configuration or resource", conflicts_with = "input")] - path: Option, + #[clap(short = 'f', long, help = "The path to a JSON or YAML file used as input to the configuration or resource. Use '-' for the file to read from STDIN.", conflicts_with = "input")] + file: Option, }, #[clap(name = "schema", about = "Get the JSON schema for a resource", arg_required_else_help = true)] Schema { #[clap(short, long, help = "The name of the resource to get the JSON schema")] resource: String, - #[clap(short = 'f', long, help = "The output format to use")] - format: Option, + #[clap(short = 'o', long, help = "The output format to use")] + output_format: Option, }, #[clap(name = "export", about = "Retrieve all resource instances", arg_required_else_help = true)] Export { #[clap(short, long, help = "The name or DscResource JSON of the resource to invoke `export` on")] resource: String, - #[clap(short = 'f', long, help = "The output format to use")] - format: Option, + #[clap(short = 'o', long, help = "The output format to use")] + output_format: Option, }, } diff --git a/dsc/src/main.rs b/dsc/src/main.rs index 1f578e30..59a52a75 100644 --- a/dsc/src/main.rs +++ b/dsc/src/main.rs @@ -4,8 +4,7 @@ use args::{Args, SubCommand}; use clap::{CommandFactory, Parser}; use clap_complete::generate; -use std::io::{self, IsTerminal, Read}; -use std::process::exit; +use std::{io, process::exit}; use sysinfo::{Process, RefreshKind, System, get_current_pid, ProcessRefreshKind}; use tracing::{error, info, warn, debug}; @@ -34,34 +33,10 @@ fn main() { let args = Args::parse(); - util::enable_tracing(&args.trace_level, &args.trace_format); + util::enable_tracing(args.trace_level.as_ref(), args.trace_format.as_ref()); debug!("Running dsc {}", env!("CARGO_PKG_VERSION")); - let input = if io::stdin().is_terminal() { - None - } else { - info!("Reading input from STDIN"); - let mut buffer: Vec = Vec::new(); - io::stdin().read_to_end(&mut buffer).unwrap(); - let input = match String::from_utf8(buffer) { - Ok(input) => input, - Err(e) => { - error!("Invalid UTF-8 sequence: {e}"); - exit(util::EXIT_INVALID_ARGS); - }, - }; - // get_input call expects at most 1 input, so wrapping Some(empty input) would throw it off - // have only seen this happen with dsc_args.test.ps1 running on the CI pipeline - if input.is_empty() { - debug!("Input from STDIN is empty"); - None - } - else { - Some(input) - } - }; - match args.subcommand { SubCommand::Completer { shell } => { info!("Generating completion script for {:?}", shell); @@ -72,7 +47,7 @@ fn main() { if let Some(file_name) = parameters_file { info!("Reading parameters from file {file_name}"); match std::fs::read_to_string(&file_name) { - Ok(parameters) => subcommand::config(&subcommand, &Some(parameters), &system_root, &input, &as_group, &as_include), + Ok(parameters) => subcommand::config(&subcommand, &Some(parameters), system_root.as_ref(), &as_group, &as_include), Err(err) => { error!("Error: Failed to read parameters file '{file_name}': {err}"); exit(util::EXIT_INVALID_INPUT); @@ -80,13 +55,13 @@ fn main() { } } else { - subcommand::config(&subcommand, ¶meters, &system_root, &input, &as_group, &as_include); + subcommand::config(&subcommand, ¶meters, system_root.as_ref(), &as_group, &as_include); } }, SubCommand::Resource { subcommand } => { - subcommand::resource(&subcommand, &input); + subcommand::resource(&subcommand); }, - SubCommand::Schema { dsc_type , format } => { + SubCommand::Schema { dsc_type , output_format } => { let schema = util::get_schema(dsc_type); let json = match serde_json::to_string(&schema) { Ok(json) => json, @@ -95,7 +70,7 @@ fn main() { exit(util::EXIT_JSON_ERROR); } }; - util::write_output(&json, &format); + util::write_output(&json, output_format.as_ref()); }, } @@ -106,7 +81,7 @@ fn ctrlc_handler() { warn!("Ctrl-C received"); // get process tree for current process and terminate all processes - let sys = System::new_with_specifics(RefreshKind::new().with_processes(ProcessRefreshKind::new())); + let sys = System::new_with_specifics(RefreshKind::nothing().with_processes(ProcessRefreshKind::everything())); info!("Found {} processes", sys.processes().len()); let Ok(current_pid) = get_current_pid() else { error!("Could not get current process id"); @@ -162,13 +137,15 @@ fn check_debug() { // Check if the dsc binary parent process is WinStore.App or Exploerer.exe #[cfg(windows)] fn check_store() { + use std::io::Read; + let message = r" DSC.exe is a command-line tool and cannot be run directly from the Windows Store or Explorer. Visit https://aka.ms/dscv3-docs for more information on how to use DSC.exe. Press any key to close this window "; - let sys = System::new_with_specifics(RefreshKind::new().with_processes(ProcessRefreshKind::new())); + let sys = System::new_with_specifics(RefreshKind::nothing().with_processes(ProcessRefreshKind::everything())); // get current process let Ok(current_pid) = get_current_pid() else { return; diff --git a/dsc/src/resource_command.rs b/dsc/src/resource_command.rs index e102a7e5..7a3ee6b7 100644 --- a/dsc/src/resource_command.rs +++ b/dsc/src/resource_command.rs @@ -15,7 +15,7 @@ use dsc_lib::{ }; use std::process::exit; -pub fn get(dsc: &DscManager, resource_type: &str, mut input: String, format: &Option) { +pub fn get(dsc: &DscManager, resource_type: &str, mut input: String, format: Option<&OutputFormat>) { let Some(mut resource) = get_resource(dsc, resource_type) else { error!("{}", DscError::ResourceNotFound(resource_type.to_string()).to_string()); exit(EXIT_DSC_RESOURCE_NOT_FOUND); @@ -56,7 +56,7 @@ pub fn get(dsc: &DscManager, resource_type: &str, mut input: String, format: &Op } } -pub fn get_all(dsc: &DscManager, resource_type: &str, format: &Option) { +pub fn get_all(dsc: &DscManager, resource_type: &str, format: Option<&OutputFormat>) { let mut input = String::new(); let Some(mut resource) = get_resource(dsc, resource_type) else { error!("{}", DscError::ResourceNotFound(resource_type.to_string()).to_string()); @@ -104,7 +104,7 @@ pub fn get_all(dsc: &DscManager, resource_type: &str, format: &Option) { +pub fn set(dsc: &DscManager, resource_type: &str, mut input: String, format: Option<&OutputFormat>) { if input.is_empty() { error!("Error: Desired input is empty"); exit(EXIT_INVALID_ARGS); @@ -150,7 +150,7 @@ pub fn set(dsc: &DscManager, resource_type: &str, mut input: String, format: &Op } } -pub fn test(dsc: &DscManager, resource_type: &str, mut input: String, format: &Option) { +pub fn test(dsc: &DscManager, resource_type: &str, mut input: String, format: Option<&OutputFormat>) { if input.is_empty() { error!("Error: Expected input is required"); exit(EXIT_INVALID_ARGS); @@ -227,7 +227,7 @@ pub fn delete(dsc: &DscManager, resource_type: &str, mut input: String) { } } -pub fn schema(dsc: &DscManager, resource_type: &str, format: &Option) { +pub fn schema(dsc: &DscManager, resource_type: &str, format: Option<&OutputFormat>) { let Some(resource) = get_resource(dsc, resource_type) else { error!("{}", DscError::ResourceNotFound(resource_type.to_string()).to_string()); exit(EXIT_DSC_RESOURCE_NOT_FOUND); @@ -256,7 +256,7 @@ pub fn schema(dsc: &DscManager, resource_type: &str, format: &Option) { +pub fn export(dsc: &mut DscManager, resource_type: &str, format: Option<&OutputFormat>) { let mut input = String::new(); let Some(dsc_resource) = get_resource(dsc, resource_type) else { error!("{}", DscError::ResourceNotFound(resource_type.to_string()).to_string()); diff --git a/dsc/src/subcommand.rs b/dsc/src/subcommand.rs index 79b83af6..d59d50af 100644 --- a/dsc/src/subcommand.rs +++ b/dsc/src/subcommand.rs @@ -35,7 +35,7 @@ use std::{ }; use tracing::{debug, error, trace}; -pub fn config_get(configurator: &mut Configurator, format: &Option, as_group: &bool) +pub fn config_get(configurator: &mut Configurator, format: Option<&OutputFormat>, as_group: &bool) { match configurator.invoke_get() { Ok(result) => { @@ -70,7 +70,7 @@ pub fn config_get(configurator: &mut Configurator, format: &Option } } -pub fn config_set(configurator: &mut Configurator, format: &Option, as_group: &bool) +pub fn config_set(configurator: &mut Configurator, format: Option<&OutputFormat>, as_group: &bool) { match configurator.invoke_set(false) { Ok(result) => { @@ -105,7 +105,7 @@ pub fn config_set(configurator: &mut Configurator, format: &Option } } -pub fn config_test(configurator: &mut Configurator, format: &Option, as_group: &bool, as_get: &bool, as_config: &bool) +pub fn config_test(configurator: &mut Configurator, format: Option<&OutputFormat>, as_group: &bool, as_get: &bool, as_config: &bool) { match configurator.invoke_test() { Ok(result) => { @@ -191,7 +191,7 @@ pub fn config_test(configurator: &mut Configurator, format: &Option) +pub fn config_export(configurator: &mut Configurator, format: Option<&OutputFormat>) { match configurator.invoke_export() { Ok(result) => { @@ -220,60 +220,74 @@ pub fn config_export(configurator: &mut Configurator, format: &Option) -> Option { - if path.is_some() { - let config_path = path.clone().unwrap_or_default(); - Some(set_dscconfigroot(&config_path)) - } else if std::env::var(DSC_CONFIG_ROOT).is_ok() { +fn initialize_config_root(path: Option<&String>) -> Option { + // code that calls this pass in either None, Some("-"), or Some(path) + // in the case of `-` we treat it as None, but need to pass it back as subsequent processing needs to handle it + let use_stdin = if let Some(specified_path) = path { + if specified_path != "-" { + return Some(set_dscconfigroot(specified_path)); + } + + true + } else { + false + }; + + if std::env::var(DSC_CONFIG_ROOT).is_ok() { let config_root = std::env::var(DSC_CONFIG_ROOT).unwrap_or_default(); debug!("Using {config_root} for {DSC_CONFIG_ROOT}"); - None } else { let current_directory = std::env::current_dir().unwrap_or_default(); debug!("Using current directory '{current_directory:?}' for {DSC_CONFIG_ROOT}"); set_dscconfigroot(¤t_directory.to_string_lossy()); - None } + + // if the path is "-", we need to return it so later processing can handle it correctly + if use_stdin { + return Some("-".to_string()); + } + + None } #[allow(clippy::too_many_lines)] -pub fn config(subcommand: &ConfigSubCommand, parameters: &Option, mounted_path: &Option, stdin: &Option, as_group: &bool, as_include: &bool) { - let (new_parameters, json_string, progress_format) = match subcommand { - ConfigSubCommand::Get { document, path, format, .. } | - ConfigSubCommand::Set { document, path, format, .. } | - ConfigSubCommand::Test { document, path, format, .. } | - ConfigSubCommand::Validate { document, path, format, .. } | - ConfigSubCommand::Export { document, path, format, .. } => { - let new_path = initialize_config_root(path); - let input = get_input(document, stdin, &new_path); +pub fn config(subcommand: &ConfigSubCommand, parameters: &Option, mounted_path: Option<&String>, as_group: &bool, as_include: &bool) { + let (new_parameters, json_string, output_format) = match subcommand { + ConfigSubCommand::Get { input, file, output_format, .. } | + ConfigSubCommand::Set { input, file, output_format, .. } | + ConfigSubCommand::Test { input, file, output_format, .. } | + ConfigSubCommand::Validate { input, file, output_format, .. } | + ConfigSubCommand::Export { input, file, output_format, .. } => { + let new_path = initialize_config_root(file.as_ref()); + let document = get_input(input.as_ref(), new_path.as_ref()); if *as_include { - let (new_parameters, config_json) = match get_contents(&input) { + let (new_parameters, config_json) = match get_contents(&document) { Ok((parameters, config_json)) => (parameters, config_json), Err(err) => { error!("{err}"); exit(EXIT_DSC_ERROR); } }; - (new_parameters, config_json, format) + (new_parameters, config_json, output_format) } else { - (None, input, format) + (None, document, output_format) } }, - ConfigSubCommand::Resolve { document, path, format, .. } => { - let new_path = initialize_config_root(path); - let input = get_input(document, stdin, &new_path); - let (new_parameters, config_json) = match get_contents(&input) { + ConfigSubCommand::Resolve { input, file, output_format, .. } => { + let new_path = initialize_config_root(file.as_ref()); + let document = get_input(input.as_ref(), new_path.as_ref()); + let (new_parameters, config_json) = match get_contents(&document) { Ok((parameters, config_json)) => (parameters, config_json), Err(err) => { error!("{err}"); exit(EXIT_DSC_ERROR); } }; - (new_parameters, config_json, format) + (new_parameters, config_json, output_format) } }; - let mut configurator = match Configurator::new(&json_string, progress_format) { + let mut configurator = match Configurator::new(&json_string, output_format) { Ok(configurator) => configurator, Err(err) => { error!("Error: {err}"); @@ -330,29 +344,29 @@ pub fn config(subcommand: &ConfigSubCommand, parameters: &Option, mounte configurator.set_system_root(path); } - if let Err(err) = configurator.set_context(¶meters) { + if let Err(err) = configurator.set_context(parameters.as_ref()) { error!("Error: Parameter input failure: {err}"); exit(EXIT_INVALID_INPUT); } match subcommand { - ConfigSubCommand::Get { format, .. } => { - config_get(&mut configurator, format, as_group); + ConfigSubCommand::Get { output_format, .. } => { + config_get(&mut configurator, output_format.as_ref(), as_group); }, - ConfigSubCommand::Set { format, .. } => { - config_set(&mut configurator, format, as_group); + ConfigSubCommand::Set { output_format, .. } => { + config_set(&mut configurator, output_format.as_ref(), as_group); }, - ConfigSubCommand::Test { format, as_get, as_config, .. } => { - config_test(&mut configurator, format, as_group, as_get, as_config); + ConfigSubCommand::Test { output_format, as_get, as_config, .. } => { + config_test(&mut configurator, output_format.as_ref(), as_group, as_get, as_config); }, - ConfigSubCommand::Validate { document, path, format} => { + ConfigSubCommand::Validate { input, file, output_format} => { let mut result = ValidateResult { valid: true, reason: None, }; if *as_include { - let new_path = initialize_config_root(path); - let input = get_input(document, stdin, &new_path); + let new_path = initialize_config_root(file.as_ref()); + let input = get_input(input.as_ref(), new_path.as_ref()); match serde_json::from_str::(&input) { Ok(_) => { // valid, so do nothing @@ -363,7 +377,7 @@ pub fn config(subcommand: &ConfigSubCommand, parameters: &Option, mounte } } } else { - match validate_config(configurator.get_config(), progress_format) { + match validate_config(configurator.get_config(), output_format) { Ok(()) => { // valid, so do nothing }, @@ -379,12 +393,12 @@ pub fn config(subcommand: &ConfigSubCommand, parameters: &Option, mounte exit(EXIT_JSON_ERROR); }; - write_output(&json, format); + write_output(&json, output_format.as_ref()); }, - ConfigSubCommand::Export { format, .. } => { - config_export(&mut configurator, format); + ConfigSubCommand::Export { output_format, .. } => { + config_export(&mut configurator, output_format.as_ref()); }, - ConfigSubCommand::Resolve { format, .. } => { + ConfigSubCommand::Resolve { output_format, .. } => { let configuration = match serde_json::from_str(&json_string) { Ok(json) => json, Err(err) => { @@ -413,7 +427,7 @@ pub fn config(subcommand: &ConfigSubCommand, parameters: &Option, mounte exit(EXIT_JSON_ERROR); } }; - write_output(&json_string, format); + write_output(&json_string, output_format.as_ref()); }, } } @@ -508,7 +522,7 @@ pub fn validate_config(config: &Configuration, progress_format: &Option) { +pub fn resource(subcommand: &ResourceSubCommand) { let mut dsc = match DscManager::new() { Ok(dsc) => dsc, Err(err) => { @@ -518,51 +532,51 @@ pub fn resource(subcommand: &ResourceSubCommand, stdin: &Option) { }; match subcommand { - ResourceSubCommand::List { resource_name, adapter_name, description, tags, format } => { - list_resources(&mut dsc, resource_name, adapter_name, description, tags, format); + ResourceSubCommand::List { resource_name, adapter_name, description, tags, output_format } => { + list_resources(&mut dsc, resource_name.as_ref(), adapter_name.as_ref(), description.as_ref(), tags.as_ref(), output_format); }, - ResourceSubCommand::Schema { resource , format } => { - dsc.find_resources(&[resource.to_string()], format); - resource_command::schema(&dsc, resource, format); + ResourceSubCommand::Schema { resource , output_format } => { + dsc.find_resources(&[resource.to_string()], output_format); + resource_command::schema(&dsc, resource, output_format.as_ref()); }, - ResourceSubCommand::Export { resource, format } => { - dsc.find_resources(&[resource.to_string()], format); - resource_command::export(&mut dsc, resource, format); + ResourceSubCommand::Export { resource, output_format } => { + dsc.find_resources(&[resource.to_string()], output_format); + resource_command::export(&mut dsc, resource, output_format.as_ref()); }, - ResourceSubCommand::Get { resource, input, path, all, format } => { - dsc.find_resources(&[resource.to_string()], format); - if *all { resource_command::get_all(&dsc, resource, format); } + ResourceSubCommand::Get { resource, input, file: path, all, output_format } => { + dsc.find_resources(&[resource.to_string()], output_format); + if *all { resource_command::get_all(&dsc, resource, output_format.as_ref()); } else { - let parsed_input = get_input(input, stdin, path); - resource_command::get(&dsc, resource, parsed_input, format); + let parsed_input = get_input(input.as_ref(), path.as_ref()); + resource_command::get(&dsc, resource, parsed_input, output_format.as_ref()); } }, - ResourceSubCommand::Set { resource, input, path, format } => { - dsc.find_resources(&[resource.to_string()], format); - let parsed_input = get_input(input, stdin, path); - resource_command::set(&dsc, resource, parsed_input, format); + ResourceSubCommand::Set { resource, input, file: path, output_format } => { + dsc.find_resources(&[resource.to_string()], output_format); + let parsed_input = get_input(input.as_ref(), path.as_ref()); + resource_command::set(&dsc, resource, parsed_input, output_format.as_ref()); }, - ResourceSubCommand::Test { resource, input, path, format } => { - dsc.find_resources(&[resource.to_string()], format); - let parsed_input = get_input(input, stdin, path); - resource_command::test(&dsc, resource, parsed_input, format); + ResourceSubCommand::Test { resource, input, file: path, output_format } => { + dsc.find_resources(&[resource.to_string()], output_format); + let parsed_input = get_input(input.as_ref(), path.as_ref()); + resource_command::test(&dsc, resource, parsed_input, output_format.as_ref()); }, - ResourceSubCommand::Delete { resource, input, path } => { + ResourceSubCommand::Delete { resource, input, file: path } => { dsc.find_resources(&[resource.to_string()], &None); - let parsed_input = get_input(input, stdin, path); + let parsed_input = get_input(input.as_ref(), path.as_ref()); resource_command::delete(&dsc, resource, parsed_input); }, } } -fn list_resources(dsc: &mut DscManager, resource_name: &Option, adapter_name: &Option, description: &Option, tags: &Option>, format: &Option) { +fn list_resources(dsc: &mut DscManager, resource_name: Option<&String>, adapter_name: Option<&String>, description: Option<&String>, tags: Option<&Vec>, format: &Option) { let mut write_table = false; let mut table = Table::new(&["Type", "Kind", "Version", "Caps", "RequireAdapter", "Description"]); if format.is_none() && io::stdout().is_terminal() { // write as table if format is not specified and interactive write_table = true; } - for resource in dsc.list_available_resources(&resource_name.clone().unwrap_or("*".to_string()), &adapter_name.clone().unwrap_or_default(), format) { + for resource in dsc.list_available_resources(resource_name.unwrap_or(&String::from("*")), adapter_name.unwrap_or(&String::new()), format) { let mut capabilities = "--------".to_string(); let capability_types = [ (Capability::Get, "g"), @@ -593,7 +607,7 @@ fn list_resources(dsc: &mut DscManager, resource_name: &Option, adapter_ // if description is specified, skip if resource description does not contain it if description.is_some() && - (manifest.description.is_none() | !manifest.description.unwrap_or_default().to_lowercase().contains(&description.as_ref().unwrap_or(&String::new()).to_lowercase())) { + (manifest.description.is_none() | !manifest.description.unwrap_or_default().to_lowercase().contains(&description.unwrap_or(&String::new()).to_lowercase())) { continue; } @@ -638,7 +652,7 @@ fn list_resources(dsc: &mut DscManager, resource_name: &Option, adapter_ exit(EXIT_JSON_ERROR); } }; - write_output(&json, format); + write_output(&json, format.as_ref()); // insert newline separating instances if writing to console if io::stdout().is_terminal() { println!(); } } diff --git a/dsc/src/util.rs b/dsc/src/util.rs index b196100d..253c2651 100644 --- a/dsc/src/util.rs +++ b/dsc/src/util.rs @@ -33,7 +33,7 @@ use serde::Deserialize; use serde_json::Value; use std::collections::HashMap; use std::env; -use std::io::IsTerminal; +use std::io::{IsTerminal, Read}; use std::path::Path; use std::process::exit; use syntect::{ @@ -214,18 +214,18 @@ pub fn get_schema(dsc_type: DscType) -> RootSchema { /// /// * `json` - The JSON to write /// * `format` - The format to use -pub fn write_output(json: &str, format: &Option) { +pub fn write_output(json: &str, format: Option<&OutputFormat>) { let mut is_json = true; - let mut output_format = format.clone(); + let mut output_format = format; let mut syntax_color = false; if std::io::stdout().is_terminal() { syntax_color = true; if output_format.is_none() { - output_format = Some(OutputFormat::Yaml); + output_format = Some(&OutputFormat::Yaml); } } else if output_format.is_none() { - output_format = Some(OutputFormat::Json); + output_format = Some(&OutputFormat::Json); } let output = match output_format { @@ -293,8 +293,8 @@ pub fn write_output(json: &str, format: &Option) { } #[allow(clippy::too_many_lines)] -pub fn enable_tracing(trace_level_arg: &Option, trace_format_arg: &Option) { - +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(); @@ -444,57 +444,60 @@ pub fn validate_json(source: &str, schema: &Value, json: &Value) -> Result<(), D }; if let Err(err) = compiled_schema.validate(json) { - let mut error = format!("'{source}' failed validation: "); - for e in err { - error.push_str(&format!("\n{e} ")); - } - return Err(DscError::Validation(error)); + return Err(DscError::Validation(format!("'{source}' failed validation: {err}"))); }; Ok(()) } -pub fn get_input(input: &Option, stdin: &Option, path: &Option) -> String { - let value = match (input, stdin, path) { - (Some(_), Some(_), None) | (None, Some(_), Some(_)) => { - error!("Error: Cannot specify both stdin and --document or --path"); - exit(EXIT_INVALID_ARGS); - }, - (Some(input), None, None) => { - debug!("Reading input from command line parameter"); +pub fn get_input(input: Option<&String>, file: Option<&String>) -> String { + trace!("Input: {input:?}, File: {file:?}"); + let value = if let Some(input) = input { + debug!("Reading input from command line parameter"); - // see if user accidentally passed in a file path - if Path::new(input).exists() { - error!("Error: Document provided is a file path, use --path instead"); - exit(EXIT_INVALID_INPUT); + // see if user accidentally passed in a file path + if Path::new(input).exists() { + error!("Error: Document provided is a file path, use '--file' instead"); + exit(EXIT_INVALID_INPUT); + } + input.clone() + } else if let Some(path) = file { + debug!("Reading input from file {}", path); + // check if need to read from STDIN + if path == "-" { + info!("Reading input from STDIN"); + let mut stdin = Vec::::new(); + match std::io::stdin().read_to_end(&mut stdin) { + Ok(_) => { + match String::from_utf8(stdin) { + Ok(input) => { + input + }, + Err(err) => { + error!("Error: Invalid utf-8 input: {err}"); + exit(EXIT_INVALID_INPUT); + } + } + }, + Err(err) => { + error!("Error: Failed to read input from STDIN: {err}"); + exit(EXIT_INVALID_INPUT); + } } - input.clone() - }, - (None, Some(stdin), None) => { - debug!("Reading input from stdin"); - stdin.clone() - }, - (None, None, Some(path)) => { - debug!("Reading input from file {}", path); + } else { match std::fs::read_to_string(path) { Ok(input) => { - input.clone() + input }, Err(err) => { error!("Error: Failed to read input file: {err}"); exit(EXIT_INVALID_INPUT); } } - }, - (None, None, None) => { - debug!("No input provided via stdin, file, or command line"); - return String::new(); - }, - _default => { - /* clap should handle these cases via conflicts_with so this should not get reached */ - error!("Error: Invalid input"); - exit(EXIT_INVALID_ARGS); } + } else { + debug!("No input provided"); + return String::new(); }; if value.trim().is_empty() { diff --git a/dsc/tests/dsc_args.tests.ps1 b/dsc/tests/dsc_args.tests.ps1 index 5b51b527..ec4b2062 100644 --- a/dsc/tests/dsc_args.tests.ps1 +++ b/dsc/tests/dsc_args.tests.ps1 @@ -65,12 +65,12 @@ Describe 'config argument tests' { '@ } ) { param($text) - $output = $text | dsc resource get -r Microsoft.DSC.Debug/Echo + $output = $text | dsc resource get -r Microsoft.DSC.Debug/Echo -f - $output = $output | ConvertFrom-Json $output.actualState.output | Should -BeExactly 'Hello There' } - It '--format is used even when redirected' -TestCases @( + It '--output-format is used even when redirected' -TestCases @( @{ format = 'yaml'; expected = @' actualState: hello: world @@ -86,7 +86,7 @@ actualState: ) { param($format, $expected) - $out = dsc resource get -r Test/Hello --format $format | Out-String + $out = dsc resource get -r Test/Hello --output-format $format | Out-String $LASTEXITCODE | Should -Be 0 $out.Trim() | Should -BeExactly $expected } @@ -101,8 +101,8 @@ actualState: } It 'input can be passed using ' -TestCases @( - @{ parameter = '-d' } - @{ parameter = '--document' } + @{ parameter = '-i' } + @{ parameter = '--input' } ) { param($parameter) @@ -121,8 +121,8 @@ resources: } It 'input can be passed using ' -TestCases @( - @{ parameter = '-p' } - @{ parameter = '--path' } + @{ parameter = '-f' } + @{ parameter = '--file' } ) { param($parameter) @@ -141,29 +141,8 @@ resources: $out.results[0].type | Should -BeExactly 'Microsoft/OSInfo' } - It '--document and --path cannot be used together' { - dsc config get --document 1 --path foo.json 2> $TestDrive/error.txt - $err = Get-Content $testdrive/error.txt -Raw - $err.Length | Should -Not -Be 0 - $LASTEXITCODE | Should -Be 2 - } - - It 'stdin and --document cannot be used together' { - '{ "foo": true }' | dsc config get --document 1 2> $TestDrive/error.txt - $err = Get-Content $testdrive/error.txt -Raw - $err.Length | Should -Not -Be 0 - $LASTEXITCODE | Should -Be 1 - } - - It 'stdin and --path cannot be used together' { - '{ "foo": true }' | dsc config get --path foo.json 2> $TestDrive/error.txt - $err = Get-Content $testdrive/error.txt -Raw - $err.Length | Should -Not -Be 0 - $LASTEXITCODE | Should -Be 1 - } - - It 'stdin, --document and --path cannot be used together' { - '{ "foo": true }' | dsc config get --document 1 --path foo.json 2> $TestDrive/error.txt + It '--input and --file cannot be used together' { + dsc config get --input 1 --file foo.json 2> $TestDrive/error.txt $err = Get-Content $testdrive/error.txt -Raw $err.Length | Should -Not -Be 0 $LASTEXITCODE | Should -Be 2 @@ -183,7 +162,7 @@ resources: } It 'stdin cannot be empty if neither input or path is provided' { - '' | dsc resource set -r Microsoft/OSInfo 2> $TestDrive/error.txt + '' | dsc resource set -r Microsoft/OSInfo -f - 2> $TestDrive/error.txt $err = Get-Content $testdrive/error.txt -Raw $err.Length | Should -Not -Be 0 $LASTEXITCODE | Should -Be 4 @@ -198,14 +177,14 @@ resources: It 'path contents cannot be empty if neither stdin or input is provided' { Set-Content -Path $TestDrive/empty.yaml -Value " " - dsc resource set -r Microsoft/OSInfo --path $TestDrive/empty.yaml 2> $TestDrive/error.txt + dsc resource set -r Microsoft/OSInfo --file $TestDrive/empty.yaml 2> $TestDrive/error.txt $err = Get-Content $testdrive/error.txt -Raw $err.Length | Should -Not -Be 0 $LASTEXITCODE | Should -Be 4 } It 'document cannot be empty if neither stdin or path is provided' { - dsc config set --document " " 2> $TestDrive/error.txt + dsc config set --input " " 2> $TestDrive/error.txt $err = Get-Content $testdrive/error.txt -Raw $err.Length | Should -Not -Be 0 $LASTEXITCODE | Should -Be 4 @@ -214,8 +193,8 @@ resources: It 'verify `dsc resource list` and `dsc resource list *`' { # return all native resources, providers, but not adapter-based resources; # results for `dsc resource list` and `dsc resource list *` should be the same - $a = dsc resource list -f json - $b = dsc resource list '*' -f json + $a = dsc resource list -o json + $b = dsc resource list '*' -o json $a.Count | Should -Be $b.Count 0..($a.Count-1) | %{ $a_obj = $a[$_] | ConvertFrom-Json @@ -229,7 +208,7 @@ resources: It 'verify `dsc resource list resource_filter`' { # same as previous but also apply resource_filter filter - $a = dsc resource list 'Test*' -f json + $a = dsc resource list 'Test*' -o json 0..($a.Count-1) | %{ $a_obj = $a[$_] | ConvertFrom-Json $a_obj.type.StartsWith("Test") | Should -Be $true @@ -240,7 +219,7 @@ resources: It 'verify `dsc resource list * -a *`' { # return all adapter-based resources - $a = dsc resource list '*' -a '*' -f json + $a = dsc resource list '*' -a '*' -o json 0..($a.Count-1) | %{ $a_obj = $a[$_] | ConvertFrom-Json $a_obj.requireAdapter | Should -Not -BeNullOrEmpty @@ -250,7 +229,7 @@ resources: It 'verify `dsc resource list * adapter_filter`' { # return all resources of adapters that match adapter_filter filter - $a = dsc resource list '*' -a Test* -f json | ConvertFrom-Json + $a = dsc resource list '*' -a Test* -o json | ConvertFrom-Json foreach ($r in $a) { $r.requireAdapter.StartsWith("Test") | Should -Be $true $r.kind | Should -Be "Resource" @@ -259,7 +238,7 @@ resources: It 'verify `dsc resource list resource_filter adapter_filter`' { # same as previous but also apply resource_filter filter to resource types - $a = dsc resource list *TestResource2 -a *TestGroup -f json | ConvertFrom-Json + $a = dsc resource list *TestResource2 -a *TestGroup -o json | ConvertFrom-Json $a.Count | Should -Be 1 $r = $a[0] $r.requireAdapter | Should -Not -BeNullOrEmpty @@ -269,8 +248,8 @@ resources: It 'passing filepath to document arg should error' { $configFile = Resolve-Path $PSScriptRoot/../examples/osinfo.dsc.json - $stderr = dsc config get -d $configFile 2>&1 - $stderr | Should -Match '.*?--path.*?' + $stderr = dsc config get -i $configFile 2>&1 + $stderr | Should -Match '.*?--file.*?' } It 'Get operation on the adapter itself should fail' { @@ -286,13 +265,13 @@ resources: } It 'Set operation on the adapter itself should fail' { - 'abc' | dsc resource set -r Microsoft.DSC/PowerShell 2> $TestDrive/tracing.txt + 'abc' | dsc resource set -r Microsoft.DSC/PowerShell -f - 2> $TestDrive/tracing.txt $LASTEXITCODE | Should -Be 2 "$TestDrive/tracing.txt" | Should -FileContentMatchExactly 'Can not perform this operation on the adapter' } It 'Test operation on the adapter itself should fail' { - 'abc' | dsc resource test -r Microsoft.DSC/PowerShell 2> $TestDrive/tracing.txt + 'abc' | dsc resource test -r Microsoft.DSC/PowerShell -f - 2> $TestDrive/tracing.txt $LASTEXITCODE | Should -Be 2 "$TestDrive/tracing.txt" | Should -FileContentMatchExactly 'Can not perform this operation on the adapter' } @@ -310,7 +289,7 @@ resources: } It 'Invalid --system-root' { - dsc config --system-root /invalid/path get -p "$PSScriptRoot/../examples/groups.dsc.yaml" 2> $TestDrive/tracing.txt + dsc config --system-root /invalid/path get -f "$PSScriptRoot/../examples/groups.dsc.yaml" 2> $TestDrive/tracing.txt $LASTEXITCODE | Should -Be 1 "$TestDrive/tracing.txt" | Should -FileContentMatchExactly "Target path '/invalid/path' does not exist" } diff --git a/dsc/tests/dsc_brew.tests.ps1 b/dsc/tests/dsc_brew.tests.ps1 index 77a82cda..3987de98 100644 --- a/dsc/tests/dsc_brew.tests.ps1 +++ b/dsc/tests/dsc_brew.tests.ps1 @@ -7,14 +7,14 @@ Describe 'Brew resource tests' { } It 'Config get works' -Skip:(-not $brewExists) { - $out = dsc config get -p $PSScriptRoot/../examples/brew.dsc.yaml | ConvertFrom-Json -Depth 10 + $out = dsc config get -f $PSScriptRoot/../examples/brew.dsc.yaml | ConvertFrom-Json -Depth 10 $LASTEXITCODE | Should -Be 0 $exists = $null -ne (Get-Command gitui -CommandType Application -ErrorAction Ignore) $out.results[1].result.actualState._exist | Should -Be $exists } It 'Config test works' -Skip:(-not $brewExists) { - $out = dsc config test -p $PSScriptRoot/../examples/brew.dsc.yaml | ConvertFrom-Json -Depth 10 + $out = dsc config test -f $PSScriptRoot/../examples/brew.dsc.yaml | ConvertFrom-Json -Depth 10 $LASTEXITCODE | Should -Be 0 $exists = $null -ne (Get-Command gitui -CommandType Application -ErrorAction Ignore) $out.results[1].result.inDesiredState | Should -Be $exists diff --git a/dsc/tests/dsc_config_get.tests.ps1 b/dsc/tests/dsc_config_get.tests.ps1 index b1e4a586..b5c248ee 100644 --- a/dsc/tests/dsc_config_get.tests.ps1 +++ b/dsc/tests/dsc_config_get.tests.ps1 @@ -9,7 +9,7 @@ Describe 'dsc config get tests' { param($config) $jsonPath = Join-Path $PSScriptRoot '../examples' $config $config = Get-Content $jsonPath -Raw - $out = $config | dsc config get | ConvertFrom-Json + $out = $config | dsc config get -f - | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 $out.hadErrors | Should -BeFalse $out.results.Count | Should -Be 3 @@ -26,13 +26,12 @@ Describe 'dsc config get tests' { It 'will fail if resource schema does not match' -Skip:(!$IsWindows) { $jsonPath = Join-Path $PSScriptRoot '../examples/invalid_schema.dsc.yaml' - $config = Get-Content $jsonPath -Raw - $testError = & {$config | dsc config get get 2>&1} - $testError[0] | Should -match 'error:' + $testError = & {dsc config get -f $jsonPath 2>&1} + $testError[0] | Should -match 'Schema:' $LASTEXITCODE | Should -Be 2 } - It 'can accept the use of --format as a subcommand' { + It 'can accept the use of --output-format as a subcommand' { $config_yaml = @" `$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json resources: @@ -41,7 +40,7 @@ Describe 'dsc config get tests' { properties: output: hello "@ - $result = $config_yaml | dsc config get --format pretty-json | ConvertFrom-Json + $result = $config_yaml | dsc config get --output-format pretty-json -f - | ConvertFrom-Json $result.hadErrors | Should -BeFalse $result.results.Count | Should -Be 1 $result.results[0].Name | Should -Be 'Echo' diff --git a/dsc/tests/dsc_config_set.tests.ps1 b/dsc/tests/dsc_config_set.tests.ps1 index d749a694..7347cd71 100644 --- a/dsc/tests/dsc_config_set.tests.ps1 +++ b/dsc/tests/dsc_config_set.tests.ps1 @@ -15,7 +15,7 @@ Describe 'dsc config set tests' { properties: _exist: false "@ - $out = $config_yaml | dsc config set | ConvertFrom-Json + $out = $config_yaml | dsc config set -f - | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 $out.hadErrors | Should -BeFalse $out.results.Count | Should -Be 2 diff --git a/dsc/tests/dsc_config_test.tests.ps1 b/dsc/tests/dsc_config_test.tests.ps1 index c6ad1be4..ec437e9a 100644 --- a/dsc/tests/dsc_config_test.tests.ps1 +++ b/dsc/tests/dsc_config_test.tests.ps1 @@ -21,7 +21,7 @@ Describe 'dsc config test tests' { family: Windows '@ - $out = dsc config test -d $configYaml | ConvertFrom-Json + $out = dsc config test -i $configYaml | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 if ($IsWindows) { diff --git a/dsc/tests/dsc_discovery.tests.ps1 b/dsc/tests/dsc_discovery.tests.ps1 index 90d349f0..cc4b4ed9 100644 --- a/dsc/tests/dsc_discovery.tests.ps1 +++ b/dsc/tests/dsc_discovery.tests.ps1 @@ -131,7 +131,7 @@ Describe 'tests for resource discovery' { $TestClassResourcePath = Resolve-Path "$PSScriptRoot/../../powershell-adapter/Tests" $env:DSC_RESOURCE_PATH = $null $env:PSModulePath += [System.IO.Path]::PathSeparator + $TestClassResourcePath - "{'Name':'TestClassResource1'}" | dsc resource get -r 'TestClassResource/TestClassResource' | Out-Null + "{'Name':'TestClassResource1'}" | dsc resource get -r 'TestClassResource/TestClassResource' -f - | Out-Null Test-Path $script:lookupTableFilePath -PathType Leaf | Should -BeTrue $script:lookupTableFilePath | Should -FileContentMatchExactly 'testclassresource/testclassresource' @@ -155,7 +155,7 @@ Describe 'tests for resource discovery' { "$TestDrive/tracing.txt" | Should -FileContentMatchExactly "Saving lookup table" # second invocation (without an update) should use but not save adapter lookup table - "{'Name':'TestClassResource1'}" | dsc -l trace resource get -r 'TestClassResource/TestClassResource' 2> $TestDrive/tracing.txt + "{'Name':'TestClassResource1'}" | dsc -l trace resource get -r 'TestClassResource/TestClassResource' -f - 2> $TestDrive/tracing.txt "$TestDrive/tracing.txt" | Should -FileContentMatchExactly "Lookup table found resource 'testclassresource/testclassresource' in adapter 'Microsoft.DSC/PowerShell'" "$TestDrive/tracing.txt" | Should -Not -FileContentMatchExactly "Saving lookup table" @@ -172,11 +172,11 @@ Describe 'tests for resource discovery' { $LASTEXITCODE | Should -Be 7 $out = dsc resource get --all -r abc/def $LASTEXITCODE | Should -Be 7 - $out = 'abc' | dsc resource set -r abc/def + $out = 'abc' | dsc resource set -r abc/def -f - $LASTEXITCODE | Should -Be 7 - $out = 'abc' | dsc resource test -r abc/def + $out = 'abc' | dsc resource test -r abc/def -f - $LASTEXITCODE | Should -Be 7 - $out = 'abc' | dsc resource delete -r abc/def + $out = 'abc' | dsc resource delete -r abc/def -f - $LASTEXITCODE | Should -Be 7 $out = dsc resource export -r abc/def $LASTEXITCODE | Should -Be 7 diff --git a/dsc/tests/dsc_export.tests.ps1 b/dsc/tests/dsc_export.tests.ps1 index b6ac7899..a0dd47aa 100644 --- a/dsc/tests/dsc_export.tests.ps1 +++ b/dsc/tests/dsc_export.tests.ps1 @@ -32,7 +32,7 @@ Describe 'resource export tests' { properties: pid: 0 '@ - $out = $yaml | dsc config export + $out = $yaml | dsc config export -f - $LASTEXITCODE | Should -Be 0 $config_with_process_list = $out | ConvertFrom-Json $config_with_process_list.'$schema' | Should -BeExactly 'https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json' @@ -51,7 +51,7 @@ Describe 'resource export tests' { properties: pid: 0 '@ - $out = $yaml | dsc config export | dsc config set + $out = $yaml | dsc config export -f - | dsc config set -f - $LASTEXITCODE | Should -Be 0 $set_results = $out | ConvertFrom-Json $set_results.results.count | Should -BeGreaterThan 1 @@ -71,13 +71,13 @@ Describe 'resource export tests' { properties: pid: 0 '@ - $out = $yaml | dsc config export 2>&1 + $out = $yaml | dsc config export -f - 2>&1 $LASTEXITCODE | Should -Be 0 } - It 'Export can be called on individual resource with the use of --format as a subcommand' { + It 'Export can be called on individual resource with the use of --output-format as a subcommand' { - $out = dsc resource export -r Microsoft/Process -f pretty-json + $out = dsc resource export -r Microsoft/Process -o pretty-json $LASTEXITCODE | Should -Be 0 $config_with_process_list = $out | ConvertFrom-Json $config_with_process_list.'$schema' | Should -BeExactly 'https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json' @@ -85,7 +85,7 @@ Describe 'resource export tests' { $config_with_process_list.resources.count | Should -BeGreaterThan 1 } - It 'Export can be called on a configuration with the use of --format as a subcommand' { + It 'Export can be called on a configuration with the use of --output-format as a subcommand' { $yaml = @' $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json @@ -95,7 +95,7 @@ Describe 'resource export tests' { properties: pid: 0 '@ - $out = $yaml | dsc config export -f pretty-json + $out = $yaml | dsc config export -o pretty-json -f - $LASTEXITCODE | Should -Be 0 $config_with_process_list = $out | ConvertFrom-Json $config_with_process_list.'$schema' | Should -BeExactly 'https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json' diff --git a/dsc/tests/dsc_expressions.tests.ps1 b/dsc/tests/dsc_expressions.tests.ps1 index 7cc765d3..8aeec82d 100644 --- a/dsc/tests/dsc_expressions.tests.ps1 +++ b/dsc/tests/dsc_expressions.tests.ps1 @@ -41,8 +41,8 @@ resources: properties: output: "$text" "@ - $debug = $yaml | dsc -l trace config get -f yaml 2>&1 | Out-String - $out = $yaml | dsc config get | ConvertFrom-Json + $debug = $yaml | dsc -l trace config get -o yaml -f - 2>&1 | Out-String + $out = $yaml | dsc config get -f - | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 -Because $debug $out.results[0].result.actualState.output | Should -Be $expected -Because $debug } @@ -62,14 +62,14 @@ resources: properties: output: "$expression" "@ - $out = dsc config get -d $yaml 2>&1 + $out = dsc config get -i $yaml 2>&1 $LASTEXITCODE | Should -Be 2 $out | Should -BeLike "*ERROR*" } It 'Multi-line string literals work' { $yamlPath = "$PSScriptRoot/../examples/multiline.dsc.yaml" - $out = dsc config get -p $yamlPath | ConvertFrom-Json + $out = dsc config get -f $yamlPath | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 $out.results[0].result.actualState.output | Should -BeExactly @" This is a diff --git a/dsc/tests/dsc_functions.tests.ps1 b/dsc/tests/dsc_functions.tests.ps1 index 7deef03d..5754e3e7 100644 --- a/dsc/tests/dsc_functions.tests.ps1 +++ b/dsc/tests/dsc_functions.tests.ps1 @@ -21,7 +21,7 @@ Describe 'tests for function expressions' { properties: output: '$escapedText' "@ - $out = $config_yaml | dsc config get | ConvertFrom-Json + $out = $config_yaml | dsc config get -f - | ConvertFrom-Json $out.results[0].result.actualState.output | Should -Be $expected } @@ -39,7 +39,7 @@ Describe 'tests for function expressions' { properties: output: "[path($path)]" "@ - $out = $config_yaml | dsc config --system-root $PSHOME get | ConvertFrom-Json + $out = $config_yaml | dsc config --system-root $PSHOME get -f - | ConvertFrom-Json $out.results[0].result.actualState.output | Should -BeExactly $expected } @@ -58,7 +58,7 @@ Describe 'tests for function expressions' { } else { '/' } - $out = $config_yaml | dsc config get | ConvertFrom-Json + $out = $config_yaml | dsc config get -f - | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 $out.results[0].result.actualState.output | Should -BeExactly $expected } diff --git a/dsc/tests/dsc_get.tests.ps1 b/dsc/tests/dsc_get.tests.ps1 index f1cf7b65..dbc9527c 100644 --- a/dsc/tests/dsc_get.tests.ps1 +++ b/dsc/tests/dsc_get.tests.ps1 @@ -29,7 +29,7 @@ Describe 'resource get tests' { "valueName": "ProductName" } '@ - $output = $json | dsc resource get -r $resource + $output = $json | dsc resource get -r $resource -f - $LASTEXITCODE | Should -Be 0 $output = $output | ConvertFrom-Json $output.actualState.keyPath | Should -BeExactly 'HKLM\Software\Microsoft\Windows NT\CurrentVersion' @@ -44,7 +44,7 @@ Describe 'resource get tests' { "Name": "ProductName" } '@ - $testError = & {$json | dsc resource get -r Microsoft.Windows/Registry get 2>&1} + $testError = & {$json | dsc resource get -r Microsoft.Windows/Registry get -f - 2>&1} $testError[0] | SHould -match 'error:' $LASTEXITCODE | Should -Be 2 } diff --git a/dsc/tests/dsc_group.tests.ps1 b/dsc/tests/dsc_group.tests.ps1 index e549c1e4..1030b765 100644 --- a/dsc/tests/dsc_group.tests.ps1 +++ b/dsc/tests/dsc_group.tests.ps1 @@ -3,7 +3,7 @@ Describe 'Group resource tests' { It 'Nested groups should work for get' { - $out = (dsc config get -p $PSScriptRoot/../examples/groups.dsc.yaml -f yaml | Out-String).Trim() + $out = (dsc config get -f $PSScriptRoot/../examples/groups.dsc.yaml -o yaml | Out-String).Trim() $LASTEXITCODE | Should -Be 0 $out | Should -BeLike @' metadata: diff --git a/dsc/tests/dsc_include.tests.ps1 b/dsc/tests/dsc_include.tests.ps1 index 5fc1df6d..8c063927 100644 --- a/dsc/tests/dsc_include.tests.ps1 +++ b/dsc/tests/dsc_include.tests.ps1 @@ -23,7 +23,7 @@ Describe 'Include tests' { "@ $configPath = Join-Path $TestDrive 'config.dsc.yaml' $config | Set-Content -Path $configPath - $out = dsc config get -p $configPath | ConvertFrom-Json + $out = dsc config get -f $configPath | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 if ($IsWindows) { $expectedOS = 'Windows' @@ -47,7 +47,7 @@ Describe 'Include tests' { "@ $configPath = Join-Path $TestDrive 'config.dsc.yaml' $config | Set-Content -Path $configPath - $out = dsc config get -p $configPath | ConvertFrom-Json + $out = dsc config get -f $configPath | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 if ($IsWindows) { $expectedOS = 'Windows' @@ -79,7 +79,7 @@ Describe 'Include tests' { $configPath = Join-Path $TestDrive 'config.dsc.yaml' $configYaml | Set-Content -Path $configPath - $out = dsc config get -p $configPath 2> $logPath + $out = dsc config get -f $configPath 2> $logPath $LASTEXITCODE | Should -Be 2 $log = Get-Content -Path $logPath -Raw $log | Should -BeLike "*ERROR*" @@ -111,7 +111,7 @@ Describe 'Include tests' { $configPath = Join-Path $TestDrive 'config.dsc.yaml' $configYaml | Set-Content -Path $configPath - $out = dsc config get -p $configPath | ConvertFrom-Json + $out = dsc config get -f $configPath | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 if ($IsWindows) { $expectedOS = 'Windows' @@ -165,7 +165,7 @@ resources: configurationFile: $nestedIncludeConfigPath "@ - $out = $includeConfig | dsc config get | ConvertFrom-Json + $out = $includeConfig | dsc config get -f - | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 $out.results[0].result[0].result.actualState.output | Should -Be 'one' $out.results[1].result[0].name | Should -Be 'nested' @@ -201,7 +201,7 @@ resources: configurationFile: "[concat('$echoConfigPathParent', '$directorySeparator', '$echoConfigPathLeaf')]" "@ - $out = dsc config set -d $includeConfig | ConvertFrom-Json + $out = dsc config set -i $includeConfig | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 $out.results[0].result.beforeState[0].name | Should -Be 'one' $out.results[0].result.beforeState[0].type | Should -Be 'Microsoft.DSC.Debug/Echo' @@ -211,7 +211,7 @@ resources: It 'Test with include works' { $includeYaml = Join-Path $PSScriptRoot ../../dsc/examples/include.dsc.yaml - $out = dsc config test -p $includeYaml | ConvertFrom-Json + $out = dsc config test -f $includeYaml | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 $out.results[0].type | Should -BeExactly 'Microsoft.DSC/Include' $out.results[0].result[0].name | Should -BeExactly 'os' diff --git a/dsc/tests/dsc_osinfo.tests.ps1 b/dsc/tests/dsc_osinfo.tests.ps1 index 4bb4eb3f..777a8be1 100644 --- a/dsc/tests/dsc_osinfo.tests.ps1 +++ b/dsc/tests/dsc_osinfo.tests.ps1 @@ -1,6 +1,6 @@ Describe 'Tests for osinfo examples' { It 'Config with default parameters and get works' { - $out = dsc config get -p $PSScriptRoot/../examples/osinfo_parameters.dsc.yaml | ConvertFrom-Json -Depth 10 + $out = dsc config get -f $PSScriptRoot/../examples/osinfo_parameters.dsc.yaml | ConvertFrom-Json -Depth 10 $LASTEXITCODE | Should -Be 0 $expected = if ($IsWindows) { 'Windows' @@ -14,12 +14,12 @@ Describe 'Tests for osinfo examples' { } It 'Config test works' { - $out = dsc config -f $PSScriptRoot/../examples/osinfo.parameters.yaml test -p $PSScriptRoot/../examples/osinfo_parameters.dsc.yaml | ConvertFrom-Json -Depth 10 + $out = dsc config -f $PSScriptRoot/../examples/osinfo.parameters.yaml test -f $PSScriptRoot/../examples/osinfo_parameters.dsc.yaml | ConvertFrom-Json -Depth 10 $LASTEXITCODE | Should -Be 0 $out.results[0].result.inDesiredState | Should -Be $IsMacOS } - It 'Verify dsc home directory is added to PATH to find included resources' -Tag z1{ + It 'Verify dsc home directory is added to PATH to find included resources' { $oldPath = $env:PATH $oldLocation = Get-Location try { @@ -29,7 +29,7 @@ Describe 'Tests for osinfo examples' { $new_path = ($oldPath.Split([System.IO.Path]::PathSeparator) | Where-Object { $_ -ne $exe_path }) -join [System.IO.Path]::PathSeparator $env:PATH = $new_path - $null = & "$exe_path/dsc" config test -p "$PSScriptRoot/../examples/osinfo_parameters.dsc.yaml" + $null = & "$exe_path/dsc" config test -f "$PSScriptRoot/../examples/osinfo_parameters.dsc.yaml" $LASTEXITCODE | Should -Be 0 } finally { diff --git a/dsc/tests/dsc_parameters.tests.ps1 b/dsc/tests/dsc_parameters.tests.ps1 index 3ece6180..75182799 100644 --- a/dsc/tests/dsc_parameters.tests.ps1 +++ b/dsc/tests/dsc_parameters.tests.ps1 @@ -24,10 +24,10 @@ Describe 'Parameters tests' { if ($inputType -eq 'file') { $file_path = "$TestDrive/test.parameters.json" Set-Content -Path $file_path -Value $params_json - $out = $config_yaml | dsc config -f $file_path get | ConvertFrom-Json + $out = $config_yaml | dsc config -f $file_path get -f - | ConvertFrom-Json } else { - $out = $config_yaml | dsc config -p $params_json get | ConvertFrom-Json + $out = $config_yaml | dsc config -p $params_json get -f - | ConvertFrom-Json } $LASTEXITCODE | Should -Be 0 @@ -55,7 +55,7 @@ Describe 'Parameters tests' { "@ $params_json = @{ parameters = @{ param1 = $value }} | ConvertTo-Json - $out = $config_yaml | dsc config -p $params_json get | ConvertFrom-Json + $out = $config_yaml | dsc config -p $params_json get -f - | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 $out.results[0].result.actualState.output | Should -BeExactly $value } @@ -81,12 +81,12 @@ Describe 'Parameters tests' { "@ $params_json = @{ parameters = @{ param1 = $value }} | ConvertTo-Json - $testError = & {$config_yaml | dsc config -p $params_json get 2>&1} + $testError = & {$config_yaml | dsc config -p $params_json get -f - 2>&1} $testError | Should -match 'Parameter input failure:' $LASTEXITCODE | Should -Be 4 } - It 'Input length is wrong for ' -TestCases @( + It 'Input length is wrong for with value: ' -TestCases @( @{ type = 'string'; value = 'hi' } @{ type = 'string'; value = 'hello' } @{ type = 'array'; value = @('hello', 'there') } @@ -109,9 +109,9 @@ Describe 'Parameters tests' { "@ $params_json = @{ parameters = @{ param1 = $value }} | ConvertTo-Json - $testError = & {$config_yaml | dsc config -p $params_json get get 2>&1} + $testError = & {$config_yaml | dsc config -p $params_json get -f - 2>&1} $testError[0] | Should -match 'error' - $LASTEXITCODE | Should -Be 2 + $LASTEXITCODE | Should -Be 4 } It 'Input number value is out of range for and ' -TestCases @( @@ -136,9 +136,9 @@ Describe 'Parameters tests' { "@ $params_json = @{ parameters = @{ param1 = $value }} | ConvertTo-Json - $testError = & {$config_yaml | dsc config -p $params_json get get 2>&1} + $testError = & {$config_yaml | dsc config -p $params_json get -f - 2>&1} $testError[0] | Should -match 'error' - $LASTEXITCODE | Should -Be 2 + $LASTEXITCODE | Should -Be 4 } It 'Input is not in the allowed value list for ' -TestCases @( @@ -161,9 +161,9 @@ Describe 'Parameters tests' { "@ $params_json = @{ parameters = @{ param1 = $value }} | ConvertTo-Json - $testError = & {$config_yaml | dsc config -p $params_json get get 2>&1} + $testError = & {$config_yaml | dsc config -p $params_json get -f - 2>&1} $testError[0] | Should -match 'error' - $LASTEXITCODE | Should -Be 2 + $LASTEXITCODE | Should -Be 4 } It 'Length constraint is incorrectly applied to with ' -TestCases @( @@ -188,9 +188,9 @@ Describe 'Parameters tests' { "@ $params_json = @{ parameters = @{ param1 = $value }} | ConvertTo-Json - $testError = & {$config_yaml | dsc config -p $params_json get get 2>&1} + $testError = & {$config_yaml | dsc config -p $params_json get -f - 2>&1} $testError[0] | Should -match 'error' - $LASTEXITCODE | Should -Be 2 + $LASTEXITCODE | Should -Be 4 } It 'Default value is used when not provided' { @@ -228,7 +228,7 @@ Describe 'Parameters tests' { output: '[parameters(''paramArray'')]' "@ - $out = $config_yaml | dsc config get | ConvertFrom-Json + $out = $config_yaml | dsc config get -f - | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 $out.results[0].result.actualState.output | Should -BeExactly 'hello' $out.results[1].result.actualState.output | Should -BeExactly 7 @@ -268,14 +268,14 @@ Describe 'Parameters tests' { family: '[parameters(''osFamily'')]' '@ - $out = dsc config -p $params test -d $config_yaml | ConvertFrom-Json + $out = dsc config -p $params test -i $config_yaml | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 $out.results[0].result.actualState.family | Should -BeExactly $os $out.results[0].result.inDesiredState | Should -BeTrue } It 'secure types can be passed as objects to resources' { - $out = dsc config -f $PSScriptRoot/../examples/secure_parameters.parameters.yaml get -p $PSScriptRoot/../examples/secure_parameters.dsc.yaml | ConvertFrom-Json + $out = dsc config -f $PSScriptRoot/../examples/secure_parameters.parameters.yaml get -f $PSScriptRoot/../examples/secure_parameters.dsc.yaml | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 $out.results[0].result.actualState.output | Should -BeExactly 'mySecret' $out.results[1].result.actualState.output | Should -BeExactly 'mySecretProperty' @@ -305,7 +305,15 @@ Describe 'Parameters tests' { "@ $params_json = @{ parameters = @{ param = $value }} | ConvertTo-Json - $null = $config_yaml | dsc config -p $params_json get + $output = $config_yaml | dsc config -p $params_json get -f - 2>&1 $LASTEXITCODE | Should -Be 4 + if ($type -eq 'secureString') { + $type = 'string' + } + elseif ($type -eq 'secureObject') { + $type = 'object' + } + + $output | Should -Match "Parameter input failure:.*?$type" } } diff --git a/dsc/tests/dsc_reference.tests.ps1 b/dsc/tests/dsc_reference.tests.ps1 index 3b106014..a809da74 100644 --- a/dsc/tests/dsc_reference.tests.ps1 +++ b/dsc/tests/dsc_reference.tests.ps1 @@ -3,7 +3,7 @@ Describe 'Tests for config using reference function' { It 'Reference works' { - $out = dsc config get -p $PSScriptRoot/../examples/reference.dsc.yaml | ConvertFrom-Json + $out = dsc config get -f $PSScriptRoot/../examples/reference.dsc.yaml | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 $os = if ($IsWindows) { 'Windows' diff --git a/dsc/tests/dsc_resource_input.tests.ps1 b/dsc/tests/dsc_resource_input.tests.ps1 index b34d26ec..c55fb8ca 100644 --- a/dsc/tests/dsc_resource_input.tests.ps1 +++ b/dsc/tests/dsc_resource_input.tests.ps1 @@ -110,7 +110,7 @@ Describe 'tests for resource input' { } "@ - $result = $json | dsc resource $operation -r Test/EnvVarInput | ConvertFrom-Json + $result = $json | dsc resource $operation -r Test/EnvVarInput -f - | ConvertFrom-Json $result.$member.Hello | Should -BeExactly 'foo' $result.$member.World | Should -Be 2 $result.$member.Boolean | Should -Be 'true' diff --git a/dsc/tests/dsc_resource_list.tests.ps1 b/dsc/tests/dsc_resource_list.tests.ps1 index 9c456a68..9e97c160 100644 --- a/dsc/tests/dsc_resource_list.tests.ps1 +++ b/dsc/tests/dsc_resource_list.tests.ps1 @@ -9,7 +9,6 @@ Describe 'Tests for listing resources' { $resources.Count | Should -BeGreaterThan 0 $resources.type | Should -Contain 'Microsoft.DSC/Assertion' $resources.type | Should -Contain 'Microsoft.DSC/Group' - $resources.type | Should -Contain 'Microsoft.DSC/Parallel' $resources.type | Should -Contain 'Microsoft/OSInfo' ($resources | Where-Object { $_.type -eq 'Microsoft.DSC/Group' }).Kind | Should -BeExactly 'Group' ($resources | Where-Object { $_.type -eq 'Microsoft/OSInfo' }).Kind | Should -BeExactly 'Resource' @@ -48,10 +47,10 @@ Describe 'Tests for listing resources' { } } - It 'can accept the use of --format as a subcommand' { + It 'can accept the use of --output-format as a subcommand' { $expectedCount = 1 $expectedType = 'Microsoft/OSInfo' - $resources = dsc resource list --description "operating system" --format pretty-json | ConvertFrom-Json + $resources = dsc resource list --description "operating system" --output-format pretty-json | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 $resources.Count | Should -Be $expectedCount if ($expectedCount -gt 0) { diff --git a/dsc/tests/dsc_resource_test.tests.ps1 b/dsc/tests/dsc_resource_test.tests.ps1 index 6e1cd9f1..44971e4b 100644 --- a/dsc/tests/dsc_resource_test.tests.ps1 +++ b/dsc/tests/dsc_resource_test.tests.ps1 @@ -15,7 +15,7 @@ Describe 'Invoke a resource test directly' { $out = @" { "family": "$os" } -"@ | dsc resource test -r Microsoft/OSInfo | ConvertFrom-Json +"@ | dsc resource test -r Microsoft/OSInfo -f - | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 $out.actualState.family | Should -BeExactly $os $out.inDesiredState | Should -Be $true diff --git a/dsc/tests/dsc_schema.tests.ps1 b/dsc/tests/dsc_schema.tests.ps1 index 65536a10..6a707a80 100644 --- a/dsc/tests/dsc_schema.tests.ps1 +++ b/dsc/tests/dsc_schema.tests.ps1 @@ -29,8 +29,8 @@ Describe 'config schema tests' { $schema.'$schema' | Should -BeExactly 'http://json-schema.org/draft-07/schema#' } - It 'can accept the use of --format as a subcommand' { - $schema = dsc resource schema -r Microsoft.DSC.Debug/Echo -f pretty-json + It 'can accept the use of --output-format as a subcommand' { + $schema = dsc resource schema -r Microsoft.DSC.Debug/Echo -o pretty-json $LASTEXITCODE | Should -Be 0 $schema | Should -Not -BeNullOrEmpty $schema = $schema | ConvertFrom-Json diff --git a/dsc/tests/dsc_securitycontext.tests.ps1 b/dsc/tests/dsc_securitycontext.tests.ps1 index 82958e00..26ac9ffc 100644 --- a/dsc/tests/dsc_securitycontext.tests.ps1 +++ b/dsc/tests/dsc_securitycontext.tests.ps1 @@ -13,7 +13,7 @@ Describe 'Tests for configuration security context metadata' { } It 'Require admin' { - $out = dsc config get -p $PSScriptRoot/../examples/require_admin.yaml + $out = dsc config get -f $PSScriptRoot/../examples/require_admin.yaml if ($isAdmin) { $LASTEXITCODE | Should -Be 0 $out | Should -Not -BeNullOrEmpty @@ -24,7 +24,7 @@ Describe 'Tests for configuration security context metadata' { } It 'Require non-admin' { - $out = dsc config get -p $PSScriptRoot/../examples/require_nonadmin.yaml + $out = dsc config get -f $PSScriptRoot/../examples/require_nonadmin.yaml if ($isAdmin) { $LASTEXITCODE | Should -Be 2 } diff --git a/dsc/tests/dsc_set.tests.ps1 b/dsc/tests/dsc_set.tests.ps1 index 3175c57d..6bbd7e78 100644 --- a/dsc/tests/dsc_set.tests.ps1 +++ b/dsc/tests/dsc_set.tests.ps1 @@ -88,7 +88,7 @@ Describe 'resource set tests' { } } '@ - $out = $json | dsc resource set -r Microsoft.Windows/Registry + $out = $json | dsc resource set -r Microsoft.Windows/Registry -f - $LASTEXITCODE | Should -Be 0 $result = $out | ConvertFrom-Json $result.afterState.keyPath | Should -Be 'HKCU\1\2\3' @@ -97,7 +97,7 @@ Describe 'resource set tests' { $result.changedProperties | Should -Be @('valueName', 'valueData', '_exist') ($result.psobject.properties | Measure-Object).Count | Should -Be 3 - $out = $json | dsc resource get -r Microsoft.Windows/Registry + $out = $json | dsc resource get -r Microsoft.Windows/Registry -f - $LASTEXITCODE | Should -Be 0 $result = $out | ConvertFrom-Json $result.actualState.keyPath | Should -Be 'HKCU\1\2\3' @@ -111,7 +111,7 @@ Describe 'resource set tests' { "_exist": false } '@ - $out = $json | dsc resource set -r Microsoft.Windows/Registry + $out = $json | dsc resource set -r Microsoft.Windows/Registry -f - $LASTEXITCODE | Should -Be 0 $result = $out | ConvertFrom-Json $result.afterState.keyPath | Should -BeExactly 'HKCU\1' @@ -119,7 +119,7 @@ Describe 'resource set tests' { ($result.psobject.properties | Measure-Object).Count | Should -Be 3 } - It 'can accept the use of --format as a subcommand' -Skip:(!$IsWindows) -TestCases @( + It 'can accept the use of --output-format as a subcommand' -Skip:(!$IsWindows) -TestCases @( @{ format = 'yaml'; expected = @' beforeState: test: true @@ -148,7 +148,7 @@ changedProperties: $oldPath = $env:DSC_RESOURCE_PATH try { $env:DSC_RESOURCE_PATH = $TestDrive - $out = '{ "test": true }' | dsc resource set -r Test/SetNoTest --format $format | Out-String + $out = '{ "test": true }' | dsc resource set -r Test/SetNoTest -f - --output-format $format | Out-String $LASTEXITCODE | Should -Be 0 $out.Trim() | Should -BeExactly $expected } @@ -161,7 +161,7 @@ changedProperties: $oldPath = $env:DSC_RESOURCE_PATH try { $env:DSC_RESOURCE_PATH = $TestDrive - $out = '{ "test": true }' | dsc resource set -r Test/SetNoTest | ConvertFrom-Json + $out = '{ "test": true }' | dsc resource set -r Test/SetNoTest -f - | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 $out.BeforeState.test | Should -Be $true $out.AfterState.test | Should -Be $false diff --git a/dsc/tests/dsc_test.tests.ps1 b/dsc/tests/dsc_test.tests.ps1 index 037e9c42..c15b0981 100644 --- a/dsc/tests/dsc_test.tests.ps1 +++ b/dsc/tests/dsc_test.tests.ps1 @@ -10,7 +10,7 @@ Describe 'resource test tests' { } '@ $current = registry config get --input $json - $out = $current | dsc resource test -r Microsoft.Windows/Registry + $out = $current | dsc resource test -r Microsoft.Windows/Registry -f - $LASTEXITCODE | Should -Be 0 $out = $out | ConvertFrom-Json $out.inDesiredState | Should -BeTrue @@ -27,7 +27,7 @@ Describe 'resource test tests' { } } '@ - $out = $json | dsc resource test -r Microsoft.Windows/Registry + $out = $json | dsc resource test -r Microsoft.Windows/Registry -f - $LASTEXITCODE | Should -Be 0 $out = $out | ConvertFrom-Json $out.inDesiredState | Should -BeFalse @@ -45,7 +45,7 @@ Describe 'resource test tests' { } } '@ - $out = $json | dsc resource test -r Microsoft.Windows/Registry + $out = $json | dsc resource test -r Microsoft.Windows/Registry -f - $LASTEXITCODE | Should -Be 0 $out = $out | ConvertFrom-Json $out.inDesiredState | Should -BeFalse @@ -54,8 +54,8 @@ Describe 'resource test tests' { $out.differingProperties[1] | Should -BeExactly '_exist' } - It 'can accept the use of --format as a subcommand' { - $null = "output: hello" | dsc resource test -r Microsoft.DSC.Debug/Echo --format pretty-json + It 'can accept the use of --output-format as a subcommand' { + $null = "output: hello" | dsc resource test -r Microsoft.DSC.Debug/Echo --output-format pretty-json -f - $LASTEXITCODE | Should -Be 0 } } diff --git a/dsc/tests/dsc_tracing.tests.ps1 b/dsc/tests/dsc_tracing.tests.ps1 index 23355531..4f00e955 100644 --- a/dsc/tests/dsc_tracing.tests.ps1 +++ b/dsc/tests/dsc_tracing.tests.ps1 @@ -12,14 +12,14 @@ Describe 'tracing tests' { param($level) $logPath = "$TestDrive/dsc_trace.log" - $null = '{}' | dsc -l $level resource get -r 'DoesNotExist' 2> $logPath + $null = dsc -l $level resource get -r 'DoesNotExist' 2> $logPath $log = Get-Content $logPath -Raw $log | Should -BeLikeExactly "* $($level.ToUpper()) *" } It 'trace level error does not emit other levels' { $logPath = "$TestDrive/dsc_trace.log" - $null = '{}' | dsc --trace-level error resource list 'DoesNotExist' 2> $logPath + $null = dsc --trace-level error resource list 'DoesNotExist' 2> $logPath $log = Get-Content $logPath -Raw $log | Should -Not -BeLikeExactly "* WARNING *" $log | Should -Not -BeLikeExactly "* INFO *" @@ -29,14 +29,14 @@ Describe 'tracing tests' { It 'trace format plaintext does not emit ANSI' { $logPath = "$TestDrive/dsc_trace.log" - $null = '{}' | dsc --trace-format plaintext resource list 'DoesNotExist' 2> $logPath + $null = dsc --trace-format plaintext resource list 'DoesNotExist' 2> $logPath $log = Get-Content $logPath -Raw $log | Should -Not -BeLikeExactly "*``[0m*" } It 'trace format json emits json' { $logPath = "$TestDrive/dsc_trace.log" - $null = '{}' | dsc --trace-format json resource list 'DoesNotExist' 2> $logPath + $null = dsc --trace-format json resource list 'DoesNotExist' 2> $logPath foreach ($line in (Get-Content $logPath)) { $trace = $line | ConvertFrom-Json -Depth 10 $trace.timestamp | Should -Not -BeNullOrEmpty @@ -55,7 +55,7 @@ Describe 'tracing tests' { param($level, $sourceExpected) $logPath = "$TestDrive/dsc_trace.log" - $null = '{}' | dsc -l $level resource list 'DoesNotExist' 2> $logPath + $null = dsc -l $level resource list 'DoesNotExist' 2> $logPath $log = Get-Content $logPath -Raw if ($sourceExpected) { $log | Should -BeLike "*dsc_lib*: *" @@ -82,15 +82,16 @@ Describe 'tracing tests' { level: trace "@ - $out = (dsc -l $level config get -d $configYaml 2> $null) | ConvertFrom-Json + $out = (dsc -l $level config get -i $configYaml 2> $null) | ConvertFrom-Json $out.results[0].result.actualState.level | Should -BeExactly $level } It 'Pass-through tracing should only emit JSON for child processes' { $logPath = "$TestDrive/dsc_trace.log" - $out = dsc -l info -f pass-through config get -p ../examples/groups.dsc.yaml 2> $logPath + $out = dsc -l info -t pass-through config get -f ../examples/groups.dsc.yaml 2> $logPath foreach ($line in (Get-Content $logPath)) { $line | Should -Not -BeNullOrEmpty + Write-Verbose -Verbose $line $json = $line | ConvertFrom-Json $json.timestamp | Should -Not -BeNullOrEmpty $json.level | Should -BeIn 'ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE' diff --git a/dsc/tests/dsc_variables.tests.ps1 b/dsc/tests/dsc_variables.tests.ps1 index 8a983249..95d3d348 100644 --- a/dsc/tests/dsc_variables.tests.ps1 +++ b/dsc/tests/dsc_variables.tests.ps1 @@ -4,7 +4,7 @@ Describe 'Configruation variables tests' { It 'Variables example config works' { $configFile = "$PSSCriptRoot/../examples/variables.dsc.yaml" - $out = dsc config get -p $configFile | ConvertFrom-Json + $out = dsc config get -f $configFile | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 $out.results[0].result.actualState.output | Should -BeExactly 'myOutput is: Hello world!, myObject is: baz' } @@ -21,7 +21,7 @@ resources: properties: output: "[variables('myVariable')]" '@ - $out = dsc config get -d $configYaml | ConvertFrom-Json + $out = dsc config get -i $configYaml | ConvertFrom-Json Write-Verbose -Verbose $out $LASTEXITCODE | Should -Be 0 $out.results[0].result.actualState.output | Should -Be 'bar' @@ -38,7 +38,7 @@ resources: properties: output: "[variables('myVariable')]" '@ - $out = dsc config get -d $configYaml 2>&1 | Out-String + $out = dsc config get -i $configYaml 2>&1 | Out-String Write-Verbose -Verbose $out $LASTEXITCODE | Should -Be 2 $out | Should -BeLike "*Variable 'myVariable' does not exist or has not been initialized yet*" diff --git a/dsc/tests/dsc_whatif.tests.ps1 b/dsc/tests/dsc_whatif.tests.ps1 index 3795c9fa..dd13ab4d 100644 --- a/dsc/tests/dsc_whatif.tests.ps1 +++ b/dsc/tests/dsc_whatif.tests.ps1 @@ -14,8 +14,8 @@ Describe 'whatif tests' { properties: output: hello "@ - $what_if_result = $config_yaml | dsc config set -w | ConvertFrom-Json - $set_result = $config_yaml | dsc config set | ConvertFrom-Json + $what_if_result = $config_yaml | dsc config set -w -f - | ConvertFrom-Json + $set_result = $config_yaml | dsc config set -f - | ConvertFrom-Json $what_if_result.metadata.'Microsoft.DSC'.executionType | Should -BeExactly 'WhatIf' $what_if_result.results.result.beforeState.output | Should -Be $set_result.results.result.beforeState.output $what_if_result.results.result.afterState.output | Should -Be $set_result.results.result.afterState.output @@ -35,8 +35,8 @@ Describe 'whatif tests' { properties: keyPath: 'HKCU\1\2' "@ - $what_if_result = $config_yaml | dsc config set -w | ConvertFrom-Json - $set_result = $config_yaml | dsc config set | ConvertFrom-Json + $what_if_result = dsc config set -w -i $config_yaml | ConvertFrom-Json + $set_result = dsc config set -i $config_yaml | ConvertFrom-Json $what_if_result.metadata.'Microsoft.DSC'.executionType | Should -BeExactly 'WhatIf' $what_if_result.results.result.beforeState._exist | Should -Be $set_result.results.result.beforeState._exist $what_if_result.results.result.beforeState.keyPath | Should -Be $set_result.results.result.beforeState.keyPath @@ -59,13 +59,13 @@ Describe 'whatif tests' { properties: _exist: false "@ - $result = $config_yaml | dsc config set -w 2>&1 + $result = $config_yaml | dsc config set -w -f - 2>&1 $result | Should -Match 'ERROR.*?Not supported.*?what-if' $LASTEXITCODE | Should -Be 2 } It 'config set whatif for group resource' { - $result = dsc config set -p $PSScriptRoot/../examples/groups.dsc.yaml -w 2>&1 + $result = dsc config set -f $PSScriptRoot/../examples/groups.dsc.yaml -w 2>&1 $result | Should -Match 'ERROR.*?Not implemented.*?what-if' $LASTEXITCODE | Should -Be 2 } @@ -79,7 +79,7 @@ Describe 'whatif tests' { properties: executionType: Actual "@ - $result = $config_yaml | dsc config set | ConvertFrom-Json + $result = $config_yaml | dsc config set -f - | ConvertFrom-Json $result.metadata.'Microsoft.DSC'.executionType | Should -BeExactly 'Actual' $result.results.result.afterState.executionType | Should -BeExactly 'Actual' $result.results.result.changedProperties | Should -Be $null @@ -97,7 +97,7 @@ Describe 'whatif tests' { properties: executionType: Actual "@ - $result = $config_yaml | dsc config set -w | ConvertFrom-Json + $result = $config_yaml | dsc config set -w -f - | ConvertFrom-Json $result.metadata.'Microsoft.DSC'.executionType | Should -BeExactly 'WhatIf' $result.results.result.afterState.executionType | Should -BeExactly 'WhatIf' $result.results.result.changedProperties | Should -BeExactly 'executionType' diff --git a/dsc_lib/src/configure/mod.rs b/dsc_lib/src/configure/mod.rs index 8ec39682..f32a18d2 100644 --- a/dsc_lib/src/configure/mod.rs +++ b/dsc_lib/src/configure/mod.rs @@ -163,7 +163,7 @@ fn add_metadata(kind: &Kind, mut properties: Option> ) -> Res Ok(serde_json::to_string(&properties)?) } -fn check_security_context(metadata: &Option) -> Result<(), DscError> { +fn check_security_context(metadata: Option<&Metadata>) -> Result<(), DscError> { if metadata.is_none() { return Ok(()); } @@ -243,7 +243,7 @@ impl Configurator { for resource in resources { Span::current().pb_inc(1); pb_span.pb_set_message(format!("Get '{}'", resource.name).as_str()); - let properties = self.invoke_property_expressions(&resource.properties)?; + let properties = self.invoke_property_expressions(resource.properties.as_ref())?; let Some(dsc_resource) = self.discovery.find_resource(&resource.resource_type) else { return Err(DscError::ResourceNotFound(resource.resource_type)); }; @@ -301,7 +301,7 @@ impl Configurator { for resource in resources { Span::current().pb_inc(1); pb_span.pb_set_message(format!("Set '{}'", resource.name).as_str()); - let properties = self.invoke_property_expressions(&resource.properties)?; + let properties = self.invoke_property_expressions(resource.properties.as_ref())?; let Some(dsc_resource) = self.discovery.find_resource(&resource.resource_type) else { return Err(DscError::ResourceNotFound(resource.resource_type)); }; @@ -409,7 +409,7 @@ impl Configurator { for resource in resources { Span::current().pb_inc(1); pb_span.pb_set_message(format!("Test '{}'", resource.name).as_str()); - let properties = self.invoke_property_expressions(&resource.properties)?; + let properties = self.invoke_property_expressions(resource.properties.as_ref())?; let Some(dsc_resource) = self.discovery.find_resource(&resource.resource_type) else { return Err(DscError::ResourceNotFound(resource.resource_type)); }; @@ -465,7 +465,7 @@ impl Configurator { for resource in &resources { Span::current().pb_inc(1); pb_span.pb_set_message(format!("Export '{}'", resource.name).as_str()); - let properties = self.invoke_property_expressions(&resource.properties)?; + let properties = self.invoke_property_expressions(resource.properties.as_ref())?; let Some(dsc_resource) = self.discovery.find_resource(&resource.resource_type) else { return Err(DscError::ResourceNotFound(resource.resource_type.clone())); }; @@ -499,14 +499,14 @@ impl Configurator { /// # Errors /// /// This function will return an error if the parameters are invalid. - pub fn set_context(&mut self, parameters_input: &Option) -> Result<(), DscError> { + pub fn set_context(&mut self, parameters_input: Option<&Value>) -> Result<(), DscError> { let config = serde_json::from_str::(self.json.as_str())?; self.set_parameters(parameters_input, &config)?; self.set_variables(&config)?; Ok(()) } - fn set_parameters(&mut self, parameters_input: &Option, config: &Configuration) -> Result<(), DscError> { + fn set_parameters(&mut self, parameters_input: Option<&Value>, config: &Configuration) -> Result<(), DscError> { // set default parameters first let Some(parameters) = &config.parameters else { if parameters_input.is_none() { @@ -647,7 +647,7 @@ impl Configurator { fn validate_config(&mut self, progress_format: &Option) -> Result<(), DscError> { let config: Configuration = serde_json::from_str(self.json.as_str())?; - check_security_context(&config.metadata)?; + check_security_context(config.metadata.as_ref())?; // Perform discovery of resources used in config let required_resources = config.resources.iter().map(|p| p.resource_type.clone()).collect::>(); @@ -656,7 +656,7 @@ impl Configurator { Ok(()) } - fn invoke_property_expressions(&mut self, properties: &Option>) -> Result>, DscError> { + fn invoke_property_expressions(&mut self, properties: Option<&Map>) -> Result>, DscError> { debug!("Invoke property expressions"); if properties.is_none() { return Ok(None); @@ -668,7 +668,7 @@ impl Configurator { trace!("Invoke property expression for {name}: {value}"); match value { Value::Object(object) => { - let value = self.invoke_property_expressions(&Some(object.clone()))?; + let value = self.invoke_property_expressions(Some(object))?; result.insert(name.clone(), serde_json::to_value(value)?); continue; }, @@ -677,7 +677,7 @@ impl Configurator { for element in array { match element { Value::Object(object) => { - let value = self.invoke_property_expressions(&Some(object.clone()))?; + let value = self.invoke_property_expressions(Some(object))?; result_array.push(serde_json::to_value(value)?); continue; }, diff --git a/dsc_lib/src/discovery/command_discovery.rs b/dsc_lib/src/discovery/command_discovery.rs index b0327804..986e7a41 100644 --- a/dsc_lib/src/discovery/command_discovery.rs +++ b/dsc_lib/src/discovery/command_discovery.rs @@ -310,7 +310,7 @@ impl ResourceDiscovery for CommandDiscovery { let mut adapter_resources_count = 0; // invoke the list command let list_command = manifest.adapter.unwrap().list; - let (exit_code, stdout, stderr) = match invoke_command(&list_command.executable, list_command.args, None, Some(&adapter.directory), None, &manifest.exit_codes) + let (exit_code, stdout, stderr) = match invoke_command(&list_command.executable, list_command.args, None, Some(&adapter.directory), None, manifest.exit_codes.as_ref()) { Ok((exit_code, stdout, stderr)) => (exit_code, stdout, stderr), Err(e) => { diff --git a/dsc_lib/src/dscresources/command_resource.rs b/dsc_lib/src/dscresources/command_resource.rs index f8cdbc55..556cb73e 100644 --- a/dsc_lib/src/dscresources/command_resource.rs +++ b/dsc_lib/src/dscresources/command_resource.rs @@ -30,14 +30,14 @@ pub fn invoke_get(resource: &ResourceManifest, cwd: &str, filter: &str) -> Resul let Some(get) = &resource.get else { return Err(DscError::NotImplemented("get".to_string())); }; - let args = process_args(&get.args, filter); + let args = process_args(get.args.as_ref(), filter); if !filter.is_empty() { verify_json(resource, cwd, filter)?; - command_input = get_command_input(&get.input, filter)?; + command_input = get_command_input(get.input.as_ref(), filter)?; } info!("Invoking get '{}' using '{}'", &resource.resource_type, &get.executable); - let (_exit_code, stdout, stderr) = invoke_command(&get.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env, &resource.exit_codes)?; + let (_exit_code, stdout, stderr) = invoke_command(&get.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env, resource.exit_codes.as_ref())?; if resource.kind == Some(Kind::Resource) { debug!("Verifying output of get '{}' using '{}'", &resource.resource_type, &get.executable); verify_json(resource, cwd, &stdout)?; @@ -134,11 +134,11 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te let Some(get) = &resource.get else { return Err(DscError::NotImplemented("get".to_string())); }; - let args = process_args(&get.args, desired); - let command_input = get_command_input(&get.input, desired)?; + let args = process_args(get.args.as_ref(), desired); + let command_input = get_command_input(get.input.as_ref(), desired)?; info!("Getting current state for set by invoking get '{}' using '{}'", &resource.resource_type, &get.executable); - let (exit_code, stdout, stderr) = invoke_command(&get.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env, &resource.exit_codes)?; + let (exit_code, stdout, stderr) = invoke_command(&get.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env, resource.exit_codes.as_ref())?; if resource.kind == Some(Kind::Resource) { debug!("Verifying output of get '{}' using '{}'", &resource.resource_type, &get.executable); @@ -154,7 +154,7 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te let mut env: Option> = None; let mut input_desired: Option<&str> = None; - let args = process_args(&set.args, desired); + let args = process_args(set.args.as_ref(), desired); match &set.input { Some(InputKind::Env) => { env = Some(json_to_hashmap(desired)?); @@ -168,7 +168,7 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te } info!("Invoking {} '{}' using '{}'", operation_type, &resource.resource_type, &set.executable); - let (exit_code, stdout, stderr) = invoke_command(&set.executable, args, input_desired, Some(cwd), env, &resource.exit_codes)?; + let (exit_code, stdout, stderr) = invoke_command(&set.executable, args, input_desired, Some(cwd), env, resource.exit_codes.as_ref())?; match set.returns { Some(ReturnKind::State) => { @@ -256,11 +256,11 @@ pub fn invoke_test(resource: &ResourceManifest, cwd: &str, expected: &str) -> Re verify_json(resource, cwd, expected)?; - let args = process_args(&test.args, expected); - let command_input = get_command_input(&test.input, expected)?; + let args = process_args(test.args.as_ref(), expected); + let command_input = get_command_input(test.input.as_ref(), expected)?; info!("Invoking test '{}' using '{}'", &resource.resource_type, &test.executable); - let (exit_code, stdout, stderr) = invoke_command(&test.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env, &resource.exit_codes)?; + let (exit_code, stdout, stderr) = invoke_command(&test.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env, resource.exit_codes.as_ref())?; if resource.kind == Some(Kind::Resource) { debug!("Verifying output of test '{}' using '{}'", &resource.resource_type, &test.executable); @@ -376,11 +376,11 @@ pub fn invoke_delete(resource: &ResourceManifest, cwd: &str, filter: &str) -> Re verify_json(resource, cwd, filter)?; - let args = process_args(&delete.args, filter); - let command_input = get_command_input(&delete.input, filter)?; + let args = process_args(delete.args.as_ref(), filter); + let command_input = get_command_input(delete.input.as_ref(), filter)?; info!("Invoking delete '{}' using '{}'", &resource.resource_type, &delete.executable); - let (_exit_code, _stdout, _stderr) = invoke_command(&delete.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env, &resource.exit_codes)?; + let (_exit_code, _stdout, _stderr) = invoke_command(&delete.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env, resource.exit_codes.as_ref())?; Ok(()) } @@ -407,11 +407,11 @@ pub fn invoke_validate(resource: &ResourceManifest, cwd: &str, config: &str) -> return Err(DscError::NotImplemented("validate".to_string())); }; - let args = process_args(&validate.args, config); - let command_input = get_command_input(&validate.input, config)?; + let args = process_args(validate.args.as_ref(), config); + let command_input = get_command_input(validate.input.as_ref(), config)?; info!("Invoking validate '{}' using '{}'", &resource.resource_type, &validate.executable); - let (_exit_code, stdout, _stderr) = invoke_command(&validate.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env, &resource.exit_codes)?; + let (_exit_code, stdout, _stderr) = invoke_command(&validate.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env, resource.exit_codes.as_ref())?; let result: ValidateResult = serde_json::from_str(&stdout)?; Ok(result) } @@ -432,7 +432,7 @@ pub fn get_schema(resource: &ResourceManifest, cwd: &str) -> Result { - let (_exit_code, stdout, _stderr) = invoke_command(&command.executable, command.args.clone(), None, Some(cwd), None, &resource.exit_codes)?; + let (_exit_code, stdout, _stderr) = invoke_command(&command.executable, command.args.clone(), None, Some(cwd), None, resource.exit_codes.as_ref())?; Ok(stdout) }, SchemaKind::Embedded(ref schema) => { @@ -468,15 +468,15 @@ pub fn invoke_export(resource: &ResourceManifest, cwd: &str, input: Option<&str> if !input.is_empty() { verify_json(resource, cwd, input)?; - command_input = get_command_input(&export.input, input)?; + command_input = get_command_input(export.input.as_ref(), input)?; } - args = process_args(&export.args, input); + args = process_args(export.args.as_ref(), input); } else { - args = process_args(&export.args, ""); + args = process_args(export.args.as_ref(), ""); } - let (_exit_code, stdout, stderr) = invoke_command(&export.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env, &resource.exit_codes)?; + let (_exit_code, stdout, stderr) = invoke_command(&export.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env, resource.exit_codes.as_ref())?; let mut instances: Vec = Vec::new(); for line in stdout.lines() { @@ -518,11 +518,11 @@ pub fn invoke_resolve(resource: &ResourceManifest, cwd: &str, input: &str) -> Re return Err(DscError::Operation(format!("Resolve is not supported by resource {}", &resource.resource_type))); }; - let args = process_args(&resolve.args, input); - let command_input = get_command_input(&resolve.input, input)?; + let args = process_args(resolve.args.as_ref(), input); + let command_input = get_command_input(resolve.input.as_ref(), input)?; info!("Invoking resolve '{}' using '{}'", &resource.resource_type, &resolve.executable); - let (_exit_code, stdout, _stderr) = invoke_command(&resolve.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env, &resource.exit_codes)?; + let (_exit_code, stdout, _stderr) = invoke_command(&resolve.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env, resource.exit_codes.as_ref())?; let result: ResolveResult = serde_json::from_str(&stdout)?; Ok(result) } @@ -542,7 +542,7 @@ pub fn invoke_resolve(resource: &ResourceManifest, cwd: &str, input: &str) -> Re /// /// Error is returned if the command fails to execute or stdin/stdout/stderr cannot be opened. /// -async fn run_process_async(executable: &str, args: Option>, input: Option<&str>, cwd: Option<&str>, env: Option>, exit_codes: &Option>) -> Result<(i32, String, String), DscError> { +async fn run_process_async(executable: &str, args: Option>, input: Option<&str>, cwd: Option<&str>, env: Option>, exit_codes: Option<&HashMap>) -> Result<(i32, String, String), DscError> { // use somewhat large initial buffer to avoid early string reallocations; // the value is based on list result of largest of built-in adapters - WMI adapter ~500KB @@ -659,7 +659,7 @@ async fn run_process_async(executable: &str, args: Option>, input: O /// Will panic if tokio runtime can't be created. /// #[allow(clippy::implicit_hasher)] -pub fn invoke_command(executable: &str, args: Option>, input: Option<&str>, cwd: Option<&str>, env: Option>, exit_codes: &Option>) -> Result<(i32, String, String), DscError> { +pub fn invoke_command(executable: &str, args: Option>, input: Option<&str>, cwd: Option<&str>, env: Option>, exit_codes: Option<&HashMap>) -> Result<(i32, String, String), DscError> { debug!("Invoking command '{}' with args {:?}", executable, args); tokio::runtime::Builder::new_multi_thread() @@ -669,7 +669,7 @@ pub fn invoke_command(executable: &str, args: Option>, input: Option .block_on(run_process_async(executable, args, input, cwd, env, exit_codes)) } -fn process_args(args: &Option>, value: &str) -> Option> { +fn process_args(args: Option<&Vec>, value: &str) -> Option> { let Some(arg_values) = args else { debug!("No args to process"); return None; @@ -700,7 +700,7 @@ struct CommandInput { stdin: Option, } -fn get_command_input(input_kind: &Option, input: &str) -> Result { +fn get_command_input(input_kind: Option<&InputKind>, input: &str) -> Result { let mut env: Option> = None; let mut stdin: Option = None; match input_kind { diff --git a/dsc_lib/src/parser/functions.rs b/dsc_lib/src/parser/functions.rs index 176de113..f03b49ef 100644 --- a/dsc_lib/src/parser/functions.rs +++ b/dsc_lib/src/parser/functions.rs @@ -51,7 +51,7 @@ impl Function { let Some(name) = function_name else { return Err(DscError::Parser("Function name node not found".to_string())); }; - let args = convert_args_node(statement_bytes, &function_args)?; + let args = convert_args_node(statement_bytes, function_args.as_ref())?; let name = name.utf8_text(statement_bytes)?; debug!("Function name: {0}", name); Ok(Function{ @@ -87,7 +87,7 @@ impl Function { } } -fn convert_args_node(statement_bytes: &[u8], args: &Option) -> Result>, DscError> { +fn convert_args_node(statement_bytes: &[u8], args: Option<&Node>) -> Result>, DscError> { let Some(args) = args else { return Ok(None); }; diff --git a/dscecho/Cargo.lock b/dscecho/Cargo.lock index 81f57d82..c7a9c6f1 100644 --- a/dscecho/Cargo.lock +++ b/dscecho/Cargo.lock @@ -1,6 +1,15 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] [[package]] name = "anstream" @@ -38,7 +47,7 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -48,9 +57,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "base62" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fa474cf7492f9a299ba6019fb99ec673e1739556d48e8a90eabaea282ef0e4" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bstr" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "clap" version = "4.5.20" @@ -97,11 +146,37 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + [[package]] name = "dscecho" version = "1.0.0" dependencies = [ "clap", + "rust-i18n", "schemars", "serde", "serde_json", @@ -113,12 +188,64 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "globset" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags 1.3.2", + "ignore", + "walkdir", +] + [[package]] name = "hashbrown" version = "0.15.0" @@ -131,6 +258,22 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "ignore" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "indexmap" version = "2.6.0" @@ -147,18 +290,72 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.167" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" + +[[package]] +name = "libyml" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64804cc6a5042d4f05379909ba25b503ec04e2c082151d62122d5dcaa274b961" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "normpath" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + [[package]] name = "proc-macro2" version = "1.0.87" @@ -177,12 +374,117 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rust-i18n" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "039f57d22229db401af3458ca939300178e99e88b938573cea12b7c2b0f09724" +dependencies = [ + "globwalk", + "once_cell", + "regex", + "rust-i18n-macro", + "rust-i18n-support", + "smallvec", +] + +[[package]] +name = "rust-i18n-macro" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde5c022360a2e54477882843d56b6f9bcb4bc62f504b651a2f497f0028d174f" +dependencies = [ + "glob", + "once_cell", + "proc-macro2", + "quote", + "rust-i18n-support", + "serde", + "serde_json", + "serde_yml", + "syn", +] + +[[package]] +name = "rust-i18n-support" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75d2844d36f62b5d6b66f9cf8f8cbdbbbdcdb5fd37a473a9cc2fb45fdcf485d2" +dependencies = [ + "arc-swap", + "base62", + "globwalk", + "itertools", + "lazy_static", + "normpath", + "once_cell", + "proc-macro2", + "regex", + "serde", + "serde_json", + "serde_yml", + "siphasher", + "toml", + "triomphe", +] + +[[package]] +name = "rustix" +version = "0.38.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schemars" version = "0.8.21" @@ -251,6 +553,50 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_yml" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e76bab63c3fd98d27c17f9cbce177f64a91f5e69ac04cafe04e1bb25d1dc3c" +dependencies = [ + "indexmap", + "itoa", + "libyml", + "log", + "memchr", + "ryu", + "serde", + "serde_json", + "tempfile", +] + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "strsim" version = "0.11.1" @@ -268,6 +614,64 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "triomphe" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" +dependencies = [ + "arc-swap", + "serde", + "stable_deref_trait", +] + [[package]] name = "unicode-ident" version = "1.0.13" @@ -280,6 +684,25 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -289,6 +712,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -352,3 +784,12 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] diff --git a/dscecho/Cargo.toml b/dscecho/Cargo.toml index 04a41cec..bf6755a4 100644 --- a/dscecho/Cargo.toml +++ b/dscecho/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] clap = { version = "4.1", features = ["derive"] } +rust-i18n = { version = "3.1" } schemars = { version = "0.8" } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["preserve_order"] } diff --git a/dscecho/locales/en-us.toml b/dscecho/locales/en-us.toml new file mode 100644 index 00000000..2dfeaedb --- /dev/null +++ b/dscecho/locales/en-us.toml @@ -0,0 +1,5 @@ +_version = 1 + +[main] +invalidJson = "Error JSON does not match schema" +noInput = "No input provided." diff --git a/dscecho/src/main.rs b/dscecho/src/main.rs index ac823683..d7dd182f 100644 --- a/dscecho/src/main.rs +++ b/dscecho/src/main.rs @@ -6,9 +6,12 @@ mod echo; use args::Args; use clap::Parser; +use rust_i18n::{i18n, t}; use schemars::schema_for; use crate::echo::Echo; +i18n!("locales", fallback = "en-us"); + fn main() { let args = Args::parse(); match args.input { @@ -16,7 +19,7 @@ fn main() { let echo = match serde_json::from_str::(&input) { Ok(echo) => echo, Err(err) => { - eprintln!("Error JSON does not match schema: {err}"); + eprintln!("{}: {err}", t!("main.invalidJson")); std::process::exit(1); } }; @@ -25,7 +28,7 @@ fn main() { return; }, None => { - eprintln!("No input provided."); + eprintln!("{}", t!("main.noInput")); } } diff --git a/osinfo/tests/osinfo.tests.ps1 b/osinfo/tests/osinfo.tests.ps1 index 5120c3be..389df8ed 100644 --- a/osinfo/tests/osinfo.tests.ps1 +++ b/osinfo/tests/osinfo.tests.ps1 @@ -31,7 +31,7 @@ Describe 'osinfo resource tests' { else { $invalid = 'Windows' } - $out = "{`"family`": `"$invalid`"}" | dsc resource test -r 'Microsoft/OSInfo' | ConvertFrom-Json + $out = "{`"family`": `"$invalid`"}" | dsc resource test -r 'Microsoft/OSInfo' -f - | ConvertFrom-Json $actual = dsc resource get -r Microsoft/OSInfo | ConvertFrom-Json $out.actualState.family | Should -BeExactly $actual.actualState.family $out.actualState.version | Should -BeExactly $actual.actualState.version diff --git a/powershell-adapter/Tests/powershellgroup.config.tests.ps1 b/powershell-adapter/Tests/powershellgroup.config.tests.ps1 index 70f31bd7..f51f32c1 100644 --- a/powershell-adapter/Tests/powershellgroup.config.tests.ps1 +++ b/powershell-adapter/Tests/powershellgroup.config.tests.ps1 @@ -26,16 +26,16 @@ Describe 'PowerShell adapter resource tests' { It 'Get works on config with class-based resources' { - $r = Get-Content -Raw $pwshConfigPath | dsc config get + $r = Get-Content -Raw $pwshConfigPath | dsc config get -f - $LASTEXITCODE | Should -Be 0 $res = $r | ConvertFrom-Json $res.results[0].result.actualState.result[0].properties.Prop1 | Should -BeExactly 'ValueForProp1' $res.results[0].result.actualState.result[0].properties.EnumProp | Should -BeExactly 'Expected' } - + It 'Test works on config with class-based resources' { - $r = Get-Content -Raw $pwshConfigPath | dsc config test + $r = Get-Content -Raw $pwshConfigPath | dsc config test -f - $LASTEXITCODE | Should -Be 0 $res = $r | ConvertFrom-Json $res.results[0].result.actualState.result[0] | Should -Not -BeNull @@ -43,7 +43,7 @@ Describe 'PowerShell adapter resource tests' { It 'Set works on config with class-based resources' { - $r = Get-Content -Raw $pwshConfigPath | dsc config set + $r = Get-Content -Raw $pwshConfigPath | dsc config set -f - $LASTEXITCODE | Should -Be 0 $res = $r | ConvertFrom-Json $res.results.result.afterState.result[0].type | Should -Be "TestClassResource/TestClassResource" @@ -61,7 +61,7 @@ Describe 'PowerShell adapter resource tests' { - name: Class-resource Info type: TestClassResource/TestClassResource '@ - $out = $yaml | dsc config export + $out = $yaml | dsc config export -f - $LASTEXITCODE | Should -Be 0 $res = $out | ConvertFrom-Json $res.'$schema' | Should -BeExactly 'https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json' @@ -82,7 +82,7 @@ Describe 'PowerShell adapter resource tests' { - name: Class-resource Info type: TestClassResource/NoExport '@ - $out = $yaml | dsc config export 2>&1 | Out-String + $out = $yaml | dsc config export -f - 2>&1 | Out-String $LASTEXITCODE | Should -Be 2 $out | Should -Not -BeNullOrEmpty $out | Should -BeLike "*ERROR*Export method not implemented by resource 'TestClassResource/NoExport'*" @@ -107,7 +107,7 @@ Describe 'PowerShell adapter resource tests' { - name: Class-resource Info type: TestClassResource/TestClassResource "@ - $out = $yaml | dsc config export + $out = $yaml | dsc config export -f - $LASTEXITCODE | Should -Be 0 $res = $out | ConvertFrom-Json $res.'$schema' | Should -BeExactly 'https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json' @@ -140,7 +140,7 @@ Describe 'PowerShell adapter resource tests' { $config_path = "$TestDrive/test_config.dsc.yaml" $yaml | Set-Content -Path $config_path - $out = dsc config get --path $config_path + $out = dsc config get --file $config_path $LASTEXITCODE | Should -Be 0 $res = $out | ConvertFrom-Json $res.results.result.actualState.result.properties.Name | Should -Be $TestDrive @@ -161,7 +161,7 @@ Describe 'PowerShell adapter resource tests' { properties: Name: "[envvar('DSC_CONFIG_ROOT')]" "@ - $out = $yaml | dsc config get | ConvertFrom-Json + $out = $yaml | dsc config get -f - | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 $out.results[0].result.actualState.result[0].properties.Name | Should -BeExactly (Get-Location).Path } diff --git a/powershell-adapter/Tests/powershellgroup.resource.tests.ps1 b/powershell-adapter/Tests/powershellgroup.resource.tests.ps1 index 3ad89bfe..fed67da1 100644 --- a/powershell-adapter/Tests/powershellgroup.resource.tests.ps1 +++ b/powershell-adapter/Tests/powershellgroup.resource.tests.ps1 @@ -33,7 +33,7 @@ Describe 'PowerShell adapter resource tests' { It 'Get works on class-based resource' { - $r = "{'Name':'TestClassResource1'}" | dsc resource get -r 'TestClassResource/TestClassResource' + $r = "{'Name':'TestClassResource1'}" | dsc resource get -r 'TestClassResource/TestClassResource' -f - $LASTEXITCODE | Should -Be 0 $res = $r | ConvertFrom-Json $res.actualState.result.properties.Prop1 | Should -BeExactly 'ValueForProp1' @@ -46,7 +46,7 @@ Describe 'PowerShell adapter resource tests' { It 'Get uses enum names on class-based resource' { - $r = "{'Name':'TestClassResource1'}" | dsc resource get -r 'TestClassResource/TestClassResource' + $r = "{'Name':'TestClassResource1'}" | dsc resource get -r 'TestClassResource/TestClassResource' -f - $LASTEXITCODE | Should -Be 0 $res = $r | ConvertFrom-Json $res.actualState.result.properties.EnumProp | Should -BeExactly 'Expected' @@ -54,7 +54,7 @@ Describe 'PowerShell adapter resource tests' { It 'Test works on class-based resource' { - $r = "{'Name':'TestClassResource1','Prop1':'ValueForProp1'}" | dsc resource test -r 'TestClassResource/TestClassResource' + $r = "{'Name':'TestClassResource1','Prop1':'ValueForProp1'}" | dsc resource test -r 'TestClassResource/TestClassResource' -f - $LASTEXITCODE | Should -Be 0 $res = $r | ConvertFrom-Json $res.actualState.result.properties.InDesiredState | Should -Be $True @@ -68,7 +68,7 @@ Describe 'PowerShell adapter resource tests' { It 'Set works on class-based resource' { - $r = "{'Name':'TestClassResource1','Prop1':'ValueForProp1'}" | dsc resource set -r 'TestClassResource/TestClassResource' + $r = "{'Name':'TestClassResource1','Prop1':'ValueForProp1'}" | dsc resource set -r 'TestClassResource/TestClassResource' -f - $LASTEXITCODE | Should -Be 0 $res = $r | ConvertFrom-Json $res.afterState.result | Should -Not -BeNull @@ -229,7 +229,7 @@ Describe 'PowerShell adapter resource tests' { $adapterPath = Join-Path $PSScriptRoot 'TestAdapter' $env:PATH += [System.IO.Path]::PathSeparator + $adapterPath - $r = '{TestCaseId: 1}'| dsc resource get -r 'Test/TestCase' + $r = '{TestCaseId: 1}'| dsc resource get -r 'Test/TestCase' -f - $LASTEXITCODE | Should -Be 0 $resources = $r | ConvertFrom-Json $resources.actualState.result | Should -Be $True @@ -245,7 +245,7 @@ Describe 'PowerShell adapter resource tests' { $adapterPath = Join-Path $PSScriptRoot 'TestAdapter' $env:PATH += [System.IO.Path]::PathSeparator + $adapterPath - $r = '{TestCaseId: 1}'| dsc resource set -r 'Test/TestCase' + $r = '{TestCaseId: 1}'| dsc resource set -r 'Test/TestCase' -f - $LASTEXITCODE | Should -Be 0 $resources = $r | ConvertFrom-Json $resources.beforeState.result | Should -Be $True @@ -262,7 +262,7 @@ Describe 'PowerShell adapter resource tests' { $adapterPath = Join-Path $PSScriptRoot 'TestAdapter' $env:PATH += [System.IO.Path]::PathSeparator + $adapterPath - $r = '{TestCaseId: 1}'| dsc resource test -r 'Test/TestCase' + $r = '{TestCaseId: 1}'| dsc resource test -r 'Test/TestCase' -f - $LASTEXITCODE | Should -Be 0 $resources = $r | ConvertFrom-Json $resources.actualState.result | Should -Be $True diff --git a/powershell-adapter/Tests/win_powershellgroup.tests.ps1 b/powershell-adapter/Tests/win_powershellgroup.tests.ps1 index d5e72204..0ae7af1f 100644 --- a/powershell-adapter/Tests/win_powershellgroup.tests.ps1 +++ b/powershell-adapter/Tests/win_powershellgroup.tests.ps1 @@ -1,12 +1,12 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -Describe 'WindowsPowerShell adapter resource tests' { +Describe 'WindowsPowerShell adapter resource tests - requires elevated permissions' { BeforeAll { if ($isWindows) { winrm quickconfig -quiet -force - } + } $OldPSModulePath = $env:PSModulePath $env:PSModulePath += [System.IO.Path]::PathSeparator + $PSScriptRoot @@ -37,7 +37,7 @@ Describe 'WindowsPowerShell adapter resource tests' { $testFile = "$testdrive\test.txt" 'test' | Set-Content -Path $testFile -Force - $r = '{"DestinationPath":"' + $testFile.replace('\','\\') + '"}' | dsc resource get -r 'PSDesiredStateConfiguration/File' + $r = '{"DestinationPath":"' + $testFile.replace('\','\\') + '"}' | dsc resource get -r 'PSDesiredStateConfiguration/File' -f - $LASTEXITCODE | Should -Be 0 $res = $r | ConvertFrom-Json $res.actualState.result.properties.DestinationPath | Should -Be "$testFile" @@ -46,7 +46,7 @@ Describe 'WindowsPowerShell adapter resource tests' { It 'Set works on Binary "File" resource' -Skip:(!$IsWindows){ $testFile = "$testdrive\test.txt" - $r = '{"DestinationPath":"' + $testFile.replace('\','\\') + '", type: File, contents: HelloWorld, Ensure: present}' | dsc resource set -r 'PSDesiredStateConfiguration/File' + $null = '{"DestinationPath":"' + $testFile.replace('\','\\') + '", type: File, contents: HelloWorld, Ensure: present}' | dsc resource set -r 'PSDesiredStateConfiguration/File' -f - $LASTEXITCODE | Should -Be 0 Get-Content -Raw -Path $testFile | Should -Be "HelloWorld" } @@ -55,7 +55,7 @@ Describe 'WindowsPowerShell adapter resource tests' { $testFile = "$testdrive\test.txt" 'test' | Set-Content -Path $testFile -Force - $r = '{"GetScript": "@{result = $(Get-Content ' + $testFile.replace('\','\\') + ')}", "SetScript": "throw", "TestScript": "throw"}' | dsc resource get -r 'PSDesiredStateConfiguration/Script' + $r = '{"GetScript": "@{result = $(Get-Content ' + $testFile.replace('\','\\') + ')}", "SetScript": "throw", "TestScript": "throw"}' | dsc resource get -r 'PSDesiredStateConfiguration/Script' -f - $LASTEXITCODE | Should -Be 0 $res = $r | ConvertFrom-Json $res.actualState.result.properties.result | Should -Be 'test' @@ -63,12 +63,12 @@ Describe 'WindowsPowerShell adapter resource tests' { It 'Get works on config with File resource for WinPS' -Skip:(!$IsWindows){ - $testFile = "$testdrive\test.txt" - 'test' | Set-Content -Path $testFile -Force - $r = (Get-Content -Raw $winpsConfigPath).Replace('c:\test.txt',"$testFile") | dsc config get - $LASTEXITCODE | Should -Be 0 - $res = $r | ConvertFrom-Json - $res.results[0].result.actualState.result[0].properties.DestinationPath | Should -Be "$testFile" + $testFile = "$testdrive\test.txt" + 'test' | Set-Content -Path $testFile -Force + $r = (Get-Content -Raw $winpsConfigPath).Replace('c:\test.txt',"$testFile") | dsc config get -f - + $LASTEXITCODE | Should -Be 0 + $res = $r | ConvertFrom-Json + $res.results[0].result.actualState.result[0].properties.DestinationPath | Should -Be "$testFile" } It 'Verify that there are no cache rebuilds for several sequential executions' -Skip:(!$IsWindows) { diff --git a/reboot_pending/tests/reboot_pending.tests.ps1 b/reboot_pending/tests/reboot_pending.tests.ps1 index 087e2146..29a514f7 100644 --- a/reboot_pending/tests/reboot_pending.tests.ps1 +++ b/reboot_pending/tests/reboot_pending.tests.ps1 @@ -10,7 +10,7 @@ Describe 'reboot_pending resource tests' { It 'reboot_pending works in a config' -Skip:(!$IsWindows) { $ConfigPath = Resolve-Path "$PSScriptRoot/reboot_pending.dsc.yaml" - $out = dsc config get --path $ConfigPath | ConvertFrom-Json + $out = dsc config get --file $ConfigPath | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 $out.results.result.actualState.rebootPending | Should -Not -BeNullOrEmpty } diff --git a/registry/tests/registry.config.set.tests.ps1 b/registry/tests/registry.config.set.tests.ps1 index 3950c6cf..7153e79f 100644 --- a/registry/tests/registry.config.set.tests.ps1 +++ b/registry/tests/registry.config.set.tests.ps1 @@ -55,18 +55,18 @@ Describe 'registry config set tests' { ) } - $out = dsc config set -d ($config | ConvertTo-Json -Depth 10) + $out = dsc config set -i ($config | ConvertTo-Json -Depth 10) $LASTEXITCODE | Should -Be 0 $config.resources[0].properties._exist = $false - $out = dsc config set -d ($config | ConvertTo-Json -Depth 10) | ConvertFrom-Json + $out = dsc config set -i ($config | ConvertTo-Json -Depth 10) | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 $out.results[0].result.afterState._exist | Should -Be $false Get-ItemProperty -Path 'HKCU:\1\2' -Name 'Test' -ErrorAction Ignore | Should -BeNullOrEmpty $config.resources[0].properties.valueName = $null - $out = dsc config set -d ($config | ConvertTo-Json -Depth 10) | ConvertFrom-Json + $out = dsc config set -i ($config | ConvertTo-Json -Depth 10) | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 $out.results[0].result.afterState._exist | Should -Be $false diff --git a/resources/apt/test/apt.tests.ps1 b/resources/apt/test/apt.tests.ps1 index af00e4ce..9142d211 100644 --- a/resources/apt/test/apt.tests.ps1 +++ b/resources/apt/test/apt.tests.ps1 @@ -27,7 +27,7 @@ Describe 'Apt resource tests' { if (-not $aptExists) { Set-ItResult -Skip -Because "Apt not found" } - $out = dsc -l trace config get -p $yamlPath 2> "$TestDrive/stderr.txt" | ConvertFrom-Json -Depth 10 + $out = dsc -l trace config get -f $yamlPath 2> "$TestDrive/stderr.txt" | ConvertFrom-Json -Depth 10 $LASTEXITCODE | Should -Be 0 -Because (Get-Content "$TestDrive/stderr.txt" | Out-String) $exists = $null -ne (Get-Command $pkgName -CommandType Application -ErrorAction Ignore) $observed = $out.results[1].result.actualState._exist @@ -39,7 +39,7 @@ Describe 'Apt resource tests' { Set-ItResult -Skip -Because "Apt not found" } - $out = dsc config test -p $yamlPath| ConvertFrom-Json -Depth 10 + $out = dsc config test -f $yamlPath| ConvertFrom-Json -Depth 10 $LASTEXITCODE | Should -Be 0 $exists = $null -ne (Get-Command pkgName -CommandType Application -ErrorAction Ignore) $out.results[1].result.inDesiredState | Should -Be $exists @@ -60,7 +60,7 @@ Describe 'Apt resource tests' { apt remove -y $pkgname } - $result = dsc config set -p $yamlInstallPath | ConvertFrom-Json + $result = dsc config set -f $yamlInstallPath | ConvertFrom-Json $result.results[1].result.beforestate._exist | Should -Be false $result.results[1].result.afterstate._exist | Should -Be true } @@ -72,7 +72,7 @@ Describe 'Apt resource tests' { apt install -y $pkgname } - $result = dsc config set -p $yamlUnInstallPath | ConvertFrom-Json + $result = dsc config set -f $yamlUnInstallPath | ConvertFrom-Json $result.results[1].result.beforestate._exist | Should -Be true $result.results[1].result.afterstate._exist | Should -Be false } diff --git a/runcommandonset/Cargo.lock b/runcommandonset/Cargo.lock index 6eeac6f8..90649bba 100644 --- a/runcommandonset/Cargo.lock +++ b/runcommandonset/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aho-corasick" @@ -47,7 +47,7 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -57,7 +57,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "base62" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fa474cf7492f9a299ba6019fb99ec673e1739556d48e8a90eabaea282ef0e4" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bstr" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22" +dependencies = [ + "memchr", + "serde", ] [[package]] @@ -112,12 +146,89 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "globset" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags 1.3.2", + "ignore", + "walkdir", +] + [[package]] name = "hashbrown" version = "0.15.0" @@ -130,6 +241,22 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "ignore" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata 0.4.8", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "indexmap" version = "2.6.0" @@ -146,6 +273,15 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -158,6 +294,24 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "libc" +version = "0.2.168" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" + +[[package]] +name = "libyml" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64804cc6a5042d4f05379909ba25b503ec04e2c082151d62122d5dcaa274b961" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "log" version = "0.4.22" @@ -179,6 +333,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "normpath" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -274,18 +437,95 @@ name = "runcommandonset" version = "0.1.0" dependencies = [ "clap", + "rust-i18n", "serde", "serde_json", "tracing", "tracing-subscriber", ] +[[package]] +name = "rust-i18n" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "039f57d22229db401af3458ca939300178e99e88b938573cea12b7c2b0f09724" +dependencies = [ + "globwalk", + "once_cell", + "regex", + "rust-i18n-macro", + "rust-i18n-support", + "smallvec", +] + +[[package]] +name = "rust-i18n-macro" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde5c022360a2e54477882843d56b6f9bcb4bc62f504b651a2f497f0028d174f" +dependencies = [ + "glob", + "once_cell", + "proc-macro2", + "quote", + "rust-i18n-support", + "serde", + "serde_json", + "serde_yml", + "syn", +] + +[[package]] +name = "rust-i18n-support" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75d2844d36f62b5d6b66f9cf8f8cbdbbbdcdb5fd37a473a9cc2fb45fdcf485d2" +dependencies = [ + "arc-swap", + "base62", + "globwalk", + "itertools", + "lazy_static", + "normpath", + "once_cell", + "proc-macro2", + "regex", + "serde", + "serde_json", + "serde_yml", + "siphasher", + "toml", + "triomphe", +] + +[[package]] +name = "rustix" +version = "0.38.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "serde" version = "1.0.210" @@ -319,6 +559,32 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_yml" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e76bab63c3fd98d27c17f9cbce177f64a91f5e69ac04cafe04e1bb25d1dc3c" +dependencies = [ + "indexmap", + "itoa", + "libyml", + "log", + "memchr", + "ryu", + "serde", + "serde_json", + "tempfile", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -328,12 +594,24 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "strsim" version = "0.11.1" @@ -351,6 +629,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "thread_local" version = "1.1.8" @@ -361,6 +652,40 @@ dependencies = [ "once_cell", ] +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tracing" version = "0.1.40" @@ -435,6 +760,17 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "triomphe" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" +dependencies = [ + "arc-swap", + "serde", + "stable_deref_trait", +] + [[package]] name = "unicode-ident" version = "1.0.13" @@ -453,6 +789,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "winapi" version = "0.3.9" @@ -469,6 +815,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -484,6 +839,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -547,3 +911,12 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] diff --git a/runcommandonset/Cargo.toml b/runcommandonset/Cargo.toml index 00d4a998..475523bc 100644 --- a/runcommandonset/Cargo.toml +++ b/runcommandonset/Cargo.toml @@ -12,6 +12,7 @@ lto = true [dependencies] clap = { version = "4.4", features = ["derive"] } +rust-i18n = { version = "3.1" } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["preserve_order"] } tracing = { version = "0.1" } diff --git a/runcommandonset/locales/en-us.toml b/runcommandonset/locales/en-us.toml new file mode 100644 index 00000000..5c043751 --- /dev/null +++ b/runcommandonset/locales/en-us.toml @@ -0,0 +1,22 @@ +_version = 1 + +[main] +emptyStdin = "Input from STDIN is empty" +invalidTraceLevel = "Defaulting to Info, invalid trace level defined in DSC_TRACE_LEVEL env var" +invalidUtf8 = "Invalid UTF-8 sequence" +notIdempotent = "This resource is not idempotent" +readStdin = "Reading input from STDIN" + +[runcommand] +invalidJson = "Failed to serialize to JSON" + +[utils] +executableRequired = "Executable is required when input is not provided via stdin" +failedToExecute = "Failed to execute" +failedOpenStderr = "Failed to open stderr for" +failedOpenStdout = "Failed to open stdout for" +failedReadStderr = "Failed to read stderr for" +failedReadStdout = "Failed to read stdout for" +failedWait = "Failed to wait for" +invalidInput = "Input is not valid" +unableToTrace = "Unable to set global default tracing subscriber. Tracing is diabled." diff --git a/runcommandonset/src/main.rs b/runcommandonset/src/main.rs index d4557d05..6ac8b437 100644 --- a/runcommandonset/src/main.rs +++ b/runcommandonset/src/main.rs @@ -2,8 +2,9 @@ // Licensed under the MIT License. use clap::Parser; +use rust_i18n::{i18n, t}; use std::{io::{self, Read, IsTerminal}, process::exit}; -use tracing::{error, warn, debug}; +use tracing::{error, warn, debug, trace}; use args::{Arguments, SubCommand, TraceLevel}; use runcommand::RunCommand; @@ -13,6 +14,8 @@ pub mod args; pub mod runcommand; pub mod utils; +i18n!("locales", fallback = "en-us"); + fn main() { let args = Arguments::parse(); let trace_level = match args.trace_level { @@ -27,7 +30,7 @@ fn main() { "debug" => TraceLevel::Debug, "trace" => TraceLevel::Trace, _ => { - warn!("Invalid trace level: {trace_level}"); + warn!("{}: {trace_level}", t!("main.invalidTraceLevel")); TraceLevel::Info } } @@ -38,24 +41,24 @@ fn main() { } }; enable_tracing(&trace_level, &args.trace_format); - warn!("This resource is not idempotent"); + warn!("{}", t!("main.notIdempotent")); let stdin = if std::io::stdin().is_terminal() { None } else { - debug!("Reading input from STDIN"); + debug!("{}", t!("main.readStdin")); let mut buffer: Vec = Vec::new(); io::stdin().read_to_end(&mut buffer).unwrap(); let stdin = match String::from_utf8(buffer) { Ok(stdin) => stdin, Err(e) => { - error!("Invalid UTF-8 sequence: {e}"); + error!("{}: {e}", t!("main.invalidUtf8")); exit(EXIT_INVALID_ARGS); }, }; // parse_input expects at most 1 input, so wrapping Some(empty input) would throw it off if stdin.is_empty() { - debug!("Input from STDIN is empty"); + debug!("{}", t!("main.emptyStdin")); None } else { @@ -72,9 +75,8 @@ fn main() { SubCommand::Set { arguments, executable, exit_code } => { command = parse_input(arguments, executable, exit_code, stdin); let (exit_code, stdout, stderr) = invoke_command(command.executable.as_ref(), command.arguments.clone()); - // TODO: convert this to tracing json once other PR is merged to handle tracing from resources - eprintln!("Stdout: {stdout}"); - eprintln!("Stderr: {stderr}"); + trace!("Stdout: {stdout}"); + trace!("Stderr: {stderr}"); command.exit_code = exit_code; } } diff --git a/runcommandonset/src/runcommand.rs b/runcommandonset/src/runcommand.rs index ae5ce81c..f7a3be6a 100644 --- a/runcommandonset/src/runcommand.rs +++ b/runcommandonset/src/runcommand.rs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +use rust_i18n::t; use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize, Clone, PartialEq, Serialize)] @@ -19,7 +20,7 @@ impl RunCommand { match serde_json::to_string(self) { Ok(json) => json, Err(e) => { - eprintln!("Failed to serialize to JSON: {e}"); + eprintln!("{}: {e}", t!("runcommand.invalidJson")); String::new() } } diff --git a/runcommandonset/src/utils.rs b/runcommandonset/src/utils.rs index 62937499..54330743 100644 --- a/runcommandonset/src/utils.rs +++ b/runcommandonset/src/utils.rs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +use rust_i18n::t; use std::{io::Read, process::{Command, exit, Stdio}}; use tracing::{Level, error, debug, trace}; use tracing_subscriber::{filter::EnvFilter, layer::SubscriberExt, Layer}; @@ -33,7 +34,7 @@ pub fn parse_input(arguments: Option>, executable: Option, e command = match serde_json::from_str(&input) { Ok(json) => json, Err(err) => { - error!("Error: Input is not valid: {err}"); + error!("{}: {err}", t!("utils.invalidInput")); exit(EXIT_INVALID_INPUT); } } @@ -45,7 +46,7 @@ pub fn parse_input(arguments: Option>, executable: Option, e }; } else { - error!("Error: Executable is required when input is not provided via stdin"); + error!("{}", t!("utils.executableRequired")); exit(EXIT_INVALID_INPUT); } command @@ -72,7 +73,7 @@ pub fn enable_tracing(trace_level: &TraceLevel, trace_format: &TraceFormat) { }; let filter = EnvFilter::try_from_default_env() - .or_else(|_| EnvFilter::try_new("warning")) + .or_else(|_| EnvFilter::try_new("warn")) .unwrap_or_default() .add_directive(tracing_level.into()); let layer = tracing_subscriber::fmt::Layer::default().with_writer(std::io::stderr); @@ -104,7 +105,7 @@ pub fn enable_tracing(trace_level: &TraceLevel, trace_format: &TraceFormat) { let subscriber = tracing_subscriber::Registry::default().with(fmt).with(filter); if tracing::subscriber::set_global_default(subscriber).is_err() { - eprintln!("Unable to set global default tracing subscriber. Tracing is diabled."); + eprintln!("{}", t!("utils.unableToTrace")); } } @@ -132,33 +133,33 @@ pub fn invoke_command(executable: &str, args: Option>) -> (i32, Stri let mut child = match command.spawn() { Ok(child) => child, Err(e) => { - error!("Failed to execute {}: {e}", executable); + error!("{} '{executable}': {e}", t!("utils.failedToExecute")); exit(EXIT_DSC_ERROR); } }; let Some(mut child_stdout) = child.stdout.take() else { - error!("Failed to open stdout for {}", executable); + error!("{} {executable}", t!("utils.failedOpenStdout")); exit(EXIT_DSC_ERROR); }; let mut stdout_buf = Vec::new(); match child_stdout.read_to_end(&mut stdout_buf) { Ok(_) => (), Err(e) => { - error!("Failed to read stdout for {}: {e}", executable); + error!("{} '{executable}': {e}", t!("utils.failedReadStdout")); exit(EXIT_DSC_ERROR); } } let Some(mut child_stderr) = child.stderr.take() else { - error!("Failed to open stderr for {}", executable); + error!("{} {executable}", t!("utils.failedOpenStderr")); exit(EXIT_DSC_ERROR); }; let mut stderr_buf = Vec::new(); match child_stderr.read_to_end(&mut stderr_buf) { Ok(_) => (), Err(e) => { - error!("Failed to read stderr for {}: {e}", executable); + error!("{} '{executable}': {e}", t!("utils.failedReadStderr")); exit(EXIT_DSC_ERROR); } } @@ -166,7 +167,7 @@ pub fn invoke_command(executable: &str, args: Option>) -> (i32, Stri let exit_status = match child.wait() { Ok(exit_status) => exit_status, Err(e) => { - error!("Failed to wait for {}: {e}", executable); + error!("{} '{executable}': {e}", t!("utils.failedWait")); exit(EXIT_DSC_ERROR); } }; diff --git a/runcommandonset/tests/runcommandonset.get.tests.ps1 b/runcommandonset/tests/runcommandonset.get.tests.ps1 index 3e166a7b..5955afcc 100644 --- a/runcommandonset/tests/runcommandonset.get.tests.ps1 +++ b/runcommandonset/tests/runcommandonset.get.tests.ps1 @@ -20,7 +20,7 @@ Describe 'tests for runcommandonset get' { } "@ - $result = $json | dsc resource get -r Microsoft.DSC.Transitional/RunCommandOnSet | ConvertFrom-Json + $result = $json | dsc resource get -r Microsoft.DSC.Transitional/RunCommandOnSet -f - | ConvertFrom-Json $result.actualState.arguments | Should -BeExactly @('bar', 'baz') $result.actualState.executable | Should -BeExactly 'foo' $result.actualState.exitCode | Should -BeExactly 5 @@ -32,7 +32,7 @@ Describe 'tests for runcommandonset get' { } It 'Executable is a required input via STDIN' { - '{ "arguments": "foo" }' | dsc resource get -r Microsoft.DSC.Transitional/RunCommandOnSet + '{ "arguments": "foo" }' | dsc resource get -r Microsoft.DSC.Transitional/RunCommandOnSet -f - $LASTEXITCODE | Should -Be 2 } } diff --git a/runcommandonset/tests/runcommandonset.set.tests.ps1 b/runcommandonset/tests/runcommandonset.set.tests.ps1 index e3188d22..c6631204 100644 --- a/runcommandonset/tests/runcommandonset.set.tests.ps1 +++ b/runcommandonset/tests/runcommandonset.set.tests.ps1 @@ -24,7 +24,7 @@ Describe 'tests for runcommandonset set' { "arguments": ["-Command", "echo hello world"] } "@ - $input_json | dsc resource set -r Microsoft.DSC.Transitional/RunCommandOnSet + $input_json | dsc resource set -r Microsoft.DSC.Transitional/RunCommandOnSet -f - # TODO: test output once DSC PR to capture it is merged $LASTEXITCODE | Should -Be 0 } @@ -36,20 +36,20 @@ Describe 'tests for runcommandonset set' { "arguments": ["-Command", "echo hello world"] } "@ - $input_json | runcommandonset set 2> $TestDrive/output.txt - $actual = Get-Content -Path $TestDrive/output.txt - $actual | Should -Contain 'Stdout: hello' - $actual | Should -Contain 'world' + $input_json | runcommandonset --trace-level trace --trace-format plaintext set 2> $TestDrive/output.txt + $actual = Get-Content -Path $TestDrive/output.txt -Raw + $actual | Should -BeLike '*Stdout: hello*' + $actual | Should -BeLike '*world*' $LASTEXITCODE | Should -Be 0 } It 'STDERR captured when calling resource directly with invalid args' { - $json = runcommandonset set -e pwsh -a "echo hello world" 2> $TestDrive/output.txt + $json = runcommandonset --trace-level trace --trace-format plaintext set -e pwsh -a "echo hello world" 2> $TestDrive/output.txt $stdout = $json | ConvertFrom-Json $stdout.exitCode | Should -Be 64 - $expected = "Stderr: The argument 'echo hello world' is not recognized as the name of a script file. Check the spelling of the name, or if a path was included, verify that the path is correct and try again." - $stderr = Get-Content -Path $TestDrive/output.txt - $stderr | Should -Contain $expected + $expected = "*Stderr: The argument 'echo hello world' is not recognized as the name of a script file. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.*" + $stderr = Get-Content -Path $TestDrive/output.txt -Raw + $stderr | Should -BeLike $expected $LASTEXITCODE | Should -Be 0 } @@ -59,28 +59,28 @@ Describe 'tests for runcommandonset set' { } It 'Executable is a required input via STDIN' { - $null = '{ "arguments": "foo" }' | dsc resource set -r Microsoft.DSC.Transitional/RunCommandOnSet + $null = '{ "arguments": "foo" }' | dsc resource set -r Microsoft.DSC.Transitional/RunCommandOnSet -f - $LASTEXITCODE | Should -Be 2 } It 'Executable can be provided without arguments' { - $result = '{ "executable": "pwsh" }' | dsc resource set -r Microsoft.DSC.Transitional/RunCommandOnSet | ConvertFrom-Json + $result = '{ "executable": "pwsh" }' | dsc resource set -r Microsoft.DSC.Transitional/RunCommandOnSet -f - | ConvertFrom-Json $result.changedProperties | Should -Be @() $LASTEXITCODE | Should -Be 0 } It 'Exit code does not need to be provided to detect difference' { - $result = '{ "executable": "pwsh", "arguments": ["invalid input"] }' | dsc resource set -r Microsoft.DSC.Transitional/RunCommandOnSet | ConvertFrom-Json + $result = '{ "executable": "pwsh", "arguments": ["invalid input"] }' | dsc resource set -r Microsoft.DSC.Transitional/RunCommandOnSet -f - | ConvertFrom-Json $result.changedProperties | Should -Be @( 'exitCode' ) $LASTEXITCODE | Should -Be 0 } It 'Executable does not exist' { - '{ "executable": "foo" }' | dsc -l trace resource set -r Microsoft.DSC.Transitional/RunCommandOnSet 2> $TestDrive/output.txt + '{ "executable": "foo" }' | dsc -l trace resource set -r Microsoft.DSC.Transitional/RunCommandOnSet -f - 2> $TestDrive/output.txt $actual = Get-Content -Path $TestDrive/output.txt -Raw - $expected_logging = 'Failed to execute foo: No such file or directory (os error 2)' + $expected_logging = "Failed to execute 'foo': No such file or directory (os error 2)" if ($IsWindows) { - $expected_logging = 'Failed to execute foo: program not found' + $expected_logging = "Failed to execute 'foo': program not found" } $actual | Should -BeLike "*$expected_logging*" $LASTEXITCODE | Should -Be 2 diff --git a/wmi-adapter/Tests/wmi.tests.ps1 b/wmi-adapter/Tests/wmi.tests.ps1 index b0cea8f7..abbef659 100644 --- a/wmi-adapter/Tests/wmi.tests.ps1 +++ b/wmi-adapter/Tests/wmi.tests.ps1 @@ -37,7 +37,7 @@ Describe 'WMI adapter resource tests' { It 'Get works on a config with WMI resources' -Skip:(!$IsWindows){ - $r = Get-Content -Raw $configPath | dsc config get + $r = Get-Content -Raw $configPath | dsc config get -f - $LASTEXITCODE | Should -Be 0 $res = $r | ConvertFrom-Json $res.results[0].result.actualState[0].LastBootUpTime | Should -BeNullOrEmpty @@ -48,7 +48,7 @@ Describe 'WMI adapter resource tests' { It 'Example config works' -Skip:(!$IsWindows) { $configPath = Join-Path $PSScriptRoot '..\..\configurations\windows\windows_inventory.dsc.yaml' - $r = dsc config get -p $configPath + $r = dsc config get -f $configPath $LASTEXITCODE | Should -Be 0 $r | Should -Not -BeNullOrEmpty $res = $r | ConvertFrom-Json