Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config schema support #25

Merged
merged 8 commits into from
Mar 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build.ps1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
param(
[switch]$Release,
[ValidateSet('none','aarch64-pc-windows-msvc',' x86_64-pc-windows-msvc')]
[ValidateSet('none','aarch64-pc-windows-msvc','x86_64-pc-windows-msvc')]
$architecture = 'none',
[switch]$Clippy,
[switch]$Test
Expand Down Expand Up @@ -37,7 +37,7 @@ foreach ($project in $projects) {
try {
Push-Location "$PSScriptRoot/$project"
if ($Clippy) {
cargo clippy @flags
cargo clippy @flags -- -Dwarnings
}
else {
cargo build @flags
Expand Down
12 changes: 10 additions & 2 deletions config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,19 @@ name = "config"
version = "0.1.0"
edition = "2021"

[profile.release]
strip = true
# optimize for size
opt-level = 2
# enable link time optimization to remove dead code
lto = true

[dependencies]
atty = { version = "0.2" }
clap = { version = "3.2", features = ["derive"] }
crossterm = { version = "0.24.0" }
clap = { version = "4.1", features = ["derive"] }
crossterm = { version = "0.26.1" }
dsc_lib = { path = "../dsc_lib" }
schemars = { version = "0.8.12" }
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
serde_yaml = { version = "0.9" }
Expand Down
42 changes: 23 additions & 19 deletions config/src/args.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,12 @@
use clap::{Parser, Subcommand};
use std::str::FromStr;
use clap::{Parser, Subcommand, ValueEnum};

#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, ValueEnum)]
pub enum OutputFormat {
Json,
PrettyJson,
Yaml,
}

impl FromStr for OutputFormat {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_lowercase().as_str() {
"json" => Ok(OutputFormat::Json),
"prettyjson" => Ok(OutputFormat::PrettyJson),
"yaml" => Ok(OutputFormat::Yaml),
_ => Err(format!("Invalid output format: {}", s)),
}
}
}

#[derive(Debug, Parser)]
#[clap(name = "config", version = "0.1.0", about = "Discover and invoke DSC resources", long_about = None)]
pub struct Args {
Expand All @@ -41,25 +27,43 @@ pub enum SubCommand {
/// Optional filter to apply to the list of resources
resource_name: Option<String>,
},
#[clap(name = "get", about = "Get the resource", arg_required_else_help = false)]
#[clap(name = "get", about = "Get the resource", arg_required_else_help = true)]
Get {
#[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")]
input: Option<String>,
},
#[clap(name = "set", about = "Set the resource", arg_required_else_help = false)]
#[clap(name = "set", about = "Set the 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")]
input: Option<String>,
},
#[clap(name = "test", about = "Test the resource", arg_required_else_help = false)]
#[clap(name = "test", about = "Test the 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")]
input: Option<String>,
},
#[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(name = "dscschema", about = "Get the JSON schema for a DSC type", arg_required_else_help = true)]
DscSchema {
#[clap(name = "type", short, long, help = "The name of the DSC type to get the JSON schema")]
dsc_type: DscType,
},
}

#[derive(Debug, Clone, PartialEq, Eq, ValueEnum)]
pub enum DscType {
GetResult,
SetResult,
TestResult,
ResourceManifest,
}
85 changes: 79 additions & 6 deletions config/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use args::*;
use atty::Stream;
use clap::Parser;
use dsc_lib::{DscManager, dscresources::dscresource::{DscResource, Invoke}};
use dsc_lib::{DscManager, dscresources::dscresource::{DscResource, Invoke}, dscresources::invoke_result::{GetResult, SetResult, TestResult}, dscresources::resource_manifest::ResourceManifest};
use schemars::schema_for;
use std::io::{self, Read};
use std::process::exit;
use syntect::easy::HighlightLines;
Expand Down Expand Up @@ -71,10 +72,10 @@ fn main() {
write_output(&json, &args.format);
// insert newline separating instances if writing to console
if atty::is(Stream::Stdout) {
println!("");
println!();
}
}
}
},
SubCommand::Get { resource, input } => {
// TODO: support streaming stdin which includes resource and input

Expand All @@ -97,7 +98,7 @@ fn main() {
exit(EXIT_DSC_ERROR);
}
}
}
},
SubCommand::Set { resource, input: _ } => {
let input = get_input(&None, &stdin);
let resource = get_resource(&mut dsc, resource.as_str());
Expand All @@ -118,7 +119,7 @@ fn main() {
exit(EXIT_DSC_ERROR);
}
}
}
},
SubCommand::Test { resource, input: _ } => {
let input = get_input(&None, &stdin);
let resource = get_resource(&mut dsc, resource.as_str());
Expand All @@ -139,7 +140,79 @@ fn main() {
exit(EXIT_DSC_ERROR);
}
}
}
},
SubCommand::Schema { resource } => {
let resource = get_resource(&mut dsc, resource.as_str());
match resource.schema() {
Ok(json) => {
// verify is json
match serde_json::from_str::<serde_json::Value>(json.as_str()) {
Ok(_) => (),
Err(err) => {
eprintln!("Error: {}", err);
exit(EXIT_JSON_ERROR);
}
};
write_output(&json, &args.format);
}
Err(err) => {
eprintln!("Error: {}", err);
exit(EXIT_DSC_ERROR);
}
}
},
SubCommand::DscSchema { dsc_type } => {
match dsc_type {
DscType::GetResult => {
let schema = schema_for!(GetResult);
// convert to json
let json = match serde_json::to_string(&schema) {
Ok(json) => json,
Err(err) => {
eprintln!("JSON Error: {}", err);
exit(EXIT_JSON_ERROR);
}
};
write_output(&json, &args.format);
},
DscType::SetResult => {
let schema = schema_for!(SetResult);
// convert to json
let json = match serde_json::to_string(&schema) {
Ok(json) => json,
Err(err) => {
eprintln!("JSON Error: {}", err);
exit(EXIT_JSON_ERROR);
}
};
write_output(&json, &args.format);
},
DscType::TestResult => {
let schema = schema_for!(TestResult);
// convert to json
let json = match serde_json::to_string(&schema) {
Ok(json) => json,
Err(err) => {
eprintln!("JSON Error: {}", err);
exit(EXIT_JSON_ERROR);
}
};
write_output(&json, &args.format);
},
DscType::ResourceManifest => {
let schema = schema_for!(ResourceManifest);
// convert to json
let json = match serde_json::to_string(&schema) {
Ok(json) => json,
Err(err) => {
eprintln!("JSON Error: {}", err);
exit(EXIT_JSON_ERROR);
}
};
write_output(&json, &args.format);
},
}
},
}

exit(EXIT_SUCCESS);
Expand Down
24 changes: 24 additions & 0 deletions config/tests/config_schema.tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Describe 'config schema tests' {
It 'return resource schema' -Skip:(!$IsWindows) {
$schema = config schema -r registry
$LASTEXITCODE | Should -Be 0
$schema | Should -Not -BeNullOrEmpty
$schema = $schema | ConvertFrom-Json
$schema.'$schema' | Should -BeExactly 'http://json-schema.org/draft-07/schema#'
}

It 'return dsc schema: <type>' -Skip:(!$IsWindows) -TestCases @(
@{ type = 'get-result' }
@{ type = 'set-result' }
@{ type = 'test-result' }
@{ type = 'resource-manifest' }
) {
param($type)

$schema = config dscschema -t $type
$LASTEXITCODE | Should -Be 0
$schema | Should -Not -BeNullOrEmpty
$schema = $schema | ConvertFrom-Json
$schema.'$schema' | Should -BeExactly 'http://json-schema.org/draft-07/schema#'
}
}
4 changes: 3 additions & 1 deletion dsc_lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ version = "0.1.0"
edition = "2021"

[dependencies]
derive_builder ="0.12"
regex = "1.7"
reqwest = { version = "0.11", features = ["blocking"] }
schemars = { version = "0.8.12" }
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
thiserror = "1.0"
derive_builder ="0.12"
10 changes: 10 additions & 0 deletions dsc_lib/src/dscerror.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
use reqwest::StatusCode;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum DscError {
#[error("HTTP status: {0}")]
HttpStatus(StatusCode),

#[error("HTTP: {0}")]
Http(#[from] reqwest::Error),

#[error("IO: {0}")]
Io(#[from] std::io::Error),

Expand All @@ -17,6 +24,9 @@ pub enum DscError {
#[error("Missing manifest: {0}")]
MissingManifest(String),

#[error("Schema missing from manifest: {0}")]
MissingSchema(String),

#[error("Not implemented")]
NotImplemented,

Expand Down
Loading