Skip to content

Commit 86a87d0

Browse files
Add a resource to manage creation/modification/deletion of a space (#216)
* Bumps octopus-serverspec-extensions to get: - new spaces test matchers - reverted behaviour around listening/polling tentacle method names - bug fix when tentacle exe doesn't exist * Echo out LCM Configuration, DSC Configuration, & DSC Status for debugging * Adds license key to second node, otherwise it tries to use free license which is no longer valid with the extra space
1 parent 3863bf5 commit 86a87d0

13 files changed

+905
-4
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
# dot-source the helper file (cannot load as a module due to scope considerations)
2+
. (Join-Path -Path (Split-Path (Split-Path $PSScriptRoot -Parent) -Parent) -ChildPath 'OctopusDSCHelpers.ps1')
3+
4+
function Get-TargetResource {
5+
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSDSCUseVerboseMessageInDSCResource", "")]
6+
[OutputType([HashTable])]
7+
param (
8+
[Parameter(Mandatory)]
9+
[ValidateNotNullOrEmpty()]
10+
[string]$Url,
11+
[Parameter(Mandatory)]
12+
[ValidateNotNullOrEmpty()]
13+
[ValidateSet("Present", "Absent")]
14+
[string]$Ensure,
15+
[Parameter(Mandatory)]
16+
[ValidateNotNullOrEmpty()]
17+
[string]$Name,
18+
[Parameter(Mandatory)]
19+
[ValidateNotNullOrEmpty()]
20+
[string]$Description,
21+
[string[]]$SpaceManagersTeamMembers,
22+
[string[]]$SpaceManagersTeams,
23+
[PSCredential]$OctopusCredentials = [PSCredential]::Empty,
24+
[PSCredential]$OctopusApiKey = [PSCredential]::Empty
25+
)
26+
27+
if (($null -eq $SpaceManagersTeamMembers) -and ($null -eq $SpaceManagersTeams)) {
28+
throw "Please provide at least one of 'SpaceManagersTeamMembers' or 'SpaceManagersTeams'."
29+
}
30+
31+
$space = Get-Space -Url $Url `
32+
-Name $Name `
33+
-OctopusCredentials $OctopusCredentials `
34+
-OctopusApiKey $OctopusApiKey
35+
$existingEnsure = 'Present'
36+
if ($null -eq $space) {
37+
$existingEnsure = 'Absent'
38+
}
39+
40+
$result = @{
41+
Url = $Url
42+
Ensure = $existingEnsure
43+
Name = $Name
44+
Description = $space.Description
45+
OctopusCredentials = $OctopusCredentials
46+
OctopusApiKey = $OctopusApiKey
47+
SpaceManagersTeamMembers = $space.SpaceManagersTeamMembers
48+
SpaceManagersTeams = $space.SpaceManagersTeams
49+
}
50+
51+
return $result
52+
}
53+
54+
function Set-TargetResource {
55+
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSDSCUseVerboseMessageInDSCResource", "")]
56+
param (
57+
[Parameter(Mandatory)]
58+
[ValidateNotNullOrEmpty()]
59+
[string]$Url,
60+
[Parameter(Mandatory)]
61+
[ValidateNotNullOrEmpty()]
62+
[ValidateSet("Present", "Absent")]
63+
[string]$Ensure,
64+
[Parameter(Mandatory)]
65+
[ValidateNotNullOrEmpty()]
66+
[string]$Name,
67+
[Parameter(Mandatory)]
68+
[ValidateNotNullOrEmpty()]
69+
[string]$Description,
70+
[string[]]$SpaceManagersTeamMembers,
71+
[string[]]$SpaceManagersTeams,
72+
[PSCredential]$OctopusCredentials = [PSCredential]::Empty,
73+
[PSCredential]$OctopusApiKey = [PSCredential]::Empty
74+
)
75+
76+
$currentResource = Get-TargetResource -Url $Url `
77+
-Ensure $Ensure `
78+
-Name $Name `
79+
-Description $Description `
80+
-SpaceManagersTeamMembers $SpaceManagersTeamMembers `
81+
-SpaceManagersTeams $SpaceManagersTeams `
82+
-OctopusCredentials $OctopusCredentials `
83+
-OctopusApiKey $OctopusApiKey
84+
85+
if ($Ensure -eq "Absent" -and $currentResource.Ensure -eq "Present") {
86+
Remove-Space -Url $Url `
87+
-Name $Name `
88+
-OctopusCredentials $OctopusCredentials `
89+
-OctopusApiKey $OctopusApiKey
90+
} elseif ($Ensure -eq "Present" -and $currentResource.Ensure -eq "Absent") {
91+
New-Space -Url $Url `
92+
-Name $Name `
93+
-Description $Description `
94+
-SpaceManagersTeamMembers $SpaceManagersTeamMembers `
95+
-SpaceManagersTeams $SpaceManagersTeams `
96+
-OctopusCredentials $OctopusCredentials `
97+
-OctopusApiKey $OctopusApiKey
98+
} else {
99+
Update-Space -Url $Url `
100+
-Name $Name `
101+
-Description $Description `
102+
-SpaceManagersTeamMembers $SpaceManagersTeamMembers `
103+
-SpaceManagersTeams $SpaceManagersTeams `
104+
-OctopusCredentials $OctopusCredentials `
105+
-OctopusApiKey $OctopusApiKey
106+
}
107+
}
108+
109+
function Test-TargetResource {
110+
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSDSCUseVerboseMessageInDSCResource", "")]
111+
[OutputType([boolean])]
112+
param (
113+
[Parameter(Mandatory)]
114+
[ValidateNotNullOrEmpty()]
115+
[string]$Url,
116+
[Parameter(Mandatory)]
117+
[ValidateNotNullOrEmpty()]
118+
[ValidateSet("Present", "Absent")]
119+
[string]$Ensure,
120+
[Parameter(Mandatory)]
121+
[ValidateNotNullOrEmpty()]
122+
[string]$Name,
123+
[Parameter(Mandatory)]
124+
[ValidateNotNullOrEmpty()]
125+
[string]$Description,
126+
[string[]]$SpaceManagersTeamMembers,
127+
[string[]]$SpaceManagersTeams,
128+
[PSCredential]$OctopusCredentials = [PSCredential]::Empty,
129+
[PSCredential]$OctopusApiKey = [PSCredential]::Empty
130+
)
131+
$currentResource = (Get-TargetResource -Url $Url `
132+
-Ensure $Ensure `
133+
-Name $Name `
134+
-Description $Description `
135+
-SpaceManagersTeamMembers $SpaceManagersTeamMembers `
136+
-SpaceManagersTeams $SpaceManagersTeams `
137+
-OctopusCredentials $OctopusCredentials `
138+
-OctopusApiKey $OctopusApiKey)
139+
140+
$params = Get-ODSCParameter $MyInvocation.MyCommand.Parameters
141+
142+
$currentConfigurationMatchesRequestedConfiguration = $true
143+
foreach ($key in $currentResource.Keys) {
144+
$currentValue = $currentResource.Item($key)
145+
$requestedValue = $params.Item($key)
146+
147+
if ($currentValue -ne $requestedValue) {
148+
Write-Verbose "(FOUND MISMATCH) Configuration parameter '$key' with value '$currentValue' mismatched the specified value '$requestedValue'"
149+
$currentConfigurationMatchesRequestedConfiguration = $false
150+
}
151+
else {
152+
Write-Verbose "Configuration parameter '$key' matches the requested value '$requestedValue'"
153+
}
154+
}
155+
156+
return $currentConfigurationMatchesRequestedConfiguration
157+
}
158+
159+
function Remove-Space {
160+
param (
161+
[Parameter(Mandatory)]
162+
[string]$Url,
163+
[Parameter(Mandatory)]
164+
[string]$Name,
165+
[PSCredential]$OctopusCredentials = [PSCredential]::Empty,
166+
[PSCredential]$OctopusApiKey = [PSCredential]::Empty
167+
)
168+
169+
$repository = Get-OctopusClientRepository -Url $Url `
170+
-OctopusCredentials $OctopusCredentials `
171+
-OctopusApiKey $OctopusApiKey
172+
173+
$space = $repository.Spaces.FindByName($Name)
174+
175+
$space.TaskQueueStopped = $true
176+
$repository.Spaces.Modify($space)
177+
178+
# todo: we should probably check to make sure the task queue is empty
179+
180+
$repository.Spaces.Delete($space)
181+
}
182+
183+
function New-Space {
184+
param (
185+
[Parameter(Mandatory)]
186+
[string]$Url,
187+
[Parameter(Mandatory)]
188+
[string]$Name,
189+
[Parameter(Mandatory)]
190+
[string]$Description,
191+
[string[]]$SpaceManagersTeamMembers,
192+
[string[]]$SpaceManagersTeams,
193+
[PSCredential]$OctopusCredentials = [PSCredential]::Empty,
194+
[PSCredential]$OctopusApiKey = [PSCredential]::Empty
195+
)
196+
$repository = Get-OctopusClientRepository -Url $Url `
197+
-OctopusCredentials $OctopusCredentials `
198+
-OctopusApiKey $OctopusApiKey
199+
200+
$space = New-SpaceResource
201+
$space.Name = $Name;
202+
$space.Description = $Description;
203+
$space.SpaceManagersTeamMembers = $SpaceManagersTeamMembers;
204+
$space.SpaceManagersTeams = $SpaceManagersTeams;
205+
206+
$users = $repository.Users.FindAll()
207+
$space.SpaceManagersTeamMembers = ConvertTo-ReferenceCollection @($SpaceManagersTeamMembers | foreach-object {
208+
$user = $_
209+
($users | where-object { $_.Username -eq $user }).Id
210+
})
211+
$teams = $repository.Teams.FindAll() | where-object { ($null -eq $_.SpaceId) -or ($_.SpaceId -eq $space.Id) }
212+
$space.SpaceManagersTeams = ConvertTo-ReferenceCollection @($SpaceManagersTeams | foreach-object {
213+
$team = $_
214+
($teams | where-object { $_.Name -eq $team }).Id
215+
})
216+
217+
$repository.Spaces.Create($space) | Out-Null
218+
}
219+
220+
function New-SpaceResource {
221+
# making this mockable, so we dont have to reference Octopus.Client.dll in the tests
222+
return New-Object Octopus.Client.Model.SpaceResource
223+
}
224+
225+
function ConvertTo-ReferenceCollection ([string[]]$list) {
226+
# making this mockable, so we dont have to reference Octopus.Client.dll in the tests
227+
return New-Object Octopus.Client.Model.ReferenceCollection ($list)
228+
}
229+
230+
function Update-Space {
231+
param (
232+
[Parameter(Mandatory)]
233+
[string]$Url,
234+
[Parameter(Mandatory)]
235+
[string]$Name,
236+
[Parameter(Mandatory)]
237+
[string]$Description,
238+
[string[]]$SpaceManagersTeamMembers,
239+
[string[]]$SpaceManagersTeams,
240+
[PSCredential]$OctopusCredentials = [PSCredential]::Empty,
241+
[PSCredential]$OctopusApiKey = [PSCredential]::Empty
242+
)
243+
$repository = Get-OctopusClientRepository -Url $Url `
244+
-OctopusCredentials $OctopusCredentials `
245+
-OctopusApiKey $OctopusApiKey
246+
247+
$space = $repository.Spaces.FindByName($Name)
248+
$space.Description = $Description
249+
250+
$users = $repository.Users.FindAll()
251+
$space.SpaceManagersTeamMembers = @($SpaceManagersTeamMembers | foreach-object {
252+
$user = $_
253+
($users | where-object { $_.Username -eq $user }).Id
254+
})
255+
$teams = $repository.Teams.FindAll() | where-object { ($null -eq $_.SpaceId) -or ($_.SpaceId -eq $space.Id) }
256+
$space.SpaceManagersTeams = @($SpaceManagersTeams | foreach-object {
257+
$team = $_
258+
($teams | where-object { $_.Name -eq $team }).Id
259+
})
260+
if ($null -eq ($SpaceManagersTeams | where-object { $_ -eq 'Space Managers' })) {
261+
$space.SpaceManagersTeams += ($teams | where-object { $_.Name -eq 'Space Managers'}).Id
262+
}
263+
$repository.Spaces.Modify($space) | Out-Null
264+
}
265+
266+
function Get-Space {
267+
param (
268+
[Parameter(Mandatory)]
269+
[string]$Url,
270+
[Parameter(Mandatory)]
271+
[string]$Name,
272+
[PSCredential]$OctopusCredentials = [PSCredential]::Empty,
273+
[PSCredential]$OctopusApiKey = [PSCredential]::Empty
274+
)
275+
276+
$repository = Get-OctopusClientRepository -Url $Url `
277+
-OctopusCredentials $OctopusCredentials `
278+
-OctopusApiKey $OctopusApiKey
279+
280+
$space = $repository.Spaces.FindByName($Name)
281+
282+
if ($null -ne $space) {
283+
# convert to json and back again, so we've got a HashTable rather than a SpaceResource
284+
# need a HashTable instead, as we want string[] for SpaceManagersTeamMembers and SpaceManagersTeams
285+
$space = ($space | ConvertTo-Json -depth 10 | ConvertFrom-Json)
286+
$users = $repository.Users.FindAll()
287+
$teams = $repository.Teams.FindAll() | where-object { ($null -eq $_.SpaceId) -or ($_.SpaceId -eq $space.Id) }
288+
$space.SpaceManagersTeamMembers = $space.SpaceManagersTeamMembers | foreach-object {
289+
$user = $_
290+
($users | where-object { $_.Id -eq $user }).Username
291+
}
292+
$space.SpaceManagersTeams = $space.SpaceManagersTeams | foreach-object {
293+
$team = $_
294+
($teams | where-object { $_.Id -eq $team }).Name
295+
}
296+
}
297+
298+
# convert to json and back again, so we've got a HashTable rather than a SpaceResource
299+
# need a HashTable instead, as we want string[] for SpaceManagersTeamMembers and SpaceManagersTeams
300+
return $space
301+
}
302+
303+
function Get-OctopusClientRepository {
304+
param (
305+
[Parameter(Mandatory)]
306+
[string]$Url,
307+
[PSCredential]$OctopusCredentials = [PSCredential]::Empty,
308+
[PSCredential]$OctopusApiKey = [PSCredential]::Empty
309+
)
310+
311+
if ((($null -eq $OctopusCredentials) -or ($OctopusCredentials -eq [PSCredential]::Empty)) -and (($null -eq $OctopusApiKey) -or ($OctopusApiKey -eq [PSCredential]::Empty))) {
312+
throw "Please provide either 'OctopusCredentials' or 'OctopusApiKey'."
313+
}
314+
if ((($null -ne $OctopusCredentials) -and ($OctopusCredentials -ne [PSCredential]::Empty)) -and (($null -ne $OctopusApiKey) -and ($OctopusApiKey -ne [PSCredential]::Empty))) {
315+
throw "Please provide either 'OctopusCredentials' or 'OctopusApiKey', not both."
316+
}
317+
318+
$tempFolder = [System.IO.Path]::GetTempPath()
319+
$shadowCopyFolder = Join-Path $tempFolder ([Guid]::NewGuid())
320+
New-Item -type Directory $shadowCopyFolder | Out-Null
321+
322+
$filename = "${env:ProgramFiles}\Octopus Deploy\Octopus\Newtonsoft.Json.dll"
323+
$version = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($filename)
324+
Write-Verbose "Shadow copying '$filename' (version $($version.FileVersion)) to $shadowCopyFolder"
325+
Copy-Item $filename $shadowCopyFolder
326+
327+
$filename = "${env:ProgramFiles}\Octopus Deploy\Octopus\Octopus.Client.dll"
328+
$version = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($filename)
329+
Write-Verbose "Shadow copying '$filename' (version $($version.FileVersion)) to $shadowCopyFolder"
330+
Copy-Item $filename $shadowCopyFolder
331+
332+
$filename = "${env:ProgramFiles}\Octopus Deploy\Octopus\Octopus.Client.Extensibility.dll"
333+
if (Test-Path $filename) {
334+
$version = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($filename)
335+
Write-Verbose "Shadow copying '$filename' (version $($version.FileVersion)) to $shadowCopyFolder"
336+
Copy-Item $filename $shadowCopyFolder
337+
}
338+
339+
#shadow copy these files, so we can uninstall octopus
340+
Add-Type -Path (Join-Path $shadowCopyFolder "Newtonsoft.Json.dll")
341+
Add-Type -Path (Join-Path $shadowCopyFolder "Octopus.Client.dll")
342+
343+
if (($null -ne $OctopusApiKey) -and ($OctopusApiKey -ne [PSCredential]::Empty)) {
344+
$apiKey = $OctopusApiKey.GetNetworkCredential().Password
345+
$endpoint = New-Object Octopus.Client.OctopusServerEndpoint($Url, $apiKey)
346+
$repository = New-Object Octopus.Client.OctopusRepository $endpoint
347+
}
348+
else {
349+
#connect
350+
$endpoint = New-Object Octopus.Client.OctopusServerEndpoint $Url
351+
$repository = New-Object Octopus.Client.OctopusRepository $endpoint
352+
353+
#sign in
354+
$credentials = New-Object Octopus.Client.Model.LoginCommand
355+
$credentials.Username = $OctopusCredentials.GetNetworkCredential().Username
356+
$credentials.Password = $OctopusCredentials.GetNetworkCredential().Password
357+
$repository.Users.SignIn($credentials)
358+
}
359+
360+
return $repository
361+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[ClassVersion("1.0.0"), FriendlyName("cOctopusServerSpace")]
2+
class cOctopusServerSpace : OMI_BaseResource
3+
{
4+
[Required, ValueMap{"Present", "Absent"},Values{"Present", "Absent"}] string Ensure;
5+
[Key] string Name;
6+
[Write] string Description;
7+
[Write] string Url;
8+
[Write] string SpaceManagersTeamMembers[];
9+
[Write] string SpaceManagersTeams[];
10+
[Write, EmbeddedInstance("MSFT_Credential")] string OctopusCredentials;
11+
[Write, EmbeddedInstance("MSFT_Credential")] string OctopusApiKey;
12+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Creates an environment in an Octopus Server instance
2+
3+
# deserialize a password from disk
4+
$password = Get-Content .\ExamplePassword.txt | ConvertTo-SecureString
5+
$creds = New-Object PSCredential "username", $password
6+
7+
Configuration SampleConfig
8+
{
9+
Import-DscResource -Module OctopusDSC
10+
11+
Node "localhost"
12+
{
13+
cOctopusServerSpace "Ensure Integration Team Space exists"
14+
{
15+
Ensure = "Present"
16+
Name = "Integration Team"
17+
Description = "The top secret work of the Integration Team"
18+
SpaceManagersTeamMembers = @('admin')
19+
SpaceManagersTeams = @('Everyone')
20+
Url = "https://octopus.example.com"
21+
OctopusCredentials = $creds
22+
}
23+
}
24+
}

0 commit comments

Comments
 (0)