Skip to content

Commit

Permalink
Merge pull request #100 from berttejeda/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
berttejeda committed Nov 4, 2019
2 parents 9e43b11 + 03270b9 commit f4c8ff0
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 63 deletions.
6 changes: 6 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
History
=======

## Release 2019-11-04 v1.3.3

* Fixed bug in cli invocation when specifying taskfile override [7a07d1a](https://github.com/berttejeda/ansible-taskrunner/commit/7a07d1af4060d96a487df4a8a69e347a94deb56e)
* Removed dependency on crayons package for ansi colors [9d985ef](https://github.com/berttejeda/ansible-taskrunner/commit/9d985ef7f4791e81aecad6475242d18050f6a8f9)
* Support for ad-hoc bastion-mode (no sftp config file) [9d985ef](https://github.com/berttejeda/ansible-taskrunner/commit/9d985ef7f4791e81aecad6475242d18050f6a8f9)

## Release 2019-11-03 v1.3.2

* Values from env should only be enabled by option tag [a792c52](https://github.com/berttejeda/ansible-taskrunner/commit/a792c5200da38ec59106f84a031e8298e7a6fb0b)
Expand Down
5 changes: 5 additions & 0 deletions Makefile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,11 @@
pandoc README.md -f markdown -o ansible_taskrunner/build/README.html --template=${pandoc_template_dir}/html/easy_template.html --toc
cd ${app_dir}
python setup.cx_freeze.py bdist_msi
tests:
shell: bash
help: Build self-contained zip-app
source: |-
python tests/test_ansible_taskrunner.py
zipapp:
shell: bash
help: Build self-contained zip-app
Expand Down
40 changes: 9 additions & 31 deletions ansible_taskrunner/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@
from libs.errorhandler import catchException
from libs.errorhandler import ERR_ARGS_TASKF_OVERRIDE
from libs.formatting import logging_format
from libs.bastion_mode import init_bastion_settings
from libs.help import SAMPLE_CONFIG
from libs.help import SAMPLE_TASKS_MANIFEST
if is_windows:
from libs.help import SAMPLE_SFTP_CONFIG
from libs.help import SAMPLE_SFTP_CONFIG
from libs.logger import init_logger
from libs.superduperconfig import SuperDuperConfig
from libs.click_extras import ExtendedEpilog
Expand Down Expand Up @@ -83,7 +83,7 @@

# Private variables
__author__ = 'etejeda'
__version__ = '1.3.2'
__version__ = '1.3.3'
__program_name__ = 'tasks'

# Logging
Expand Down Expand Up @@ -262,12 +262,12 @@ def cli(**kwargs):
@cli.command(cls=ExtendedHelp, help='Initialize local directory with sample files',
epilog=init_epilog, context_settings=dict(max_content_width=180))
@click.version_option(version=__version__)
@click.option('--show-samples', '-m', is_flag=True,
@click.option('---show-samples', is_flag=True,
help='Only show a sample task manifest, don\'t write it')
@extend_cli.bastion_mode
def init(**kwargs):
logger.info('Initializing ...')
if kwargs['show_samples']:
if kwargs.get('_show_samples'):
logger.info('Displaying sample config')
print(SAMPLE_CONFIG)
logger.info('Displaying sample manifest')
Expand All @@ -292,28 +292,8 @@ def init(**kwargs):
else:
logger.info(
'File exists, not writing manifest %s' % tasks_file)
if is_windows:
bastion_remote_path = kwargs.get('bastion_remote_path')
bastion_host = kwargs['bastion_host']
bastion_host_port = kwargs.get('bastion_host_port') or '22'
bastion_user = kwargs.get('bastion_user') or local_username
bastion_ssh_key_file = kwargs.get('bastion_ssh_key_file')
if not bastion_remote_path:
cur_dir = os.path.basename(os.getcwd())
bastion_remote_path = '/home/{}/ansible-taskrunner/{}'.format(bastion_user, cur_dir)
if not bastion_ssh_key_file:
home_dir = os.path.expanduser('~')
bastion_ssh_key_file = os.path.join(home_dir, '.ssh', 'id_rsa')
if not os.path.exists(bastion_ssh_key_file):
logger.error("SSH key '%s' not found, specify/generate a new/different key" % bastion_ssh_key_file)
sys.exit(1)
settings_vars = {
'bastion_remote_path': bastion_remote_path,
'bastion_host': bastion_host,
'bastion_host_port': bastion_host_port,
'bastion_user': bastion_user,
'bastion_ssh_key_file': bastion_ssh_key_file.replace('\\', '\\\\')
}
if is_windows or kwargs.get('_bastion_mode'):
settings_vars = init_bastion_settings(kwargs)
if not os.path.exists(sftp_config_file):
logger.info(
'Existing sftp config not found, writing %s' % sftp_config_file)
Expand Down Expand Up @@ -395,13 +375,11 @@ def init(**kwargs):
@click.option('---raw', is_flag=False,
help='Specify raw options for underlying subprocess',
required=False)
@click.option('---bastion-mode',
is_flag=True,
help='Execute commands via a bastion host')
@click.option('---echo',
is_flag=True,
help='Don\'t run, simply echo underlying commands')
@extend_cli.options
@extend_cli.bastion_mode
@provider_cli.options
def run(args=None, **kwargs):
global param_set
Expand All @@ -417,8 +395,8 @@ def run(args=None, **kwargs):
bastion_mode_enabled = True if is_windows else kwargs.get('_bastion_mode', False)
if bastion_mode_enabled:
bastion_settings = {
# Turn bastion Mode off if we explicitly don't want it
'config_file': yamlr.deep_get(config, 'bastion_mode.config_file', 'sftp-config.json'),
# Turn bastion Mode off if we explicitly don't want it
'enabled': yamlr.deep_get(config, 'bastion_mode.enabled', True),
'keep_alive': yamlr.deep_get(config, 'bastion_mode.keep_alive', True),
'poll_wait_time': yamlr.deep_get(config, 'bastion_mode.poll_wait_time', 5),
Expand Down
29 changes: 29 additions & 0 deletions ansible_taskrunner/libs/bastion_mode/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import getpass
import os
import sys

local_username = getpass.getuser()

def init_bastion_settings(kwargs):
bastion_remote_path = kwargs.get('_bastion_remote_path')
bastion_host = kwargs.get('_bastion_host')
bastion_host_port = kwargs.get('_bastion_host_port') or '22'
bastion_user = kwargs.get('_bastion_user') or local_username
bastion_ssh_key_file = kwargs.get('_bastion_ssh_key_file')
if not bastion_remote_path:
cur_dir = os.path.basename(os.getcwd())
bastion_remote_path = '/home/{}/ansible-taskrunner/{}'.format(bastion_user, cur_dir)
if not bastion_ssh_key_file:
home_dir = os.path.expanduser('~')
bastion_ssh_key_file = os.path.join(home_dir, '.ssh', 'id_rsa')
if not os.path.exists(bastion_ssh_key_file):
logger.error("SSH key '%s' not found, specify/generate a new/different key" % bastion_ssh_key_file)
sys.exit(1)
settings = {
'bastion_remote_path': bastion_remote_path,
'bastion_host': bastion_host,
'bastion_host_port': bastion_host_port,
'bastion_user': bastion_user,
'bastion_ssh_key_file': bastion_ssh_key_file.replace('\\', '\\\\')
}
return settings
49 changes: 32 additions & 17 deletions ansible_taskrunner/libs/click_extras/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
# Import third-party and custom modules
try:
import click
import crayons
except ImportError as e:
print('Failed to import at least one required module')
print('Error was %s' % e)
Expand All @@ -35,13 +34,13 @@ class ExtendedHelp(click.Command):

def colorize(self, formatter, color, string):
if color == 'green':
formatter.write_text('%s' % crayons.green(string))
formatter.write_text('%s' % click.style(string, fg='green'))
elif color == 'magenta':
formatter.write_text('%s' % crayons.magenta(string))
formatter.write_text('%s' % click.style(string, fg='magenta'))
elif color == 'red':
formatter.write_text('%s' % crayons.red(string))
formatter.write_text('%s' % click.style(string, fg='red'))
elif color == 'yellow':
formatter.write_text('%s' % crayons.yellow(string))
formatter.write_text('%s' % click.style(string, fg='yellow'))
else:
formatter.write_text(string)

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

def bastion_mode(self, func):
if sys.platform in ['win32', 'cygwin']:
option = click.option('--bastion-host', '-h',
help='Specify host (bastion mode settings file)',
required=True)
"""
Explicity define command-line options for bastion mode operation
"""
option = click.option('---bastion-mode',
is_flag=True,
help='Force execution of commands via a bastion host')
func = option(func)
# Determine if bastion host is a required parameter
if len(sys.argv) > 0 and sys.argv[0] == 'init':
bastion_host_required = True if sys.argv[0] == 'init' else False
elif len(sys.argv) > 1:
bastion_host_required = True if sys.argv[1] == 'init' else False
else:
bastion_host_required = False
# Determine if bastion mode is forced
force_bastion_mode = True if '---bastion-mode' in sys.argv else False
if sys.platform in ['win32', 'cygwin'] or force_bastion_mode:
option = click.option('---bastion-host',
help='Specify bastion host',
required=bastion_host_required)
func = option(func)
option = click.option('--bastion-host-port', '-p',
help='Specify host port (bastion mode settings file)')
option = click.option('---bastion-host-port',
help='Specify bastion host port')
func = option(func)
option = click.option('--bastion-user', '-u',
help='Override username (bastion mode settings file)')
option = click.option('---bastion-user',
help='Override default username')
func = option(func)
option = click.option('--bastion-remote-path', '-r',
help='Specify remote workspace (bastion mode settings file)')
option = click.option('---bastion-remote-path',
help='Specify remote workspace')
func = option(func)
option = click.option('--bastion-ssh-key-file', '-k',
help='Override ssh key file (bastion mode settings file)')
option = click.option('---bastion-ssh-key-file',
help='Override default ssh key file')
func = option(func)
return func

Expand Down
2 changes: 1 addition & 1 deletion ansible_taskrunner/libs/cliutil/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def func(typ, value, traceback): return catchException(
run_args = sys.argv[demark + 1:]
run_flgs = [a for a in sys.argv[:demark] if a.startswith(
'-') and a != sys.argv[arg_tf_index]]
cli_args = run_flgs + run_args
cli_args = [sys.argv[0]] + run_flgs + run_args
if any([ext in tf_override for ext in ["yaml", "yml"]]):
# Call main function as per parameter set
invocation['cli'] = cli_args
Expand Down
37 changes: 26 additions & 11 deletions ansible_taskrunner/libs/providers/ansible.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ def options(func):
option = click.option('---debug', type=str, help='Start task run with ansible in debug mode',
default=False, required=False)
func = option(func)
option = click.option('---inventory', is_flag=False, help='Override embedded inventory specification',
option = click.option('---inventory', help='Override embedded inventory specification',
required=False)
func = option(func)
return func

def invoke_bastion_mode(self, bastion_settings, invocation, remote_command):
def invoke_bastion_mode(self, bastion_settings, invocation, remote_command, kwargs):
"""Execute the underlying subprocess via a bastion host"""
logger.info('Engage Bastion Mode')
paramset = invocation.get('param_set')
Expand All @@ -85,8 +85,28 @@ def invoke_bastion_mode(self, bastion_settings, invocation, remote_command):
)
sys.exit(1)
else:
logger.error("Could not find %s, please run 'tasks init --help'" % bastion.config_file)
sys.exit(1)
# If no sftp config is found,
# we should check if a bastion-host
# was specified
bastion_host = kwargs.get('_bastion_host')
if bastion_host:
from string import Template
try:
from libs.bastion_mode import init_bastion_settings
from libs.help import SAMPLE_SFTP_CONFIG
except ImportError as e:
print('Error in %s ' % os.path.basename(self_file_name))
print('Failed to import at least one required module')
print('Error was %s' % e)
print('Please install/update the required modules:')
print('pip install -U -r requirements.txt')
sys.exit(1)
settings_vars = init_bastion_settings(kwargs)
in_memory_sftp_settings = Template(SAMPLE_SFTP_CONFIG).safe_substitute(**settings_vars)
settings = Struct(**json.loads(in_memory_sftp_settings))
else:
logger.error("Could not find %s, and no bastion-host was specified. Please run 'tasks init --help'" % bastion.config_file)
sys.exit(1)
# Import third-party and custom modules
try:
from libs.proc_mgmt import Remote_CLIInvocation
Expand All @@ -97,7 +117,7 @@ def invoke_bastion_mode(self, bastion_settings, invocation, remote_command):
print('Error was %s' % e)
print('Please install/update the required modules:')
print('pip install -U -r requirements.txt')
sys.exit(1)
sys.exit(1)
ssh_client = SSHUtilClient(settings)
sftp_sync = ssh_client.sync()
remote_sub_process = Remote_CLIInvocation(settings, ssh_client)
Expand Down Expand Up @@ -162,11 +182,6 @@ def invoke_bastion_mode(self, bastion_settings, invocation, remote_command):
remote_path = os.path.normpath(_remote_path).replace('\\','/')
logger.debug('Syncing {} to remote {}'.format(file_path, remote_path))
sftp_sync.to_remote(file_path, remote_path)
# Derive remote command
# (accounting for parameter sets)
if paramset:
for p in enumerate(paramset):
sys.argv.insert(p[0] + 1, p[1])
# remote_command = ' '.join([a for a in sys.argv if a != '---bastion-mode'][1:])
# tasks_file_override = invocation.get('tasks_file_override')
# if tasks_file_override:
Expand Down Expand Up @@ -289,7 +304,7 @@ def invocation(self,
print(ansible_command)
else:
if bastion_settings.get('enabled'):
self.invoke_bastion_mode(bastion_settings, invocation, command)
self.invoke_bastion_mode(bastion_settings, invocation, command, kwargs)
else:
sub_process = CLIInvocation()
sub_process.call(command, debug_enabled=debug)
Expand Down
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
click==6.7
crayons==0.2.0
PyYAML==4.2b1
paramiko==2.6.0; sys_platform == 'win32' or sys_platform == 'cygwin'
3 changes: 1 addition & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = ansible_taskrunner
author = Engelbert Tejeda
author_email = berttejeda@gmail.com
description = ansible-playbook wrapper with YAML-abstracted python click cli options
version: 1.3.2
version: 1.3.3
url = https://github.com/berttejeda/ansible_taskrunner
keywords =
ansible
Expand Down Expand Up @@ -47,7 +47,6 @@ scripts =
# somescript.py
install_requires =
click==6.7
crayons==0.2.0
PyYAML==4.2b1
paramiko==2.6.0; sys_platform == 'win32' or sys_platform == 'cygwin'

Expand Down

0 comments on commit f4c8ff0

Please sign in to comment.