[win_get_url] feature: Add support checksum to module win_get_url (#51986)

* set valid_until equal to current time + spot_wait_timeout

* Add checksum check for downloaded file.

* refactoring

* fix typo

* add fixes

* mart try,catch handling

* revert lib/ansible/modules/cloud/amazon/ec2.py from upstream

* refactoring

* remove empty lines

* add checksum verification for existing file

* fix current file check

* refactoring destination file check

* add handling exceptions

* refactoring

* Added download file hash data from url

* fix string aligning

* fix bug with uri

* Added get hash from multy-string file

* Added URI support for checksum file location

* refactoing

* Remove any non-alphanumeric characters for hash from url

* fix discussions; add support for PS3

* refactoring

* add size return value

* checkout from upstream for lib/ansible/modules/cloud/amazon/ec2.py

* add Ansible.ModuleUtils.Legacy support; refactoring

* Copyright added

* Checking files size before and after downloading added.

* remove unused code

* Corrected regexp for dotted slashed file name prefix in hash-file

* hotfix typo error; add int tests

* remove legacy module support; split checksum to checksum, checksum_algorithm, checksum_url

* changed default hash algorithm

* Fixed case for ContentLength = -1

* Old comment removed

* fix typo

* Remove file size check before downloading

* add alias to ; fix tests

* adjust tests; fix lint warnings from PSScritpAnalyzer

* workaround for bug in win_chocolatey module on win2008

* remove win_get_url.ps1 from /test/sanity/pslint/ignore.txt

* add checksum_algorithm as retuen value

* first normalise before return Result

* resolve discussions

Signed-off-by: Viktor Utkin <viktor.utkin7@yandex.ru>

* fix discussions
fix http tests as discussed

* fix last discussions

* Reduce code duplication and add idempotency check

* fix sanity issue and remove testing code

* move back to using tmp file for checksum comparison
This commit is contained in:
Viktor Utkin 2019-03-05 13:37:00 +03:00 committed by Jordan Borean
parent aafc5538bc
commit b2a7561a7f
8 changed files with 616 additions and 156 deletions

View file

@ -0,0 +1,3 @@
minor_changes:
- win_get_url - Add the ``checksum`` option to verify the integrity of a downloaded file.
- win_get_url - Add idempotency check if the remote file has the same contents as the dest file.

View file

@ -3,10 +3,13 @@
# Copyright: (c) 2015, Paul Durivage <paul.durivage@rackspace.com>
# Copyright: (c) 2015, Tal Auslander <tal@cloudshare.com>
# Copyright: (c) 2017, Dag Wieers <dag@wieers.com>
# Copyright: (c) 2019, Viktor Utkin <viktor_utkin@epam.com>
# Copyright: (c) 2019, Uladzimir Klybik <uladzimir_klybik@epam.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#AnsibleRequires -CSharpUtil Ansible.Basic
#Requires -Module Ansible.ModuleUtils.AddType
#Requires -Module Ansible.ModuleUtils.FileUtil
$spec = @{
options = @{
@ -23,7 +26,13 @@ $spec = @{
proxy_username = @{ type='str' }
proxy_password = @{ type='str'; no_log=$true }
force = @{ type='bool'; default=$true }
checksum = @{ type='str' }
checksum_algorithm = @{ type='str'; default='sha1'; choices = @("md5", "sha1", "sha256", "sha384", "sha512") }
checksum_url = @{ type='str' }
}
mutually_exclusive = @(
,@('checksum', 'checksum_url')
)
supports_check_mode = $true
}
@ -42,145 +51,271 @@ $proxy_url = $module.Params.proxy_url
$proxy_username = $module.Params.proxy_username
$proxy_password = $module.Params.proxy_password
$force = $module.Params.force
$checksum = $module.Params.checksum
$checksum_algorithm = $module.Params.checksum_algorithm
$checksum_url = $module.Params.checksum_url
Add-CSharpType -AnsibleModule $module -References @'
using System.Net;
public class ExtendedWebClient : WebClient {
public int Timeout;
public ExtendedWebClient() {
Timeout = 600000; // Default timeout value
}
protected override WebRequest GetWebRequest(System.Uri address) {
WebRequest request = base.GetWebRequest(address);
request.Timeout = Timeout;
return request;
}
}
'@
Function CheckModified-File($module, $url, $dest, $headers, $credentials, $timeout, $use_proxy, $proxy) {
$fileLastMod = ([System.IO.FileInfo]$dest).LastWriteTimeUtc
$webLastMod = $null
$webRequest = [System.Net.WebRequest]::Create($url)
foreach ($header in $headers.GetEnumerator()) {
$webRequest.Headers.Add($header.Name, $header.Value)
}
if ($timeout) {
$webRequest.Timeout = $timeout * 1000
}
if (-not $use_proxy) {
# Ignore the system proxy settings
$webRequest.Proxy = $null
} elseif ($proxy) {
$webRequest.Proxy = $proxy
}
if ($credentials) {
if ($force_basic_auth) {
$webRequest.Headers.Add("Authorization", "Basic $credentials")
} else {
$webRequest.Credentials = $credentials
}
}
if ($webRequest -is [System.Net.FtpWebRequest]) {
$webRequest.Method = [System.Net.WebRequestMethods+Ftp]::GetDateTimestamp
} else {
$webRequest.Method = [System.Net.WebRequestMethods+Http]::Head
}
# FIXME: Split both try-statements and single-out catched exceptions with more specific error messages
Try {
$webResponse = $webRequest.GetResponse()
$webLastMod = $webResponse.LastModified
} Catch [System.Net.WebException] {
$module.Result.status_code = [int] $_.Exception.Response.StatusCode
$module.FailJson("Error requesting '$url'. $($_.Exception.Message)", $_)
} Catch {
$module.FailJson("Error when requesting 'Last-Modified' date from '$url'. $($_.Exception.Message)", $_)
}
$module.Result.status_code = [int] $webResponse.StatusCode
$module.Result.msg = [string] $webResponse.StatusDescription
$webResponse.Close()
if ($webLastMod -and ((Get-Date -Date $webLastMod).ToUniversalTime() -lt $fileLastMod)) {
return $false
} else {
return $true
}
}
Function Download-File($module, $url, $dest, $headers, $credentials, $timeout, $use_proxy, $proxy) {
$module_start = Get-Date
# Check $dest parent folder exists before attempting download, which avoids unhelpful generic error message.
$dest_parent = Split-Path -LiteralPath $dest
if (-not (Test-Path -LiteralPath $dest_parent -PathType Container)) {
$module.FailJson("The path '$dest_parent' does not exist for destination '$dest', or is not visible to the current user. Ensure download destination folder exists (perhaps using win_file state=directory) before win_get_url runs.")
}
# TODO: Replace this with WebRequest
$extWebClient = New-Object ExtendedWebClient
foreach ($header in $headers.GetEnumerator()) {
$extWebClient.Headers.Add($header.Name, $header.Value)
}
if ($timeout) {
$extWebClient.Timeout = $timeout * 1000
}
if (-not $use_proxy) {
# Ignore the system proxy settings
$extWebClient.Proxy = $null
} elseif ($proxy) {
$extWebClient.Proxy = $proxy
}
if ($credentials) {
if ($force_basic_auth) {
$extWebClient.Headers.Add("Authorization","Basic $credentials")
} else {
$extWebClient.Credentials = $credentials
}
}
if (-not $module.CheckMode) {
# FIXME: Single-out catched exceptions with more specific error messages
Try {
$extWebClient.DownloadFile($url, $dest)
} Catch [System.Net.WebException] {
$module.Result.status_code = [int] $_.Exception.Response.StatusCode
$module.Result.elapsed = ((Get-Date) - $module_start).TotalSeconds
$module.FailJson("Error downloading '$url' to '$dest': $($_.Exception.Message)", $_)
} Catch {
$module.Result.elapsed = ((Get-Date) - $module_start).TotalSeconds
$module.FailJson("Unknown error downloading '$url' to '$dest': $($_.Exception.Message)", $_)
}
}
$module.Result.status_code = 200
$module.Result.changed = $true
$module.Result.msg = 'OK'
$module.Result.dest = $dest
$module.Result.elapsed = ((Get-Date) - $module_start).TotalSeconds
}
$module.Result.dest = $dest
$module.Result.elapsed = 0
$module.Result.url = $url
Function Invoke-AnsibleWebRequest {
<#
.SYNOPSIS
Creates a WebRequest and invokes a ScriptBlock with the response passed in.
It handles the common module options like credential, timeout, proxy options
in a single location to reduce code duplication.
#>
param(
[Parameter(Mandatory=$true)][Ansible.Basic.AnsibleModule]$Module,
[Parameter(Mandatory=$true)][Uri]$Uri,
[Parameter(Mandatory=$true)][Hashtable]$Method,
[Parameter(Mandatory=$true)][ScriptBlock]$Script, # Invoked in this cmdlet
[System.Collections.IDictionary]$Headers,
[Int32]$Timeout,
[Switch]$UseProxy,
[System.Net.WebProxy]$Proxy,
$Credential # Either a String (force_basic_auth) or NetCredentials
)
$web_request = [System.Net.WebRequest]::Create($Uri)
$web_request.Method = $Method.($web_request.GetType().Name)
foreach ($header in $headers.GetEnumerator()) {
$web_request.Headers.Add($header.Key, $header.Value)
}
if ($timeout) {
$web_request.Timeout = $timeout * 1000
}
if (-not $UseProxy) {
$web_request.Proxy = $null
} elseif ($ProxyUri) {
$web_request.Proxy = $Proxy
}
if ($Credential) {
if ($Credential -is [String]) {
# force_basic_auth=yes is set
$web_request.Headers.Add("Authorization", "Basic $Credential")
} else {
$web_request.Credentials = $Credential
}
}
try {
$web_response = $web_request.GetResponse()
$response_stream = $web_response.GetResponseStream()
try {
# Invoke the ScriptBlock and pass in the WebResponse and ResponseStream
&$Script -Response $web_response -Stream $response_stream
} finally {
$response_stream.Dispose()
}
if ($Uri.IsFile) {
# A FileWebResponse won't have these properties set
$module.Result.msg = "OK"
$module.Result.status_code = 200
} else {
$module.Result.msg = [string]$web_response.StatusDescription
$module.Result.status_code = [int]$web_response.StatusCode
}
} catch [System.Net.WebException] {
$module.Result.status_code = [int]$_.Exception.Response.StatusCode
$module.FailJson("Error requesting '$Uri'. $($_.Exception.Message)", $_)
} finally {
if ($web_response) {
$web_response.Close()
}
}
}
Function Get-ChecksumFromUri {
param(
[Parameter(Mandatory=$true)][Ansible.Basic.AnsibleModule]$Module,
[Parameter(Mandatory=$true)][Uri]$Uri,
[Uri]$SourceUri,
[Hashtable]$RequestParams
)
$script = {
param($Response, $Stream)
$read_stream = New-Object -TypeName System.IO.StreamReader -ArgumentList $Stream
$web_checksum = $read_stream.ReadToEnd()
$basename = (Split-Path -Path $SourceUri.LocalPath -Leaf)
$basename = [regex]::Escape($basename)
$web_checksum_str = $web_checksum -split '\r?\n' | Select-String -Pattern $("\s+\.?\/?\\?" + $basename + "\s*$")
if (-not $web_checksum_str) {
$Module.FailJson("Checksum record not found for file name '$basename' in file from url: '$Uri'")
}
$web_checksum_str_splitted = $web_checksum_str[0].ToString().split(" ", 2)
$hash_from_file = $web_checksum_str_splitted[0].Trim()
# Remove any non-alphanumeric characters
$hash_from_file = $hash_from_file -replace '\W+', ''
Write-Output -InputObject $hash_from_file
}
$invoke_args = @{
Module = $Module
Uri = $Uri
Method = @{
FileWebRequest = [System.Net.WebRequestMethods+File]::DownloadFile
FtpWebRequest = [System.Net.WebRequestMethods+Ftp]::DownloadFile
HttpWebRequest = [System.Net.WebRequestMethods+Http]::Get
}
Script = $script
}
try {
Invoke-AnsibleWebRequest @invoke_args @RequestParams
} catch {
$Module.FailJson("Error when getting the remote checksum from '$Uri'. $($_.Exception.Message)", $_)
}
}
Function Compare-ModifiedFile {
<#
.SYNOPSIS
Compares the remote URI resource against the local Dest resource. Will
return true if the LastWriteTime/LastModificationDate of the remote is
newer than the local resource date.
#>
param(
[Parameter(Mandatory=$true)][Ansible.Basic.AnsibleModule]$Module,
[Parameter(Mandatory=$true)][Uri]$Uri,
[Parameter(Mandatory=$true)][String]$Dest,
[Hashtable]$RequestParams
)
$dest_last_mod = (Get-AnsibleItem -Path $Dest).LastWriteTimeUtc
# If the URI is a file we don't need to go through the whole WebRequest
if ($Uri.IsFile) {
$src_last_mod = (Get-AnsibleItem -Path $Uri.AbsolutePath).LastWriteTimeUtc
} else {
$invoke_args = @{
Module = $Module
Uri = $Uri
Method = @{
FtpWebRequest = [System.Net.WebRequestMethods+Ftp]::GetDateTimestamp
HttpWebRequest = [System.Net.WebRequestMethods+Http]::Head
}
Script = { param($Response, $Stream); $Response.LastModified }
}
try {
$src_last_mod = Invoke-AnsibleWebRequest @invoke_args @RequestParams
} catch {
$Module.FailJson("Error when requesting 'Last-Modified' date from '$Uri'. $($_.Exception.Message)", $_)
}
}
# Return $true if the Uri LastModification date is newer than the Dest LastModification date
((Get-Date -Date $src_last_mod).ToUniversalTime() -gt $dest_last_mod)
}
Function Get-Checksum {
param(
[Parameter(Mandatory=$true)][String]$Path,
[String]$Algorithm = "sha1"
)
switch ($Algorithm) {
'md5' { $sp = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider }
'sha1' { $sp = New-Object -TypeName System.Security.Cryptography.SHA1CryptoServiceProvider }
'sha256' { $sp = New-Object -TypeName System.Security.Cryptography.SHA256CryptoServiceProvider }
'sha384' { $sp = New-Object -TypeName System.Security.Cryptography.SHA384CryptoServiceProvider }
'sha512' { $sp = New-Object -TypeName System.Security.Cryptography.SHA512CryptoServiceProvider }
}
$fs = [System.IO.File]::Open($Path, [System.IO.Filemode]::Open, [System.IO.FileAccess]::Read,
[System.IO.FileShare]::ReadWrite)
try {
$hash = [System.BitConverter]::ToString($sp.ComputeHash($fs)).Replace("-", "").ToLower()
} finally {
$fs.Dispose()
}
return $hash
}
Function Invoke-DownloadFile {
param(
[Parameter(Mandatory=$true)][Ansible.Basic.AnsibleModule]$Module,
[Parameter(Mandatory=$true)][Uri]$Uri,
[Parameter(Mandatory=$true)][String]$Dest,
[String]$Checksum,
[String]$ChecksumAlgorithm,
[Hashtable]$RequestParams
)
# Check $dest parent folder exists before attempting download, which avoids unhelpful generic error message.
$dest_parent = Split-Path -LiteralPath $Dest
if (-not (Test-Path -LiteralPath $dest_parent -PathType Container)) {
$module.FailJson("The path '$dest_parent' does not exist for destination '$Dest', or is not visible to the current user. Ensure download destination folder exists (perhaps using win_file state=directory) before win_get_url runs.")
}
$download_script = {
param($Response, $Stream)
# Download the file to a temporary directory so we can compare it
$tmp_dest = Join-Path -Path $Module.Tmpdir -ChildPath ([System.IO.Path]::GetRandomFileName())
$fs = [System.IO.File]::Create($tmp_dest)
try {
$Stream.CopyTo($fs)
$fs.Flush()
} finally {
$fs.Dispose()
}
$tmp_checksum = Get-Checksum -Path $tmp_dest -Algorithm $ChecksumAlgorithm
$Module.Result.checksum_src = $tmp_checksum
# If the checksum has been set, verify the checksum of the remote against the input checksum.
if ($Checksum -and $Checksum -ne $tmp_checksum) {
$Module.FailJson(("The checksum for {0} did not match '{1}', it was '{2}'" -f $Uri, $Checksum, $tmp_checksum))
}
$download = $true
if (Test-Path -LiteralPath $Dest) {
# Validate the remote checksum against the existing downloaded file
$dest_checksum = Get-Checksum -Path $Dest -Algorithm $ChecksumAlgorithm
# If we don't need to download anything, save the dest checksum so we don't waste time calculating it
# again at the end of the script
if ($dest_checksum -eq $tmp_checksum) {
$download = $false
$Module.Result.checksum_dest = $dest_checksum
$Module.Result.size = (Get-AnsibleItem -Path $Dest).Length
}
}
if ($download) {
Copy-Item -Path $tmp_dest -Destination $Dest -Force -WhatIf:$Module.CheckMode > $null
$Module.Result.changed = $true
}
}
$invoke_args = @{
Module = $Module
Uri = $Uri
Method = @{
FileWebRequest = [System.Net.WebRequestMethods+File]::DownloadFile
FtpWebRequest = [System.Net.WebRequestMethods+Ftp]::DownloadFile
HttpWebRequest = [System.Net.WebRequestMethods+Http]::Get
}
Script = $download_script
}
$module_start = Get-Date
try {
Invoke-AnsibleWebRequest @invoke_args @RequestParams
} catch {
$Module.FailJson("Unknown error downloading '$Uri' to '$Dest': $($_.Exception.Message)", $_)
} finally {
$Module.Result.elapsed = ((Get-Date) - $module_start).TotalSeconds
}
}
if (-not $use_proxy -and ($proxy_url -or $proxy_username -or $proxy_password)) {
$module.Warn("Not using a proxy on request, however a 'proxy_url', 'proxy_username' or 'proxy_password' was defined.")
}
@ -224,6 +359,7 @@ if (Test-Path -LiteralPath $dest -PathType Container) {
# We have a trailing path separator
$module.FailJson("The destination path '$dest' does not exist, or is not visible to the current user. Ensure download destination folder exists (perhaps using win_file state=directory) before win_get_url runs.")
}
$module.Result.dest = $dest
# Enable TLS1.1/TLS1.2 if they're available but disabled (eg. .NET 4.5)
@ -236,22 +372,53 @@ if ([Net.SecurityProtocolType].GetMember("Tls12").Count -gt 0) {
}
[Net.ServicePointManager]::SecurityProtocol = $security_protocols
if ($force -or -not (Test-Path -LiteralPath $dest)) {
$request_params = @{
Credential = $credentials
Headers = $headers
Timeout = $timeout
UseProxy = $use_proxy
Proxy = $proxy
}
Download-File -module $module -url $url -dest $dest -credentials $credentials `
-headers $headers -timeout $timeout -use_proxy $use_proxy -proxy $proxy
} else {
$is_modified = CheckModified-File -module $module -url $url -dest $dest -credentials $credentials `
-headers $headers -timeout $timeout -use_proxy $use_proxy -proxy $proxy
if ($is_modified) {
Download-File -module $module -url $url -dest $dest -credentials $credentials `
-headers $headers -timeout $timeout -use_proxy $use_proxy -proxy $proxy
if ($checksum) {
$checksum = $checksum.Trim().toLower()
}
if ($checksum_algorithm) {
$checksum_algorithm = $checksum_algorithm.Trim().toLower()
}
if ($checksum_url) {
$checksum_url = $checksum_url.Trim().toLower()
}
# Check for case $checksum variable contain url. If yes, get file data from url and replace original value in $checksum
if ($checksum_url) {
$checksum_uri = [System.Uri]$checksum_url
if ($checksum_uri.Scheme -notin @("file", "ftp", "http", "https")) {
$module.FailJson("Unsupported 'checksum_url' value for '$dest': '$checksum_url'")
}
$checksum = Get-ChecksumFromUri -Module $Module -Uri $checksum_uri -SourceUri $url -RequestParams $request_params
}
if ($force -or -not (Test-Path -LiteralPath $dest)) {
# force=yes or dest does not exist, download the file
# Note: Invoke-DownloadFile will compare the checksums internally if dest exists
Invoke-DownloadFile -Module $module -Uri $url -Dest $dest -Checksum $checksum `
-ChecksumAlgorithm $checksum_algorithm -RequestParams $request_params
} else {
# force=no, we want to check the last modified dates and only download if they don't match
$is_modified = Compare-ModifiedFile -Module $module -Uri $url -Dest $dest -RequestParams $request_params
if ($is_modified) {
Invoke-DownloadFile -Module $module -Uri $url -Dest $dest -Checksum $checksum `
-ChecksumAlgorithm $checksum_algorithm -RequestParams $request_params
}
}
if ((-not $module.Result.ContainsKey("checksum_dest")) -and (Test-Path -LiteralPath $dest)) {
# Calculate the dest file checksum if it hasn't already been done
$module.Result.checksum_dest = Get-Checksum -Path $dest -Algorithm $checksum_algorithm
$module.Result.size = (Get-AnsibleItem -Path $dest).Length
}
$module.ExitJson()

View file

@ -34,7 +34,7 @@ options:
required: yes
force:
description:
- If C(yes), will always download the file. If C(no), will only
- If C(yes), will download the file every time and replace the file if the contents change. If C(no), will only
download the file if it does not exist or the remote file has been
modified more recently than the local file.
- This works by sending an http HEAD request to retrieve last modified
@ -73,6 +73,36 @@ options:
type: bool
default: yes
version_added: '2.4'
checksum:
description:
- If a I(checksum) is passed to this parameter, the digest of the
destination file will be calculated after it is downloaded to ensure
its integrity and verify that the transfer completed successfully.
- This option cannot be set with I(checksum_url).
type: str
version_added: "2.8"
checksum_algorithm:
description:
- Specifies the hashing algorithm used when calculating the checksum of
the remote and destination file.
type: str
choices:
- md5
- sha1
- sha256
- sha385
- sha512
default: sha1
version_added: "2.8"
checksum_url:
description:
- Specifies a URL that contains the checksum values for the resource at
I(url).
- Like C(checksum), this is used to verify the integrity of the remote
transfer.
- This option cannot be set with I(checksum).
type: str
version_added: "2.8"
proxy_url:
description:
- The full URL of the proxy server to download through.
@ -105,6 +135,9 @@ notes:
- If your URL includes an escaped slash character (%2F) this module will convert it to a real slash.
This is a result of the behaviour of the System.Uri class as described in
L(the documentation,https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/network/schemesettings-element-uri-settings#remarks).
- Since Ansible 2.8, the module will skip reporting a change if the remote
checksum is the same as the local local even when C(force=yes). This is to
better align with M(get_url).
seealso:
- module: get_url
- module: uri
@ -140,6 +173,22 @@ EXAMPLES = r'''
dest: '%TEMP%\ftp-file.txt'
url_username: ftp-user
url_password: ftp-password
- name: Download src with sha256 checksum url
win_get_url:
url: http://www.example.com/earthrise.jpg
dest: C:\temp\earthrise.jpg
checksum_url: http://www.example.com/sha256sum.txt
checksum_algorithm: sha256
force: True
- name: Download src with sha256 checksum url
win_get_url:
url: http://www.example.com/earthrise.jpg
dest: C:\temp\earthrise.jpg
checksum: a97e6837f60cec6da4491bab387296bbcd72bdba
checksum_algorithm: sha1
force: True
'''
RETURN = r'''
@ -148,11 +197,26 @@ dest:
returned: always
type: str
sample: C:\Users\RandomUser\earthrise.jpg
checksum_dest:
description: <algorithm> checksum of the file after the download
returned: success and dest has been downloaded
type: str
sample: 6e642bb8dd5c2e027bf21dd923337cbb4214f827
checksum_src:
description: <algorithm> checksum of the remote resource
returned: force=yes or dest did not exist
type: str
sample: 6e642bb8dd5c2e027bf21dd923337cbb4214f827
elapsed:
description: The elapsed seconds between the start of poll and the end of the module.
returned: always
type: float
sample: 2.1406487
size:
description: size of the dest file
returned: success
type: int
sample: 1220
url:
description: requested url
returned: always

View file

@ -2,3 +2,6 @@
test_win_get_url_path: '{{win_output_dir}}\win_get_url'
test_win_get_url_host: www.redhat.com
test_win_get_url_env_var: WIN_GET_URL
remote_tmp_path: '{{win_output_dir}}\win_get_url_dest'
remote_http_path: '{{test_win_get_url_path}}\http'

View file

@ -1,8 +1,12 @@
---
- name: ensure testing folder is present
win_file:
path: '{{test_win_get_url_path}}'
path: '{{ item }}'
state: directory
loop:
- '{{ test_win_get_url_path }}'
- '{{ remote_http_path }}'
- '{{ remote_tmp_path }}'
- name: copy across testing files
win_copy:
@ -41,6 +45,9 @@
- name: run FTP tests
include_tasks: tests_ftp.yml
- name: run checksum tests
include_tasks: tests_checksum.yml
always:
- name: remove SlimFTPd service
win_service:
@ -53,7 +60,10 @@
level: machine
state: absent
- name: remove testing folder
- name: remove testing folders
win_file:
path: '{{test_win_get_url_path}}'
path: '{{ item }}'
state: absent
loop:
- '{{ test_win_get_url_path }}'
- '{{ remote_tmp_path }}'

View file

@ -0,0 +1,202 @@
---
- name: create src file
win_copy:
dest: '{{ remote_http_path }}\27617.txt'
content: 'ptux'
- name: create sha1 checksum file of src
win_copy:
dest: '{{ remote_http_path }}\sha1sum.txt'
content: 'a97e6837f60cec6da4491bab387296bbcd72bdba 27617.txt'
- name: add sha1 checksum not going to be downloaded
win_lineinfile:
dest: '{{ remote_http_path }}\sha1sum.txt'
line: '{{ item }}'
loop:
- '3911340502960ca33aece01129234460bfeb2791 not_target1.txt'
- '1b4b6adf30992cedb0f6edefd6478ff0a593b2e4 not_target2.txt'
- name: create sha256 checksum file of src
win_copy:
dest: '{{ remote_http_path }}\sha256sum.txt'
content: 'b1b6ce5073c8fac263a8fc5edfffdbd5dec1980c784e09c5bc69f8fb6056f006. 27617.txt'
- name: add sha256 checksum not going to be downloaded
win_lineinfile:
dest: '{{ remote_http_path }}\sha256sum.txt'
line: '{{ item }}'
loop:
- '30949cc401e30ac494d695ab8764a9f76aae17c5d73c67f65e9b558f47eff892 not_target1.txt'
- 'd0dbfc1945bc83bf6606b770e442035f2c4e15c886ee0c22fb3901ba19900b5b not_target2.txt'
- name: create sha256 checksum file of src with a dot leading path
win_copy:
dest: '{{ remote_http_path }}\sha256sum_with_dot.txt'
content: 'b1b6ce5073c8fac263a8fc5edfffdbd5dec1980c784e09c5bc69f8fb6056f006. ./27617.txt'
- name: add sha256 checksums with dot leading path not going to be downloaded
win_lineinfile:
dest: '{{ remote_http_path }}\sha256sum_with_dot.txt'
line: '{{ item }}'
loop:
- '30949cc401e30ac494d695ab8764a9f76aae17c5d73c67f65e9b558f47eff892 ./not_target1.txt'
- 'd0dbfc1945bc83bf6606b770e442035f2c4e15c886ee0c22fb3901ba19900b5b ./not_target2.txt'
- name: copy files to ftp
win_copy:
src: '{{ remote_http_path }}\'
dest: '{{ test_win_get_url_path }}\ftp\{{ item }}\'
remote_src: yes
loop: '{{ dest_folders }}'
vars:
dest_folders:
- anon
- user
- user-pass
- name: download src with sha1 checksum url
win_get_url:
url: 'ftp://localhost/anon/27617.txt'
dest: '{{ remote_tmp_path }}'
checksum_url: 'ftp://localhost/anon/sha1sum.txt'
force: True
register: result_sha1
- name: download src with sha1 checksum value
win_get_url:
url: 'ftp://localhost/anon/27617.txt'
dest: '{{ remote_tmp_path }}'
checksum: 'a97e6837f60cec6da4491bab387296bbcd72bdba'
force: True
register: result_sha1_alt
- win_stat:
path: '{{ remote_tmp_path }}\27617.txt'
register: stat_result_sha1
- name: download src with sha256 checksum url
win_get_url:
url: 'ftp://localhost/anon/27617.txt'
dest: '{{ remote_tmp_path }}\27617sha256.txt'
checksum_url: 'ftp://localhost/anon/sha256sum.txt'
checksum_algorithm: sha256
force: True
register: result_sha256
- win_stat:
path: '{{ remote_tmp_path }}\27617.txt'
register: stat_result_sha256
- name: download src with sha256 checksum url with dot leading paths
win_get_url:
url: 'ftp://localhost/anon/27617.txt'
dest: '{{ remote_tmp_path }}\27617sha256_with_dot.txt'
checksum_url: 'ftp://localhost/anon/sha256sum_with_dot.txt'
checksum_algorithm: sha256
force: True
register: result_sha256_with_dot
- win_stat:
path: '{{ remote_tmp_path }}\27617sha256_with_dot.txt'
register: stat_result_sha256_with_dot
- name: Assert that the file was downloaded
assert:
that:
- result_sha1 is changed
- result_sha1.checksum_dest == 'a97e6837f60cec6da4491bab387296bbcd72bdba'
- result_sha1_alt is succeeded
- not result_sha1_alt is changed
- result_sha1_alt.checksum_dest == 'a97e6837f60cec6da4491bab387296bbcd72bdba'
- result_sha256 is changed
- result_sha256_with_dot is changed
- stat_result_sha1.stat.exists | bool
- stat_result_sha256.stat.exists | bool
- stat_result_sha256_with_dot.stat.exists | bool
# Check download with force: False
- name: download src with sha1 checksum url
win_get_url:
url: 'ftp://localhost/anon/27617.txt'
dest: '{{ remote_tmp_path }}'
checksum_url: 'ftp://localhost/anon/sha1sum.txt'
checksum_algorithm: sha1
force: False
register: result_sha1_no_force
- name: download src with sha256 checksum url
win_get_url:
url: 'ftp://localhost/anon/27617.txt'
dest: '{{ remote_tmp_path }}/27617sha256.txt'
checksum_url: 'ftp://localhost/anon/sha256sum.txt'
checksum_algorithm: sha256
force: False
register: result_sha256_no_force
- name: assert download single file with force no
assert:
that:
- result_sha1_no_force is not changed
- result_sha256_no_force is not changed
# Check download with check_mode: True
- name: download src with sha1 checksum url | checkmode
win_get_url:
url: 'ftp://localhost/anon/27617.txt'
dest: '{{ remote_tmp_path }}'
checksum_url: 'ftp://localhost/anon/sha1sum.txt'
checksum_algorithm: sha1
check_mode: True
register: result_sha1_check_mode
- name: download src with sha256 checksum url | checkmode
win_get_url:
url: 'ftp://localhost/anon/27617.txt'
dest: '{{ remote_tmp_path }}\27617sha256.txt'
checksum_url: 'ftp://localhost/anon/sha256sum.txt'
checksum_algorithm: sha256
check_mode: True
register: result_sha256_check_mode
- name: assert download single file with force no
assert:
that:
- result_sha1_check_mode.checksum_dest == 'a97e6837f60cec6da4491bab387296bbcd72bdba'
- result_sha256_check_mode.checksum_dest == 'b1b6ce5073c8fac263a8fc5edfffdbd5dec1980c784e09c5bc69f8fb6056f006'
- result_sha1_check_mode.checksum_src | length
- result_sha256_check_mode.checksum_src | length
# Check download with wrong checksum
- name: download src with sha1 checksum value | fail
win_get_url:
url: 'ftp://localhost/anon/27617.txt'
dest: '{{ remote_tmp_path }}'
checksum: 'redacted'
checksum_algorithm: sha1
failed_when:
- '"did not match" not in result_sha1_failed.msg'
- '"The checksum" not in result_sha1_failed.msg'
- '"Unknown error downloading" not in result_sha1_failed.msg'
register: result_sha1_failed
- name: download src with sha256 checksum value | fail
win_get_url:
url: 'ftp://localhost/anon/27617.txt'
dest: '{{ remote_tmp_path }}\27617sha256.txt'
checksum: 'redacted'
checksum_algorithm: sha256
failed_when:
- '"did not match" not in result_sha256_failed.msg'
- '"The checksum" not in result_sha256_failed.msg'
- '"Unknown error downloading" not in result_sha256_failed.msg'
register: result_sha256_failed
- name: assert failed downloads
assert:
that:
- result_sha1_failed is succeeded
- result_sha256_failed is succeeded

View file

@ -39,7 +39,16 @@
- http_download.dest
- http_download_result.stat.exists
# TODO: add check for idempotent run once it is added with force: yes
- name: download single file (idempotent)
win_get_url:
url: https://{{test_win_get_url_host}}
dest: '{{test_win_get_url_path}}\web.html'
register: http_download_again
- name: assert download single file (idempotent)
assert:
that:
- not http_download_again is changed
- name: download single file with force no
win_get_url:
@ -53,8 +62,10 @@
that:
- http_download_no_force is not changed
- name: manually change last modified time on FTP source to older datetime
win_shell: (Get-Item -Path '{{test_win_get_url_path}}\web.html').LastWriteTime = (Get-Date -Date "01/01/1970")
- name: manually change the content and last modified time on FTP source to older datetime
win_shell: |
Set-Content -Path '{{test_win_get_url_path}}\web.html' -Value 'abc'
(Get-Item -Path '{{test_win_get_url_path}}\web.html').LastWriteTime = (Get-Date -Date "01/01/1970")
- name: download newer file with force no
win_get_url:

View file

@ -38,7 +38,7 @@ lib/ansible/modules/windows/win_find.ps1 PSAvoidUsingEmptyCatchBlock
lib/ansible/modules/windows/win_find.ps1 PSAvoidUsingWMICmdlet
lib/ansible/modules/windows/win_firewall_rule.ps1 PSAvoidUsingCmdletAliases
lib/ansible/modules/windows/win_firewall_rule.ps1 PSUseApprovedVerbs
lib/ansible/modules/windows/win_get_url.ps1 PSUseApprovedVerbs
lib/ansible/modules/windows/win_get_url.ps1 PSUsePSCredentialType # Credential param can take a base64 encoded string as well as a PSCredential
lib/ansible/modules/windows/win_hotfix.ps1 PSUseApprovedVerbs
lib/ansible/modules/windows/win_iis_webbinding.ps1 PSUseApprovedVerbs
lib/ansible/modules/windows/win_iis_website.ps1 PSAvoidUsingCmdletAliases