From 0aba04fdad7283eb9c7fee81f49cda3b18ef3f84 Mon Sep 17 00:00:00 2001 From: Dag Wieers Date: Mon, 19 Jun 2017 18:30:08 +0200 Subject: [PATCH] win_uri: Add integration tests, new functionality... (#25373) This is a cleanup of the win_uri module to make it feature-complete. This PR includes: - Added check-mode support - Add as many options from the uri module as possible - Added creates - Added follow_redirects - Added maximum_redirection - Added password - Added removes - Added return_content - Added status_code - Added timeout - Added user - Added validate_certs - Fixed list-handling for comma-separated strings - Added basic integration tests (should come from uri module) --- lib/ansible/module_utils/powershell.ps1 | 2 +- lib/ansible/modules/windows/win_uri.ps1 | 124 ++++++++++++----- lib/ansible/modules/windows/win_uri.py | 129 +++++++++++++----- .../targets/uri/files/testserver.ps1 | 36 +++++ 4 files changed, 225 insertions(+), 66 deletions(-) create mode 100644 test/integration/targets/uri/files/testserver.ps1 diff --git a/lib/ansible/module_utils/powershell.ps1 b/lib/ansible/module_utils/powershell.ps1 index 0823e98573..62cf3476fe 100644 --- a/lib/ansible/module_utils/powershell.ps1 +++ b/lib/ansible/module_utils/powershell.ps1 @@ -222,7 +222,7 @@ Function Get-AnsibleParam($obj, $name, $default = $null, $resultobj = @{}, $fail # Nothing to do } elseif ($value -is [string]) { # Convert string type to real Powershell array - $value = $value -split "," + $value = $value.Split(",").Trim() } else { Fail-Json -obj $resultobj -message "Get-AnsibleParam: Parameter $name is not a YAML list." } diff --git a/lib/ansible/modules/windows/win_uri.ps1 b/lib/ansible/modules/windows/win_uri.ps1 index 2cd53e93b8..d84b224a18 100644 --- a/lib/ansible/modules/windows/win_uri.ps1 +++ b/lib/ansible/modules/windows/win_uri.ps1 @@ -2,6 +2,7 @@ # This file is part of Ansible # # Copyright 2015, Corwin Brown +# Copyright 2017, Dag Wieers # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -20,80 +21,137 @@ # POWERSHELL_COMMON $ErrorActionPreference = "Stop" - -# Functions ############################################### +$safe_methods = @("GET", "HEAD") +$content_keys = @("Content", "Images", "InputFields", "Links", "RawContent") Function ConvertTo-SnakeCase($input_string) { $snake_case = $input_string -csplit "(? +# (c) 2017, Dag Wieers # # This file is part of Ansible # @@ -25,57 +26,121 @@ ANSIBLE_METADATA = {'metadata_version': '1.0', 'status': ['preview'], 'supported_by': 'community'} - DOCUMENTATION = r''' --- module: win_uri -version_added: "2.1" -short_description: Interacts with webservices. +version_added: '2.1' +short_description: Interacts with webservices description: - - Interacts with HTTP and HTTPS web services and supports Digest, Basic and WSSE HTTP authentication mechanisms. +- Interacts with FTP, HTTP and HTTPS web services. +- Supports Digest, Basic and WSSE HTTP authentication mechanisms. options: url: description: - - HTTP or HTTPS URL in the form of (http|https)://host.domain:port/path - required: True + - Supports FTP, HTTP or HTTPS URLs in the form of (ftp|http|https)://host.domain:port/path. + - Also supports file:/// URLs through Invoke-WebRequest. + required: yes method: description: - - The HTTP Method of the request or response. + - The HTTP Method of the request or response. + choices: [ CONNECT, DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT, REFRESH, TRACE ] default: GET - choices: - - GET - - POST - - PUT - - HEAD - - DELETE - - OPTIONS - - PATCH - - TRACE - - CONNECT - - REFRESH content_type: description: - - Sets the "Content-Type" header. + - Sets the "Content-Type" header. body: description: - - The body of the HTTP request/response to the web service. - dest: - version_added: "2.3" + - The body of the HTTP request/response to the web service. + user: description: - - Output the response body to a file. + - Username to use for authentication. + version_added: '2.4' + password: + description: + - Password to use for authentication. + version_added: '2.4' + dest: + description: + - Output the response body to a file. + version_added: '2.3' headers: description: - - 'Key Value pairs for headers. Example "Host: www.somesite.com"' + - 'Key Value pairs for headers. Example "Host: www.somesite.com"' use_basic_parsing: description: - - This module relies upon 'Invoke-WebRequest', which by default uses the Internet Explorer Engine to parse a webpage. There's an edge-case where if a - user hasn't run IE before, this will fail. The only advantage to using the Internet Explorer praser is that you can traverse the DOM in a - powershell script. That isn't useful for Ansible, so by default we toggle 'UseBasicParsing'. However, you can toggle that off here. - choices: - - True - - False - default: True -author: Corwin Brown (@blakfeld) + - This module relies upon 'Invoke-WebRequest', which by default uses the Internet Explorer Engine to parse a webpage. + - There's an edge-case where if a user hasn't run IE before, this will fail. + - The only advantage to using the Internet Explorer praser is that you can traverse the DOM in a powershell script. + - That isn't useful for Ansible, so by default we toggle 'UseBasicParsing'. However, you can toggle that off here. + type: bool + default: 'yes' + creates: + description: + - A filename, when it already exists, this step will be skipped. + version_added: '2.4' + removes: + description: + - A filename, when it does not exist, this step will be skipped. + version_added: '2.4' + return_content: + description: + - Whether or not to return the body of the request as a "content" key in + the dictionary result. If the reported Content-type is + "application/json", then the JSON is additionally loaded into a key + called C(json) in the dictionary results. + type: bool + default: 'no' + version_added: '2.4' + status_code: + description: + - A valid, numeric, HTTP status code that signifies success of the request. + - Can also be comma separated list of status codes. + default: 200 + version_added: '2.4' + timeout: + description: + - Specifies how long the request can be pending before it times out (in seconds). + - The value 0 (zero) specifies an indefinite time-out. + - A Domain Name System (DNS) query can take up to 15 seconds to return or time out. + If your request contains a host name that requires resolution, and you set + C(timeout) to a value greater than zero, but less than 15 seconds, it can + take 15 seconds or more before your request times out. + default: 30 + version_added: '2.4' + follow_redirects: + description: + - Whether or not the C(win_uri) module should follow redirects. + - C(all) will follow all redirects. + - C(none) will not follow any redirects. + - C(safe) will follow only "safe" redirects, where "safe" means that the client is only + doing a C(GET) or C(HEAD) on the URI to which it is being redirected. + choices: [ all, none, safe ] + default: safe + version_added: '2.4' + maximum_redirection: + description: + - Specifies how many times C(win_uri) redirects a connection to an alternate + Uniform Resource Identifier (URI) before the connection fails. + - If C(maximum_redirection) is set to 0 (zero) + or C(follow_redirects) is set to C(none), + or set to C(safe) when not doing C(GET) or C(HEAD) it prevents all redirection. + default: 5 + version_added: '2.4' + validate_certs: + description: + - If C(no), SSL certificates will not be validated. This should only + set to C(no) used on personally controlled sites using self-signed + certificates. + type: bool + default: 'yes' + version_added: '2.4' + client_cert: + description: + - Specifies the client certificate(.pfx) that is used for a secure web request. + version_added: '2.4' +author: +- Corwin Brown (@blakfeld) +- Dag Wieers (@dagwieers) ''' EXAMPLES = r''' diff --git a/test/integration/targets/uri/files/testserver.ps1 b/test/integration/targets/uri/files/testserver.ps1 new file mode 100644 index 0000000000..5a0655516c --- /dev/null +++ b/test/integration/targets/uri/files/testserver.ps1 @@ -0,0 +1,36 @@ +param ( + [int]$port = 8000, +) + +$listener = New-Object Net.HttpListener +$listener.Prefixes.Add("http://+:$port/") +$listener.Start() + +try { + while ($listener.IsListening) { + # process received request + $context = $listener.GetContext() + $Request = $context.Request + $Response = $context.Response + #$Response.Headers.Add("Content-Type","text/plain") + + $received = '{0} {1}' -f $Request.httpmethod, $Request.url.localpath + + # is there HTML content for this URL? + $html = $htmlcontents[$received] + if ($html -eq $null) { + $Response.statuscode = 404 + $html = 'Oops, the page is not available!' + } + + # return the HTML to the caller + $buffer = [Text.Encoding]::UTF8.GetBytes($html) + $Response.ContentLength64 = $buffer.length + $Response.OutputStream.Write($buffer, 0, $buffer.length) + + $Response.Close() + } +} finally { + $listener.Stop() + $listener.Close() +}