diff --git a/dsc/src/args.rs b/dsc/src/args.rs index b3d2b1d90..d929f1d06 100644 --- a/dsc/src/args.rs +++ b/dsc/src/args.rs @@ -280,6 +280,7 @@ pub enum SchemaType { TestResult, ResolveResult, DscResource, + Resource, ResourceManifest, Include, Configuration, diff --git a/dsc/src/util.rs b/dsc/src/util.rs index 909117d0a..4769194c8 100644 --- a/dsc/src/util.rs +++ b/dsc/src/util.rs @@ -10,6 +10,7 @@ use dsc_lib::{ configure::{ config_doc::{ Configuration, + Resource, RestartRequired, }, config_result::{ @@ -164,6 +165,9 @@ pub fn get_schema(schema: SchemaType) -> Schema { SchemaType::DscResource => { schema_for!(DscResource) }, + SchemaType::Resource => { + schema_for!(Resource) + }, SchemaType::ResourceManifest => { schema_for!(ResourceManifest) }, diff --git a/dsc/tests/dsc_resource_schema.tests.ps1 b/dsc/tests/dsc_resource_schema.tests.ps1 new file mode 100644 index 000000000..e40b78d36 --- /dev/null +++ b/dsc/tests/dsc_resource_schema.tests.ps1 @@ -0,0 +1,30 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +Describe 'tests for the Resource schema within a configuration' { + It 'Unknown properties are an error' { + $yaml = @' + $schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json + resources: + - name: test + type: Microsft.Dsc.Debug/Echo + unknownProperty: true + properties: + output: "Hello World" +'@ + dsc config get -i $yaml 2>$TestDrive/error.log + $LASTEXITCODE | Should -Be 2 + (Get-Content $TestDrive/error.log -Raw) | Should -Match 'Error: JSON: unknown field `unknownProperty`' + + } + + It 'dsc schema returns a valid schema' { + $schema = dsc schema -t resource + $LASTEXITCODE | Should -Be 0 + $schema | Should -Not -BeNullOrEmpty + $schema = $schema | ConvertFrom-Json + $schema.'$schema' | Should -BeExactly 'https://json-schema.org/draft/2020-12/schema' + $schema.title | Should -BeExactly 'Resource' + $schema.additionalProperties | Should -Be $false + } +} diff --git a/dsc_lib/src/configure/config_doc.rs b/dsc_lib/src/configure/config_doc.rs index fba5044df..88b2a4df8 100644 --- a/dsc_lib/src/configure/config_doc.rs +++ b/dsc_lib/src/configure/config_doc.rs @@ -163,23 +163,101 @@ pub enum DataType { } #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +pub enum CopyMode { + #[serde(rename = "serial")] + Serial, + #[serde(rename = "parallel")] + Parallel, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct Copy { + pub name: String, + pub count: i32, + #[serde(skip_serializing_if = "Option::is_none")] + pub mode: Option, + #[serde(skip_serializing_if = "Option::is_none", rename = "batchSize")] + pub batch_size: Option, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct Plan { + pub name: String, + #[serde(skip_serializing_if = "Option::is_none", rename = "promotionCode")] + pub promotion_code: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub publisher: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub product: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub version: Option, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct Identity { + #[serde(skip_serializing_if = "Option::is_none")] + pub r#type: Option, + #[serde(skip_serializing_if = "Option::is_none", rename = "userAssignedIdentities")] + pub user_assigned_identities: Option>, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct Sku { + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub tier: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub size: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub family: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub capacity: Option, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[serde(deny_unknown_fields)] pub struct Resource { + #[serde(skip_serializing_if = "Option::is_none")] + pub condition: Option, /// The fully qualified name of the resource type #[serde(rename = "type")] pub resource_type: String, + #[serde(skip_serializing_if = "Option::is_none", rename = "apiVersion")] + pub api_version: Option, /// A friendly name for the resource instance pub name: String, // friendly unique instance name + #[serde(skip_serializing_if = "Option::is_none")] + pub comments: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub location: Option, #[serde(rename = "dependsOn", skip_serializing_if = "Option::is_none")] #[schemars(regex(pattern = r"^\[resourceId\(\s*'[a-zA-Z0-9\.]+/[a-zA-Z0-9]+'\s*,\s*'[a-zA-Z0-9 ]+'\s*\)]$"))] pub depends_on: Option>, #[serde(skip_serializing_if = "Option::is_none")] + pub tags: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub identity: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub sku: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub kind: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub scope: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub copy: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub plan: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub properties: Option>, #[serde(skip_serializing_if = "Option::is_none")] - pub metadata: Option, + pub resources: Option>, #[serde(skip_serializing_if = "Option::is_none")] - pub condition: Option, + pub metadata: Option, } impl Default for Configuration { @@ -237,6 +315,16 @@ impl Resource { properties: None, metadata: None, condition: None, + identity: None, + sku: None, + scope: None, + copy: None, + plan: None, + resources: None, + comments: None, + location: None, + tags: None, + api_version: None, } } }