win_shell/win_command changes + tests (#17557)
This commit is contained in:
parent
81072fcba1
commit
f497d771c8
6 changed files with 319 additions and 6 deletions
|
@ -85,7 +85,7 @@ class AdHocCLI(CLI):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _play_ds(self, pattern, async, poll):
|
def _play_ds(self, pattern, async, poll):
|
||||||
check_raw = self.options.module_name in ('command', 'shell', 'script', 'raw')
|
check_raw = self.options.module_name in ('command', 'win_command', 'shell', 'win_shell', 'script', 'raw')
|
||||||
return dict(
|
return dict(
|
||||||
name = "Ansible Ad-Hoc",
|
name = "Ansible Ad-Hoc",
|
||||||
hosts = pattern,
|
hosts = pattern,
|
||||||
|
|
|
@ -326,8 +326,8 @@ COLOR_DIFF_LINES = get_config(p, 'colors', 'diff_lines', 'ANSIBLE_COLOR_DIFF_LI
|
||||||
DIFF_CONTEXT = get_config(p, 'diff', 'context', 'ANSIBLE_DIFF_CONTEXT', 3, integer=True)
|
DIFF_CONTEXT = get_config(p, 'diff', 'context', 'ANSIBLE_DIFF_CONTEXT', 3, integer=True)
|
||||||
|
|
||||||
# non-configurable things
|
# non-configurable things
|
||||||
MODULE_REQUIRE_ARGS = ['command', 'shell', 'raw', 'script']
|
MODULE_REQUIRE_ARGS = ['command', 'win_command', 'shell', 'win_shell', 'raw', 'script']
|
||||||
MODULE_NO_JSON = ['command', 'shell', 'raw']
|
MODULE_NO_JSON = ['command', 'win_command', 'shell', 'win_shell', 'raw']
|
||||||
DEFAULT_BECOME_PASS = None
|
DEFAULT_BECOME_PASS = None
|
||||||
DEFAULT_SUDO_PASS = None
|
DEFAULT_SUDO_PASS = None
|
||||||
DEFAULT_REMOTE_PASS = None
|
DEFAULT_REMOTE_PASS = None
|
||||||
|
|
|
@ -29,7 +29,9 @@ from ansible.template import Templar
|
||||||
# For filtering out modules correctly below
|
# For filtering out modules correctly below
|
||||||
RAW_PARAM_MODULES = ([
|
RAW_PARAM_MODULES = ([
|
||||||
'command',
|
'command',
|
||||||
|
'win_command',
|
||||||
'shell',
|
'shell',
|
||||||
|
'win_shell',
|
||||||
'script',
|
'script',
|
||||||
'include',
|
'include',
|
||||||
'include_vars',
|
'include_vars',
|
||||||
|
@ -161,7 +163,7 @@ class ModuleArgsParser:
|
||||||
|
|
||||||
# only internal variables can start with an underscore, so
|
# only internal variables can start with an underscore, so
|
||||||
# we don't allow users to set them directy in arguments
|
# we don't allow users to set them directy in arguments
|
||||||
if args and action not in ('command', 'shell', 'script', 'raw'):
|
if args and action not in ('command', 'win_command', 'shell', 'win_shell', 'script', 'raw'):
|
||||||
for arg in args:
|
for arg in args:
|
||||||
if arg.startswith('_ansible_'):
|
if arg.startswith('_ansible_'):
|
||||||
raise AnsibleError("invalid parameter specified for action '%s': '%s'" % (action, arg))
|
raise AnsibleError("invalid parameter specified for action '%s': '%s'" % (action, arg))
|
||||||
|
@ -191,7 +193,7 @@ class ModuleArgsParser:
|
||||||
args = thing
|
args = thing
|
||||||
elif isinstance(thing, string_types):
|
elif isinstance(thing, string_types):
|
||||||
# form is like: local_action: copy src=a dest=b ... pretty common
|
# form is like: local_action: copy src=a dest=b ... pretty common
|
||||||
check_raw = action in ('command', 'shell', 'script', 'raw')
|
check_raw = action in ('command', 'win_command', 'shell', 'win_shell', 'script', 'raw')
|
||||||
args = parse_kv(thing, check_raw=check_raw)
|
args = parse_kv(thing, check_raw=check_raw)
|
||||||
elif thing is None:
|
elif thing is None:
|
||||||
# this can happen with modules which take no params, like ping:
|
# this can happen with modules which take no params, like ping:
|
||||||
|
@ -217,7 +219,7 @@ class ModuleArgsParser:
|
||||||
action = None
|
action = None
|
||||||
args = None
|
args = None
|
||||||
|
|
||||||
actions_allowing_raw = ('command', 'shell', 'script', 'raw')
|
actions_allowing_raw = ('command', 'win_command', 'shell', 'win_shell', 'script', 'raw')
|
||||||
if isinstance(thing, dict):
|
if isinstance(thing, dict):
|
||||||
# form is like: copy: { src: 'a', dest: 'b' } ... common for structured (aka "complex") args
|
# form is like: copy: { src: 'a', dest: 'b' } ... common for structured (aka "complex") args
|
||||||
thing = thing.copy()
|
thing = thing.copy()
|
||||||
|
|
134
test/integration/roles/test_win_command/tasks/main.yml
Normal file
134
test/integration/roles/test_win_command/tasks/main.yml
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
- name: execute a command
|
||||||
|
win_command: whoami /groups
|
||||||
|
register: cmdout
|
||||||
|
|
||||||
|
- name: validate result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- cmdout|success
|
||||||
|
- cmdout|changed
|
||||||
|
- cmdout.cmd == 'whoami /groups'
|
||||||
|
- cmdout.delta is match('^\d:(\d){2}:(\d){2}.(\d){6}$')
|
||||||
|
- cmdout.end is match('^(\d){4}\-(\d){2}\-(\d){2} (\d){2}:(\d){2}:(\d){2}.(\d){6}$')
|
||||||
|
- cmdout.rc == 0
|
||||||
|
- cmdout.start is match('^(\d){4}\-(\d){2}\-(\d){2} (\d){2}:(\d){2}:(\d){2}.(\d){6}$')
|
||||||
|
- cmdout.stderr == ""
|
||||||
|
- cmdout.stdout is search('GROUP INFORMATION')
|
||||||
|
- '"GROUP INFORMATION" in cmdout.stdout_lines'
|
||||||
|
- cmdout.warnings == []
|
||||||
|
|
||||||
|
- name: execute something nonexistent
|
||||||
|
win_command: bogus_command1234
|
||||||
|
register: cmdout
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: validate result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- cmdout|failed
|
||||||
|
- not cmdout|changed
|
||||||
|
- cmdout.cmd == 'bogus_command1234'
|
||||||
|
- cmdout.rc == 2
|
||||||
|
- cmdout.msg is search('cannot find the file specified')
|
||||||
|
|
||||||
|
- name: execute something with error output
|
||||||
|
win_command: cmd /c "echo some output & echo some error 1>&2"
|
||||||
|
register: cmdout
|
||||||
|
|
||||||
|
- name: validate result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- cmdout|success
|
||||||
|
- cmdout|changed
|
||||||
|
- cmdout.cmd == 'cmd /c "echo some output & echo some error 1>&2"'
|
||||||
|
- cmdout.delta is match('^\d:(\d){2}:(\d){2}.(\d){6}$')
|
||||||
|
- cmdout.end is match('^(\d){4}\-(\d){2}\-(\d){2} (\d){2}:(\d){2}:(\d){2}.(\d){6}$')
|
||||||
|
- cmdout.rc == 0
|
||||||
|
- cmdout.start is match('^(\d){4}\-(\d){2}\-(\d){2} (\d){2}:(\d){2}:(\d){2}.(\d){6}$')
|
||||||
|
- cmdout.stderr is search('some error')
|
||||||
|
- cmdout.stdout == "some output \r\n"
|
||||||
|
- cmdout.stdout_lines == ["some output "]
|
||||||
|
- cmdout.warnings == []
|
||||||
|
|
||||||
|
- name: ensure test file is absent
|
||||||
|
win_file:
|
||||||
|
path: c:\testfile.txt
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: run with creates, should create
|
||||||
|
win_command: cmd /c "echo $null >> c:\testfile.txt"
|
||||||
|
args:
|
||||||
|
creates: c:\testfile.txt
|
||||||
|
register: cmdout
|
||||||
|
|
||||||
|
- name: validate result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- cmdout|success
|
||||||
|
- cmdout|changed
|
||||||
|
|
||||||
|
- name: run again with creates, should skip
|
||||||
|
win_command: cmd /c "echo $null >> c:\testfile.txt"
|
||||||
|
args:
|
||||||
|
creates: c:\testfile.txt
|
||||||
|
register: cmdout
|
||||||
|
|
||||||
|
- name: validate result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- cmdout|skipped
|
||||||
|
- cmdout.msg is search('exists')
|
||||||
|
|
||||||
|
- name: ensure testfile is still present
|
||||||
|
win_stat:
|
||||||
|
path: c:\testfile.txt
|
||||||
|
register: statout
|
||||||
|
|
||||||
|
- name: validate result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- statout.stat.exists == true
|
||||||
|
|
||||||
|
- name: run with removes, should remove
|
||||||
|
win_command: cmd /c "del c:\testfile.txt"
|
||||||
|
args:
|
||||||
|
removes: c:\testfile.txt
|
||||||
|
register: cmdout
|
||||||
|
|
||||||
|
- name: validate result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- cmdout|success
|
||||||
|
- cmdout|changed
|
||||||
|
|
||||||
|
- name: run again with removes, should skip
|
||||||
|
win_command: cmd /c "del c:\testfile.txt"
|
||||||
|
args:
|
||||||
|
removes: c:\testfile.txt
|
||||||
|
register: cmdout
|
||||||
|
|
||||||
|
- name: validate result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- cmdout|skipped
|
||||||
|
- cmdout.msg is search('does not exist')
|
||||||
|
|
||||||
|
- name: run something with known nonzero exit code
|
||||||
|
win_command: cmd /c "exit 254"
|
||||||
|
register: cmdout
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: validate result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- cmdout|failed
|
||||||
|
- cmdout.rc == 254
|
||||||
|
|
||||||
|
- name: write large buffer to stdout
|
||||||
|
win_command: powershell /c "$ba = New-Object byte[] 16384; (New-Object System.Random 32).NextBytes($ba); [Convert]::ToBase64String($ba) | Write-Output"
|
||||||
|
register: cmdout
|
||||||
|
|
||||||
|
# TODO: fix small buffer deadlock on large write to stderr before stdout has been consumed
|
||||||
|
#- name: write large buffer to stderr
|
||||||
|
# win_shell: $ba = New-Object byte[] 16384; (New-Object System.Random 32).NextBytes($ba); [Convert]::ToBase64String($ba) | Write-Error; Write-Output test
|
||||||
|
# register: cmdout
|
175
test/integration/roles/test_win_shell/tasks/main.yml
Normal file
175
test/integration/roles/test_win_shell/tasks/main.yml
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
- name: execute a powershell cmdlet
|
||||||
|
win_shell: Write-Output "hello from Ansible"
|
||||||
|
register: shellout
|
||||||
|
|
||||||
|
- name: validate result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- shellout|success
|
||||||
|
- shellout|changed
|
||||||
|
- shellout.cmd == 'Write-Output "hello from Ansible"'
|
||||||
|
- shellout.delta is match('^\d:(\d){2}:(\d){2}.(\d){6}$')
|
||||||
|
- shellout.end is match('^(\d){4}\-(\d){2}\-(\d){2} (\d){2}:(\d){2}:(\d){2}.(\d){6}$')
|
||||||
|
- shellout.rc == 0
|
||||||
|
- shellout.start is match('^(\d){4}\-(\d){2}\-(\d){2} (\d){2}:(\d){2}:(\d){2}.(\d){6}$')
|
||||||
|
- shellout.stderr == ""
|
||||||
|
- shellout.stdout == "hello from Ansible\r\n"
|
||||||
|
- shellout.stdout_lines == ["hello from Ansible"]
|
||||||
|
- shellout.warnings == []
|
||||||
|
|
||||||
|
- name: execute a powershell cmdlet with multi-line output
|
||||||
|
win_shell: Write-Output "hello from Ansible"; Write-Output "another line"; Write-Output "yet another line"
|
||||||
|
register: shellout
|
||||||
|
|
||||||
|
- name: validate result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- shellout|success
|
||||||
|
- shellout|changed
|
||||||
|
- shellout.cmd == 'Write-Output "hello from Ansible"; Write-Output "another line"; Write-Output "yet another line"'
|
||||||
|
- shellout.delta is match('^\d:(\d){2}:(\d){2}.(\d){6}$')
|
||||||
|
- shellout.end is match('^(\d){4}\-(\d){2}\-(\d){2} (\d){2}:(\d){2}:(\d){2}.(\d){6}$')
|
||||||
|
- shellout.rc == 0
|
||||||
|
- shellout.start is match('^(\d){4}\-(\d){2}\-(\d){2} (\d){2}:(\d){2}:(\d){2}.(\d){6}$')
|
||||||
|
- shellout.stderr == ""
|
||||||
|
- shellout.stdout == "hello from Ansible\r\nanother line\r\nyet another line\r\n"
|
||||||
|
- shellout.stdout_lines == ["hello from Ansible","another line", "yet another line"]
|
||||||
|
- shellout.warnings == []
|
||||||
|
|
||||||
|
- name: execute something nonexistent
|
||||||
|
win_shell: bogus_command1234
|
||||||
|
register: shellout
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: validate result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- shellout|failed
|
||||||
|
- shellout|changed
|
||||||
|
- shellout.cmd == 'bogus_command1234'
|
||||||
|
- shellout.delta is match('^\d:(\d){2}:(\d){2}.(\d){6}$')
|
||||||
|
- shellout.end is match('^(\d){4}\-(\d){2}\-(\d){2} (\d){2}:(\d){2}:(\d){2}.(\d){6}$')
|
||||||
|
- shellout.rc == 1
|
||||||
|
- shellout.start is match('^(\d){4}\-(\d){2}\-(\d){2} (\d){2}:(\d){2}:(\d){2}.(\d){6}$')
|
||||||
|
- shellout.stderr is search('not recognized')
|
||||||
|
- shellout.stdout == ""
|
||||||
|
- shellout.stdout_lines == []
|
||||||
|
- shellout.warnings == []
|
||||||
|
|
||||||
|
- name: execute something with error output
|
||||||
|
win_shell: Write-Error "it broke"; Write-Output "some output"
|
||||||
|
register: shellout
|
||||||
|
|
||||||
|
- name: validate result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- shellout|success
|
||||||
|
- shellout|changed
|
||||||
|
- shellout.cmd == 'Write-Error "it broke"; Write-Output "some output"'
|
||||||
|
- shellout.delta is match('^\d:(\d){2}:(\d){2}.(\d){6}$')
|
||||||
|
- shellout.end is match('^(\d){4}\-(\d){2}\-(\d){2} (\d){2}:(\d){2}:(\d){2}.(\d){6}$')
|
||||||
|
- shellout.rc == 0
|
||||||
|
- shellout.start is match('^(\d){4}\-(\d){2}\-(\d){2} (\d){2}:(\d){2}:(\d){2}.(\d){6}$')
|
||||||
|
- shellout.stderr is search('it broke')
|
||||||
|
- shellout.stdout == "some output\r\n"
|
||||||
|
- shellout.stdout_lines == ["some output"]
|
||||||
|
- shellout.warnings == []
|
||||||
|
|
||||||
|
- name: ensure test file is absent
|
||||||
|
win_file:
|
||||||
|
path: c:\testfile.txt
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: run with creates, should create
|
||||||
|
win_shell: echo $null >> c:\testfile.txt
|
||||||
|
args:
|
||||||
|
creates: c:\testfile.txt
|
||||||
|
register: shellout
|
||||||
|
|
||||||
|
- name: validate result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- shellout|success
|
||||||
|
- shellout|changed
|
||||||
|
|
||||||
|
- name: run again with creates, should skip
|
||||||
|
win_shell: echo $null >> c:\testfile.txt
|
||||||
|
args:
|
||||||
|
creates: c:\testfile.txt
|
||||||
|
register: shellout
|
||||||
|
|
||||||
|
- name: validate result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- shellout|skipped
|
||||||
|
- shellout.msg is search('exists')
|
||||||
|
|
||||||
|
- name: ensure testfile is still present
|
||||||
|
win_stat:
|
||||||
|
path: c:\testfile.txt
|
||||||
|
register: statout
|
||||||
|
|
||||||
|
- name: validate result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- statout.stat.exists == true
|
||||||
|
|
||||||
|
- name: run with removes, should remove
|
||||||
|
win_shell: Remove-Item c:\testfile.txt
|
||||||
|
args:
|
||||||
|
removes: c:\testfile.txt
|
||||||
|
register: shellout
|
||||||
|
|
||||||
|
- name: validate result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- shellout|success
|
||||||
|
- shellout|changed
|
||||||
|
|
||||||
|
- name: run again with removes, should skip
|
||||||
|
win_shell: echo $null >> c:\testfile.txt
|
||||||
|
args:
|
||||||
|
removes: c:\testfile.txt
|
||||||
|
register: shellout
|
||||||
|
|
||||||
|
- name: validate result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- shellout|skipped
|
||||||
|
- shellout.msg is search('does not exist')
|
||||||
|
|
||||||
|
- name: run something with known nonzero exit code
|
||||||
|
win_shell: exit 254
|
||||||
|
register: shellout
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: validate result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- shellout|failed
|
||||||
|
- shellout.rc == 254
|
||||||
|
|
||||||
|
- name: run something via cmd that will fail in powershell
|
||||||
|
win_shell: echo line1 & echo.line2
|
||||||
|
args:
|
||||||
|
executable: cmd
|
||||||
|
register: shellout
|
||||||
|
|
||||||
|
- name: validate result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- shellout|success
|
||||||
|
- shellout|changed
|
||||||
|
- shellout.rc == 0
|
||||||
|
- shellout.stdout == "line1 \r\nline2\r\n"
|
||||||
|
- shellout.stdout_lines == ["line1 ", "line2"]
|
||||||
|
- shellout.stderr == ""
|
||||||
|
|
||||||
|
- name: write large buffer to stdout
|
||||||
|
win_shell: $ba = New-Object byte[] 16384; (New-Object System.Random 32).NextBytes($ba); [Convert]::ToBase64String($ba) | Write-Output
|
||||||
|
register: shellout
|
||||||
|
|
||||||
|
# TODO: fix small buffer deadlock on large write to stderr before stdout has been consumed
|
||||||
|
#- name: write large buffer to stderr
|
||||||
|
# win_shell: $ba = New-Object byte[] 16384; (New-Object System.Random 32).NextBytes($ba); [Convert]::ToBase64String($ba) | Write-Error; Write-Output test
|
||||||
|
# register: shellout
|
|
@ -5,3 +5,5 @@
|
||||||
- { role: test_win_feature, tags: test_win_feature }
|
- { role: test_win_feature, tags: test_win_feature }
|
||||||
- { role: test_win_user, tags: test_win_user }
|
- { role: test_win_user, tags: test_win_user }
|
||||||
- { role: test_win_async_wrapper, tags: test_win_async_wrapper }
|
- { role: test_win_async_wrapper, tags: test_win_async_wrapper }
|
||||||
|
- { role: test_win_shell, tags: test_win_shell }
|
||||||
|
- { role: test_win_command, tags: test_win_command }
|
||||||
|
|
Loading…
Reference in a new issue