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

New Resource for managing choco config #132

Closed
Closed
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
180 changes: 180 additions & 0 deletions DSCResources/cChocoConfig/cChocoConfig.psm1
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# Copyright (c) 2017 Chocolatey Software, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

<#
.SYNOPSIS
DSC Resource to set/unset chocolatey config settings

.NOTES
This resource supports querying the XML file directly. This is faster but not recommended as it does not respect the chocolatey api.
Use this feature if you will not be upgrading chocolatey as updates may change the xml format and break this resource!
#>
function Get-TargetResource {
[CmdletBinding()]
[OutputType([System.Collections.Hashtable])]
param (
[Parameter(Mandatory)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid confusion can we use Mandatory = $true?

[System.String]$Key,

[Parameter()]
[System.String]$Value,

[Parameter()]
[ValidateSet('Present', 'Absent')]
[System.String]$Ensure = 'Present',

[Parameter()]
[System.Boolean]$QueryXML
Comment on lines +25 to +38
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The OutputType and Parameter types all use the .NET types. Can we stick with the PowerShell types?

)

$return = @{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you rename this as return is a statement and giving a variable the same name is going to cause confusion.

'Key' = $Key
'Value' = $Value
'Ensure' = $Ensure
'QueryXML' = $QueryXML
}

if ($Ensure -eq 'Absent') {
# if value and absent specified remove value as it can be confusing output
$return['Value'] = $null
}
Comment on lines +48 to +51
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused a little but this. The comment says 'if value and absent specified ...' but the if statement only checks for absent. As $Value is an optional parameter it might not be provided.


return $return
}

function Set-TargetResource {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mandatory = $true

[System.String]$Key,

[Parameter()]
[System.String]$Value,

[Parameter()]
[ValidateSet('Present', 'Absent')]
[System.String]$Ensure = 'Present',

[Parameter()]
[System.Boolean]$QueryXML
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a Boolean here but a switch in Get-ChocoConfig function? Is there a reason for that?

)
Comment on lines +60 to +71
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the PowerShell types and not the .NET ones.


# validate value is given when ensure present
if ($Ensure -eq 'Present' -and -not $PSBoundParameters.ContainsKey('Value')) {
throw "Missing parameter 'Value' when ensuring config is present!"
}

# query config to determine current setting
$ChocoConfig = @{
'Key' = $Key
'QueryXML' = $QueryXML
}
$CurrentValue = Get-ChocoConfig @ChocoConfig

# make it so
if ($Ensure -eq 'Absent' -and -not [String]::IsNullOrEmpty($CurrentValue)) {
Write-Verbose "Unsetting $Key..."
& choco config unset $Key
}
else {
Write-Verbose "Setting $Key to '$Value'..."
& choco config set --name $Key --value $Value
}

}

function Test-TargetResource {
[CmdletBinding()]
[OutputType([System.Boolean])]
param (
[Parameter(Mandatory)]
[System.String]$Key,

[Parameter()]
[System.String]$Value,

[Parameter()]
[ValidateSet('Present', 'Absent')]
[System.String]$Ensure = 'Present',

[Parameter()]
[System.Boolean]$QueryXML
)

# validate value is given when ensure present
if ($Ensure -eq 'Present' -and -not $PSBoundParameters.ContainsKey('Value')) {
throw "Missing parameter 'Value' when ensuring config is present!"
}

# query config to determine current setting
$ChocoConfig = @{
'Key' = $Key
'QueryXML' = $QueryXML
}
$CurrentValue = Get-ChocoConfig @ChocoConfig

# determine if in desired state
if ($Ensure -eq 'Absent') {
if ([String]::IsNullOrEmpty($CurrentValue)) {
Write-Verbose "$Key is in desired state"
return $true
}
else {
Write-Verbose "$key not in desired state: value should be unset but was '$CurrentValue'"
return $false
}
}
else {
if ($Value -eq $CurrentValue) {
Write-Verbose "$Key is in desired state"
return $true
}
else {
Write-Verbose "$key not in desired state: value should be '$Value' but was '$CurrentValue'"
return $false
}
}
}

function Get-ChocoConfig {
[CmdletBinding()]
[OutputType([System.String])]
param (
[Parameter(Mandatory)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same with Mandatory parameter.

[System.String]$Key,
Comment on lines +152 to +155
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PowerShell types to be used.


[Parameter()]
[Switch]$QueryXML
)

if ($QueryXML.IsPresent) {
Write-Verbose "Querying chco config via chocolatey.config xml..."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in 'choco'

$ConfigXmlPath = Join-Path $env:ChocolateyInstall -ChildPath "config\chocolatey.config"
try {
$ConfigXml = [xml](Get-Content -Path $ConfigXmlPath)
# xpath is case sensitive so we must convert all to lower case
$XPath = "/chocolatey/config/add[translate(@key, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = '$($Key.ToLower())']"
$ConfigValue = $ConfigXml.SelectSingleNode($XPath).value
}
catch {
$PSCmdlet.ThrowTerminatingError($_)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just use a throw here rather than the .NET

}
Comment on lines +164 to +172
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little unsure about this. The code catches any exceptions being thrown but just throws the same exception. This would be no different than not having the try / catch block. So I'm wondering why it's there?

}
else {
Write-Verbose "Querying choco config via CLI"
$ConfigValue = & choco config get $Key -r
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't use aliases (&)? Can you also use the full named Chocolatey switches - so instead of -r use --limit-output.

There is no sanity check being done on the output of this command. What if the key does not exist? The caller will get a response back that is not what they are expecting.

}

return $ConfigValue
}
8 changes: 8 additions & 0 deletions DSCResources/cChocoConfig/cChocoConfig.schema.mof
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[ClassVersion("1.0.0.0"), FriendlyName("cChocoConfig")]
class cChocoConfig : OMI_BaseResource
{
[Key] String Key;
[Write] String Value;
[Write,ValueMap{"Present", "Absent"},Values{"Present", "Absent"}] String Ensure;
[Write] Boolean QueryXML;
};
39 changes: 39 additions & 0 deletions Examples/cChocoConfigExample.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright (c) 2017 Chocolatey Software, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


configuration ChocoConfig {

Import-DscResource -ModuleName cChoco

Node 'localhost' {

cChocoConfig webRequestTimeoutSeconds {
Key = "webRequestTimeoutSeconds"
Ensure = 'Present'
Value = 30
}

cChocoConfig proxy {
Key = "proxy"
Ensure = 'Absent'
}
}

}


$config = ChocoConfig

Start-DscConfiguration -Path $config.psparentpath -Wait -Verbose -Force
163 changes: 163 additions & 0 deletions Tests/cChocoConfig_Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# Copyright (c) 2017 Chocolatey Software, Inc.
# Copyright (c) 2013 - 2017 Lawrence Gripper & original authors/contributors from https://github.com/chocolatey/cChoco
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

#----------------------------------------#
# Pester tests for cChocoPackageInstall #
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong one 😄

#----------------------------------------#
$ResourceName = ((Split-Path -Path $MyInvocation.MyCommand.Path -Leaf) -split '_')[0]
$ResourceFile = (Get-DscResource -Name $ResourceName).Path

$TestsPath = (split-path -path $MyInvocation.MyCommand.Path -Parent)
$ResourceFile = Get-ChildItem -Recurse $TestsPath\.. -File | Where-Object {$_.name -eq "$ResourceName.psm1"}

Import-Module -Name $ResourceFile.FullName

Describe -Name "Testing $ResourceName loaded from $ResourceFile" -Fixture {
$MyProxyUrl = 'http://foo'
Context -Name "Proxy config is set to '$MyProxyUrl'" -Fixture {
Mock -CommandName 'Get-ChocoConfig' -ModuleName 'cChocoConfig' -MockWith {
return 'http://foo'
}

# returns true if config matches supplied value
$Scenario1 = @{
Key = 'Proxy'
Ensure = 'Present'
Value = $MyProxyUrl
}
It -name "Test-TargetResource -ensure 'Present' -Value '$MyProxyUrl' should return True" -test {
Test-TargetResource @Scenario1 | Should Be $true
}

# returns true if config matches supplied value (via xml)
$Scenario2 = @{
Key = 'Proxy'
Ensure = 'Present'
Value = $MyProxyUrl
QueryXML = $true
}
It -name "Test-TargetResource -ensure 'Present' -Value '$MyProxyUrl' -QueryXML `$true should return True" -test {
Test-TargetResource @Scenario2 | Should Be $true
}

# returns false if config does not match supplied value
$Scenario3 = @{
Key = 'Proxy'
Ensure = 'Present'
Value = 'http://some/other/url'
}
It -name "Test-TargetResource -ensure 'Present' -Value 'http://some/other/url' should return False" -test {
Test-TargetResource @Scenario3 | Should Be $false
}

# returns false if config does not match supplied value (via xml)
$Scenario4 = @{
Key = 'Proxy'
Ensure = 'Present'
Value = 'http://some/other/url'
QueryXML = $true
}
It -name "Test-TargetResource -ensure 'Present' -Value 'http://some/other/url' -QueryXML `$true should return False" -test {
Test-TargetResource @Scenario4 | Should Be $false
}

# returns false if ensure absent
$Scenario5 = @{
Key = 'Proxy'
Ensure = 'Absent'
}
It -name "Test-TargetResource -ensure 'Absent' should return False" -test {
Test-TargetResource @Scenario5 | Should Be $false
}

# returns false if ensure absent (via xml)
$Scenario6 = @{
Key = 'Proxy'
Ensure = 'Absent'
QueryXML = $true
}
It -name "Test-TargetResource -ensure 'Absent' -QueryXML `$true should return False" -test {
Test-TargetResource @Scenario6 | Should Be $false
}

# throws when ensure present but no value given
$Scenario7 = @{
Key = 'Proxy'
Ensure = 'Present'
}
It -name "Test-TargetResource -ensure 'Present' should throw" -test {
{Test-TargetResource @Scenario7} | Should -Throw "Missing parameter 'Value' when ensuring config is present!"
}

# throws when ensure present but no value given (via xml)
$Scenario8 = @{
Key = 'Proxy'
Ensure = 'Present'
QueryXML = $true
}
It -name "Test-TargetResource -ensure 'Present' -QueryXML `$true should throw" -test {
{Test-TargetResource @Scenario8} | Should -Throw "Missing parameter 'Value' when ensuring config is present!"
}
}

Context -Name "Proxy config is not set" -Fixture {
Mock -CommandName 'Get-ChocoConfig' -ModuleName 'cChocoConfig' -MockWith {
return ''
}

# returns false if value supplied
$Scenario1 = @{
Key = 'Proxy'
Ensure = 'Present'
Value = $MyProxyUrl
}
It -name "Test-TargetResource -ensure 'Present' -Value '$MyProxyUrl' should return False" -test {
Test-TargetResource @Scenario1 | Should Be $false
}

# returns true if value supplied (via xml)
$Scenario2 = @{
Key = 'Proxy'
Ensure = 'Present'
Value = $MyProxyUrl
QueryXML = $true
}
It -name "Test-TargetResource -ensure 'Present' -Value '$MyProxyUrl' -QueryXML `$true should return False" -test {
Test-TargetResource @Scenario2 | Should Be $false
}

# returns true if ensure absent
$Scenario5 = @{
Key = 'Proxy'
Ensure = 'Absent'
}
It -name "Test-TargetResource -ensure 'Absent' should return True" -test {
Test-TargetResource @Scenario5 | Should Be $true
}

# returns true if ensure absent (via xml)
$Scenario6 = @{
Key = 'Proxy'
Ensure = 'Absent'
QueryXML = $true
}
It -name "Test-TargetResource -ensure 'Absent' -QueryXML `$true should return True" -test {
Test-TargetResource @Scenario6 | Should Be $true
}
}
}

#Clean-up
Remove-Module cChocoConfig