Skip to content

Commit 8392237

Browse files
authored
Merge pull request #27 from berttejeda/develop
Develop
2 parents ca68614 + 2df99ef commit 8392237

File tree

9 files changed

+142
-100
lines changed

9 files changed

+142
-100
lines changed

HISTORY.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22
History
33
=======
44

5+
## Release 2019-09-15 v1.0.2
6+
7+
* Utilize threading to better handle ctrl-c/breaking out of subprocess calls [c248c99](https://github.com/berttejeda/ansible-taskrunner/commit/c248c99e355e058f9e0d775c4ddd5fe45025a9f7)
8+
* Renamed yamlc module to proc_mgmt [8e72a54](https://github.com/berttejeda/ansible-taskrunner/commit/8e72a54ff9db57109144ab9855ae6eb4361200dd)
9+
* Fixed bug in logic for preserving variables/cli order for python 2.x [4c16bd3](https://github.com/berttejeda/ansible-taskrunner/commit/4c16bd3a8493b595bb28e915b02cf18b8d8740ba)
10+
* Improved debugging for subprocess calls [e00dc4b](https://github.com/berttejeda/ansible-taskrunner/commit/e00dc4bd36e9b7baba860f592a8cd2ad46a2bb0e)
11+
* Preserve order of variables and cli options for python 2.x [22d330c](https://github.com/berttejeda/ansible-taskrunner/commit/22d330ca8e91af3ff0a287d75e58582391376f23)
12+
* Refactored handling of variables [ddbc787](https://github.com/berttejeda/ansible-taskrunner/commit/ddbc7870827c304f62a7ca8a733fd021745c7a27)
13+
* Fixed formatting [ab21879](https://github.com/berttejeda/ansible-taskrunner/commit/ab21879959f1ee1895656c2fc0f1672b3c45f328)
14+
* Added more documentation for variable declarations [2f4efa7](https://github.com/berttejeda/ansible-taskrunner/commit/2f4efa75f433b71e98f4e954456263697b5e1f45)
15+
516
## Release 2019-05-08 v1.0.1
617

718
* Deleted unused library [255fcf1](https://github.com/berttejeda/ansible-taskrunner/commit/255fcf1999f901ef9d01318c40c916fcd8461f02)

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.0.1
1+
1.0.2

ansible_taskrunner/cli.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
from click_extras import ExtendedEpilog
4242
from click_extras import ExtendedHelp
4343
from click_extras import ExtendCLI
44-
from yamlc import shell_invocation_mappings
45-
from yamlc import CLIInvocation
44+
from proc_mgmt import shell_invocation_mappings
45+
from proc_mgmt import CLIInvocation
4646
from yamlr import YamlReader
4747
# TODO
4848
# Employ language/regional options
@@ -71,7 +71,7 @@
7171

7272
# Private variables
7373
__author__ = 'etejeda'
74-
__version__ = '1.0.1'
74+
__version__ = '1.0.2'
7575
__program_name__ = 'tasks'
7676
__debug = False
7777
verbose = 0
@@ -109,7 +109,7 @@ def main(args, tasks_file='Taskfile.yaml', param_set=None, path_string='vars'):
109109
# Instantiate YAML Reader Class
110110
yamlr = YamlReader()
111111
# Instantiate the cli invocation class
112-
yamlcli = CLIInvocation()
112+
sub_process = CLIInvocation()
113113
# Load Tasks Manifest
114114
yaml_input_file = tasks_file
115115
if os.path.exists(yaml_input_file):
@@ -418,7 +418,7 @@ def run(args=None, **kwargs):
418418
if prefix == 'echo':
419419
print(command)
420420
else:
421-
yamlcli.call(command, debug_enabled=__debug)
421+
sub_process.call(command, debug_enabled=__debug)
422422
else:
423423
# Invoke the cli provider
424424
provider_cli.invocation(
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import os
2+
import subprocess
3+
from subprocess import Popen, PIPE, STDOUT
4+
import sys
5+
import threading
6+
import time
7+
8+
# Define how we handle different shell invocations
9+
shell_invocation_mappings = {
10+
'bash': '{src}',
11+
'python': 'python -c """{src}"""',
12+
'ruby': 'ruby < <(echo -e """{src}""")'
13+
}
14+
15+
16+
class CLIInvocation:
17+
18+
def __init__(self):
19+
20+
self.proc = None
21+
self.done = False
22+
self.invocation = type('obj', (object,), {
23+
'stdout': None,
24+
'failed': False,
25+
'returncode': 0
26+
}
27+
)
28+
29+
@staticmethod
30+
def which(program):
31+
"""
32+
Returns the fully-qualified path to the specified binary
33+
"""
34+
def is_exe(filepath):
35+
if sys.platform == 'win32':
36+
filepath = filepath.replace('\\', '/')
37+
for exe in [filepath, filepath + '.exe']:
38+
if all([os.path.isfile(exe), os.access(exe, os.X_OK)]):
39+
return True
40+
else:
41+
return os.path.isfile(filepath) and os.access(filepath, os.X_OK)
42+
fpath, fname = os.path.split(program)
43+
if fpath:
44+
if is_exe(program):
45+
return program
46+
else:
47+
for path in os.environ["PATH"].split(os.pathsep):
48+
path = path.strip('"')
49+
exe_file = os.path.join(path, program)
50+
if is_exe(exe_file):
51+
return exe_file
52+
return None
53+
54+
def call(self, cmd, exe='bash', debug_enabled=False):
55+
56+
executable = self.which(exe)
57+
if debug_enabled:
58+
process_invocation = [executable,'-x','-c', cmd]
59+
else:
60+
process_invocation = [executable, '-c', cmd]
61+
62+
def s():
63+
try:
64+
if sys.version_info[0] >= 3:
65+
with Popen(process_invocation, stdout=PIPE, stderr=STDOUT, bufsize=1, universal_newlines=True) as self.proc:
66+
for line in self.proc.stdout:
67+
sys.stdout.write(line) # process line here
68+
if self.proc.returncode != 0:
69+
self.invocation.failed = True
70+
self.invocation.returncode = p.returncode
71+
self.invocation.stdout = 'Encountered error code {errcode} in the specified command {args}'.format(
72+
errcode=p.returncode, args=p.args)
73+
return self.invocation
74+
else:
75+
# Invoke process
76+
self.proc = Popen(
77+
process_invocation,
78+
stdout=PIPE,
79+
stderr=STDOUT)
80+
# Poll for new output until finished
81+
while True:
82+
nextline = self.proc.stdout.readline()
83+
if nextline == '' and self.proc.poll() is not None:
84+
break
85+
sys.stdout.write(nextline)
86+
sys.stdout.flush()
87+
self.done = True
88+
except Exception:
89+
self.done = True
90+
self.done = True
91+
92+
try:
93+
if sys.version_info[0] >= 3:
94+
t = threading.Thread(target=s, daemon=True)
95+
else:
96+
t = threading.Thread(target=s)
97+
t.start()
98+
except Exception:
99+
pass
100+
try:
101+
while not self.done:
102+
time.sleep(0.1)
103+
104+
except KeyboardInterrupt:
105+
print("KeyboardInterrupt")
106+
try:
107+
self.proc.terminate()
108+
except Exception:
109+
pass

ansible_taskrunner/lib/providers/ansible.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
try:
1616
import click
1717
from formatting import ansi_colors
18-
from yamlc import CLIInvocation
18+
from proc_mgmt import shell_invocation_mappings
19+
from proc_mgmt import CLIInvocation
1920
except ImportError as e:
2021
print('Failed to import at least one required module')
2122
print('Error was %s' % e)
@@ -60,7 +61,7 @@ def invocation(yaml_input_file=None,
6061
kwargs={}):
6162
"""Invoke commands according to provider"""
6263
logger.info('Ansible Command Provider')
63-
yamlcli = CLIInvocation()
64+
sub_process = CLIInvocation()
6465
ansible_playbook_command = default_vars.get(
6566
'ansible_playbook_command', 'ansible-playbook')
6667
# Embedded inventory logic
@@ -131,7 +132,7 @@ def invocation(yaml_input_file=None,
131132
print(inventory_command)
132133
print(ansible_command)
133134
else:
134-
yamlcli.call(command, debug_enabled=debug)
135+
sub_process.call(command, debug_enabled=debug)
135136
# Debugging
136137
if debug:
137138
ansible_command_file_descriptor, ansible_command_file_path = mkstemp(prefix='ansible-command',

ansible_taskrunner/lib/providers/bash.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
# Import third-party and custom modules
1212
try:
1313
import click
14-
from yamlc import CLIInvocation
14+
from proc_mgmt import shell_invocation_mappings
15+
from proc_mgmt import CLIInvocation
1516
except ImportError as e:
1617
print('Failed to import at least one required module')
1718
print('Error was %s' % e)
@@ -48,7 +49,7 @@ def invocation(yaml_input_file=None,
4849
kwargs={}):
4950
"""Invoke commands according to provider"""
5051
logger.info('Bash Command Provider')
51-
yamlcli = CLIInvocation()
52+
sub_process = CLIInvocation()
5253
command = '''
5354
{clv}
5455
{dsv}
@@ -69,4 +70,4 @@ def invocation(yaml_input_file=None,
6970
logger.info("ECHO MODE ON")
7071
print(command)
7172
else:
72-
yamlcli.call(command, debug_enabled=debug)
73+
sub_process.call(command, debug_enabled=debug)

ansible_taskrunner/lib/providers/vagrant.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
# Import third-party and custom modules
1212
try:
1313
import click
14-
from yamlc import CLIInvocation
14+
from proc_mgmt import shell_invocation_mappings
15+
from proc_mgmt import CLIInvocation
1516
except ImportError as e:
1617
print('Failed to import at least one required module')
1718
print('Error was %s' % e)
@@ -53,7 +54,7 @@ def invocation(yaml_input_file=None,
5354
kwargs={}):
5455
"""Invoke commands according to provider"""
5556
logger.info('Vagrant Command Provider')
56-
yamlcli = CLIInvocation()
57+
sub_process = CLIInvocation()
5758
command = '''{clv}
5859
{dsv}
5960
{psv}
@@ -73,4 +74,4 @@ def invocation(yaml_input_file=None,
7374
logger.info("ECHO MODE ON")
7475
print(command)
7576
else:
76-
yamlcli.call(command, debug_enabled=debug)
77+
sub_process.call(command, debug_enabled=debug)

ansible_taskrunner/lib/yamlc/__init__.py

Lines changed: 0 additions & 83 deletions
This file was deleted.

examples/custom-cli-provider/plugins/providers/example/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
from lib.py3 import click
1717
else:
1818
from lib.py2 import click
19-
from lib.common.yamlc import CLIInvocation
19+
from lib.proc_mgmt import shell_invocation_mappings
20+
from lib.proc_mgmt import CLIInvocation
2021
except ImportError as e:
2122
print('Failed to import at least one required module')
2223
print('Error was %s' % e)
@@ -51,6 +52,7 @@ def invocation(self,
5152
raw_args='',
5253
kwargs={}):
5354
logger.info('Example Command Provider')
55+
sub_process = CLIInvocation()
5456
command = '''{dsv}
5557
{dlv}
5658
{clv}
@@ -68,4 +70,4 @@ def invocation(self,
6870
logger.info("ECHO MODE ON")
6971
print(command)
7072
else:
71-
CLIInvocation().call(command)
73+
sub_process.call(command)

0 commit comments

Comments
 (0)