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

change ManifestVersion to $schema and validate #199

Merged
merged 4 commits into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion dsc/assertion.dsc.resource.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"manifestVersion": "1.0",
"$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/bundled/resource/manifest.json",
SteveL-MSFT marked this conversation as resolved.
Show resolved Hide resolved
"type": "DSC/AssertionGroup",
"version": "0.1.0",
"description": "`test` will be invoked for all resources in the supplied configuration.",
Expand Down
2 changes: 1 addition & 1 deletion dsc/group.dsc.resource.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"manifestVersion": "1.0",
"$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/bundled/resource/manifest.json",
"type": "DSC/Group",
"version": "0.1.0",
"description": "All resources in the supplied configuration is treated as a group.",
Expand Down
2 changes: 1 addition & 1 deletion dsc/parallel.dsc.resource.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"manifestVersion": "1.0",
"$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/bundled/resource/manifest.json",
"type": "DSC/ParallelGroup",
"version": "0.1.0",
"description": "All resources in the supplied configuration run concurrently.",
Expand Down
4 changes: 2 additions & 2 deletions dsc/src/subcommand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use dsc_lib::{
configure::{Configurator, ErrorAction},
DscManager,
dscresources::dscresource::{ImplementedAs, Invoke},
dscresources::resource_manifest::ResourceManifest,
dscresources::resource_manifest::{import_manifest, ResourceManifest},
};
use jsonschema::{JSONSchema, ValidationError};
use serde_yaml::Value;
Expand Down Expand Up @@ -321,7 +321,7 @@ pub fn resource(subcommand: &ResourceSubCommand, format: &Option<OutputFormat>,
let Some(ref resource_manifest) = resource.manifest else {
continue;
};
let manifest = match serde_json::from_value::<ResourceManifest>(resource_manifest.clone()) {
let manifest = match import_manifest(resource_manifest.clone()) {
Ok(resource_manifest) => resource_manifest,
Err(err) => {
eprintln!("Error in manifest for {0}: {err}", resource.type_name);
Expand Down
2 changes: 1 addition & 1 deletion dsc/tests/dsc_discovery.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Describe 'tests for resource discovery' {
It 'Use DSC_RESOURCE_PATH instead of PATH when defined' {
$resourceJson = @'
{
"manifestVersion": "1.0",
"$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/bundled/resource/manifest.json",
"type": "DSC/TestPathResource",
"version": "0.1.0",
"get": {
Expand Down
2 changes: 1 addition & 1 deletion dsc/tests/dsc_set.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Describe 'config set tests' {
It 'set can be used on a resource that does not implement test' {
$manifest = @'
{
"manifestVersion": "1.0.0",
"$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/bundled/resource/manifest.json",
"type": "Test/SetNoTest",
"version": "0.1.0",
"get": {
Expand Down
12 changes: 6 additions & 6 deletions dsc_lib/src/discovery/command_discovery.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use crate::discovery::discovery_trait::{ResourceDiscovery};
use crate::discovery::discovery_trait::ResourceDiscovery;
use crate::dscresources::dscresource::{DscResource, ImplementedAs};
use crate::dscresources::resource_manifest::ResourceManifest;
use crate::dscresources::resource_manifest::{ResourceManifest, import_manifest};
use crate::dscresources::command_resource::invoke_command;
use crate::dscerror::{DscError, StreamMessage, StreamMessageType};
use std::collections::BTreeMap;
Expand Down Expand Up @@ -71,9 +71,9 @@ impl ResourceDiscovery for CommandDiscovery {
if path.is_file() {
let file_name = path.file_name().unwrap().to_str().unwrap();
if file_name.to_lowercase().ends_with(".dsc.resource.json") {
let resource = import_manifest(&path)?;
let resource = load_manifest(&path)?;
if resource.manifest.is_some() {
let manifest = serde_json::from_value::<ResourceManifest>(resource.manifest.clone().unwrap())?;
let manifest = import_manifest(resource.manifest.clone().unwrap())?;
if manifest.provider.is_some() {
self.provider_resources.push(resource.type_name.clone());
}
Expand All @@ -90,7 +90,7 @@ impl ResourceDiscovery for CommandDiscovery {
let provider_resource = self.resources.get(provider).unwrap();
let provider_type_name = provider_resource.type_name.clone();
let provider_path = provider_resource.path.clone();
let manifest = serde_json::from_value::<ResourceManifest>(provider_resource.manifest.clone().unwrap())?;
let manifest = import_manifest(provider_resource.manifest.clone().unwrap())?;
// invoke the list command
let list_command = manifest.provider.unwrap().list;
let (exit_code, stdout, stderr) = match invoke_command(&list_command.executable, list_command.args, None, Some(&provider_resource.directory))
Expand Down Expand Up @@ -151,7 +151,7 @@ impl ResourceDiscovery for CommandDiscovery {
}
}

fn import_manifest(path: &Path) -> Result<DscResource, DscError> {
fn load_manifest(path: &Path) -> Result<DscResource, DscError> {
let file = File::open(path)?;
let reader = BufReader::new(file);
let manifest: ResourceManifest = match serde_json::from_reader(reader) {
Expand Down
27 changes: 15 additions & 12 deletions dsc_lib/src/dscerror.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ pub enum DscError {
#[error("Invalid configuration:\n{0}")]
InvalidConfiguration(String),

#[error("Unsupported manifest version: {0}. Must be: {1}")]
InvalidManifestSchemaVersion(String, String),

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

Expand Down Expand Up @@ -107,15 +110,15 @@ impl StreamMessage {
}

/// Create a new error message
///
///
/// # Arguments
///
///
/// * `message` - The message to display
/// * `resource_type_name` - The name of the resource type
/// * `resource_path` - The path to the resource
///
///
/// # Returns
///
///
/// * `StreamMessage` - The new message
#[must_use]
pub fn new_error(message: String, resource_type_name: Option<String>, resource_path: Option<String>) -> StreamMessage {
Expand All @@ -129,15 +132,15 @@ impl StreamMessage {
}

/// Create a new warning message
///
///
/// # Arguments
///
///
/// * `message` - The message to display
/// * `resource_type_name` - The name of the resource type
/// * `resource_path` - The path to the resource
///
///
/// # Returns
///
///
/// * `StreamMessage` - The new message
#[must_use]
pub fn new_warning(message: String, resource_type_name: Option<String>, resource_path: Option<String>) -> StreamMessage {
Expand All @@ -151,14 +154,14 @@ impl StreamMessage {
}

/// Print the message to the console
///
///
/// # Arguments
///
///
/// * `error_format` - The format to use for error messages
/// * `warning_format` - The format to use for warning messages
///
///
/// # Errors
///
///
/// * `DscError` - If there is an error writing to the console
pub fn print(&self, error_format:&StreamMessageType, warning_format:&StreamMessageType) -> Result<(), DscError>{
if self.message_type == StreamMessageType::Error
Expand Down
15 changes: 7 additions & 8 deletions dsc_lib/src/dscresources/dscresource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@
// Licensed under the MIT License.

use dscerror::DscError;
use resource_manifest::ResourceManifest;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use super::{command_resource, dscerror, resource_manifest, invoke_result::{GetResult, SetResult, TestResult, ValidateResult, ExportResult}};
use super::{command_resource, dscerror, resource_manifest::import_manifest, invoke_result::{GetResult, SetResult, TestResult, ValidateResult, ExportResult}};

#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
#[serde(deny_unknown_fields)]
Expand Down Expand Up @@ -140,7 +139,7 @@ impl Invoke for DscResource {
let Some(manifest) = &self.manifest else {
return Err(DscError::MissingManifest(self.type_name.clone()));
};
let resource_manifest = serde_json::from_value::<ResourceManifest>(manifest.clone())?;
let resource_manifest = import_manifest(manifest.clone())?;
command_resource::invoke_get(&resource_manifest, &self.directory, filter)
},
}
Expand All @@ -155,7 +154,7 @@ impl Invoke for DscResource {
let Some(manifest) = &self.manifest else {
return Err(DscError::MissingManifest(self.type_name.clone()));
};
let resource_manifest = serde_json::from_value::<ResourceManifest>(manifest.clone())?;
let resource_manifest = import_manifest(manifest.clone())?;
command_resource::invoke_set(&resource_manifest, &self.directory, desired, skip_test)
},
}
Expand All @@ -172,7 +171,7 @@ impl Invoke for DscResource {
};

// if test is not directly implemented, then we need to handle it here
let resource_manifest = serde_json::from_value::<ResourceManifest>(manifest.clone())?;
let resource_manifest = import_manifest(manifest.clone())?;
if resource_manifest.test.is_none() {
let get_result = self.get(expected)?;
let desired_state = serde_json::from_str(expected)?;
Expand Down Expand Up @@ -201,7 +200,7 @@ impl Invoke for DscResource {
let Some(manifest) = &self.manifest else {
return Err(DscError::MissingManifest(self.type_name.clone()));
};
let resource_manifest = serde_json::from_value::<ResourceManifest>(manifest.clone())?;
let resource_manifest = import_manifest(manifest.clone())?;
command_resource::invoke_validate(&resource_manifest, &self.directory, config)
},
}
Expand All @@ -216,7 +215,7 @@ impl Invoke for DscResource {
let Some(manifest) = &self.manifest else {
return Err(DscError::MissingManifest(self.type_name.clone()));
};
let resource_manifest = serde_json::from_value::<ResourceManifest>(manifest.clone())?;
let resource_manifest = import_manifest(manifest.clone())?;
command_resource::get_schema(&resource_manifest, &self.directory)
},
}
Expand All @@ -231,7 +230,7 @@ impl Invoke for DscResource {
let Some(manifest) = &self.manifest else {
return Err(DscError::MissingManifest(self.type_name.clone()));
};
let resource_manifest = serde_json::from_value::<ResourceManifest>(manifest.clone())?;
let resource_manifest = import_manifest(manifest.clone())?;
command_resource::invoke_export(&resource_manifest, &self.directory)
},
}
Expand Down
29 changes: 27 additions & 2 deletions dsc_lib/src/dscresources/resource_manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;

use crate::dscerror::DscError;

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct ResourceManifest {
/// The version of the resource manifest schema.
#[serde(rename = "manifestVersion")]
pub manifest_version: String,
#[serde(rename = "$schema")]
pub schema_version: String,
/// The namespaced name of the resource.
#[serde(rename = "type")]
pub resource_type: String,
Expand Down Expand Up @@ -168,3 +170,26 @@ pub struct ListMethod {
/// The arguments to pass to the command to perform a List.
pub args: Option<Vec<String>>,
}

/// Import a resource manifest from a JSON value.
///
/// # Arguments
///
/// * `manifest` - The JSON value to import.
///
/// # Returns
///
/// * `Result<ResourceManifest, DscError>` - The imported resource manifest.
///
/// # Errors
///
/// * `DscError` - The JSON value is invalid or the schema version is not supported.
pub fn import_manifest(manifest: Value) -> Result<ResourceManifest, DscError> {
const MANIFEST_SCHEMA_VERSION: &str = "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/bundled/resource/manifest.json";
let manifest = serde_json::from_value::<ResourceManifest>(manifest)?;
if !manifest.schema_version.eq(MANIFEST_SCHEMA_VERSION) {
return Err(DscError::InvalidManifestSchemaVersion(manifest.schema_version, MANIFEST_SCHEMA_VERSION.to_string()));
}

Ok(manifest)
}
2 changes: 1 addition & 1 deletion osinfo/osinfo.dsc.resource.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"manifestVersion": "1.0",
"$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/bundled/resource/manifest.json",
"description": "Returns information about the operating system.",
"tags": [
"os",
Expand Down
2 changes: 1 addition & 1 deletion powershellgroup/powershellgroup.dsc.resource.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"manifestVersion": "1.0",
"$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/bundled/resource/manifest.json",
"type": "DSC/PowerShellGroup",
"version": "0.1.0",
"description": "Resource provider to classic DSC Powershell resources.",
Expand Down
2 changes: 1 addition & 1 deletion process/process.dsc.resource.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"manifestVersion": "1.0",
"$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/bundled/resource/manifest.json",
"description": "Returns information about running processes.",
"type": "Microsoft/Process",
"version": "0.1.0",
Expand Down
2 changes: 1 addition & 1 deletion registry/registry.dsc.resource.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"manifestVersion": "1.0",
"$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/bundled/resource/manifest.json",
"type": "Microsoft.Windows/Registry",
"description": "Registry configuration provider for the Windows Registry",
"tags": [
Expand Down
2 changes: 1 addition & 1 deletion test_group_resource/testGroup.dsc.resource.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"manifestVersion": "1.0",
"$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/bundled/resource/manifest.json",
"type": "Test/TestGroup",
"version": "0.1.0",
"get": {
Expand Down
2 changes: 1 addition & 1 deletion test_group_resource/tests/provider.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Describe 'Resource provider tests' {
It 'Error if provider resource is missing "requires" member' {
$invalid_manifest = @'
{
"manifestVersion": "1.0",
"$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/bundled/resource/manifest.json",
"type": "Test/InvalidTestGroup",
"version": "0.1.0",
"get": {
Expand Down