diff options
author | Kevin Subileau <github@kevinsubileau.fr> | 2018-09-27 05:15:04 +0200 |
---|---|---|
committer | Jordan Borean <jborean93@gmail.com> | 2018-09-27 13:15:04 +1000 |
commit | ac9d506a617176510e39f8d69a020855527c19fa (patch) | |
tree | 345cc62da55467bdc1ac43b73f9c3430be88b089 | |
parent | 69594c5370cf2be67cf03eaf4d067303e8819a12 (diff) | |
download | ansible-ac9d506a617176510e39f8d69a020855527c19fa.tar.gz |
win_nssm: tests and several bug fixes (#44755)
* win_nssm: add failing tests for issue #44079
* win_nssm: use Run-Command instead of Invoke-Expression to prevent interpretation issue
Fix #44079
* win_nssm: add more failing tests
These tests highlight several issues with this module:
* Service not started when state=started
* Errors with app_parameters (see #25265)
* Exception when passing several dependencies separated by comma as specified in doc
* win_nssm: fix service not started when state=started
Nssm status returns a multiline output that doesn't match any of the strict patterns in the switch statement.
* win_nssm: fix incorrect separator in doc for service dependencies
The dependencies parameter works with space as separator, but not with comma as shown in the documentation
* win_nssm: fix error with app_parameters parameter
Fix #25265
* win_nssm: add idempotence tests
* win_nssm: fix several idempotence issues and misbehaviors
Add missing space between arguments when app_parameters contains several keys.
Use Argv-ToString and Escape-Argument to improve arguments handling (parameters with quotes, backslashes or spaces).
* win_nssm: test parameters with spaces, quotes or backslashes
* win_nssm: restore comma as separator for service dependencies
Revert commit ddd4b4b
* win_nssm: restore support of string as dict form for app_parameters and remove support of literal YAML dict
* win_nssm: wrong variable in tests
-rw-r--r-- | lib/ansible/modules/windows/win_nssm.ps1 | 276 | ||||
-rw-r--r-- | lib/ansible/modules/windows/win_nssm.py | 24 | ||||
-rw-r--r-- | test/integration/targets/win_nssm/aliases | 1 | ||||
-rw-r--r-- | test/integration/targets/win_nssm/defaults/main.yml | 4 | ||||
-rw-r--r-- | test/integration/targets/win_nssm/tasks/main.yml | 44 | ||||
-rw-r--r-- | test/integration/targets/win_nssm/tasks/tests.yml | 256 | ||||
-rw-r--r-- | test/sanity/pslint/ignore.txt | 1 |
7 files changed, 447 insertions, 159 deletions
diff --git a/lib/ansible/modules/windows/win_nssm.ps1 b/lib/ansible/modules/windows/win_nssm.ps1 index c10b6ce2b2..0a9cb420a9 100644 --- a/lib/ansible/modules/windows/win_nssm.ps1 +++ b/lib/ansible/modules/windows/win_nssm.ps1 @@ -6,6 +6,8 @@ # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) #Requires -Module Ansible.ModuleUtils.Legacy +#Requires -Module Ansible.ModuleUtils.ArgvParser +#Requires -Module Ansible.ModuleUtils.CommandUtil $ErrorActionPreference = "Stop" @@ -19,13 +21,13 @@ $name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $tru $state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present","absent","started","stopped","restarted" -resultobj $result $application = Get-AnsibleParam -obj $params -name "application" -type "str" -$appParameters = Get-AnsibleParam -obj $params -name "app_parameters" -type "str" +$appParameters = Get-AnsibleParam -obj $params -name "app_parameters" $appParametersFree = Get-AnsibleParam -obj $params -name "app_parameters_free_form" -type "str" $startMode = Get-AnsibleParam -obj $params -name "start_mode" -type "str" -default "auto" -validateset "auto","delayed","manual","disabled" -resultobj $result $stdoutFile = Get-AnsibleParam -obj $params -name "stdout_file" -type "str" $stderrFile = Get-AnsibleParam -obj $params -name "stderr_file" -type "str" -$dependencies = Get-AnsibleParam -obj $params -name "dependencies" -type "str" +$dependencies = Get-AnsibleParam -obj $params -name "dependencies" -type "list" $user = Get-AnsibleParam -obj $params -name "user" -type "str" $password = Get-AnsibleParam -obj $params -name "password" -type "str" @@ -34,9 +36,10 @@ if (($appParameters -ne $null) -and ($appParametersFree -ne $null)) { Fail-Json $result "Use either app_parameters or app_parameteres_free_form, but not both" } +if (($appParameters -ne $null) -and ($appParameters -isnot [string])) { + Fail-Json -obj $result -message "The app_parameters parameter must be a string representing a dictionary." +} -#abstract the calling of nssm because some PowerShell environments -#mishandle its stdout(which is Unicode) as UTF8 Function Nssm-Invoke { [CmdletBinding()] @@ -44,21 +47,10 @@ Function Nssm-Invoke [Parameter(Mandatory=$true)] [string]$cmd ) - Try { - $encodingWas = [System.Console]::OutputEncoding - [System.Console]::OutputEncoding = [System.Text.Encoding]::Unicode - $nssmOutput = invoke-expression "nssm $cmd" - return $nssmOutput - } - Catch { - $ErrorMessage = $_.Exception.Message - Fail-Json $result "an exception occurred when invoking NSSM: $ErrorMessage" - } - Finally { - # Set the console encoding back to what it was - [System.Console]::OutputEncoding = $encodingWas - } + $nssm_result = Run-Command -command "nssm $cmd" + + return $nssm_result } Function Service-Exists @@ -84,15 +76,15 @@ Function Nssm-Remove { if ((Get-Service -Name $name).Status -ne "Stopped") { $cmd = "stop ""$name""" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd } $cmd = "remove ""$name"" confirm" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd - if ($LastExitCode -ne 0) + if ($nssm_result.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $nssm_result.stderr Throw "Error removing service ""$name""" } @@ -122,12 +114,12 @@ Function Nssm-Install if (!(Service-Exists -name $name)) { - $results = Nssm-Invoke "install ""$name"" ""$application""" + $nssm_result = Nssm-Invoke "install ""$name"" ""$application""" - if ($LastExitCode -ne 0) + if ($nssm_result.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $nssm_result.stderr Throw "Error installing service ""$name""" } @@ -135,25 +127,25 @@ Function Nssm-Install $result.changed = $true } else { - $results = Nssm-Invoke "get ""$name"" Application" + $nssm_result = Nssm-Invoke "get ""$name"" Application" - if ($LastExitCode -ne 0) + if ($nssm_result.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $nssm_result.stderr Throw "Error installing service ""$name""" } - if ($results -cnotlike $application) + if ($nssm_result.stdout.split("`n`r")[0] -ne $application) { $cmd = "set ""$name"" Application ""$application""" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd - if ($LastExitCode -ne 0) + if ($nssm_result.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $nssm_result.stderr Throw "Error installing service ""$name""" } $result.application = "$application" @@ -166,14 +158,14 @@ Function Nssm-Install if ($result.changed) { $applicationPath = (Get-Item $application).DirectoryName - $cmd = "nssm set ""$name"" AppDirectory ""$applicationPath""" + $cmd = "set ""$name"" AppDirectory ""$applicationPath""" - $results = invoke-expression $cmd + $nssm_result = Nssm-Invoke $cmd - if ($LastExitCode -ne 0) + if ($nssm_result.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $nssm_result.stderr Throw "Error installing service ""$name""" } } @@ -193,26 +185,23 @@ Function ParseAppParameters() return ConvertFrom-StringData -StringData $escapedAppParameters } - Function Nssm-Update-AppParameters { [CmdletBinding()] param( [Parameter(Mandatory=$true)] [string]$name, - [Parameter(Mandatory=$true)] - [AllowEmptyString()] - [string]$appParameters, + $appParameters, [string]$appParametersFree ) $cmd = "get ""$name"" AppParameters" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd - if ($LastExitCode -ne 0) + if ($nssm_result.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $nssm_result.stderr Throw "Error updating AppParameters for service ""$name""" } @@ -220,29 +209,30 @@ Function Nssm-Update-AppParameters $appParamVals = @() $singleLineParams = "" - if ($appParameters) + if ($null -ne $appParameters) { $appParametersHash = ParseAppParameters -appParameters $appParameters - $appParametersHash.GetEnumerator() | - % { - $key = $($_.Name) - $val = $($_.Value) - - $appParamKeys += $key - $appParamVals += $val - - if ($key -eq "_") { - $singleLineParams = "$val " + $singleLineParams - } else { - $singleLineParams = $singleLineParams + "$key ""$val""" - } + $appParamsArray = @() + $appParametersHash.GetEnumerator() | foreach { + $key = $($_.Name) + $val = $($_.Value) + + $appParamKeys += $key + $appParamVals += $val + + if ($key -ne "_") { + $appParamsArray += $key } - $result.nssm_app_parameters_parsed = $appParametersHash + $appParamsArray += $val + } + $result.nssm_app_parameters_keys = $appParamKeys $result.nssm_app_parameters_vals = $appParamVals + + $singleLineParams = Argv-ToString -arguments $appParamsArray } - elseif ($appParametersFree) { + elseif ($null -ne $appParametersFree) { $result.nssm_app_parameters_free_form = $appParametersFree $singleLineParams = $appParametersFree } @@ -250,20 +240,18 @@ Function Nssm-Update-AppParameters $result.nssm_app_parameters = $appParameters $result.nssm_single_line_app_parameters = $singleLineParams - if ($results -ne $singleLineParams) + if ($nssm_result.stdout.split("`n`r")[0] -ne $singleLineParams) { - if ($appParameters -or $appParametersFree) - { - $cmd = "set ""$name"" AppParameters $singleLineParams" - } else { - $cmd = "set ""$name"" AppParameters '""""'" - } - $results = Nssm-Invoke $cmd + # Escape argument line to preserve possible quotes and spaces + $singleLineParams = Escape-Argument -argument $singleLineParams + $cmd = "set ""$name"" AppParameters $singleLineParams" + + $nssm_result = Nssm-Invoke $cmd - if ($LastExitCode -ne 0) + if ($nssm_result.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $nssm_result.stderr Throw "Error updating AppParameters for service ""$name""" } @@ -283,16 +271,16 @@ Function Nssm-Set-Output-Files ) $cmd = "get ""$name"" AppStdout" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd - if ($LastExitCode -ne 0) + if ($nssm_result.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $nssm_result.stderr Throw "Error retrieving existing stdout file for service ""$name""" } - if ($results -cnotlike $stdout) + if ($nssm_result.stdout.split("`n`r")[0] -ne $stdout) { if (!$stdout) { @@ -301,12 +289,12 @@ Function Nssm-Set-Output-Files $cmd = "set ""$name"" AppStdout $stdout" } - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd - if ($LastExitCode -ne 0) + if ($nssm_result.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $nssm_result.stderr Throw "Error setting stdout file for service ""$name""" } @@ -315,36 +303,36 @@ Function Nssm-Set-Output-Files } $cmd = "get ""$name"" AppStderr" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd - if ($LastExitCode -ne 0) + if ($nssm_result.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $nssm_result.stderr Throw "Error retrieving existing stderr file for service ""$name""" } - if ($results -cnotlike $stderr) + if ($nssm_result.stdout.split("`n`r")[0] -ne $stderr) { if (!$stderr) { $cmd = "reset ""$name"" AppStderr" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd - if ($LastExitCode -ne 0) + if ($nssm_result.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $nssm_result.stderr Throw "Error clearing stderr file setting for service ""$name""" } } else { $cmd = "set ""$name"" AppStderr $stderr" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd - if ($LastExitCode -ne 0) + if ($nssm_result.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $nssm_result.stderr Throw "Error setting stderr file for service ""$name""" } } @@ -359,27 +347,27 @@ Function Nssm-Set-Output-Files #set files to overwrite $cmd = "set ""$name"" AppStdoutCreationDisposition 2" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd $cmd = "set ""$name"" AppStderrCreationDisposition 2" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd #enable file rotation $cmd = "set ""$name"" AppRotateFiles 1" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd #don't rotate until the service restarts $cmd = "set ""$name"" AppRotateOnline 0" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd #both of the below conditions must be met before rotation will happen #minimum age before rotating $cmd = "set ""$name"" AppRotateSeconds 86400" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd #minimum size before rotating $cmd = "set ""$name"" AppRotateBytes 104858" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd } Function Nssm-Update-Credentials @@ -395,12 +383,12 @@ Function Nssm-Update-Credentials ) $cmd = "get ""$name"" ObjectName" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd - if ($LastExitCode -ne 0) + if ($nssm_result.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $nssm_result.stderr Throw "Error updating credentials for service ""$name""" } @@ -414,14 +402,14 @@ Function Nssm-Update-Credentials $fullUser = ".\" + $user } - If ($results -ne $fullUser) { + If ($nssm_result.stdout.split("`n`r")[0] -ne $fullUser) { $cmd = "set ""$name"" ObjectName $fullUser '$password'" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd - if ($LastExitCode -ne 0) + if ($nssm_result.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $nssm_result.stderr Throw "Error updating credentials for service ""$name""" } @@ -439,27 +427,35 @@ Function Nssm-Update-Dependencies [Parameter(Mandatory=$true)] [string]$name, [Parameter(Mandatory=$false)] - [string]$dependencies + $dependencies ) + if($null -eq $dependencies) { + # Don't make any change to dependencies if the parameter is omitted + return + } + $cmd = "get ""$name"" DependOnService" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd - if ($LastExitCode -ne 0) + if ($nssm_result.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $nssm_result.stderr Throw "Error updating dependencies for service ""$name""" } - If (($dependencies) -and ($results.Tolower() -ne $dependencies.Tolower())) { - $cmd = "set ""$name"" DependOnService $dependencies" - $results = Nssm-Invoke $cmd + $current_dependencies = @($nssm_result.stdout.split("`n`r") | where { $_ -ne '' }) + + If (@(Compare-Object -ReferenceObject $current_dependencies -DifferenceObject $dependencies).Length -ne 0) { + $dependencies_str = Argv-ToString -arguments $dependencies + $cmd = "set ""$name"" DependOnService $dependencies_str" + $nssm_result = Nssm-Invoke $cmd - if ($LastExitCode -ne 0) + if ($nssm_result.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $nssm_result.stderr Throw "Error updating dependencies for service ""$name""" } @@ -479,25 +475,25 @@ Function Nssm-Update-StartMode ) $cmd = "get ""$name"" Start" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd - if ($LastExitCode -ne 0) + if ($nssm_result.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $nssm_result.stderr Throw "Error updating start mode for service ""$name""" } $modes=@{"auto" = "SERVICE_AUTO_START"; "delayed" = "SERVICE_DELAYED_AUTO_START"; "manual" = "SERVICE_DEMAND_START"; "disabled" = "SERVICE_DISABLED"} $mappedMode = $modes.$mode - if ($results -cnotlike $mappedMode) { + if ($nssm_result.stdout -notlike "*$mappedMode*") { $cmd = "set ""$name"" Start $mappedMode" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd - if ($LastExitCode -ne 0) + if ($nssm_result.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $nssm_result.stderr Throw "Error updating start mode for service ""$name""" } @@ -515,9 +511,9 @@ Function Nssm-Get-Status ) $cmd = "status ""$name""" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd - return ,$results + return $nssm_result } Function Nssm-Start @@ -530,23 +526,23 @@ Function Nssm-Start $currentStatus = Nssm-Get-Status -name $name - if ($LastExitCode -ne 0) + if ($currentStatus.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $currentStatus.stderr Throw "Error starting service ""$name""" } - switch ($currentStatus) + switch -wildcard ($currentStatus.stdout) { - "SERVICE_RUNNING" { <# Nothing to do #> } - "SERVICE_STOPPED" { Nssm-Start-Service-Command -name $name } - - "SERVICE_CONTINUE_PENDING" { Nssm-Stop-Service-Command -name $name; Nssm-Start-Service-Command -name $name } - "SERVICE_PAUSE_PENDING" { Nssm-Stop-Service-Command -name $name; Nssm-Start-Service-Command -name $name } - "SERVICE_PAUSED" { Nssm-Stop-Service-Command -name $name; Nssm-Start-Service-Command -name $name } - "SERVICE_START_PENDING" { Nssm-Stop-Service-Command -name $name; Nssm-Start-Service-Command -name $name } - "SERVICE_STOP_PENDING" { Nssm-Stop-Service-Command -name $name; Nssm-Start-Service-Command -name $name } + "*SERVICE_RUNNING*" { <# Nothing to do #> } + "*SERVICE_STOPPED*" { Nssm-Start-Service-Command -name $name } + + "*SERVICE_CONTINUE_PENDING*" { Nssm-Stop-Service-Command -name $name; Nssm-Start-Service-Command -name $name } + "*SERVICE_PAUSE_PENDING*" { Nssm-Stop-Service-Command -name $name; Nssm-Start-Service-Command -name $name } + "*SERVICE_PAUSED*" { Nssm-Stop-Service-Command -name $name; Nssm-Start-Service-Command -name $name } + "*SERVICE_START_PENDING*" { Nssm-Stop-Service-Command -name $name; Nssm-Start-Service-Command -name $name } + "*SERVICE_STOP_PENDING*" { Nssm-Stop-Service-Command -name $name; Nssm-Start-Service-Command -name $name } } } @@ -560,12 +556,12 @@ Function Nssm-Start-Service-Command $cmd = "start ""$name""" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd - if ($LastExitCode -ne 0) + if ($nssm_result.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $nssm_result.stderr Throw "Error starting service ""$name""" } @@ -583,12 +579,12 @@ Function Nssm-Stop-Service-Command $cmd = "stop ""$name""" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd - if ($LastExitCode -ne 0) + if ($nssm_result.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $nssm_result.stderr Throw "Error stopping service ""$name""" } @@ -606,23 +602,23 @@ Function Nssm-Stop $currentStatus = Nssm-Get-Status -name $name - if ($LastExitCode -ne 0) + if ($currentStatus.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $currentStatus.stderr Throw "Error stopping service ""$name""" } - if ($currentStatus -ne "SERVICE_STOPPED") + if ($currentStatus.stdout -notlike "*SERVICE_STOPPED*") { $cmd = "stop ""$name""" - $results = Nssm-Invoke $cmd + $nssm_result = Nssm-Invoke $cmd - if ($LastExitCode -ne 0) + if ($nssm_result.rc -ne 0) { $result.nssm_error_cmd = $cmd - $result.nssm_error_log = "$results" + $result.nssm_error_log = $nssm_result.stderr Throw "Error stopping service ""$name""" } diff --git a/lib/ansible/modules/windows/win_nssm.py b/lib/ansible/modules/windows/win_nssm.py index f896d1e3f9..9b16aff784 100644 --- a/lib/ansible/modules/windows/win_nssm.py +++ b/lib/ansible/modules/windows/win_nssm.py @@ -49,7 +49,7 @@ options: - Path to receive error output. app_parameters: description: - - Parameters to be passed to the application when it starts. + - A string representing a dictionary of parameters to be passed to the application when it starts. - Use either this or C(app_parameters_free_form), not both. app_parameters_free_form: version_added: "2.3.0" @@ -87,37 +87,25 @@ EXAMPLES = r''' application: C:\windows\foo.exe # Install and start the foo service with a key-value pair argument -# This will yield the following command: C:\windows\foo.exe bar "true" +# This will yield the following command: C:\windows\foo.exe -bar true - win_nssm: name: foo application: C:\windows\foo.exe - app_parameters: - bar: 'true' - -# Install and start the foo service with a key-value pair argument, where the argument needs to start with a dash -# This will yield the following command: C:\windows\\foo.exe -bar "true" -- win_nssm: - name: foo - application: C:\windows\foo.exe - app_parameters: - "-bar": 'true' + app_parameters: -bar=true # Install and start the foo service with a single parameter # This will yield the following command: C:\windows\\foo.exe bar - win_nssm: name: foo application: C:\windows\foo.exe - app_parameters: - _: bar + app_parameters: _=bar # Install and start the foo service with a mix of single params, and key value pairs -# This will yield the following command: C:\windows\\foo.exe bar -file output.bat +# This will yield the following command: C:\windows\\foo.exe bar -file output.bat -foo false - win_nssm: name: foo application: C:\windows\foo.exe - app_parameters: - _: bar - "-file": "output.bat" + app_parameters: _=bar; -file=output.bat; -foo=false # Use the single line parameters option to specify an arbitrary string of parameters # for the service executable diff --git a/test/integration/targets/win_nssm/aliases b/test/integration/targets/win_nssm/aliases new file mode 100644 index 0000000000..215e0b0692 --- /dev/null +++ b/test/integration/targets/win_nssm/aliases @@ -0,0 +1 @@ +shippable/windows/group4 diff --git a/test/integration/targets/win_nssm/defaults/main.yml b/test/integration/targets/win_nssm/defaults/main.yml new file mode 100644 index 0000000000..6ebafb24a1 --- /dev/null +++ b/test/integration/targets/win_nssm/defaults/main.yml @@ -0,0 +1,4 @@ +test_service_name: ansible_nssm_test +test_win_nssm_path: '{{win_output_dir}}\win_nssm' +test_win_nssm_username: testnssmuser +test_win_nssm_password: Password123!
\ No newline at end of file diff --git a/test/integration/targets/win_nssm/tasks/main.yml b/test/integration/targets/win_nssm/tasks/main.yml new file mode 100644 index 0000000000..49368cf1a4 --- /dev/null +++ b/test/integration/targets/win_nssm/tasks/main.yml @@ -0,0 +1,44 @@ +--- +- name: install NSSM + win_chocolatey: + name: NSSM + state: present + +- name: ensure testing folder exists + win_file: + path: '{{test_win_nssm_path}}' + state: directory + +- name: create test user for service execution + win_user: + name: '{{test_win_nssm_username}}' + password: '{{test_win_nssm_password}}' + state: present + groups: + - Users + +# Run actual tests +- block: + - include_tasks: tests.yml + + always: + - name: ensure test service is absent + win_service: + name: '{{ test_service_name }}' + state: absent + + - name: remove test user + win_user: + name: '{{test_win_nssm_username}}' + state: absent + + - name: cleanup test folder + win_file: + path: '{{test_win_nssm_path}}' + state: absent + + - name: uninstall NSSM + win_chocolatey: + name: NSSM + state: absent + failed_when: false diff --git a/test/integration/targets/win_nssm/tasks/tests.yml b/test/integration/targets/win_nssm/tasks/tests.yml new file mode 100644 index 0000000000..78ab9b0cdf --- /dev/null +++ b/test/integration/targets/win_nssm/tasks/tests.yml @@ -0,0 +1,256 @@ +--- +- name: get register cmd that will get service info + set_fact: + test_service_cmd: | + $res = @{} + $srvobj = Get-WmiObject Win32_Service -Filter "Name=""$service""" | Select Name,PathName,StartMode,StartName,State + if ($srvobj) { + $srvobj | Get-Member -MemberType *Property | % { $res.($_.name) = $srvobj.($_.name) } + $res.Exists = $true + $res.Dependencies = Get-WmiObject -Query "Associators of {Win32_Service.Name=""$service""} Where AssocClass=Win32_DependentService" | select -ExpandProperty Name + $res.Parameters = @{} + $srvkey = "HKLM:\SYSTEM\CurrentControlSet\Services\$service\Parameters" + Get-Item "$srvkey" | Select-Object -ExpandProperty property | % { $res.Parameters.$_ = (Get-ItemProperty -Path "$srvkey" -Name $_).$_} + } else { + $res.Exists = $false + } + ConvertTo-Json -InputObject $res -Compress + +- name: install service + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + state: present + register: install_service + +- name: get result of install service + win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: install_service_actual + +- name: assert results of install service + assert: + that: + - install_service.changed == true + - (install_service_actual.stdout|from_json).Exists == true + - (install_service_actual.stdout|from_json).State == 'Stopped' + - (install_service_actual.stdout|from_json).StartMode == 'Auto' + - (install_service_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe" + +- name: test install service (idempotent) + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + state: present + register: install_service_again + +- name: get result of install service (idempotent) + win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: install_service_again_actual + +- name: assert results of install service (idempotent) + assert: + that: + - install_service_again.changed == false + - (install_service_again_actual.stdout|from_json).Exists == true + - (install_service_again_actual.stdout|from_json).State == 'Stopped' + - (install_service_again_actual.stdout|from_json).StartMode == 'Auto' + - (install_service_again_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe" + +- name: install and start service + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + state: started + register: install_start_service + +- name: get result of install and start service + win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: install_start_service_actual + +- name: assert results of install and start service + assert: + that: + - install_start_service.changed == true + - (install_start_service_actual.stdout|from_json).Exists == true + - (install_start_service_actual.stdout|from_json).State == 'Running' + - (install_start_service_actual.stdout|from_json).StartMode == 'Auto' + - (install_start_service_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe" + +- name: install and start service with more parameters + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + start_mode: manual + dependencies: 'tcpip,dnscache' + user: '{{ test_win_nssm_username }}' + password: '{{ test_win_nssm_password }}' + stdout_file: '{{ test_win_nssm_path }}\log.txt' + stderr_file: '{{ test_win_nssm_path }}\error.txt' + state: started + register: install_service_complex + +- name: get result of install and start service with more parameters + win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: install_service_complex_actual + +- name: assert results of install and start service with more parameters + assert: + that: + - install_service_complex.changed == true + - (install_service_complex_actual.stdout|from_json).Exists == true + - (install_service_complex_actual.stdout|from_json).State == 'Running' + - (install_service_complex_actual.stdout|from_json).StartMode == 'Manual' + - (install_service_complex_actual.stdout|from_json).StartName == '.\\' + test_win_nssm_username + - (install_service_complex_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe" + - (install_service_complex_actual.stdout|from_json).Parameters.AppStdout == test_win_nssm_path + '\\log.txt' + - (install_service_complex_actual.stdout|from_json).Parameters.AppStderr == test_win_nssm_path + '\\error.txt' + - (install_service_complex_actual.stdout|from_json).Dependencies|length == 2 + - '"Tcpip" in (install_service_complex_actual.stdout|from_json).Dependencies' + - '"Dnscache" in (install_service_complex_actual.stdout|from_json).Dependencies' + +- name: install and start service with more parameters (idempotent) + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + start_mode: manual + # Dependencies order should not trigger a change + dependencies: 'dnscache,tcpip' + user: '{{ test_win_nssm_username }}' + password: '{{ test_win_nssm_password }}' + stdout_file: '{{ test_win_nssm_path }}\log.txt' + stderr_file: '{{ test_win_nssm_path }}\error.txt' + state: started + register: install_service_complex_again + +- name: get result of install and start service with more parameters (idempotent) + win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: install_service_complex_again_actual + +- name: assert results of install and start service with more parameters (idempotent) + assert: + that: + - install_service_complex_again.changed == false + - (install_service_complex_again_actual.stdout|from_json).Exists == true + - (install_service_complex_again_actual.stdout|from_json).State == 'Running' + - (install_service_complex_again_actual.stdout|from_json).StartMode == 'Manual' + - (install_service_complex_again_actual.stdout|from_json).StartName == '.\\' + test_win_nssm_username + - (install_service_complex_again_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe" + - (install_service_complex_again_actual.stdout|from_json).Parameters.AppStdout == test_win_nssm_path + '\\log.txt' + - (install_service_complex_again_actual.stdout|from_json).Parameters.AppStderr == test_win_nssm_path + '\\error.txt' + - (install_service_complex_again_actual.stdout|from_json).Dependencies|length == 2 + - '"Tcpip" in (install_service_complex_again_actual.stdout|from_json).Dependencies' + - '"Dnscache" in (install_service_complex_again_actual.stdout|from_json).Dependencies' + +- name: install service with free form parameters + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + app_parameters_free_form: '-v -Dcom.test.string=value "C:\with space\\"' + state: present + register: free_params + +- name: get result of install service with free form parameters + win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: free_params_actual + +- name: assert results of install service with free form parameters + assert: + that: + - free_params.changed == true + - (free_params_actual.stdout|from_json).Exists == true + - (free_params_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe" + # Expected value: -v -Dcom.test.string=value "C:\with space\\" (backslashes doubled for jinja) + - (free_params_actual.stdout|from_json).Parameters.AppParameters == '-v -Dcom.test.string=value "C:\\with space\\\\"' + +- name: install service with free form parameters (idempotent) + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + app_parameters_free_form: '-v -Dcom.test.string=value "C:\with space\\"' + state: present + register: free_params_again + +- name: get result of install service with free form parameters (idempotent) + win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: free_params_again_actual + +- name: assert results of install service with free form parameters (idempotent) + assert: + that: + - free_params_again.changed == false + - (free_params_again_actual.stdout|from_json).Exists == true + - (free_params_again_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe" + # Expected value: -v -Dcom.test.string=value "C:\with space\\" (backslashes doubled for jinja) + - (free_params_again_actual.stdout|from_json).Parameters.AppParameters == '-v -Dcom.test.string=value "C:\\with space\\\\"' + +- name: install service with dict parameters + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + app_parameters: foo=true; -file.out=output.bat; -path=C:\with space\; -str=test"quotes; _=bar + register: mixed_params + +- name: get result of install service with dict parameters + win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: mixed_params_actual + +- name: assert results of install service with dict parameters + assert: + that: + - mixed_params.changed == true + - (mixed_params_actual.stdout|from_json).Exists == true + - (mixed_params_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe" + # Expected value: bar -file.out output.bat -str "test\"quotes" foo true -path "C:\with space\\" (backslashes doubled for jinja) + - (mixed_params_actual.stdout|from_json).Parameters.AppParameters == 'bar -file.out output.bat -str "test\\"quotes" foo true -path "C:\\with space\\\\"' + +- name: install service with dict parameters (idempotent) + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + app_parameters: foo=true; -file.out=output.bat; -path=C:\with space\; -str=test"quotes; _=bar + register: mixed_params_again + +- name: get result of install service with dict parameters (idempotent) + win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: mixed_params_again_actual + +- name: assert results of install service with dict parameters (idempotent) + assert: + that: + - mixed_params_again.changed == false + - (mixed_params_again_actual.stdout|from_json).Exists == true + - (mixed_params_again_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe" + # Expected value: bar -file.out output.bat -str "test\"quotes" foo true -path "C:\with space\\" (backslashes doubled for jinja) + - (mixed_params_again_actual.stdout|from_json).Parameters.AppParameters == 'bar -file.out output.bat -str "test\\"quotes" foo true -path "C:\\with space\\\\"' + +- name: remove service + win_nssm: + name: '{{ test_service_name }}' + state: absent + register: remove_service + +- name: get result of remove service + win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: remove_service_actual + +- name: assert results of remove service + assert: + that: + - remove_service.changed == true + - (remove_service_actual.stdout|from_json).Exists == false + +- name: remove service (idempotent) + win_nssm: + name: '{{ test_service_name }}' + state: absent + register: remove_service_again + +- name: get result of remove service (idempotent) + win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: remove_service_again_actual + +- name: assert results of remove service (idempotent) + assert: + that: + - remove_service_again.changed == false + - (remove_service_again_actual.stdout|from_json).Exists == false
\ No newline at end of file diff --git a/test/sanity/pslint/ignore.txt b/test/sanity/pslint/ignore.txt index 4b6eb1b017..a5e4218987 100644 --- a/test/sanity/pslint/ignore.txt +++ b/test/sanity/pslint/ignore.txt @@ -63,7 +63,6 @@ lib/ansible/modules/windows/win_iis_website.ps1 PSUseBOMForUnicodeEncodedFile lib/ansible/modules/windows/win_iis_website.ps1 PSUseDeclaredVarsMoreThanAssignments lib/ansible/modules/windows/win_mapped_drive.ps1 PSAvoidUsingConvertToSecureStringWithPlainText lib/ansible/modules/windows/win_nssm.ps1 PSAvoidUsingCmdletAliases -lib/ansible/modules/windows/win_nssm.ps1 PSAvoidUsingInvokeExpression lib/ansible/modules/windows/win_nssm.ps1 PSAvoidUsingPlainTextForPassword lib/ansible/modules/windows/win_nssm.ps1 PSAvoidUsingUserNameAndPassWordParams lib/ansible/modules/windows/win_nssm.ps1 PSUseApprovedVerbs |