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

win_chocolatey - add ability to pin a package #53157

Merged
merged 2 commits into from
Mar 5, 2019
Merged
Show file tree
Hide file tree
Changes from all 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: 2 additions & 0 deletions changelogs/fragments/win_chocolatey-pin.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
minor_changes:
- win_chocolatey - Added the ability to pin a package using the ``pinned`` option - https://github.com/ansible/ansible/issues/38526
95 changes: 95 additions & 0 deletions lib/ansible/modules/windows/win_chocolatey.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ $spec = @{
force = @{ type = "bool"; default = $false }
name = @{ type = "list"; elements = "str"; required = $true }
package_params = @{ type = "str"; aliases = "params" }
pinned = @{ type = "bool" }
proxy_url = @{ type = "str" }
proxy_username = @{ type = "str" }
proxy_password = @{ type = "str"; no_log = $true }
Expand All @@ -51,6 +52,7 @@ $ignore_dependencies = $module.Params.ignore_dependencies
$force = $module.Params.force
$name = $module.Params.name
$package_params = $module.Params.package_params
$pinned = $module.Params.pinned
$proxy_url = $module.Params.proxy_url
$proxy_username = $module.Params.proxy_username
$proxy_password = $module.Params.proxy_password
Expand Down Expand Up @@ -342,6 +344,75 @@ Function Get-ChocolateyPackageVersion {
return ,$versions
}

Function Get-ChocolateyPin {
param(
[Parameter(Mandatory=$true)][String]$choco_path
)

$command = Argv-ToString -arguments @($choco_path, "pin", "list", "--limit-output")
$res = Run-Command -command $command
if ($res.rc -ne 0) {
$module.Result.command = $command
$module.Result.rc = $res.rc
$module.Result.stdout = $res.stdout
$module.Result.stderr = $res.stderr
$module.FailJson("Error getting list of pinned packages")
}

$stdout = $res.stdout.Trim()
$pins = @{}

$stdout.Split("`r`n", [System.StringSplitOptions]::RemoveEmptyEntries) | ForEach-Object {
$package = $_.Substring(0, $_.LastIndexOf("|"))
$version = $_.Substring($_.LastIndexOf("|") + 1)

if ($pins.ContainsKey($package)) {
$pinned_versions = $pins.$package
} else {
$pinned_versions = [System.Collections.Generic.List`1[String]]@()
}
$pinned_versions.Add($version)
$pins.$package = $pinned_versions
}
return ,$pins
}

Function Set-ChocolateyPin {
param(
[Parameter(Mandatory=$true)][String]$choco_path,
[Parameter(Mandatory=$true)][String]$name,
[Switch]$pin,
[String]$version
)
if ($pin) {
$action = "add"
$err_msg = "Error pinning package '$name'"
} else {
$action = "remove"
$err_msg = "Error unpinning package '$name'"
}

$arguments = [System.Collections.ArrayList]@($choco_path, "pin", $action, "--name", $name)
if ($version) {
$err_msg += " at '$version'"
$arguments.Add("--version") > $null
$arguments.Add($version) > $null
}
$common_args = Get-CommonChocolateyArguments
$arguments.AddRange($common_args)

$command = Argv-ToString -arguments $arguments
$res = Run-Command -command $command
if ($res.rc -ne 0) {
$module.Result.command = $command
$module.Result.rc = $res.rc
$module.Result.stdout = $res.stdout
$module.Result.stderr = $res.stderr
$module.FailJson($err_msg)
}
$module.result.changed = $true
}

Function Update-ChocolateyPackage {
param(
[Parameter(Mandatory=$true)][String]$choco_path,
Expand Down Expand Up @@ -646,6 +717,30 @@ if ($state -in @("downgrade", "latest", "present", "reinstalled")) {
Update-ChocolateyPackage -packages $installed_packages @common_args
}
}

# Now we want to pin/unpin any packages now that it has been installed/upgraded
if ($null -ne $pinned) {
$pins = Get-ChocolateyPin -choco_path $choco_path

foreach ($package in $name) {
if ($pins.ContainsKey($package)) {
if (-not $pinned -and $null -eq $version) {
# No version is set and pinned=no, we want to remove all pins on the package. There is a bug in
# 'choco pin remove' with multiple versions where an older version might be pinned but
# 'choco pin remove' will still fail without an explicit version. Instead we take the literal
# interpretation that pinned=no and no version means the package has no pins at all
foreach ($v in $pins.$package) {
Set-ChocolateyPin -choco_path $choco_path -name $package -version $v
}
} elseif ($null -ne $version -and $pins.$package.Contains($version) -ne $pinned) {
Set-ChocolateyPin -choco_path $choco_path -name $package -pin:$pinned -version $version
}
} elseif ($pinned) {
# Package had no pins but pinned=yes is set.
Set-ChocolateyPin -choco_path $choco_path -name $package -pin -version $version
}
}
}
}

$module.ExitJson()
Expand Down
24 changes: 24 additions & 0 deletions lib/ansible/modules/windows/win_chocolatey.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@
type: str
version_added: '2.1'
aliases: [ params ]
pinned:
description:
- Whether to pin the Chocolatey package or not.
- If omitted then no checks on package pins are done.
- Will pin/unpin the specific version if I(version) is set.
- Will pin the latest version of a package if C(yes), I(version) is not set
and and no pin already exists.
- Will unpin all versions of a package if C(no) and I(version) is not set.
- This is ignored when C(state=absent).
type: bool
version_added: '2.8'
proxy_url:
description:
- Proxy URL used to install chocolatey and the package.
Expand Down Expand Up @@ -328,6 +339,19 @@
become: yes
become_user: Administrator
become_method: runas

- name: install and pin Notepad++ at 7.6.3
win_chocolatey:
name: notepadplusplus
version: 7.6.3
pinned: yes
state: present

- name: remove all pins for Notepad++ on all versions
win_chocolatey:
name: notepadplusplus
pinned: no
state: present
'''

RETURN = r'''
Expand Down
108 changes: 108 additions & 0 deletions test/integration/targets/win_chocolatey/tasks/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,114 @@
- allow_multiple is changed
- allow_multiple_actual.stdout == "ansible|0.1.0\r\nansible|0.0.1\r\n"

- name: pin 2 packages (check mode)
win_chocolatey:
name:
- '{{ test_choco_package1 }}'
- '{{ test_choco_package2 }}'
state: present
pinned: yes
register: pin_multiple_check
check_mode: True

- name: get result of pin 2 packages (check mode)
win_command: choco.exe pin list --limit-output
register: pin_multiple_actual_check

- name: assert pin 2 packages (check mode)
assert:
that:
- pin_multiple_check is changed
- pin_multiple_actual_check.stdout == ""

- name: pin 2 packages
win_chocolatey:
name:
- '{{ test_choco_package1 }}'
- '{{ test_choco_package2 }}'
state: present
pinned: yes
register: pin_multiple

- name: get result of pin 2 packages
win_command: choco.exe pin list --limit-output
register: pin_multiple_actual

- name: assert pin 2 packages
assert:
that:
- pin_multiple is changed
- pin_multiple_actual.stdout_lines == ["ansible|0.1.0", "ansible-test|1.0.1-beta1"]

- name: pin 2 packages (idempotent)
win_chocolatey:
name:
- '{{ test_choco_package1 }}'
- '{{ test_choco_package2 }}'
state: present
pinned: yes
register: pin_multiple_again

- name: assert pin 2 packages (idempoent)
assert:
that:
- not pin_multiple_again is changed

- name: pin specific older version
win_chocolatey:
name: '{{ test_choco_package1 }}'
state: present
pinned: yes
version: '0.0.1'
register: pin_older

- name: get result of pin specific older version
win_command: choco.exe pin list --limit-output
register: pin_older_actual

- name: assert pin specific older version
assert:
that:
- pin_older is changed
- pin_older_actual.stdout_lines == ["ansible|0.1.0", "ansible|0.0.1", "ansible-test|1.0.1-beta1"]

- name: unpin package at version
win_chocolatey:
name: '{{ test_choco_package1 }}'
state: present
pinned: no
version: '0.1.0'
register: unpin_version

- name: get result of unpin package at version
win_command: choco.exe pin list --limit-output
register: unpin_version_actual

- name: assert unpin package at version
assert:
that:
- unpin_version is changed
- unpin_version_actual.stdout_lines == ["ansible|0.0.1", "ansible-test|1.0.1-beta1"]

- name: unpin multiple packages without a version
win_chocolatey:
name:
- '{{ test_choco_package1 }}'
- '{{ test_choco_package2 }}'
state: present
pinned: no
register: unpin_multiple

- name: get result of unpin multiple packages without a version
win_command: choco.exe pin list --limit-output
register: unpin_multiple_actual

- name: assert unpin multiple packages without a version
assert:
that:
- unpin_multiple is changed
- unpin_multiple_actual.stdout == ""

- name: uninstall specific version installed with allow_multiple
win_chocolatey:
name: '{{ test_choco_package1 }}'
Expand Down