Skip to content

Commit 9d9863c

Browse files
authored
Merge pull request #1449 from michaeltlombardi/gh-538/main/version-plumbing-redux
(GH-538) Plumb defined types for versions and names through CLI
2 parents df636d6 + 82b65e2 commit 9d9863c

42 files changed

Lines changed: 3327 additions & 519 deletions

Some content is hidden

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

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
msrustup
12
build/
23
target/
34
bin/

adapters/powershell/Tests/TestAdapter/testadapter.resource.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ switch ($Operation) {
3737
@{
3838
type = "Test/TestCase"
3939
kind = 'resource'
40-
version = '1'
40+
version = '1.0.0'
4141
capabilities = @('get', 'set', 'test', 'export')
4242
path = $PSScriptRoot
4343
directory = Split-Path $PSScriptRoot

adapters/powershell/Tests/powershellgroup.resource.tests.ps1

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,9 @@ Describe 'PowerShell adapter resource tests' {
195195
$srcPath = Join-Path $PSScriptRoot 'TestClassResource'
196196
$pathRoot1 = Join-Path $TestDrive 'A'
197197
$pathRoot2 = Join-Path $TestDrive 'B'
198-
$path1 = Join-Path $pathRoot1 'TestClassResource' '1.0'
199-
$path2 = Join-Path $pathRoot1 'TestClassResource' '1.1'
200-
$path3 = Join-Path $pathRoot2 'TestClassResource' '2.0'
198+
$path1 = Join-Path $pathRoot1 'TestClassResource' '1.0.0'
199+
$path2 = Join-Path $pathRoot1 'TestClassResource' '1.1.0'
200+
$path3 = Join-Path $pathRoot2 'TestClassResource' '2.0.0'
201201
$path4 = Join-Path $pathRoot2 'TestClassResource' '2.0.1'
202202

203203
New-Item -ItemType Directory -Force -Path $path1 | Out-Null
@@ -212,11 +212,11 @@ Describe 'PowerShell adapter resource tests' {
212212
$files | Copy-Item -Destination $path4
213213

214214
$filePath = Join-Path $path1 'TestClassResource.psd1'
215-
(Get-Content -Raw $filePath).Replace("ModuleVersion = `'0.0.1`'", "ModuleVersion = `'1.0`'") | Set-Content $filePath
215+
(Get-Content -Raw $filePath).Replace("ModuleVersion = `'0.0.1`'", "ModuleVersion = `'1.0.0`'") | Set-Content $filePath
216216
$filePath = Join-Path $path2 'TestClassResource.psd1'
217-
(Get-Content -Raw $filePath).Replace("ModuleVersion = `'0.0.1`'", "ModuleVersion = `'1.1`'") | Set-Content $filePath
217+
(Get-Content -Raw $filePath).Replace("ModuleVersion = `'0.0.1`'", "ModuleVersion = `'1.1.0`'") | Set-Content $filePath
218218
$filePath = Join-Path $path3 'TestClassResource.psd1'
219-
(Get-Content -Raw $filePath).Replace("ModuleVersion = `'0.0.1`'", "ModuleVersion = `'2.0`'") | Set-Content $filePath
219+
(Get-Content -Raw $filePath).Replace("ModuleVersion = `'0.0.1`'", "ModuleVersion = `'2.0.0`'") | Set-Content $filePath
220220
$filePath = Join-Path $path4 'TestClassResource.psd1'
221221
(Get-Content -Raw $filePath).Replace("ModuleVersion = `'0.0.1`'", "ModuleVersion = `'2.0.1`'") | Set-Content $filePath
222222

@@ -377,15 +377,15 @@ Describe 'PowerShell adapter resource tests' {
377377
}
378378

379379
It 'Specifying version works' {
380-
$out = dsc resource get -r TestClassResource/TestClassResource --version 0.0.1 | ConvertFrom-Json
380+
$out = dsc resource get -r TestClassResource/TestClassResource --version '=0.0.1' | ConvertFrom-Json
381381
$LASTEXITCODE | Should -Be 0
382382
$out.actualState.Ensure | Should -BeExactly 'Present'
383383
}
384384

385385
It 'Specifying a non-existent version returns an error' {
386-
$null = dsc resource get -r TestClassResource/TestClassResource --version 0.0.2 2> $TestDrive/error.log
386+
$null = dsc resource get -r TestClassResource/TestClassResource --version '=0.0.2' 2> $TestDrive/error.log
387387
$LASTEXITCODE | Should -Be 7
388-
(Get-Content -Raw -Path $TestDrive/error.log) | Should -BeLike '*Resource not found: TestClassResource/TestClassResource 0.0.2*' -Because (Get-Content -Raw -Path $TestDrive/error.log)
388+
(Get-Content -Raw -Path $TestDrive/error.log) | Should -BeLike '*Resource not found: TestClassResource/TestClassResource =0.0.2*' -Because (Get-Content -Raw -Path $TestDrive/error.log)
389389
}
390390

391391
It 'Can process SecureString property' {

adapters/powershell/psDscAdapter/psDscAdapter.psm1

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,26 @@ function Add-AstMembers {
8686
}
8787
}
8888

89+
function ConvertTo-SemanticVersionString {
90+
[cmdletbinding()]
91+
param([version]$version)
92+
93+
$patch = if ($version.Build -ne -1) { $version.Build } else { 0 }
94+
95+
"{0}.{1}.{2}" -f @(
96+
$version.Major,
97+
$version.Minor,
98+
$patch
99+
)
100+
}
101+
89102
function FindAndParseResourceDefinitions {
90103
[CmdletBinding(HelpUri = '')]
91104
param(
92105
[Parameter(Mandatory = $true)]
93106
[string]$filePath,
94107
[Parameter(Mandatory = $true)]
95-
[string]$moduleVersion
108+
[version]$moduleVersion
96109
)
97110

98111
if (-not (Test-Path $filePath)) {
@@ -134,7 +147,7 @@ function FindAndParseResourceDefinitions {
134147
#TODO: ModuleName, Version and ParentPath should be taken from psd1 contents
135148
$DscResourceInfo.ModuleName = [System.IO.Path]::GetFileNameWithoutExtension($filePath)
136149
$DscResourceInfo.ParentPath = [System.IO.Path]::GetDirectoryName($filePath)
137-
$DscResourceInfo.Version = $moduleVersion
150+
$DscResourceInfo.Version = ConvertTo-SemanticVersionString $moduleVersion
138151

139152
$DscResourceInfo.Properties = [System.Collections.Generic.List[DscResourcePropertyInfo]]::new()
140153
$DscResourceInfo.Capabilities = GetClassBasedCapabilities $typeDefinitionAst.Members
@@ -206,7 +219,7 @@ function LoadPowerShellClassResourcesFromModule {
206219
$scriptPath = $moduleInfo.Path;
207220
}
208221

209-
$version = if ($moduleInfo.Version) { $moduleInfo.Version.ToString() } else { '0.0.0' }
222+
$version = if ($moduleInfo.Version) { $moduleInfo.Version } else { [version]'0.0.0' }
210223
$Resources = FindAndParseResourceDefinitions $scriptPath $version
211224

212225
if ($moduleInfo.NestedModules) {

adapters/powershell/psDscAdapter/win_psDscAdapter.psm1

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@ if ($PSVersionTable.PSVersion.Major -gt 5) {
2626
}
2727
}
2828

29+
function ConvertTo-SemanticVersionString {
30+
[cmdletbinding()]
31+
param([version]$version)
32+
33+
$patch = if ($version.Build -ne -1) { $version.Build } else { 0 }
34+
35+
"{0}.{1}.{2}" -f @(
36+
$version.Major,
37+
$version.Minor,
38+
$patch
39+
)
40+
}
41+
2942
<# public function Invoke-DscCacheRefresh
3043
.SYNOPSIS
3144
This function caches the results of the Get-DscResource call to optimize performance.
@@ -187,7 +200,12 @@ function Invoke-DscCacheRefresh {
187200
$DscResourceInfo = [DscResourceInfo]::new()
188201
$dscResource.PSObject.Properties | ForEach-Object -Process {
189202
if ($null -ne $_.Value) {
190-
$DscResourceInfo.$($_.Name) = $_.Value
203+
# Handle version specially to munge as semantic version
204+
if ($_.Name -eq 'Version') {
205+
$DscResourceInfo.$($_.Name) = ConvertTo-SemanticVersionString $_.Value
206+
} else {
207+
$DscResourceInfo.$($_.Name) = $_.Value
208+
}
191209
} else {
192210
$DscResourceInfo.$($_.Name) = ''
193211
}
@@ -212,7 +230,7 @@ function Invoke-DscCacheRefresh {
212230
# workaround: populate module version from psmoduleinfo if available
213231
if ($moduleInfo = $Modules | Where-Object { $_.Name -eq $moduleName }) {
214232
$moduleInfo = $moduleInfo | Sort-Object -Property Version -Descending | Select-Object -First 1
215-
$DscResourceInfo.Version = $moduleInfo.Version.ToString()
233+
$DscResourceInfo.Version = ConvertTo-SemanticVersionString $moduleInfo.Version
216234
}
217235
}
218236

adapters/wmi/wmi.resource.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ switch ($Operation) {
2828
'List' {
2929
$clases = Get-CimClass
3030

31-
foreach ($r in $clases) {
32-
$version_string = ""
31+
foreach ($r in $clases) {`
32+
$version_string = "1.0.0" # WMI resources don't have a version, default to 1.0.0
3333
$author_string = ""
3434
$description = ""
3535

dsc-bicep-ext/src/main.rs

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use dsc_lib::{
99
dscresource::Invoke,
1010
invoke_result::{GetResult, SetResult},
1111
},
12+
types::{FullyQualifiedTypeName, ResourceVersionReq},
1213
DscManager,
1314
};
1415
use rust_i18n::{i18n, t};
@@ -54,11 +55,18 @@ impl BicepExtension for BicepExtensionService {
5455
)
5556
);
5657

58+
let type_name = FullyQualifiedTypeName::parse(&resource_type)
59+
.map_err(|e| Status::invalid_argument(e.to_string()))?;
60+
let version_req = version.as_deref()
61+
.map(|v| ResourceVersionReq::parse(&format!("={v}")))
62+
.transpose()
63+
.map_err(|e| Status::invalid_argument(e.to_string()))?;
64+
5765
let mut dsc = DscManager::new();
5866
let Some(resource) = dsc
5967
.find_resource(&DiscoveryFilter::new(
60-
&resource_type,
61-
version.as_deref(),
68+
&type_name,
69+
version_req,
6270
None,
6371
))
6472
.unwrap_or(None)
@@ -109,11 +117,18 @@ impl BicepExtension for BicepExtensionService {
109117
)
110118
);
111119

120+
let type_name = FullyQualifiedTypeName::parse(&resource_type)
121+
.map_err(|e| Status::invalid_argument(e.to_string()))?;
122+
let version_req = version.as_deref()
123+
.map(|v| ResourceVersionReq::parse(&format!("={v}")))
124+
.transpose()
125+
.map_err(|e| Status::invalid_argument(e.to_string()))?;
126+
112127
let mut dsc = DscManager::new();
113128
let Some(resource) = dsc
114129
.find_resource(&DiscoveryFilter::new(
115-
&resource_type,
116-
version.as_deref(),
130+
&type_name,
131+
version_req,
117132
None,
118133
))
119134
.unwrap_or(None)
@@ -164,11 +179,18 @@ impl BicepExtension for BicepExtensionService {
164179
)
165180
);
166181

182+
let type_name = FullyQualifiedTypeName::parse(&resource_type)
183+
.map_err(|e| Status::invalid_argument(e.to_string()))?;
184+
let version_req = version.as_deref()
185+
.map(|v| ResourceVersionReq::parse(&format!("={v}")))
186+
.transpose()
187+
.map_err(|e| Status::invalid_argument(e.to_string()))?;
188+
167189
let mut dsc = DscManager::new();
168190
let Some(resource) = dsc
169191
.find_resource(&DiscoveryFilter::new(
170-
&resource_type,
171-
version.as_deref(),
192+
&type_name,
193+
version_req,
172194
None,
173195
))
174196
.unwrap_or(None)
@@ -219,11 +241,18 @@ impl BicepExtension for BicepExtensionService {
219241
)
220242
);
221243

244+
let type_name = FullyQualifiedTypeName::parse(&resource_type)
245+
.map_err(|e| Status::invalid_argument(e.to_string()))?;
246+
let version_req = version.as_deref()
247+
.map(|v| ResourceVersionReq::parse(&format!("={v}")))
248+
.transpose()
249+
.map_err(|e| Status::invalid_argument(e.to_string()))?;
250+
222251
let mut dsc = DscManager::new();
223252
let Some(resource) = dsc
224253
.find_resource(&DiscoveryFilter::new(
225-
&resource_type,
226-
version.as_deref(),
254+
&type_name,
255+
version_req,
227256
None,
228257
))
229258
.unwrap_or(None)

dsc/src/args.rs

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use clap::{Parser, Subcommand, ValueEnum};
55
use clap_complete::Shell;
66
use dsc_lib::dscresources::command_resource::TraceLevel;
77
use dsc_lib::progress::ProgressFormat;
8+
use dsc_lib::types::{FullyQualifiedTypeName, ResourceVersionReq, TypeNameFilter};
89
use rust_i18n::t;
910
use serde::Deserialize;
1011

@@ -178,7 +179,8 @@ pub enum ExtensionSubCommand {
178179
#[clap(name = "list", about = t!("args.listExtensionAbout").to_string())]
179180
List {
180181
/// Optional extension name to filter the list
181-
extension_name: Option<String>,
182+
#[clap(default_value_t)]
183+
extension_name: TypeNameFilter,
182184
#[clap(short = 'o', long, help = t!("args.outputFormat").to_string())]
183185
output_format: Option<ListOutputFormat>,
184186
},
@@ -200,10 +202,11 @@ pub enum ResourceSubCommand {
200202
#[clap(name = "list", about = t!("args.listAbout").to_string())]
201203
List {
202204
/// Optional resource name to filter the list
203-
resource_name: Option<String>,
205+
#[clap(default_value_t)]
206+
resource_name: TypeNameFilter,
204207
/// Optional adapter filter to apply to the list of resources
205208
#[clap(short = 'a', long = "adapter", help = t!("args.adapter").to_string())]
206-
adapter_name: Option<String>,
209+
adapter_name: Option<TypeNameFilter>,
207210
#[clap(short, long, help = t!("args.description").to_string())]
208211
description: Option<String>,
209212
#[clap(short, long, help = t!("args.tags").to_string())]
@@ -216,9 +219,9 @@ pub enum ResourceSubCommand {
216219
#[clap(short, long, help = t!("args.getAll").to_string())]
217220
all: bool,
218221
#[clap(short, long, help = t!("args.resource").to_string())]
219-
resource: String,
222+
resource: FullyQualifiedTypeName,
220223
#[clap(short, long, help = t!("args.version").to_string())]
221-
version: Option<String>,
224+
version: Option<ResourceVersionReq>,
222225
#[clap(short, long, help = t!("args.input").to_string(), conflicts_with = "file")]
223226
input: Option<String>,
224227
#[clap(short = 'f', long, help = t!("args.file").to_string(), conflicts_with = "input")]
@@ -229,9 +232,9 @@ pub enum ResourceSubCommand {
229232
#[clap(name = "set", about = "Invoke the set operation to a resource", arg_required_else_help = true)]
230233
Set {
231234
#[clap(short, long, help = t!("args.resource").to_string())]
232-
resource: String,
235+
resource: FullyQualifiedTypeName,
233236
#[clap(short, long, help = t!("args.version").to_string())]
234-
version: Option<String>,
237+
version: Option<ResourceVersionReq>,
235238
#[clap(short, long, help = t!("args.input").to_string(), conflicts_with = "file")]
236239
input: Option<String>,
237240
#[clap(short = 'f', long, help = t!("args.file").to_string(), conflicts_with = "input")]
@@ -244,9 +247,9 @@ pub enum ResourceSubCommand {
244247
#[clap(name = "test", about = "Invoke the test operation to a resource", arg_required_else_help = true)]
245248
Test {
246249
#[clap(short, long, help = t!("args.resource").to_string())]
247-
resource: String,
250+
resource: FullyQualifiedTypeName,
248251
#[clap(short, long, help = t!("args.version").to_string())]
249-
version: Option<String>,
252+
version: Option<ResourceVersionReq>,
250253
#[clap(short, long, help = t!("args.input").to_string(), conflicts_with = "file")]
251254
input: Option<String>,
252255
#[clap(short = 'f', long, help = t!("args.file").to_string(), conflicts_with = "input")]
@@ -257,9 +260,9 @@ pub enum ResourceSubCommand {
257260
#[clap(name = "delete", about = "Invoke the delete operation to a resource", arg_required_else_help = true)]
258261
Delete {
259262
#[clap(short, long, help = t!("args.resource").to_string())]
260-
resource: String,
263+
resource: FullyQualifiedTypeName,
261264
#[clap(short, long, help = t!("args.version").to_string())]
262-
version: Option<String>,
265+
version: Option<ResourceVersionReq>,
263266
#[clap(short, long, help = t!("args.input").to_string(), conflicts_with = "file")]
264267
input: Option<String>,
265268
#[clap(short = 'f', long, help = t!("args.file").to_string(), conflicts_with = "input")]
@@ -272,18 +275,18 @@ pub enum ResourceSubCommand {
272275
#[clap(name = "schema", about = "Get the JSON schema for a resource", arg_required_else_help = true)]
273276
Schema {
274277
#[clap(short, long, help = t!("args.resource").to_string())]
275-
resource: String,
278+
resource: FullyQualifiedTypeName,
276279
#[clap(short, long, help = t!("args.version").to_string())]
277-
version: Option<String>,
280+
version: Option<ResourceVersionReq>,
278281
#[clap(short = 'o', long, help = t!("args.outputFormat").to_string())]
279282
output_format: Option<OutputFormat>,
280283
},
281284
#[clap(name = "export", about = "Retrieve all resource instances", arg_required_else_help = true)]
282285
Export {
283286
#[clap(short, long, help = t!("args.resource").to_string())]
284-
resource: String,
287+
resource: FullyQualifiedTypeName,
285288
#[clap(short, long, help = t!("args.version").to_string())]
286-
version: Option<String>,
289+
version: Option<ResourceVersionReq>,
287290
#[clap(short, long, help = t!("args.input").to_string(), conflicts_with = "file")]
288291
input: Option<String>,
289292
#[clap(short = 'f', long, help = t!("args.file").to_string(), conflicts_with = "input")]

0 commit comments

Comments
 (0)