From 9ac89af35581c28449b64ec5db7000505a602f1b Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Wed, 14 Nov 2018 16:18:34 +1000 Subject: [PATCH] win_chocolatey - move over to AnsibleModule and add allow_multiple (#48370) --- .../win_chocolatey-allow-multiple.yaml | 3 + .../modules/windows/win_chocolatey.ps1 | 311 +++++++++++------- lib/ansible/modules/windows/win_chocolatey.py | 11 +- .../modules/windows/win_chocolatey_facts.ps1 | 4 +- .../targets/win_chocolatey/tasks/tests.yml | 42 ++- 5 files changed, 246 insertions(+), 125 deletions(-) create mode 100644 changelogs/fragments/win_chocolatey-allow-multiple.yaml diff --git a/changelogs/fragments/win_chocolatey-allow-multiple.yaml b/changelogs/fragments/win_chocolatey-allow-multiple.yaml new file mode 100644 index 0000000000..15842dad21 --- /dev/null +++ b/changelogs/fragments/win_chocolatey-allow-multiple.yaml @@ -0,0 +1,3 @@ +minor_changes: +- win_chocolatey - added the allow_multiple module option to allow side by side installs of the same package +- win_chocolatey - support bootstrapping Chocolatey from other URLs with any PS script that ends with ``.ps1``, originally this script had to be ``install.ps1`` diff --git a/lib/ansible/modules/windows/win_chocolatey.ps1 b/lib/ansible/modules/windows/win_chocolatey.ps1 index b034ac3cb5..68174967e0 100644 --- a/lib/ansible/modules/windows/win_chocolatey.ps1 +++ b/lib/ansible/modules/windows/win_chocolatey.ps1 @@ -7,44 +7,63 @@ #Requires -Module Ansible.ModuleUtils.ArgvParser #Requires -Module Ansible.ModuleUtils.CommandUtil -#Requires -Module Ansible.ModuleUtils.Legacy - -$ErrorActionPreference = 'Stop' +#AnsibleRequires -CSharpUtil Ansible.Basic # As of chocolatey 0.9.10, non-zero success exit codes can be returned # See https://github.com/chocolatey/choco/issues/512#issuecomment-214284461 $successexitcodes = (0, 1605, 1614, 1641, 3010) -$params = Parse-Args $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false -$verbosity = Get-AnsibleParam -obj $params -name "_ansible_verbosity" -type "int" -default 0 - -$name = Get-AnsibleParam -obj $params -name "name" -type "list" -failifempty $true - -$allow_empty_checksums = Get-AnsibleParam -obj $params -name "allow_empty_checksums" -type "bool" -default $false -$allow_prerelease = Get-AnsibleParam -obj $params -name "allow_prerelease" -type "bool" -default $false -$architecture = Get-AnsibleParam -obj $params -name "architecture" -type "str" -default "default" -validateset "default", "x86" -$install_args = Get-AnsibleParam -obj $params -name "install_args" -type "str" -$ignore_checksums = Get-AnsibleParam -obj $params -name "ignore_checksums" -type "bool" -default $false -$ignore_dependencies = Get-AnsibleParam -obj $params -name "ignore_dependencies" -type "bool" -default $false -$force = Get-AnsibleParam -obj $params -name "force" -type "bool" -default $false -$package_params = Get-AnsibleParam -obj $params -name "package_params" -type "str" -aliases "params" -$proxy_url = Get-AnsibleParam -obj $params -name "proxy_url" -type "str" -$proxy_username = Get-AnsibleParam -obj $params -name "proxy_username" -type "str" -$proxy_password = Get-AnsibleParam -obj $params -name "proxy_password" -type "str" -failifempty ($null -ne $proxy_username) -$skip_scripts = Get-AnsibleParam -obj $params -name "skip_scripts" -type "bool" -default $false -$source = Get-AnsibleParam -obj $params -name "source" -type "str" -$source_username = Get-AnsibleParam -obj $params -name "source_username" -type "str" -$source_password = Get-AnsibleParam -obj $params -name "source_password" -type "str" -failifempty ($null -ne $source_username) -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "absent","downgrade","latest","present","reinstalled" -$timeout = Get-AnsibleParam -obj $params -name "timeout" -type "int" -default 2700 -aliases "execution_timeout" -$validate_certs = Get-AnsibleParam -obj $params -name "validate_certs" -type "bool" -default $true -$version = Get-AnsibleParam -obj $params -name "version" -type "str" - -$result = @{ - changed = $false - rc = 0 +$spec = @{ + options = @{ + allow_empty_checksums = @{ type = "bool"; default = $false } + allow_multiple = @{ type = "bool"; default = $false } + allow_prerelease = @{ type = "bool"; default = $false } + architecture = @{ type = "str"; default = "default"; choices = "default", "x86" } + install_args = @{ type = "str" } + ignore_checksums = @{ type = "bool"; default = $false } + ignore_dependencies = @{ type = "bool"; default = $false } + force = @{ type = "bool"; default = $false } + name = @{ type = "list"; elements = "str"; required = $true } + package_params = @{ type = "str"; aliases = "params" } + proxy_url = @{ type = "str" } + proxy_username = @{ type = "str" } + proxy_password = @{ type = "str"; no_log = $true } + skip_scripts = @{ type = "bool"; default = $false } + source = @{ type = "str" } + source_username = @{ type = "str" } + source_password = @{ type = "str"; no_log = $true } + state = @{ type = "str"; default = "present"; choices = "absent", "downgrade", "latest", "present", "reinstalled" } + timeout = @{ type = "int"; default = 2700; aliases = "execution_timeout" } + validate_certs = @{ type = "bool"; default = $false } + version = @{ type = "str" } + } + supports_check_mode = $true } +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) + +$allow_empty_checksums = $module.Params.allow_empty_checksums +$allow_multiple = $module.Params.allow_multiple +$allow_prerelease = $module.Params.allow_prerelease +$architecture = $module.Params.architecture +$install_args = $module.Params.install_args +$ignore_checksums = $module.Params.ignore_checksums +$ignore_dependencies = $module.Params.ignore_dependencies +$force = $module.Params.force +$name = $module.Params.name +$package_params = $module.Params.package_params +$proxy_url = $module.Params.proxy_url +$proxy_username = $module.Params.proxy_username +$proxy_password = $module.Params.proxy_password +$skip_scripts = $module.Params.skip_scripts +$source = $module.Params.source +$source_username = $module.Params.source_username +$source_password = $module.Params.source_password +$state = $module.Params.state +$timeout = $module.Params.timeout +$validate_certs = $module.Params.validate_certs +$version = $module.Params.version + +$module.Result.rc = 0 if (-not $validate_certs) { [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } @@ -55,13 +74,13 @@ Function Get-CommonChocolateyArguments { # run with Chocolatey $arguments = [System.Collections.ArrayList]@("--yes", "--no-progress") # global vars that control the arguments - if ($check_mode) { + if ($module.CheckMode) { $arguments.Add("--what-if") > $null } - if ($verbosity -gt 4) { + if ($module.Verbosity -gt 4) { $arguments.Add("--debug") > $null $arguments.Add("--verbose") > $null - } elseif ($verbosity -gt 3) { + } elseif ($module.Verbosity -gt 3) { $arguments.Add("--verbose") > $null } else { $arguments.Add("--limit-output") > $null @@ -74,6 +93,7 @@ Function Get-InstallChocolateyArguments { param( [bool]$allow_downgrade, [bool]$allow_empty_checksums, + [bool]$allow_multiple, [bool]$allow_prerelease, [String]$architecture, [bool]$force, @@ -102,6 +122,9 @@ Function Get-InstallChocolateyArguments { if ($allow_empty_checksums) { $arguments.Add("--allow-empty-checksums") > $null } + if ($allow_multiple) { + $arguments.Add("--allow-multiple") > $null + } if ($allow_prerelease) { $arguments.Add("--prerelease") > $null } @@ -202,8 +225,8 @@ Function Install-Chocolatey { } if ($source) { - # check if the URL already contains the path to install.ps1 - if ($source.EndsWith("install.ps1")) { + # check if the URL already contains the path to PS script + if ($source.EndsWith(".ps1")) { $script_url = $source } else { # chocolatey server automatically serves a script at @@ -230,19 +253,19 @@ Function Install-Chocolatey { try { $install_script = $client.DownloadString($script_url) } catch { - Fail-Json -obj $result -message "Failed to download Chocolatey script from '$script_url': $($_.Exception.Message)" + $module.FailJson("Failed to download Chocolatey script from '$script_url'; $($_.Exception.Message)", $_) } - if (-not $check_mode) { + if (-not $module.CheckMode) { $res = Run-Command -command "powershell.exe -" -stdin $install_script -environment $environment if ($res.rc -ne 0) { - $result.rc = $res.rc - $result.stdout = $res.stdout - $result.stderr = $res.stderr - Fail-Json -obj $result -message "Chocolatey bootstrap installation failed." + $module.Result.rc = $res.rc + $module.Result.stdout = $res.stdout + $module.Result.stderr = $res.stderr + $module.FailJson("Chocolatey bootstrap installation failed.") } - Add-Warning -obj $result -message "Chocolatey was missing from this system, so it was installed during this task run." + $module.Warn("Chocolatey was missing from this system, so it was installed during this task run.") } - $result.changed = $true + $module.Result.changed = $true # locate the newly installed choco.exe $choco_app = Get-Command -Name choco.exe -CommandType Application -ErrorAction SilentlyContinue @@ -257,24 +280,24 @@ Function Install-Chocolatey { $choco_app = Get-Command -Name $choco_path -CommandType Application -ErrorAction SilentlyContinue } } - if ($check_mode -and $null -eq $choco_app) { - $result.skipped = $true - $result.msg = "Skipped check mode run on win_chocolatey as choco.exe cannot be found on the system" - Exit-Json -obj $result + if ($module.CheckMode -and $null -eq $choco_app) { + $module.Result.skipped = $true + $module.Result.msg = "Skipped check mode run on win_chocolatey as choco.exe cannot be found on the system" + $module.ExitJson() } if (-not (Test-Path -Path $choco_app.Path)) { - Fail-Json -obj $result -message "Failed to find choco.exe, make sure it is added to the PATH or the env var 'ChocolateyInstall' is set" + $module.FailJson("Failed to find choco.exe, make sure it is added to the PATH or the env var 'ChocolateyInstall' is set") } - $actual_version = Get-ChocolateyPackageVersion -choco_path $choco_app.Path -name chocolatey + $actual_version = (Get-ChocolateyPackageVersion -choco_path $choco_app.Path -name chocolatey)[0] if ([Version]$actual_version -lt [Version]"0.10.5") { - if ($check_mode) { - $result.skipped = $true - $result.msg = "Skipped check mode run on win_chocolatey as choco.exe is too old, a real run would have upgraded the executable. Actual: '$actual_version', Minimum Version: '0.10.5'" - Exit-Json -obj $result + if ($module.CheckMode) { + $module.Result.skipped = $true + $module.Result.msg = "Skipped check mode run on win_chocolatey as choco.exe is too old, a real run would have upgraded the executable. Actual: '$actual_version', Minimum Version: '0.10.5'" + $module.ExitJson() } - Add-Warning -obj $result -message "Chocolatey was older than v0.10.5 so it was upgraded during this task run." + $module.Warn("Chocolatey was older than v0.10.5 so it was upgraded during this task run.") Update-ChocolateyPackage -choco_path $choco_app.Path -packages @("chocolatey") ` -proxy_url $proxy_url -proxy_username $proxy_username ` -proxy_password $proxy_password -source $source ` @@ -290,25 +313,24 @@ Function Get-ChocolateyPackageVersion { [Parameter(Mandatory=$true)][String]$name ) - $command = Argv-ToString -arguments @($choco_path, "list", "--local-only", "--exact", "--limit-output", $name) + $command = Argv-ToString -arguments @($choco_path, "list", "--local-only", "--exact", "--limit-output", "--all-versions", $name) $res = Run-Command -command $command if ($res.rc -ne 0) { - $result.command = $command - $result.rc = $res.rc - $result.stdout = $res.stdout - $result.stderr = $res.stderr - Fail-Json -obj $result -message "Error checking installation status for the package '$name'" + $module.Result.command = $command + $module.Result.rc = $res.rc + $module.Result.stdout = $res.stdout + $module.Result.stderr = $res.stderr + $module.FailJson("Error checking installation status for the package '$name'") } $stdout = $res.stdout.Trim() - $version = $null + $versions = $null if ($stdout) { # if a match occurs it is in the format of "package|version" we split # by the last | to get the version in case package contains a pipe char - $pipe_index = $stdout.LastIndexOf("|") - $version = $stdout.Substring($pipe_index + 1) + $versions = @($stdout.Split("`r`n", [System.StringSplitOptions]::RemoveEmptyEntries) | ForEach-Object { $_.Substring($_.LastIndexOf("|") + 1) }) } - return $version + return ,$versions } Function Update-ChocolateyPackage { @@ -317,6 +339,7 @@ Function Update-ChocolateyPackage { [Parameter(Mandatory=$true)][String[]]$packages, [bool]$allow_downgrade, [bool]$allow_empty_checksums, + [bool]$allow_multiple, [bool]$allow_prerelease, [String]$architecture, [bool]$force, @@ -337,37 +360,52 @@ Function Update-ChocolateyPackage { $arguments = [System.Collections.ArrayList]@($choco_path, "upgrade") $arguments.AddRange($packages) - $common_args = Get-InstallChocolateyArguments -allow_downgrade $allow_downgrade ` - -allow_empty_checksums $allow_empty_checksums -allow_prerelease $allow_prerelease ` - -architecture $architecture -force $force -ignore_checksums $ignore_checksums ` - -ignore_dependencies $ignore_dependencies -install_args $install_args ` - -package_params $package_params -proxy_url $proxy_url -proxy_username $proxy_username ` - -proxy_password $proxy_password -skip_scripts $skip_scripts -source $source ` - -source_username $source_username -source_password $source_password -timeout $timeout ` - -version $version + + $common_params = @{ + allow_downgrade = $allow_downgrade + allow_empty_checksums = $allow_empty_checksums + allow_multiple = $allow_multiple + allow_prerelease = $allow_prerelease + architecture = $architecture + force = $force + ignore_checksums = $ignore_checksums + ignore_dependencies = $ignore_dependencies + install_args = $install_args + package_params = $package_params + proxy_url = $proxy_url + proxy_username = $proxy_username + proxy_password = $proxy_password + skip_scripts = $skip_scripts + source = $source + source_username = $source_username + source_password = $source_password + timeout = $timeout + version = $version + } + $common_args = Get-InstallChocolateyArguments @common_params $arguments.AddRange($common_args) $command = Argv-ToString -arguments $arguments $res = Run-Command -command $command - $result.rc = $res.rc + $module.Result.rc = $res.rc if ($res.rc -notin $successexitcodes) { - $result.command = $command - $result.stdout = $res.stdout - $result.stderr = $res.stderr - Fail-Json -obj $result -message "Error updating package(s) '$($packages -join ", ")'" + $module.Result.command = $command + $module.Result.stdout = $res.stdout + $module.Result.stderr = $res.stderr + $module.FailJson("Error updating package(s) '$($packages -join ", ")'") } - if ($verbosity -gt 1) { - $result.stdout = $res.stdout + if ($module.Verbosity -gt 1) { + $module.Result.stdout = $res.stdout } if ($res.stdout -match ' upgraded (\d+)/\d+ package') { if ($Matches[1] -gt 0) { - $result.changed = $true + $module.Result.changed = $true } } # need to set to false in case the rc is not 0 and a failure didn't actually occur - $result.failed = $false + $module.Result.failed = $false } Function Install-ChocolateyPackage { @@ -376,6 +414,7 @@ Function Install-ChocolateyPackage { [Parameter(Mandatory=$true)][String[]]$packages, [bool]$allow_downgrade, [bool]$allow_empty_checksums, + [bool]$allow_multiple, [bool]$allow_prerelease, [String]$architecture, [bool]$force, @@ -396,33 +435,47 @@ Function Install-ChocolateyPackage { $arguments = [System.Collections.ArrayList]@($choco_path, "install") $arguments.AddRange($packages) - $common_args = Get-InstallChocolateyArguments -allow_downgrade $allow_downgrade ` - -allow_empty_checksums $allow_empty_checksums -allow_prerelease $allow_prerelease ` - -architecture $architecture -force $force -ignore_checksums $ignore_checksums ` - -ignore_dependencies $ignore_dependencies -install_args $install_args ` - -package_params $package_params -proxy_url $proxy_url -proxy_username $proxy_username ` - -proxy_password $proxy_password -skip_scripts $skip_scripts -source $source ` - -source_username $source_username -source_password $source_password -timeout $timeout ` - -version $version + $common_params = @{ + allow_downgrade = $allow_downgrade + allow_empty_checksums = $allow_empty_checksums + allow_multiple = $allow_multiple + allow_prerelease = $allow_prerelease + architecture = $architecture + force = $force + ignore_checksums = $ignore_checksums + ignore_dependencies = $ignore_dependencies + install_args = $install_args + package_params = $package_params + proxy_url = $proxy_url + proxy_username = $proxy_username + proxy_password = $proxy_password + skip_scripts = $skip_scripts + source = $source + source_username = $source_username + source_password = $source_password + timeout = $timeout + version = $version + } + $common_args = Get-InstallChocolateyArguments @common_params $arguments.AddRange($common_args) $command = Argv-ToString -arguments $arguments $res = Run-Command -command $command - $result.rc = $res.rc + $module.Result.rc = $res.rc if ($res.rc -notin $successexitcodes) { - $result.command = $command - $result.stdout = $res.stdout - $result.stderr = $res.stderr - Fail-Json -obj $result -message "Error installing package(s) '$($packages -join ', ')'" + $module.Result.command = $command + $module.Result.stdout = $res.stdout + $module.Result.stderr = $res.stderr + $module.FailJson("Error installing package(s) '$($packages -join ', ')'") } - if ($verbosity -gt 1) { - $result.stdout = $res.stdout + if ($module.Verbosity -gt 1) { + $module.Result.stdout = $res.stdout } - $result.changed = $true + $module.Result.changed = $true # need to set to false in case the rc is not 0 and a failure didn't actually occur - $result.failed = $false + $module.Result.failed = $false } Function Uninstall-ChocolateyPackage { @@ -456,26 +509,30 @@ Function Uninstall-ChocolateyPackage { $arguments.Add($timeout) > $null } if ($version) { + # Need to set allow-multiple to make sure choco doesn't uninstall all versions + $arguments.Add("--allow-multiple") > $null $arguments.Add("--version") > $null $arguments.Add($version) > $null + } else { + $arguments.Add("--all-versions") > $null } $command = Argv-ToString -arguments $arguments $res = Run-Command -command $command - $result.rc = $res.rc + $module.Result.rc = $res.rc if ($res.rc -notin $successexitcodes) { - $result.command = $command - $result.stdout = $res.stdout - $result.stderr = $res.stderr - Fail-Json -obj $result -message "Error uninstalling package(s) '$($packages -join ", ")'" + $module.Result.command = $command + $module.Result.stdout = $res.stdout + $module.Result.stderr = $res.stderr + $module.FailJson("Error uninstalling package(s) '$($packages -join ", ")'") } - if ($verbosity -gt 1) { - $result.stdout = $res.stdout + if ($module.Verbosity -gt 1) { + $module.Result.stdout = $res.stdout } - $result.changed = $true + $module.Result.changed = $true # need to set to false in case the rc is not 0 and a failure didn't actually occur - $result.failed = $false + $module.Result.failed = $false } # get the full path to choco.exe, otherwise install/upgrade to at least 0.10.5 @@ -490,13 +547,13 @@ foreach ($package in $name) { # a dummy version so absent, latest, and downgrade will run with all if ($package -eq "all") { if ($state -in @("present", "reinstalled")) { - Fail-Json -obj $result -message "Cannot specify the package name as 'all' when state=$state" + $module.FailJson("Cannot specify the package name as 'all' when state=$state") } - $package_version = "0.0.0" + $package_versions = @("0.0.0") } else { - $package_version = Get-ChocolateyPackageVersion -choco_path $choco_path -name $package + $package_versions = Get-ChocolateyPackageVersion -choco_path $choco_path -name $package } - $package_info.$package = $package_version + $package_info.$package = $package_versions } if ($state -in "absent", "reinstalled") { @@ -520,16 +577,26 @@ if ($state -in @("downgrade", "latest", "present", "reinstalled")) { $missing_packages = $name } else { # otherwise only install the packages that are not installed - $missing_packages = ($package_info.GetEnumerator() | Where-Object { $null -eq $_.Value }).Key + $missing_packages = [System.Collections.ArrayList]@() + foreach ($package in $package_info.GetEnumerator()) { + if ($null -eq $package.Value) { + $missing_packages.Add($package.Key) > $null + } + } } - # if version is specified and installed version does not match, throw error - # ignore this if force or is set + # if version is specified and installed version does not match or not + # allow_multiple, throw error ignore this if force is set if ($state -eq "present" -and $null -ne $version -and -not $force) { foreach ($package in $name) { - $package_version = ($package_info.GetEnumerator() | Where-Object { $name -eq $_.Key -and $null -ne $_.Value }).Value - if ($null -ne $package_version -and $package_version -ne $version) { - Fail-Json -obj $result -message "Chocolatey package '$package' is already installed at version '$package_version' but was expecting '$version'. Either change the expected version, set state=latest, or set force=yes to continue" + $package_versions = [System.Collections.ArrayList]$package_info.$package + if ($package_versions.Count -gt 0) { + if (-not $package_versions.Contains($version) -and -not $allow_multiple) { + $module.FailJson("Chocolatey package '$package' is already installed with version(s) '$($package_versions -join "', '")' but was expecting '$version'. Either change the expected version, set state=latest, set allow_multiple=yes, or set force=yes to continue") + } elseif ($version -notin $package_versions -and $allow_multiple) { + # add the package back into the list of missing packages if installing multiple + $missing_packages.Add($package) > $null + } } } } @@ -537,6 +604,7 @@ if ($state -in @("downgrade", "latest", "present", "reinstalled")) { choco_path = $choco_path allow_downgrade = ($state -eq "downgrade") allow_empty_checksums = $allow_empty_checksums + allow_multiple = $allow_multiple allow_prerelease = $allow_prerelease architecture = $architecture force = $force @@ -555,7 +623,7 @@ if ($state -in @("downgrade", "latest", "present", "reinstalled")) { version = $version } - if ($null -ne $missing_packages) { + if ($missing_packages) { Install-ChocolateyPackage -packages $missing_packages @common_args } @@ -571,4 +639,5 @@ if ($state -in @("downgrade", "latest", "present", "reinstalled")) { } } -Exit-Json -obj $result +$module.ExitJson() + diff --git a/lib/ansible/modules/windows/win_chocolatey.py b/lib/ansible/modules/windows/win_chocolatey.py index c9209e7bce..7c8aa27eb7 100644 --- a/lib/ansible/modules/windows/win_chocolatey.py +++ b/lib/ansible/modules/windows/win_chocolatey.py @@ -33,6 +33,14 @@ options: type: bool default: 'no' version_added: '2.2' + allow_multiple: + description: + - Allow the installation of multiple packages when I(version) is specified. + - Having multiple packages at different versions can cause issues if the + package doesn't support this. Use at your own risk. + type: bool + default: no + version_added: '2.8' allow_prerelease: description: - Allow the installation of pre-release packages. @@ -194,7 +202,8 @@ options: version: description: - Specific version of the package to be installed. - - Ignored when I(state) is set to C(absent). + - When I(state) is set to C(absent), will uninstall the specific version + otherwise all versions of that package will be removed. type: str notes: - Provide the C(version) parameter value as a string (e.g. C('6.1')), otherwise it diff --git a/lib/ansible/modules/windows/win_chocolatey_facts.ps1 b/lib/ansible/modules/windows/win_chocolatey_facts.ps1 index 6e7b5766c2..b3abc84ea7 100644 --- a/lib/ansible/modules/windows/win_chocolatey_facts.ps1 +++ b/lib/ansible/modules/windows/win_chocolatey_facts.ps1 @@ -87,7 +87,7 @@ Function Get-ChocolateyPackages { param($choco_app) - $command = Argv-ToString -arguments $choco_app.Path, "list", "--local-only", "-r" + $command = Argv-ToString -arguments $choco_app.Path, "list", "--local-only", "--limit-output", "--all-versions" $res = Run-Command -command $command if ($res.rc -ne 0) { $result.stdout = $res.stdout @@ -97,7 +97,7 @@ Function Get-ChocolateyPackages { } $packages_info = [System.Collections.ArrayList]@() - $res.stdout -split "`r`n" | Where-Object { $_ -ne "" } | ForEach-Object { + $res.stdout.Split("`r`n", [System.StringSplitOptions]::RemoveEmptyEntries) | ForEach-Object { $packages_split = $_ -split "\|" $package_info = @{ package = $packages_split[0] diff --git a/test/integration/targets/win_chocolatey/tasks/tests.yml b/test/integration/targets/win_chocolatey/tasks/tests.yml index 998128f3b5..0052a4a0fe 100644 --- a/test/integration/targets/win_chocolatey/tasks/tests.yml +++ b/test/integration/targets/win_chocolatey/tasks/tests.yml @@ -270,7 +270,7 @@ state: present version: 0.1.0 register: fail_multiple_versions - failed_when: fail_multiple_versions.msg != "Chocolatey package '" + test_choco_package1 + "' is already installed at version '0.0.1' but was expecting '0.1.0'. Either change the expected version, set state=latest, or set force=yes to continue" + failed_when: fail_multiple_versions.msg != "Chocolatey package '" + test_choco_package1 + "' is already installed with version(s) '0.0.1' but was expecting '0.1.0'. Either change the expected version, set state=latest, set allow_multiple=yes, or set force=yes to continue" - name: force the upgrade of an existing version win_chocolatey: @@ -450,3 +450,43 @@ that: - all_latest is changed - all_latest_actual.stdout_lines == [test_choco_package1 + "|0.1.0"] + +- name: install newer version of package + win_chocolatey: + name: '{{ test_choco_package1 }}' + state: present + +- name: install older package with allow_multiple + win_chocolatey: + name: '{{ test_choco_package1 }}' + state: present + allow_multiple: True + version: '0.0.1' + register: allow_multiple + +- name: get result of install older package with allow_multiple + win_command: choco.exe list --local-only --limit-output --all-versions --exact {{ test_choco_package1|quote }} + register: allow_multiple_actual + +- name: assert install older package with allow_multiple + assert: + that: + - allow_multiple is changed + - allow_multiple_actual.stdout == "ansible|0.1.0\r\nansible|0.0.1\r\n" + +- name: uninstall specific version installed with allow_multiple + win_chocolatey: + name: '{{ test_choco_package1 }}' + state: absent + version: '0.0.1' + register: remove_multiple + +- name: get result of uninstall specific version installed with allow_multiple + win_command: choco.exe list --local-only --limit-output --all-versions --exact {{ test_choco_package1|quote }} + register: remove_multiple_actual + +- name: assert uninstall specific version installed with allow_multiple + assert: + that: + - remove_multiple is changed + - remove_multiple_actual.stdout == "ansible|0.1.0\r\n"