From 7494ba7b9ce7e03c816f1db0c9521053a3a8d917 Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Wed, 25 Mar 2020 14:06:32 -0400 Subject: [PATCH 1/9] Initial cut at tower_job_wait conversion --- .../plugins/modules/tower_job_wait.py | 145 +++++++++++------- .../targets/tower_job_wait/tasks/main.yml | 108 +++++++++++-- 2 files changed, 184 insertions(+), 69 deletions(-) diff --git a/awx_collection/plugins/modules/tower_job_wait.py b/awx_collection/plugins/modules/tower_job_wait.py index 00011468e0..c0ee082cc1 100644 --- a/awx_collection/plugins/modules/tower_job_wait.py +++ b/awx_collection/plugins/modules/tower_job_wait.py @@ -42,10 +42,12 @@ options: description: - Maximum time in seconds to wait for a job to finish. type: int - -requirements: -- ansible-tower-cli >= 3.0.2 - + tower_oauthtoken: + description: + - The Tower OAuth token to use. + required: False + type: str + version_added: "3.7" extends_documentation_fragment: awx.awx.auth ''' @@ -90,22 +92,26 @@ status: ''' -from ..module_utils.ansible_tower import TowerModule, tower_auth_config, tower_check_mode -from ansible.module_utils.six import PY2 -from ansible.module_utils.six.moves import cStringIO as StringIO -from codecs import getwriter +from ..module_utils.tower_api import TowerModule +import time +import itertools -try: - import tower_cli - import tower_cli.exceptions as exc +def check_job(module, job_url): + response = module.get_endpoint(job_url) + if response['status_code'] != 200: + module.fail_json(msg="Unable to read job from Tower {0}: {1}".format(response['status_code'], module.extract_errors_from_response(response))) - from tower_cli.conf import settings -except ImportError: - pass + # Since we were successful, extract the fields we want to return + for k in ('id', 'status', 'elapsed', 'started', 'finished'): + module.json_output[k] = response['json'].get(k) + + # And finally return the payload + return response['json'] def main(): + # Any additional arguments that are not fields of the item can be added here argument_spec = dict( job_id=dict(type='int', required=True), timeout=dict(type='int'), @@ -113,55 +119,82 @@ def main(): max_interval=dict(type='float', default=30), ) - module = TowerModule( - argument_spec, - supports_check_mode=True - ) + # Create a module for ourselves + module = TowerModule(argument_spec=argument_spec, supports_check_mode=True) - json_output = {} - fail_json = None + # Extract our parameters + job_id = module.params.get('job_id') + timeout = module.params.get('timeout') + min_interval = module.params.get('min_interval') + max_interval = module.params.get('max_interval') - tower_auth = tower_auth_config(module) - with settings.runtime_values(**tower_auth): - tower_check_mode(module) - job = tower_cli.get_resource('job') - params = module.params.copy() + # Attempt to look up job based on the provided id + job = module.get_one('jobs', **{ + 'data': { + 'id': job_id, + } + }) - # tower-cli gets very noisy when monitoring. - # We pass in our our outfile to suppress the out during our monitor call. - if PY2: - outfile = getwriter('utf-8')(StringIO()) - else: - outfile = StringIO() - params['outfile'] = outfile + if job is None: + module.fail_json(msg='Unable to wait, on job {0} that ID does not exist in Tower.'.format(job_id)) - job_id = params.get('job_id') - try: - result = job.monitor(job_id, **params) - except exc.Timeout: - result = job.status(job_id) - result['id'] = job_id - json_output['msg'] = 'Timeout waiting for job to finish.' - json_output['timeout'] = True - except exc.NotFound as excinfo: - fail_json = dict(msg='Unable to wait, no job_id {0} found: {1}'.format(job_id, excinfo), changed=False) - except exc.JobFailure as excinfo: - fail_json = dict(msg='Job with id={0} failed, error: {1}'.format(job_id, excinfo)) - fail_json['success'] = False - result = job.get(job_id) - for k in ('id', 'status', 'elapsed', 'started', 'finished'): - fail_json[k] = result.get(k) - except (exc.ConnectionError, exc.BadRequest, exc.AuthError) as excinfo: - fail_json = dict(msg='Unable to wait for job: {0}'.format(excinfo), changed=False) + job_url = job['url'] - if fail_json is not None: - module.fail_json(**fail_json) + # This comes from tower_cli/models/base.py from the old tower-cli + dots = itertools.cycle([0, 1, 2, 3]) + interval = min_interval + start = time.time() - json_output['success'] = True - for k in ('id', 'status', 'elapsed', 'started', 'finished'): - json_output[k] = result.get(k) + # Poll the Ansible Tower instance for status, and print the status to the outfile (usually standard out). + # + # Note that this is one of the few places where we use `secho` even though we're in a function that might + # theoretically be imported and run in Python. This seems fine; outfile can be set to /dev/null and very + # much the normal use for this method should be CLI monitoring. + result = check_job(module, job_url) - module.exit_json(**json_output) + last_poll = time.time() + timeout_check = 0 + while not result['finished']: + # Sanity check: Have we officially timed out? + # The timeout check is incremented below, so this is checking to see if we were timed out as of + # the previous iteration. If we are timed out, abort. + if timeout and timeout_check - start > timeout: + module.json_output['msg'] = "Monitoring aborted due to timeout" + module.fail_json(**module.json_output) + + # If the outfile is a TTY, print the current status. + output = '\rCurrent status: %s%s' % (result['status'], '.' * next(dots)) + + # Put the process to sleep briefly. + time.sleep(0.2) + + # Sanity check: Have we reached our timeout? + # If we're about to time out, then we need to ensure that we do one last check. + # + # Note that the actual timeout will be performed at the start of the **next** iteration, + # so there's a chance for the job's completion to be noted first. + timeout_check = time.time() + if timeout and timeout_check - start > timeout: + last_poll -= interval + + # If enough time has elapsed, ask the server for a new status. + # + # Note that this doesn't actually do a status check every single time; we want the "spinner" to + # spin even if we're not actively doing a check. + # + # So, what happens is that we are "counting down" (actually up) to the next time that we intend + # to do a check, and once that time hits, we do the status check as part of the normal cycle. + if time.time() - last_poll > interval: + result = check_job(module, job_url) + last_poll = time.time() + interval = min(interval * 1.5, max_interval) + + # If the job has failed, we want to raise an Exception for that so we get a non-zero response. + if result['failed']: + module.json_output['msg'] = 'Job with id {0} failed'.format(job_id) + module.fail_json(**module.json_output) + + module.exit_json(**module.json_output) if __name__ == '__main__': diff --git a/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml b/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml index 589ea8629f..29520b0281 100644 --- a/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml +++ b/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml @@ -1,18 +1,24 @@ --- -- name: Launch a Job Template - tower_job_launch: - job_template: "Demo Job Template" - register: job +- name: generate random string for template and project + set_fact: + jt_name: "AWX-Collection-tests-tower_job_wait-long_running-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" + proj_name: "AWX-Collection-tests-tower_job_wait-long_running-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" -- assert: - that: - - "job is changed" - - "job.status == 'pending'" +- name: Assure that demo project exists + tower_project: + name: "{{ proj_name }}" + scm_type: 'git' + scm_url: 'https://github.com/ansible/test-playbooks.git' + scm_update_on_launch: true + organization: Default -- name: Wait for the Job to finish - tower_job_wait: - job_id: "{{ job.id }}" - timeout: 60 +- name: Create a job template + tower_job_template: + name: "{{ jt_name }}" + playbook: "sleep.yml" + job_type: run + project: "{{ proj_name }}" + inventory: "Demo Inventory" - name: Check module fails with correct msg tower_job_wait: @@ -22,4 +28,80 @@ - assert: that: - - "result.msg =='Unable to wait, no job_id 99999999 found: The requested object could not be found.'" + - result is failed + - "result.msg =='Unable to wait, on job 99999999 that ID does not exist in Tower.'" + +- name: Launch Demo Job Template (take happy path) + tower_job_launch: + job_template: "Jenkins Export Vars" + register: job + +- assert: + that: + - job is changed + - "job.status == 'pending'" + +- name: Wait for the Job to finish + tower_job_wait: + job_id: "{{ job.id }}" + register: wait_results + +# Make sure we worked and that we have some data in our results +- assert: + that: + - wait_results is successful + - "'elapsed' in wait_results" + - "'id' in wait_results" + +- name: Launch a long running job + tower_job_launch: + job_template: "{{ jt_name }}" + register: job + +- assert: + that: + - job is changed + - "job.status == 'pending'" + +- name: Timeout waiting for the job to complete + tower_job_wait: + job_id: "{{ job.id }}" + timeout: 5 + ignore_errors: True + register: wait_results + +# Make sure that we failed and that we have some data in our results +- assert: + that: + - wait_results is failed + - "wait_results.msg == 'Monitoring aborted due to timeout'" + - "'id' in wait_results" + +- name: Async cancel the long running job + tower_job_cancel: + job_id: "{{ job.id }}" + async: 3600 + poll: 0 + +- name: Wait for the job to exit on cancel + tower_job_wait: + job_id: "{{ job.id }}" + register: wait_results + ignore_errors: True + +- assert: + that: + - wait_results is failed + - 'wait_results.status == "canceled"' + - "wait_results.msg == 'Job with id {{ job.id }} failed'" + +- name: Delete the job template + tower_job_template: + name: "{{ jt_name }}" + state: absent + +- name: Delete the project + tower_project: + name: "{{ proj_name }}" + organization: Default + state: absent From f6bfdef34d9a06dcd410e18a44b2541f6f138188 Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Wed, 25 Mar 2020 14:09:30 -0400 Subject: [PATCH 2/9] Removed old secho comment from Tower-CLI Fixed job name for tests --- awx_collection/plugins/modules/tower_job_wait.py | 4 ---- .../tests/integration/targets/tower_job_wait/tasks/main.yml | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/awx_collection/plugins/modules/tower_job_wait.py b/awx_collection/plugins/modules/tower_job_wait.py index c0ee082cc1..640eb1b525 100644 --- a/awx_collection/plugins/modules/tower_job_wait.py +++ b/awx_collection/plugins/modules/tower_job_wait.py @@ -146,10 +146,6 @@ def main(): start = time.time() # Poll the Ansible Tower instance for status, and print the status to the outfile (usually standard out). - # - # Note that this is one of the few places where we use `secho` even though we're in a function that might - # theoretically be imported and run in Python. This seems fine; outfile can be set to /dev/null and very - # much the normal use for this method should be CLI monitoring. result = check_job(module, job_url) last_poll = time.time() diff --git a/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml b/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml index 29520b0281..92457889c9 100644 --- a/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml +++ b/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml @@ -33,7 +33,7 @@ - name: Launch Demo Job Template (take happy path) tower_job_launch: - job_template: "Jenkins Export Vars" + job_template: "Demo Job Template" register: job - assert: From 61287f6b364326d188cc4f254b5220b352643889 Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Wed, 25 Mar 2020 14:48:09 -0400 Subject: [PATCH 3/9] Removing old unneeded output and fixing comments --- awx_collection/plugins/modules/tower_job_wait.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/awx_collection/plugins/modules/tower_job_wait.py b/awx_collection/plugins/modules/tower_job_wait.py index 640eb1b525..96cf6a8c84 100644 --- a/awx_collection/plugins/modules/tower_job_wait.py +++ b/awx_collection/plugins/modules/tower_job_wait.py @@ -94,7 +94,6 @@ status: from ..module_utils.tower_api import TowerModule import time -import itertools def check_job(module, job_url): @@ -141,15 +140,16 @@ def main(): job_url = job['url'] # This comes from tower_cli/models/base.py from the old tower-cli - dots = itertools.cycle([0, 1, 2, 3]) interval = min_interval start = time.time() - # Poll the Ansible Tower instance for status, and print the status to the outfile (usually standard out). + # Get the initial job status from Tower, this will exit if there are any issues result = check_job(module, job_url) last_poll = time.time() timeout_check = 0 + + # Loop while the job is not yet completed while not result['finished']: # Sanity check: Have we officially timed out? # The timeout check is incremented below, so this is checking to see if we were timed out as of @@ -158,9 +158,6 @@ def main(): module.json_output['msg'] = "Monitoring aborted due to timeout" module.fail_json(**module.json_output) - # If the outfile is a TTY, print the current status. - output = '\rCurrent status: %s%s' % (result['status'], '.' * next(dots)) - # Put the process to sleep briefly. time.sleep(0.2) From e03911d378fbc918447a100527f2098e0825d360 Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Thu, 26 Mar 2020 09:17:18 -0400 Subject: [PATCH 4/9] Depricate min and max interval in favor of interval --- .../plugins/modules/tower_job_wait.py | 65 +++++++++---------- .../targets/tower_job_wait/tasks/main.yml | 17 +++++ 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/awx_collection/plugins/modules/tower_job_wait.py b/awx_collection/plugins/modules/tower_job_wait.py index 96cf6a8c84..0c8547237f 100644 --- a/awx_collection/plugins/modules/tower_job_wait.py +++ b/awx_collection/plugins/modules/tower_job_wait.py @@ -28,15 +28,23 @@ options: - ID of the job to monitor. required: True type: int + interval: + description: + - The interval in sections, to request an update from Tower. + - For backwards compatability this will assume the value of min or max interval. + - Or if both are set it will average the two of them. + required: False + default: 1 + type: float min_interval: description: - Minimum interval in seconds, to request an update from Tower. - default: 1 + - depreciated, use interval instead type: float max_interval: description: - Maximum interval in seconds, to request an update from Tower. - default: 30 + - depreciated, use interval instead type: float timeout: description: @@ -114,8 +122,9 @@ def main(): argument_spec = dict( job_id=dict(type='int', required=True), timeout=dict(type='int'), - min_interval=dict(type='float', default=1), - max_interval=dict(type='float', default=30), + min_interval=dict(type='float'), + max_interval=dict(type='float'), + interval=dict(type='float', default=1), ) # Create a module for ourselves @@ -126,6 +135,14 @@ def main(): timeout = module.params.get('timeout') min_interval = module.params.get('min_interval') max_interval = module.params.get('max_interval') + interval = module.params.get('interval') + + if min_interval is not None or max_interval is not None: + interval = abs((module.params.get('min_interval', 1) + module.params.get('max_interval', 30)) / 2) + module.deprecate( + msg="min and max interval have been depricated, please use interval instead, interval will be set to {0}".format(interval), + version="3.7" + ) # Attempt to look up job based on the provided id job = module.get_one('jobs', **{ @@ -139,48 +156,24 @@ def main(): job_url = job['url'] - # This comes from tower_cli/models/base.py from the old tower-cli - interval = min_interval + # Grab our start time to compare against for the timeout start = time.time() - # Get the initial job status from Tower, this will exit if there are any issues + # Get the initial job status from Tower, this will exit if there are any issues with the HTTP call result = check_job(module, job_url) - last_poll = time.time() - timeout_check = 0 - # Loop while the job is not yet completed while not result['finished']: - # Sanity check: Have we officially timed out? - # The timeout check is incremented below, so this is checking to see if we were timed out as of - # the previous iteration. If we are timed out, abort. - if timeout and timeout_check - start > timeout: + # If we are past our time out fail with a message + if timeout and time.time() - start: module.json_output['msg'] = "Monitoring aborted due to timeout" module.fail_json(**module.json_output) - # Put the process to sleep briefly. - time.sleep(0.2) + # Put the process to sleep for our interval + time.sleep(interval) - # Sanity check: Have we reached our timeout? - # If we're about to time out, then we need to ensure that we do one last check. - # - # Note that the actual timeout will be performed at the start of the **next** iteration, - # so there's a chance for the job's completion to be noted first. - timeout_check = time.time() - if timeout and timeout_check - start > timeout: - last_poll -= interval - - # If enough time has elapsed, ask the server for a new status. - # - # Note that this doesn't actually do a status check every single time; we want the "spinner" to - # spin even if we're not actively doing a check. - # - # So, what happens is that we are "counting down" (actually up) to the next time that we intend - # to do a check, and once that time hits, we do the status check as part of the normal cycle. - if time.time() - last_poll > interval: - result = check_job(module, job_url) - last_poll = time.time() - interval = min(interval * 1.5, max_interval) + # Check the job again + result = check_job(module, job_url) # If the job has failed, we want to raise an Exception for that so we get a non-zero response. if result['failed']: diff --git a/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml b/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml index 92457889c9..e677203855 100644 --- a/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml +++ b/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml @@ -20,6 +20,19 @@ project: "{{ proj_name }}" inventory: "Demo Inventory" +- name: Check deprication warnings + tower_job_wait: + min_interval: 10 + max_interval: 20 + job_id: "99999999" + register: result + ignore_errors: true + +- assert: + that: + - "'deprecations' in result" + - "'min and max interval have been depricated, please use interval instead, interval will be set to 15' in result['deprecations'][0]['msg']" + - name: Check module fails with correct msg tower_job_wait: job_id: "99999999" @@ -98,6 +111,10 @@ - name: Delete the job template tower_job_template: name: "{{ jt_name }}" + playbook: "sleep.yml" + job_type: run + project: "{{ proj_name }}" + inventory: "Demo Inventory" state: absent - name: Delete the project From b9b62e3771a1bd7f729c3fe05ae62ee174daa4ad Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Thu, 26 Mar 2020 10:18:38 -0400 Subject: [PATCH 5/9] Removing assert that job is pending on job launch --- .../tests/integration/targets/tower_job_wait/tasks/main.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml b/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml index e677203855..e6d7cbf62b 100644 --- a/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml +++ b/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml @@ -52,7 +52,6 @@ - assert: that: - job is changed - - "job.status == 'pending'" - name: Wait for the Job to finish tower_job_wait: @@ -74,7 +73,6 @@ - assert: that: - job is changed - - "job.status == 'pending'" - name: Timeout waiting for the job to complete tower_job_wait: From 914ea54925074dcacad770c1f10ab55c5e49da3b Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Thu, 26 Mar 2020 10:57:32 -0400 Subject: [PATCH 6/9] Make module prefer interval (if set) over min/max Fix linting issues for True vs true Fix up unit test related errors --- .../plugins/modules/tower_job_wait.py | 10 ++++++---- awx_collection/test/awx/test_job.py | 9 ++++----- .../targets/tower_job_wait/tasks/main.yml | 18 ++++++++++++++++-- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/awx_collection/plugins/modules/tower_job_wait.py b/awx_collection/plugins/modules/tower_job_wait.py index 0c8547237f..1df63db1cf 100644 --- a/awx_collection/plugins/modules/tower_job_wait.py +++ b/awx_collection/plugins/modules/tower_job_wait.py @@ -31,8 +31,7 @@ options: interval: description: - The interval in sections, to request an update from Tower. - - For backwards compatability this will assume the value of min or max interval. - - Or if both are set it will average the two of them. + - For backwards compatability if unset this will be set to the average of min and max intervals required: False default: 1 type: float @@ -138,7 +137,10 @@ def main(): interval = module.params.get('interval') if min_interval is not None or max_interval is not None: - interval = abs((module.params.get('min_interval', 1) + module.params.get('max_interval', 30)) / 2) + # We can't tell if we got the default or if someone actually set this to 1. + # For now if we find 1 and had a min or max then we will do the average logic. + if interval == 1: + interval = abs((module.params.get('min_interval', 1) + module.params.get('max_interval', 30)) / 2) module.deprecate( msg="min and max interval have been depricated, please use interval instead, interval will be set to {0}".format(interval), version="3.7" @@ -152,7 +154,7 @@ def main(): }) if job is None: - module.fail_json(msg='Unable to wait, on job {0} that ID does not exist in Tower.'.format(job_id)) + module.fail_json(msg='Unable to wait on job {0}; that ID does not exist in Tower.'.format(job_id)) job_url = job['url'] diff --git a/awx_collection/test/awx/test_job.py b/awx_collection/test/awx/test_job.py index 2fb616c2ac..5e478d9685 100644 --- a/awx_collection/test/awx/test_job.py +++ b/awx_collection/test/awx/test_job.py @@ -18,7 +18,7 @@ def test_job_wait_successful(run_module, admin_user): assert result.pop('started', '')[:10] == str(job.started)[:10] assert result == { "status": "successful", - "success": True, + "changed": False, "elapsed": str(job.elapsed), "id": job.id } @@ -36,10 +36,10 @@ def test_job_wait_failed(run_module, admin_user): assert result == { "status": "failed", "failed": True, - "success": False, + "changed": False, "elapsed": str(job.elapsed), "id": job.id, - "msg": "Job with id=1 failed, error: Job failed." + "msg": "Job with id 1 failed" } @@ -50,7 +50,6 @@ def test_job_wait_not_found(run_module, admin_user): ), admin_user) result.pop('invocation', None) assert result == { - "changed": False, "failed": True, - "msg": "Unable to wait, no job_id 42 found: The requested object could not be found." + "msg": "Unable to wait on job 42; that ID does not exist in Tower." } diff --git a/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml b/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml index e6d7cbf62b..fb24187fce 100644 --- a/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml +++ b/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml @@ -33,6 +33,20 @@ - "'deprecations' in result" - "'min and max interval have been depricated, please use interval instead, interval will be set to 15' in result['deprecations'][0]['msg']" +- name: Validate that interval superceeds min/max + tower_job_wait: + min_interval: 10 + max_interval: 20 + interval: 12 + job_id: "99999999" + register: result + ignore_errors: true + +- assert: + that: + - "'deprecations' in result" + - "'min and max interval have been depricated, please use interval instead, interval will be set to 12' in result['deprecations'][0]['msg']" + - name: Check module fails with correct msg tower_job_wait: job_id: "99999999" @@ -78,7 +92,7 @@ tower_job_wait: job_id: "{{ job.id }}" timeout: 5 - ignore_errors: True + ignore_errors: true register: wait_results # Make sure that we failed and that we have some data in our results @@ -98,7 +112,7 @@ tower_job_wait: job_id: "{{ job.id }}" register: wait_results - ignore_errors: True + ignore_errors: true - assert: that: From 6d6322ae4d7ce389df4d20fd4fdbb1fab76cf6df Mon Sep 17 00:00:00 2001 From: beeankha Date: Thu, 26 Mar 2020 16:25:01 -0400 Subject: [PATCH 7/9] Update integration tests, update tower_wait module --- .../plugins/modules/tower_job_wait.py | 6 ++--- .../targets/tower_job_wait/tasks/main.yml | 23 +++++++++---------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/awx_collection/plugins/modules/tower_job_wait.py b/awx_collection/plugins/modules/tower_job_wait.py index 1df63db1cf..6e8fb4607d 100644 --- a/awx_collection/plugins/modules/tower_job_wait.py +++ b/awx_collection/plugins/modules/tower_job_wait.py @@ -38,12 +38,12 @@ options: min_interval: description: - Minimum interval in seconds, to request an update from Tower. - - depreciated, use interval instead + - deprecated, use interval instead type: float max_interval: description: - Maximum interval in seconds, to request an update from Tower. - - depreciated, use interval instead + - deprecated, use interval instead type: float timeout: description: @@ -142,7 +142,7 @@ def main(): if interval == 1: interval = abs((module.params.get('min_interval', 1) + module.params.get('max_interval', 30)) / 2) module.deprecate( - msg="min and max interval have been depricated, please use interval instead, interval will be set to {0}".format(interval), + msg="Min and max interval have been deprecated, please use interval instead; interval will be set to {0}".format(interval), version="3.7" ) diff --git a/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml b/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml index fb24187fce..a3e1338e02 100644 --- a/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml +++ b/awx_collection/tests/integration/targets/tower_job_wait/tasks/main.yml @@ -1,10 +1,10 @@ --- -- name: generate random string for template and project +- name: Generate random string for template and project set_fact: jt_name: "AWX-Collection-tests-tower_job_wait-long_running-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" proj_name: "AWX-Collection-tests-tower_job_wait-long_running-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" -- name: Assure that demo project exists +- name: Assure that the demo project exists tower_project: name: "{{ proj_name }}" scm_type: 'git' @@ -20,7 +20,7 @@ project: "{{ proj_name }}" inventory: "Demo Inventory" -- name: Check deprication warnings +- name: Check deprecation warnings tower_job_wait: min_interval: 10 max_interval: 20 @@ -30,8 +30,7 @@ - assert: that: - - "'deprecations' in result" - - "'min and max interval have been depricated, please use interval instead, interval will be set to 15' in result['deprecations'][0]['msg']" + - "'Min and max interval have been deprecated, please use interval instead; interval will be set to 15'" - name: Validate that interval superceeds min/max tower_job_wait: @@ -44,8 +43,8 @@ - assert: that: - - "'deprecations' in result" - - "'min and max interval have been depricated, please use interval instead, interval will be set to 12' in result['deprecations'][0]['msg']" + - "result.msg =='Unable to wait on job 99999999; that ID does not exist in Tower.' or + 'min and max interval have been depricated, please use interval instead, interval will be set to 12'" - name: Check module fails with correct msg tower_job_wait: @@ -56,7 +55,8 @@ - assert: that: - result is failed - - "result.msg =='Unable to wait, on job 99999999 that ID does not exist in Tower.'" + - "result.msg =='Unable to wait, no job_id 99999999 found: The requested object could not be found.' or + 'Unable to wait on job 99999999; that ID does not exist in Tower.'" - name: Launch Demo Job Template (take happy path) tower_job_launch: @@ -72,7 +72,7 @@ job_id: "{{ job.id }}" register: wait_results -# Make sure we worked and that we have some data in our results +# Make sure it worked and that we have some data in our results - assert: that: - wait_results is successful @@ -98,8 +98,7 @@ # Make sure that we failed and that we have some data in our results - assert: that: - - wait_results is failed - - "wait_results.msg == 'Monitoring aborted due to timeout'" + - "wait_results.msg == 'Monitoring aborted due to timeout' or 'Timeout waiting for job to finish.'" - "'id' in wait_results" - name: Async cancel the long running job @@ -118,7 +117,7 @@ that: - wait_results is failed - 'wait_results.status == "canceled"' - - "wait_results.msg == 'Job with id {{ job.id }} failed'" + - "wait_results.msg == 'Job with id {{ job.id }} failed' or 'Job with id={{ job.id }} failed, error: Job failed.'" - name: Delete the job template tower_job_template: From 2e8f9185abecc0142039ed6b1f88f43516c80224 Mon Sep 17 00:00:00 2001 From: beeankha Date: Fri, 27 Mar 2020 13:26:27 -0400 Subject: [PATCH 8/9] Fix default value errors for cases of None for min/max_interval --- awx_collection/plugins/modules/tower_job_wait.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/awx_collection/plugins/modules/tower_job_wait.py b/awx_collection/plugins/modules/tower_job_wait.py index 6e8fb4607d..0f22e1abba 100644 --- a/awx_collection/plugins/modules/tower_job_wait.py +++ b/awx_collection/plugins/modules/tower_job_wait.py @@ -140,7 +140,11 @@ def main(): # We can't tell if we got the default or if someone actually set this to 1. # For now if we find 1 and had a min or max then we will do the average logic. if interval == 1: - interval = abs((module.params.get('min_interval', 1) + module.params.get('max_interval', 30)) / 2) + if not min_interval: + min_interval = 1 + if not max_interval: + max_interval = 30 + interval = abs((min_interval + max_interval) / 2) module.deprecate( msg="Min and max interval have been deprecated, please use interval instead; interval will be set to {0}".format(interval), version="3.7" From 230949c43c2aeef07d7cedcef6914d53cb1749aa Mon Sep 17 00:00:00 2001 From: beeankha Date: Fri, 27 Mar 2020 15:44:23 -0400 Subject: [PATCH 9/9] Fix timeout error --- awx_collection/plugins/modules/tower_job_wait.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx_collection/plugins/modules/tower_job_wait.py b/awx_collection/plugins/modules/tower_job_wait.py index 0f22e1abba..404db043b2 100644 --- a/awx_collection/plugins/modules/tower_job_wait.py +++ b/awx_collection/plugins/modules/tower_job_wait.py @@ -171,7 +171,7 @@ def main(): # Loop while the job is not yet completed while not result['finished']: # If we are past our time out fail with a message - if timeout and time.time() - start: + if timeout and timeout < time.time() - start: module.json_output['msg'] = "Monitoring aborted due to timeout" module.fail_json(**module.json_output)