Skip to content

Commit f4c8ff0

Browse files
authored
Merge pull request #100 from berttejeda/develop
Develop
2 parents 9e43b11 + 03270b9 commit f4c8ff0

File tree

9 files changed

+109
-63
lines changed

9 files changed

+109
-63
lines changed

HISTORY.md

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

5+
## Release 2019-11-04 v1.3.3
6+
7+
* Fixed bug in cli invocation when specifying taskfile override [7a07d1a](https://github.com/berttejeda/ansible-taskrunner/commit/7a07d1af4060d96a487df4a8a69e347a94deb56e)
8+
* Removed dependency on crayons package for ansi colors [9d985ef](https://github.com/berttejeda/ansible-taskrunner/commit/9d985ef7f4791e81aecad6475242d18050f6a8f9)
9+
* Support for ad-hoc bastion-mode (no sftp config file) [9d985ef](https://github.com/berttejeda/ansible-taskrunner/commit/9d985ef7f4791e81aecad6475242d18050f6a8f9)
10+
511
## Release 2019-11-03 v1.3.2
612

713
* Values from env should only be enabled by option tag [a792c52](https://github.com/berttejeda/ansible-taskrunner/commit/a792c5200da38ec59106f84a031e8298e7a6fb0b)

Makefile.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,11 @@
220220
pandoc README.md -f markdown -o ansible_taskrunner/build/README.html --template=${pandoc_template_dir}/html/easy_template.html --toc
221221
cd ${app_dir}
222222
python setup.cx_freeze.py bdist_msi
223+
tests:
224+
shell: bash
225+
help: Build self-contained zip-app
226+
source: |-
227+
python tests/test_ansible_taskrunner.py
223228
zipapp:
224229
shell: bash
225230
help: Build self-contained zip-app

ansible_taskrunner/cli.py

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@
4141
from libs.errorhandler import catchException
4242
from libs.errorhandler import ERR_ARGS_TASKF_OVERRIDE
4343
from libs.formatting import logging_format
44+
from libs.bastion_mode import init_bastion_settings
4445
from libs.help import SAMPLE_CONFIG
4546
from libs.help import SAMPLE_TASKS_MANIFEST
46-
if is_windows:
47-
from libs.help import SAMPLE_SFTP_CONFIG
47+
from libs.help import SAMPLE_SFTP_CONFIG
4848
from libs.logger import init_logger
4949
from libs.superduperconfig import SuperDuperConfig
5050
from libs.click_extras import ExtendedEpilog
@@ -83,7 +83,7 @@
8383

8484
# Private variables
8585
__author__ = 'etejeda'
86-
__version__ = '1.3.2'
86+
__version__ = '1.3.3'
8787
__program_name__ = 'tasks'
8888

8989
# Logging
@@ -262,12 +262,12 @@ def cli(**kwargs):
262262
@cli.command(cls=ExtendedHelp, help='Initialize local directory with sample files',
263263
epilog=init_epilog, context_settings=dict(max_content_width=180))
264264
@click.version_option(version=__version__)
265-
@click.option('--show-samples', '-m', is_flag=True,
265+
@click.option('---show-samples', is_flag=True,
266266
help='Only show a sample task manifest, don\'t write it')
267267
@extend_cli.bastion_mode
268268
def init(**kwargs):
269269
logger.info('Initializing ...')
270-
if kwargs['show_samples']:
270+
if kwargs.get('_show_samples'):
271271
logger.info('Displaying sample config')
272272
print(SAMPLE_CONFIG)
273273
logger.info('Displaying sample manifest')
@@ -292,28 +292,8 @@ def init(**kwargs):
292292
else:
293293
logger.info(
294294
'File exists, not writing manifest %s' % tasks_file)
295-
if is_windows:
296-
bastion_remote_path = kwargs.get('bastion_remote_path')
297-
bastion_host = kwargs['bastion_host']
298-
bastion_host_port = kwargs.get('bastion_host_port') or '22'
299-
bastion_user = kwargs.get('bastion_user') or local_username
300-
bastion_ssh_key_file = kwargs.get('bastion_ssh_key_file')
301-
if not bastion_remote_path:
302-
cur_dir = os.path.basename(os.getcwd())
303-
bastion_remote_path = '/home/{}/ansible-taskrunner/{}'.format(bastion_user, cur_dir)
304-
if not bastion_ssh_key_file:
305-
home_dir = os.path.expanduser('~')
306-
bastion_ssh_key_file = os.path.join(home_dir, '.ssh', 'id_rsa')
307-
if not os.path.exists(bastion_ssh_key_file):
308-
logger.error("SSH key '%s' not found, specify/generate a new/different key" % bastion_ssh_key_file)
309-
sys.exit(1)
310-
settings_vars = {
311-
'bastion_remote_path': bastion_remote_path,
312-
'bastion_host': bastion_host,
313-
'bastion_host_port': bastion_host_port,
314-
'bastion_user': bastion_user,
315-
'bastion_ssh_key_file': bastion_ssh_key_file.replace('\\', '\\\\')
316-
}
295+
if is_windows or kwargs.get('_bastion_mode'):
296+
settings_vars = init_bastion_settings(kwargs)
317297
if not os.path.exists(sftp_config_file):
318298
logger.info(
319299
'Existing sftp config not found, writing %s' % sftp_config_file)
@@ -395,13 +375,11 @@ def init(**kwargs):
395375
@click.option('---raw', is_flag=False,
396376
help='Specify raw options for underlying subprocess',
397377
required=False)
398-
@click.option('---bastion-mode',
399-
is_flag=True,
400-
help='Execute commands via a bastion host')
401378
@click.option('---echo',
402379
is_flag=True,
403380
help='Don\'t run, simply echo underlying commands')
404381
@extend_cli.options
382+
@extend_cli.bastion_mode
405383
@provider_cli.options
406384
def run(args=None, **kwargs):
407385
global param_set
@@ -417,8 +395,8 @@ def run(args=None, **kwargs):
417395
bastion_mode_enabled = True if is_windows else kwargs.get('_bastion_mode', False)
418396
if bastion_mode_enabled:
419397
bastion_settings = {
420-
# Turn bastion Mode off if we explicitly don't want it
421398
'config_file': yamlr.deep_get(config, 'bastion_mode.config_file', 'sftp-config.json'),
399+
# Turn bastion Mode off if we explicitly don't want it
422400
'enabled': yamlr.deep_get(config, 'bastion_mode.enabled', True),
423401
'keep_alive': yamlr.deep_get(config, 'bastion_mode.keep_alive', True),
424402
'poll_wait_time': yamlr.deep_get(config, 'bastion_mode.poll_wait_time', 5),
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import getpass
2+
import os
3+
import sys
4+
5+
local_username = getpass.getuser()
6+
7+
def init_bastion_settings(kwargs):
8+
bastion_remote_path = kwargs.get('_bastion_remote_path')
9+
bastion_host = kwargs.get('_bastion_host')
10+
bastion_host_port = kwargs.get('_bastion_host_port') or '22'
11+
bastion_user = kwargs.get('_bastion_user') or local_username
12+
bastion_ssh_key_file = kwargs.get('_bastion_ssh_key_file')
13+
if not bastion_remote_path:
14+
cur_dir = os.path.basename(os.getcwd())
15+
bastion_remote_path = '/home/{}/ansible-taskrunner/{}'.format(bastion_user, cur_dir)
16+
if not bastion_ssh_key_file:
17+
home_dir = os.path.expanduser('~')
18+
bastion_ssh_key_file = os.path.join(home_dir, '.ssh', 'id_rsa')
19+
if not os.path.exists(bastion_ssh_key_file):
20+
logger.error("SSH key '%s' not found, specify/generate a new/different key" % bastion_ssh_key_file)
21+
sys.exit(1)
22+
settings = {
23+
'bastion_remote_path': bastion_remote_path,
24+
'bastion_host': bastion_host,
25+
'bastion_host_port': bastion_host_port,
26+
'bastion_user': bastion_user,
27+
'bastion_ssh_key_file': bastion_ssh_key_file.replace('\\', '\\\\')
28+
}
29+
return settings

ansible_taskrunner/libs/click_extras/__init__.py

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
# Import third-party and custom modules
1515
try:
1616
import click
17-
import crayons
1817
except ImportError as e:
1918
print('Failed to import at least one required module')
2019
print('Error was %s' % e)
@@ -35,13 +34,13 @@ class ExtendedHelp(click.Command):
3534

3635
def colorize(self, formatter, color, string):
3736
if color == 'green':
38-
formatter.write_text('%s' % crayons.green(string))
37+
formatter.write_text('%s' % click.style(string, fg='green'))
3938
elif color == 'magenta':
40-
formatter.write_text('%s' % crayons.magenta(string))
39+
formatter.write_text('%s' % click.style(string, fg='magenta'))
4140
elif color == 'red':
42-
formatter.write_text('%s' % crayons.red(string))
41+
formatter.write_text('%s' % click.style(string, fg='red'))
4342
elif color == 'yellow':
44-
formatter.write_text('%s' % crayons.yellow(string))
43+
formatter.write_text('%s' % click.style(string, fg='yellow'))
4544
else:
4645
formatter.write_text(string)
4746

@@ -284,22 +283,38 @@ def process_options(self, parameters, func, is_required=False):
284283
return func
285284

286285
def bastion_mode(self, func):
287-
if sys.platform in ['win32', 'cygwin']:
288-
option = click.option('--bastion-host', '-h',
289-
help='Specify host (bastion mode settings file)',
290-
required=True)
286+
"""
287+
Explicity define command-line options for bastion mode operation
288+
"""
289+
option = click.option('---bastion-mode',
290+
is_flag=True,
291+
help='Force execution of commands via a bastion host')
292+
func = option(func)
293+
# Determine if bastion host is a required parameter
294+
if len(sys.argv) > 0 and sys.argv[0] == 'init':
295+
bastion_host_required = True if sys.argv[0] == 'init' else False
296+
elif len(sys.argv) > 1:
297+
bastion_host_required = True if sys.argv[1] == 'init' else False
298+
else:
299+
bastion_host_required = False
300+
# Determine if bastion mode is forced
301+
force_bastion_mode = True if '---bastion-mode' in sys.argv else False
302+
if sys.platform in ['win32', 'cygwin'] or force_bastion_mode:
303+
option = click.option('---bastion-host',
304+
help='Specify bastion host',
305+
required=bastion_host_required)
291306
func = option(func)
292-
option = click.option('--bastion-host-port', '-p',
293-
help='Specify host port (bastion mode settings file)')
307+
option = click.option('---bastion-host-port',
308+
help='Specify bastion host port')
294309
func = option(func)
295-
option = click.option('--bastion-user', '-u',
296-
help='Override username (bastion mode settings file)')
310+
option = click.option('---bastion-user',
311+
help='Override default username')
297312
func = option(func)
298-
option = click.option('--bastion-remote-path', '-r',
299-
help='Specify remote workspace (bastion mode settings file)')
313+
option = click.option('---bastion-remote-path',
314+
help='Specify remote workspace')
300315
func = option(func)
301-
option = click.option('--bastion-ssh-key-file', '-k',
302-
help='Override ssh key file (bastion mode settings file)')
316+
option = click.option('---bastion-ssh-key-file',
317+
help='Override default ssh key file')
303318
func = option(func)
304319
return func
305320

ansible_taskrunner/libs/cliutil/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def func(typ, value, traceback): return catchException(
9696
run_args = sys.argv[demark + 1:]
9797
run_flgs = [a for a in sys.argv[:demark] if a.startswith(
9898
'-') and a != sys.argv[arg_tf_index]]
99-
cli_args = run_flgs + run_args
99+
cli_args = [sys.argv[0]] + run_flgs + run_args
100100
if any([ext in tf_override for ext in ["yaml", "yml"]]):
101101
# Call main function as per parameter set
102102
invocation['cli'] = cli_args

ansible_taskrunner/libs/providers/ansible.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ def options(func):
5454
option = click.option('---debug', type=str, help='Start task run with ansible in debug mode',
5555
default=False, required=False)
5656
func = option(func)
57-
option = click.option('---inventory', is_flag=False, help='Override embedded inventory specification',
57+
option = click.option('---inventory', help='Override embedded inventory specification',
5858
required=False)
5959
func = option(func)
6060
return func
6161

62-
def invoke_bastion_mode(self, bastion_settings, invocation, remote_command):
62+
def invoke_bastion_mode(self, bastion_settings, invocation, remote_command, kwargs):
6363
"""Execute the underlying subprocess via a bastion host"""
6464
logger.info('Engage Bastion Mode')
6565
paramset = invocation.get('param_set')
@@ -85,8 +85,28 @@ def invoke_bastion_mode(self, bastion_settings, invocation, remote_command):
8585
)
8686
sys.exit(1)
8787
else:
88-
logger.error("Could not find %s, please run 'tasks init --help'" % bastion.config_file)
89-
sys.exit(1)
88+
# If no sftp config is found,
89+
# we should check if a bastion-host
90+
# was specified
91+
bastion_host = kwargs.get('_bastion_host')
92+
if bastion_host:
93+
from string import Template
94+
try:
95+
from libs.bastion_mode import init_bastion_settings
96+
from libs.help import SAMPLE_SFTP_CONFIG
97+
except ImportError as e:
98+
print('Error in %s ' % os.path.basename(self_file_name))
99+
print('Failed to import at least one required module')
100+
print('Error was %s' % e)
101+
print('Please install/update the required modules:')
102+
print('pip install -U -r requirements.txt')
103+
sys.exit(1)
104+
settings_vars = init_bastion_settings(kwargs)
105+
in_memory_sftp_settings = Template(SAMPLE_SFTP_CONFIG).safe_substitute(**settings_vars)
106+
settings = Struct(**json.loads(in_memory_sftp_settings))
107+
else:
108+
logger.error("Could not find %s, and no bastion-host was specified. Please run 'tasks init --help'" % bastion.config_file)
109+
sys.exit(1)
90110
# Import third-party and custom modules
91111
try:
92112
from libs.proc_mgmt import Remote_CLIInvocation
@@ -97,7 +117,7 @@ def invoke_bastion_mode(self, bastion_settings, invocation, remote_command):
97117
print('Error was %s' % e)
98118
print('Please install/update the required modules:')
99119
print('pip install -U -r requirements.txt')
100-
sys.exit(1)
120+
sys.exit(1)
101121
ssh_client = SSHUtilClient(settings)
102122
sftp_sync = ssh_client.sync()
103123
remote_sub_process = Remote_CLIInvocation(settings, ssh_client)
@@ -162,11 +182,6 @@ def invoke_bastion_mode(self, bastion_settings, invocation, remote_command):
162182
remote_path = os.path.normpath(_remote_path).replace('\\','/')
163183
logger.debug('Syncing {} to remote {}'.format(file_path, remote_path))
164184
sftp_sync.to_remote(file_path, remote_path)
165-
# Derive remote command
166-
# (accounting for parameter sets)
167-
if paramset:
168-
for p in enumerate(paramset):
169-
sys.argv.insert(p[0] + 1, p[1])
170185
# remote_command = ' '.join([a for a in sys.argv if a != '---bastion-mode'][1:])
171186
# tasks_file_override = invocation.get('tasks_file_override')
172187
# if tasks_file_override:
@@ -289,7 +304,7 @@ def invocation(self,
289304
print(ansible_command)
290305
else:
291306
if bastion_settings.get('enabled'):
292-
self.invoke_bastion_mode(bastion_settings, invocation, command)
307+
self.invoke_bastion_mode(bastion_settings, invocation, command, kwargs)
293308
else:
294309
sub_process = CLIInvocation()
295310
sub_process.call(command, debug_enabled=debug)

requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
click==6.7
2-
crayons==0.2.0
32
PyYAML==4.2b1
43
paramiko==2.6.0; sys_platform == 'win32' or sys_platform == 'cygwin'

setup.cfg

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = ansible_taskrunner
33
author = Engelbert Tejeda
44
author_email = berttejeda@gmail.com
55
description = ansible-playbook wrapper with YAML-abstracted python click cli options
6-
version: 1.3.2
6+
version: 1.3.3
77
url = https://github.com/berttejeda/ansible_taskrunner
88
keywords =
99
ansible
@@ -47,7 +47,6 @@ scripts =
4747
# somescript.py
4848
install_requires =
4949
click==6.7
50-
crayons==0.2.0
5150
PyYAML==4.2b1
5251
paramiko==2.6.0; sys_platform == 'win32' or sys_platform == 'cygwin'
5352

0 commit comments

Comments
 (0)