Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Versions review - necessary updates and features #45

Merged
merged 12 commits into from
Nov 18, 2019
Merged
34 changes: 34 additions & 0 deletions .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Python package

on: [push]

jobs:
build:

runs-on: ubuntu-latest
strategy:
max-parallel: 3
matrix:
python-version: [3.5, 3.6, 3.7]

steps:
- uses: actions/checkout@v1
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Lint with flake8
run: |
pip install flake8
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --ignore=D203,D100,D103,D202,D200,W504 --max-complexity=10 --max-line-length=120 --statistics
- name: Test with pytest
run: |
pip install pytest
pytest --tb native -ra -v -s
17 changes: 16 additions & 1 deletion CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- List yet unreleased changes.

## [0.2.0] - 2019-11-16
### Changed
- Dependency versions updated to current libjuju (waiting only for macaroonbakery to also support python 3.8)
- Allow upgrade without using origin - if not supported by charm - origin now has to be specified explicitly
- Charm upgrade does not check locally built ('local:app') charms
- When using upgrade-only, charms are not compared with charmstore versions, skipping straight to upgrade of services
- Improved wait on charm config change when setting origin
### Added
- Support passing of --cacert $CACERT parameter in authentication using libjuju when accessing from remote machine
- Origin keys can be specified as parameter (key=value,key2=value2)
- Upgrade action name as parameters - defaulting to 'openstack-upgrade' in perform_upgrade
- Support key value parameters for upgrade action (key=value,key2=value2)
- Debug parameter - default log level without debug is INFO
- Async (class) methods mocking for unit-testing of jujuna code
- Tests for upgrade - perform_upgrade (covering simple, rolling and hacluster cases)

## [0.1.0] - 2018-10-24
### Added
- Initial opensourcing commit

2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.1.6
0.2.0
117 changes: 76 additions & 41 deletions jujuna/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,30 @@

from juju import loop

from jujuna.helper import log_traceback

from jujuna.deploy import deploy # noqa
from jujuna.upgrade import upgrade # noqa
from jujuna.tests import test # noqa
from jujuna.clean import clean # noqa


logger = logging.getLogger('jujuna')
logger.setLevel(logging.DEBUG)
logger.setLevel(logging.INFO)

# Double logging cleanup
while logger.handlers:
logger.handlers.pop()

logHandler = logging.StreamHandler(sys.stdout)
logHandler.setLevel(logging.DEBUG)
logHandler.setLevel(logging.INFO)
logFormatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logHandler.setFormatter(logFormatter)
logger.addHandler(logHandler)


def get_parser():
parser = argparse.ArgumentParser(
# formatter_class=utils.ParagraphDescriptionFormatter,
description="Deploy a local bundle, execute upgrade procedure, "
"run the deployment through a suite of tests to ensure "
"that it can handle the types of operations and failures "
Expand All @@ -51,17 +52,21 @@ def get_parser():
help="Juju endpoint (requires model uuid instead of name)")
p_deploy.add_argument("--username", default=None, dest="username", help="Juju username")
p_deploy.add_argument("--password", default=None, dest="password", help="Juju password")
p_deploy.add_argument("--cacert", default=None, dest="cacert", help="Juju CA certificate")
p_deploy.add_argument("--debug", action='store_true', help="Log level debug.")

p_upgrade = subparsers.add_parser('upgrade', help="Upgrade applications deployed in the current or selected model")
p_upgrade.add_argument("-c", "--controller", default=None, dest="ctrl_name", help="Controller (def: current)")
p_upgrade.add_argument("-m", "--model", default=None, dest="model_name", help="Model to use instead of current")
p_upgrade.add_argument("-o", "--origin", default='cloud:xenial-ocata', dest="origin",
p_upgrade.add_argument("-o", "--origin", default='', dest="origin",
help="""Openstack origin:
cloud:xenial-newton,
cloud:xenial-ocata (default),
cloud:xenial-pike,
cloud:xenial-queens,
cloud:bionic-rocky
'cloud:xenial-newton',
'cloud:xenial-ocata',
'cloud:xenial-pike',
'cloud:xenial-queens',
'cloud:bionic-rocky',
'cloud:bionic-stein',
'cloud:bionic-train',
""")
p_upgrade.add_argument("-a", "--apps", nargs='*', default=[], help="Apps to be upgraded (ordered)")
p_upgrade.add_argument("-i", "--ignore-errors", action='store_true', dest='ignore_errors',
Expand All @@ -70,8 +75,14 @@ def get_parser():
p_upgrade.add_argument("-e", "--evacuate", action='store_true', help="Evacuate nova-compute nodes during upgrade")
p_upgrade.add_argument("--upgrade-only", action='store_true', dest="upgrade_only",
help="Upgrade using upgrade hooks without changing the revision")
p_upgrade.add_argument("--charms-only", action='store_true',
p_upgrade.add_argument("--charms-only", action='store_true', dest="charms_only",
help="Upgrade only charms without running upgrade hooks")
p_upgrade.add_argument("--upgrade-action", dest="upgrade_action", default=None,
help="Action name to upgrade application")
p_upgrade.add_argument("--upgrade-params", dest="upgrade_params", default=None,
help="Action parameters comma separated e.g. 'service=name,version=2'")
p_upgrade.add_argument("--origin-keys", dest="origin_keys", default=None,
help="Config keys to set origin in apps e.g. 'ceph-mon=source,ceph-mon=source'")
p_upgrade.add_argument("--dry-run", action='store_true', dest="dry_run",
help="Dry run - only show changes without upgrading")
p_upgrade.add_argument("-t", "--timeout", default=0, type=int, help="Timeout after N seconds.")
Expand All @@ -81,6 +92,8 @@ def get_parser():
help="Juju endpoint (requires model uuid instead of name)")
p_upgrade.add_argument("--username", default=None, dest="username", help="Juju username")
p_upgrade.add_argument("--password", default=None, dest="password", help="Juju password")
p_upgrade.add_argument("--cacert", default=None, dest="cacert", help="Juju CA certificate")
p_upgrade.add_argument("--debug", action='store_true', help="Log level debug.")

p_test = subparsers.add_parser('test', help="Test applications in the current or selected model")
p_test.add_argument("test_suite", type=argparse.FileType('r'),
Expand All @@ -92,6 +105,8 @@ def get_parser():
help="Juju endpoint (requires model uuid instead of name)")
p_test.add_argument("--username", default=None, dest="username", help="Juju username")
p_test.add_argument("--password", default=None, dest="password", help="Juju password")
p_test.add_argument("--cacert", default=None, dest="cacert", help="Juju CA certificate")
p_test.add_argument("--debug", action='store_true', help="Log level debug.")

p_clean = subparsers.add_parser(
'clean',
Expand All @@ -109,14 +124,11 @@ def get_parser():
help="Juju endpoint (requires model uuid instead of name)")
p_clean.add_argument("--username", default=None, dest="username", help="Juju username")
p_clean.add_argument("--password", default=None, dest="password", help="Juju password")
p_clean.add_argument("--cacert", default=None, dest="cacert", help="Juju CA certificate")
p_clean.add_argument("--debug", action='store_true', help="Log level debug.")

argcomplete.autocomplete(parser)
# options = add_bundle_opts(options, parser)

# if not utils.valid_bundle_or_spell(options.path):
# parser.error('Invalid bundle directory: %s' % options.path)

# configLogging(options)
return parser


Expand All @@ -129,47 +141,70 @@ async def run_action(action, timeout, args):
"""
selected_action = globals()[action]

# Remove used vars
del vars(args)['action']
if 'timeout' in vars(args):
del vars(args)['timeout']

if timeout:
if timeout == 0:
return await selected_action(**args)
else:
at = None
try:
async with async_timeout.timeout(timeout) as at:
return await selected_action(**vars(args))
except Exception:
ret = await selected_action(**args)
except Exception as e:
# Exit with timeout code if expired
if at and at.expired:
logger.warn('Operation timed out!')
return 124
ret = 124
logger.warn('Action {} timed out!'.format(action))
else:
raise
ret = 1
logger.warn('Action {} failed!'.format(action))
logger.warn(str(e))
return ret


def load_kv_arg(args, item):
if args.get(item, False):
up_list = args[item].split(',')
upgrade_params = dict([
item.split('=') if '=' in item else [item, True] for item in up_list
])
args[item] = upgrade_params
else:
return await selected_action(**vars(args))
args[item] = {}
return args


def action(args):
def parse_args(argv):
parser = get_parser()
try:
if hasattr(args, 'action'):
return loop.run(run_action(args.action, args.timeout, args))
except Exception as e:
logger.warn(e)
return 1

return 255
parsed = parser.parse_args(argv)
args = vars(parsed)
action = args.pop('action')
timeout = args.pop('timeout')


def parse_args(args):
parser = get_parser()
return parser.parse_args(args)
args = load_kv_arg(args, 'upgrade_params')
args = load_kv_arg(args, 'origin_keys')
except Exception as e:
log_traceback(e)
parser.print_help()
sys.exit(1)
try:
if args.get('debug', False):
logger.setLevel(logging.DEBUG)
logHandler.setLevel(logging.DEBUG)
except Exception:
pass
return action, timeout, args


def main():
args = parse_args(sys.argv[1:])
action, timeout, args = parse_args(sys.argv[1:])

ret = action(args)
try:
ret = loop.run(
run_action(action, timeout, args)
)
except Exception as e:
log_traceback(e)
ret = 1

sys.exit(ret)

Expand Down
16 changes: 10 additions & 6 deletions jujuna/clean.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,17 @@ async def _block(log_count):


async def clean(
ctrl_name=None,
model_name=None,
ctrl_name='',
model_name='',
ignore=[],
wait=False,
force=False,
dry_run=False,
endpoint=None,
username=None,
password=None
endpoint='',
username='',
password='',
cacert='',
**kwargs
):
"""Destroy applications present in the current or selected model.

Expand All @@ -66,13 +68,15 @@ async def clean(
:param endpoint: string
:param username: string
:param password: string
:param cacert: string
"""
controller, model = await connect_juju(
ctrl_name,
model_name,
endpoint=endpoint,
username=username,
password=password
password=password,
cacert=cacert
)

try:
Expand Down
Loading