From 4af2a039c94771032b186ec1daffe41e3c6c67a7 Mon Sep 17 00:00:00 2001 From: Michael Abashian Date: Mon, 20 Mar 2017 11:39:00 -0400 Subject: [PATCH 01/23] Fixed bug when search job_events with key:value --- awx/ui/client/src/job-results/job-results.controller.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/awx/ui/client/src/job-results/job-results.controller.js b/awx/ui/client/src/job-results/job-results.controller.js index 26d9b0b81b..73137f98d0 100644 --- a/awx/ui/client/src/job-results/job-results.controller.js +++ b/awx/ui/client/src/job-results/job-results.controller.js @@ -38,7 +38,8 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy // used for tag search $scope.list = { - basePath: jobData.related.job_events + basePath: jobData.related.job_events, + name: 'job_events' }; // used for tag search From f69c2569d65c5d9512d8fda0af4e58820faa4498 Mon Sep 17 00:00:00 2001 From: Aaron Tan Date: Fri, 17 Mar 2017 17:54:29 -0400 Subject: [PATCH 02/23] Smarter log aggregator host name generation. --- awx/main/utils/handlers.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/awx/main/utils/handlers.py b/awx/main/utils/handlers.py index b1b96b492f..2346ee5c30 100644 --- a/awx/main/utils/handlers.py +++ b/awx/main/utils/handlers.py @@ -6,6 +6,7 @@ import logging import json import requests import time +import re from concurrent.futures import ThreadPoolExecutor from copy import copy @@ -118,10 +119,18 @@ class BaseHTTPSHandler(logging.Handler): def get_http_host(self): host = self.host or '' - if not host.startswith('http'): - host = 'http://%s' % self.host - if self.port != 80 and self.port is not None: - host = '%s:%s' % (host, str(self.port)) + # Force using http(s) protocol + if re.match(r'https?://', host) is None: + if re.match(r'[a-zA-Z]+://', host) is None: + host = 'http://%s' % self.host + else: + host = re.sub(r'[a-zA-Z]+://', r'http://', host, count=1) + # Insert self.port if its special and port number not given in host + if (self.port != 80 and self.port is not None and + re.match(r'https?://[^:/]+:[0-9]+', host) is None): + host = re.sub(r'https?://[^/]+', + lambda m: '%s:%s' % (m.group(0), str(self.port), + host, count=1) return host def get_post_kwargs(self, payload_input): From eb2e7c38b03d3a77a82d8e7bca96a399a7af12c0 Mon Sep 17 00:00:00 2001 From: Matthew Jones Date: Tue, 21 Mar 2017 10:39:40 -0400 Subject: [PATCH 03/23] Bring up logstash as part of the development cluster --- tools/docker-compose-cluster.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/docker-compose-cluster.yml b/tools/docker-compose-cluster.yml index 5f8807c485..ae5b6ad8a6 100644 --- a/tools/docker-compose-cluster.yml +++ b/tools/docker-compose-cluster.yml @@ -67,3 +67,7 @@ services: image: postgres:9.4.1 memcached: image: memcached:alpine + logstash: + build: + context: ./docker-compose + dockerfile: Dockerfile-logstash From f9b5b9aa91a00a5c6586b3b9edcaa5f7b42b1e6f Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Wed, 22 Mar 2017 11:14:48 -0400 Subject: [PATCH 04/23] property filter `no_log` for item event loops see: #5691 --- awx/lib/tests/test_display_callback.py | 2 +- awx/lib/tower_display_callback/module.py | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/awx/lib/tests/test_display_callback.py b/awx/lib/tests/test_display_callback.py index d0c53f4fc0..f84ab0df4f 100644 --- a/awx/lib/tests/test_display_callback.py +++ b/awx/lib/tests/test_display_callback.py @@ -60,7 +60,7 @@ def executor(tmpdir_factory, request): cli = PlaybookCLI(['', 'playbook.yml']) cli.parse() - options = cli.parser.parse_args([])[0] + options = cli.parser.parse_args(['-v'])[0] loader = DataLoader() variable_manager = VariableManager() inventory = Inventory(loader=loader, variable_manager=variable_manager, diff --git a/awx/lib/tower_display_callback/module.py b/awx/lib/tower_display_callback/module.py index 59575d7989..76dcb5be7f 100644 --- a/awx/lib/tower_display_callback/module.py +++ b/awx/lib/tower_display_callback/module.py @@ -30,6 +30,8 @@ from ansible.plugins.callback.default import CallbackModule as DefaultCallbackMo from .events import event_context from .minimal import CallbackModule as MinimalCallbackModule +CENSORED = "the output has been hidden due to the fact that 'no_log: true' was specified for this result" # noqa + class BaseCallbackModule(CallbackBase): ''' @@ -69,8 +71,12 @@ class BaseCallbackModule(CallbackBase): else: task = None - if event_data.get('res') and event_data['res'].get('_ansible_no_log', False): - event_data['res'] = {'censored': "the output has been hidden due to the fact that 'no_log: true' was specified for this result"} # noqa + if event_data.get('res'): + if event_data['res'].get('_ansible_no_log', False): + event_data['res'] = {'censored': CENSORED} + for i, item in enumerate(event_data['res'].get('results', [])): + if event_data['res']['results'][i].get('_ansible_no_log', False): + event_data['res']['results'][i] = {'censored': CENSORED} with event_context.display_lock: try: From 37bfa3c3a80f316fa6f6608fc16d36c64228a13e Mon Sep 17 00:00:00 2001 From: Michael Abashian Date: Thu, 23 Mar 2017 11:24:52 -0400 Subject: [PATCH 05/23] Add validation to the multi-select questions in survey --- awx/ui/client/src/shared/directives.js | 29 +++++++++++++++++++ .../render/multiple-choice.partial.html | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/awx/ui/client/src/shared/directives.js b/awx/ui/client/src/shared/directives.js index 0255aaee8b..3531e78f0b 100644 --- a/awx/ui/client/src/shared/directives.js +++ b/awx/ui/client/src/shared/directives.js @@ -1371,4 +1371,33 @@ function(ConfigurationUtils, i18n, $rootScope) { }); } }; +}]) + +.directive('awRequireMultiple', [function() { + return { + require: 'ngModel', + link: function postLink(scope, element, attrs, ngModel) { + // Watch for changes to the required attribute + attrs.$observe('required', function(value) { + if(value) { + ngModel.$validators.required = function (value) { + if(angular.isArray(value)) { + if(value.length === 0) { + return false; + } + else { + return (!value[0] || value[0] === "") ? false : true; + } + } + else { + return false; + } + }; + } + else { + delete ngModel.$validators.required; + } + }); + } + }; }]); diff --git a/awx/ui/client/src/templates/survey-maker/render/multiple-choice.partial.html b/awx/ui/client/src/templates/survey-maker/render/multiple-choice.partial.html index 580c8e5805..7fa7e1fa0d 100644 --- a/awx/ui/client/src/templates/survey-maker/render/multiple-choice.partial.html +++ b/awx/ui/client/src/templates/survey-maker/render/multiple-choice.partial.html @@ -1,5 +1,5 @@
-
From 0e2a2d61e2d337c761a6c57e809992d3c9934fc4 Mon Sep 17 00:00:00 2001 From: Shane McDonald Date: Wed, 22 Mar 2017 22:58:11 -0400 Subject: [PATCH 06/23] Create a setup subpackage --- Makefile | 7 ++++++- setup.py | 3 ++- tools/scripts/ansible-tower-setup | 3 +++ 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100755 tools/scripts/ansible-tower-setup diff --git a/Makefile b/Makefile index 0d933cff61..c45d70324b 100644 --- a/Makefile +++ b/Makefile @@ -690,18 +690,23 @@ setup_bundle_tarball: setup-bundle-build setup-bundle-build/$(OFFLINE_TAR_FILE) rpm-build: mkdir -p $@ -rpm-build/$(SDIST_TAR_FILE): rpm-build dist/$(SDIST_TAR_FILE) +rpm-build/$(SDIST_TAR_FILE): rpm-build dist/$(SDIST_TAR_FILE) tar-build/$(SETUP_TAR_FILE) cp packaging/rpm/$(NAME).spec rpm-build/ cp packaging/rpm/tower.te rpm-build/ cp packaging/rpm/tower.fc rpm-build/ cp packaging/rpm/$(NAME).sysconfig rpm-build/ cp packaging/remove_tower_source.py rpm-build/ cp packaging/bytecompile.sh rpm-build/ + cp tar-build/$(SETUP_TAR_FILE) rpm-build/ if [ "$(OFFICIAL)" != "yes" ] ; then \ (cd dist/ && tar zxf $(SDIST_TAR_FILE)) ; \ (cd dist/ && mv $(NAME)-$(VERSION)-$(BUILD) $(NAME)-$(VERSION)) ; \ (cd dist/ && tar czf ../rpm-build/$(SDIST_TAR_FILE) $(NAME)-$(VERSION)) ; \ ln -sf $(SDIST_TAR_FILE) rpm-build/$(NAME)-$(VERSION).tar.gz ; \ + (cd tar-build/ && tar zxf $(SETUP_TAR_FILE)) ; \ + (cd tar-build/ && mv $(NAME)-setup-$(VERSION)-$(BUILD) $(NAME)-setup-$(VERSION)) ; \ + (cd tar-build/ && tar czf ../rpm-build/$(SETUP_TAR_FILE) $(NAME)-setup-$(VERSION)) ; \ + ln -sf $(SETUP_TAR_FILE) rpm-build/$(NAME)-setup-$(VERSION).tar.gz ; \ else \ cp -a dist/$(SDIST_TAR_FILE) rpm-build/ ; \ fi diff --git a/setup.py b/setup.py index 334b0e78d5..bd7d5bd19e 100755 --- a/setup.py +++ b/setup.py @@ -126,7 +126,8 @@ setup( ("%s" % docdir, ["docs/licenses/*",]), ("%s" % bindir, ["tools/scripts/ansible-tower-service", "tools/scripts/failure-event-handler", - "tools/scripts/tower-python"]), + "tools/scripts/tower-python", + "tools/scripts/ansible-tower-setup"]), ("%s" % sosconfig, ["tools/sosreport/tower.py"])]), options = { 'egg_info': { diff --git a/tools/scripts/ansible-tower-setup b/tools/scripts/ansible-tower-setup new file mode 100755 index 0000000000..074a91f6f7 --- /dev/null +++ b/tools/scripts/ansible-tower-setup @@ -0,0 +1,3 @@ +#!/bin/bash + +exec /var/lib/awx/setup/setup.sh "$@" From c67687acfd13d0f5305825f15dc24cad94cf5810 Mon Sep 17 00:00:00 2001 From: Michael Abashian Date: Fri, 24 Mar 2017 10:35:08 -0400 Subject: [PATCH 07/23] Host Event json should be read-only --- .../client/src/job-results/host-event/host-event.controller.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/awx/ui/client/src/job-results/host-event/host-event.controller.js b/awx/ui/client/src/job-results/host-event/host-event.controller.js index 398ae70ab4..5059140f00 100644 --- a/awx/ui/client/src/job-results/host-event/host-event.controller.js +++ b/awx/ui/client/src/job-results/host-event/host-event.controller.js @@ -27,7 +27,8 @@ var container = document.getElementById(el); var editor = CodeMirror.fromTextArea(container, { // jshint ignore:line lineNumbers: true, - mode: mode + mode: mode, + readOnly: true }); editor.setSize("100%", 200); editor.getDoc().setValue(data); From 07e7e4cfd04330d477b0a94753ca7b479f70badf Mon Sep 17 00:00:00 2001 From: Aaron Tan Date: Mon, 20 Mar 2017 14:27:02 -0400 Subject: [PATCH 08/23] Modify according to review feedback. --- awx/main/tests/unit/utils/test_handlers.py | 12 ++++++++- awx/main/utils/handlers.py | 29 ++++++++++++---------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/awx/main/tests/unit/utils/test_handlers.py b/awx/main/tests/unit/utils/test_handlers.py index d7b7f5aed9..518a213669 100644 --- a/awx/main/tests/unit/utils/test_handlers.py +++ b/awx/main/tests/unit/utils/test_handlers.py @@ -107,7 +107,17 @@ def test_https_logging_handler_splunk_auth_info(): ('http://localhost', None, 'http://localhost'), ('http://localhost', 80, 'http://localhost'), ('http://localhost', 8080, 'http://localhost:8080'), - ('https://localhost', 443, 'https://localhost:443') + ('https://localhost', 443, 'https://localhost:443'), + ('ftp://localhost', 443, 'ftp://localhost:443'), + ('https://localhost:550', 443, 'https://localhost:550'), + ('https://localhost:yoho/foobar', 443, 'https://localhost:443/foobar'), + ('https://localhost:yoho/foobar', None, 'https://localhost:yoho/foobar'), + ('http://splunk.server:8088/services/collector/event', 80, + 'http://splunk.server:8088/services/collector/event'), + ('http://splunk.server/services/collector/event', 80, + 'http://splunk.server/services/collector/event'), + ('http://splunk.server/services/collector/event', 8088, + 'http://splunk.server:8088/services/collector/event'), ]) def test_https_logging_handler_http_host_format(host, port, normalized): handler = HTTPSHandler(host=host, port=port) diff --git a/awx/main/utils/handlers.py b/awx/main/utils/handlers.py index 2346ee5c30..3eb9852472 100644 --- a/awx/main/utils/handlers.py +++ b/awx/main/utils/handlers.py @@ -6,7 +6,7 @@ import logging import json import requests import time -import re +import urlparse from concurrent.futures import ThreadPoolExecutor from copy import copy @@ -119,18 +119,21 @@ class BaseHTTPSHandler(logging.Handler): def get_http_host(self): host = self.host or '' - # Force using http(s) protocol - if re.match(r'https?://', host) is None: - if re.match(r'[a-zA-Z]+://', host) is None: - host = 'http://%s' % self.host - else: - host = re.sub(r'[a-zA-Z]+://', r'http://', host, count=1) - # Insert self.port if its special and port number not given in host - if (self.port != 80 and self.port is not None and - re.match(r'https?://[^:/]+:[0-9]+', host) is None): - host = re.sub(r'https?://[^/]+', - lambda m: '%s:%s' % (m.group(0), str(self.port), - host, count=1) + # urlparse requires scheme to be provided, default to use http if + # missing + if not urlparse.urlsplit(host).scheme: + host = 'http://%s' % host + parsed = urlparse.urlsplit(host) + # Insert self.port if its special and port number is either not + # given in host or given as non-numerical + try: + port = parsed.port or self.port + except ValueError: + port = self.port + if port not in (80, None): + new_netloc = '%s:%s' % (parsed.hostname, port) + return urlparse.urlunsplit((parsed.scheme, new_netloc, parsed.path, + parsed.query, parsed.fragment)) return host def get_post_kwargs(self, payload_input): From b421670c10f2d16b69f2b3d83be082d62e3a1c5a Mon Sep 17 00:00:00 2001 From: Aaron Tan Date: Fri, 24 Mar 2017 11:57:56 -0400 Subject: [PATCH 09/23] Remove log aggregator port required mark. --- awx/main/conf.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/awx/main/conf.py b/awx/main/conf.py index 098b84f639..674c7fabee 100644 --- a/awx/main/conf.py +++ b/awx/main/conf.py @@ -242,9 +242,11 @@ register( field_class=fields.IntegerField, allow_null=True, label=_('Logging Aggregator Port'), - help_text=_('Port on Logging Aggregator to send logs to (if required).'), + help_text=_('Port on Logging Aggregator to send logs to (if required and not' + ' provided in Logging Aggregator).'), category=_('Logging'), category_slug='logging', + required=False ) register( 'LOG_AGGREGATOR_TYPE', From c99d4659dacb9ecdf017088a6184b6bfa1e5eb8d Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Fri, 24 Mar 2017 15:43:52 -0400 Subject: [PATCH 10/23] Do not set the default if the field was not passed in to kwargs_extra_vars --- awx/main/models/mixins.py | 4 +++- awx/main/tests/functional/api/test_survey_spec.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/awx/main/models/mixins.py b/awx/main/models/mixins.py index 3ae26eaf71..9ec0a8d736 100644 --- a/awx/main/models/mixins.py +++ b/awx/main/models/mixins.py @@ -130,12 +130,14 @@ class SurveyJobTemplateMixin(models.Model): for survey_element in self.survey_spec.get("spec", []): default = survey_element.get('default') variable_key = survey_element.get('variable') + if survey_element.get('type') == 'password': if variable_key in kwargs_extra_vars and default: kw_value = kwargs_extra_vars[variable_key] if kw_value.startswith('$encrypted$') and kw_value != default: kwargs_extra_vars[variable_key] = default - if default is not None: + + if default is not None and variable_key in extra_vars: extra_vars[variable_key] = default # Overwrite job template extra vars with explicit job extra vars diff --git a/awx/main/tests/functional/api/test_survey_spec.py b/awx/main/tests/functional/api/test_survey_spec.py index f954538973..1aff4b8eea 100644 --- a/awx/main/tests/functional/api/test_survey_spec.py +++ b/awx/main/tests/functional/api/test_survey_spec.py @@ -211,7 +211,7 @@ def test_launch_with_non_empty_survey_spec_no_license(job_template_factory, post @pytest.mark.django_db @pytest.mark.survey def test_redact_survey_passwords_in_activity_stream(job_template_with_survey_passwords): - job_template_with_survey_passwords.create_unified_job() + job_template_with_survey_passwords.create_unified_job(extra_vars={'secret_key':''}) AS_record = ActivityStream.objects.filter(object1='job').all()[0] changes_dict = json.loads(AS_record.changes) extra_vars = json.loads(changes_dict['extra_vars']) From 18eaacf4bb205d291ce907e1c3a7b3327593138c Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Fri, 24 Mar 2017 17:57:52 -0400 Subject: [PATCH 11/23] create _survey_element_validation and use it for updating extra_vars --- awx/main/models/mixins.py | 123 ++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 58 deletions(-) diff --git a/awx/main/models/mixins.py b/awx/main/models/mixins.py index 9ec0a8d736..e818b5b648 100644 --- a/awx/main/models/mixins.py +++ b/awx/main/models/mixins.py @@ -137,8 +137,11 @@ class SurveyJobTemplateMixin(models.Model): if kw_value.startswith('$encrypted$') and kw_value != default: kwargs_extra_vars[variable_key] = default - if default is not None and variable_key in extra_vars: - extra_vars[variable_key] = default + if default is not None: + data = {variable_key: default} + errors = self._survey_element_validation(survey_element, data) + if not errors: + extra_vars[variable_key] = default # Overwrite job template extra vars with explicit job extra vars # and add on job extra vars @@ -146,6 +149,65 @@ class SurveyJobTemplateMixin(models.Model): kwargs['extra_vars'] = json.dumps(extra_vars) return kwargs + def _survey_element_validation(self, survey_element, data): + errors = [] + if survey_element['variable'] not in data and survey_element['required']: + errors.append("'%s' value missing" % survey_element['variable']) + elif survey_element['type'] in ["textarea", "text", "password"]: + if survey_element['variable'] in data: + if type(data[survey_element['variable']]) not in (str, unicode): + errors.append("Value %s for '%s' expected to be a string." % (data[survey_element['variable']], + survey_element['variable'])) + return errors + if 'min' in survey_element and survey_element['min'] not in ["", None] and len(data[survey_element['variable']]) < int(survey_element['min']): + errors.append("'%s' value %s is too small (length is %s must be at least %s)." % + (survey_element['variable'], data[survey_element['variable']], len(data[survey_element['variable']]), survey_element['min'])) + if 'max' in survey_element and survey_element['max'] not in ["", None] and len(data[survey_element['variable']]) > int(survey_element['max']): + errors.append("'%s' value %s is too large (must be no more than %s)." % + (survey_element['variable'], data[survey_element['variable']], survey_element['max'])) + elif survey_element['type'] == 'integer': + if survey_element['variable'] in data: + if type(data[survey_element['variable']]) != int: + errors.append("Value %s for '%s' expected to be an integer." % (data[survey_element['variable']], + survey_element['variable'])) + return errors + if 'min' in survey_element and survey_element['min'] not in ["", None] and survey_element['variable'] in data and \ + data[survey_element['variable']] < int(survey_element['min']): + errors.append("'%s' value %s is too small (must be at least %s)." % + (survey_element['variable'], data[survey_element['variable']], survey_element['min'])) + if 'max' in survey_element and survey_element['max'] not in ["", None] and survey_element['variable'] in data and \ + data[survey_element['variable']] > int(survey_element['max']): + errors.append("'%s' value %s is too large (must be no more than %s)." % + (survey_element['variable'], data[survey_element['variable']], survey_element['max'])) + elif survey_element['type'] == 'float': + if survey_element['variable'] in data: + if type(data[survey_element['variable']]) not in (float, int): + errors.append("Value %s for '%s' expected to be a numeric type." % (data[survey_element['variable']], + survey_element['variable'])) + return errors + if 'min' in survey_element and survey_element['min'] not in ["", None] and data[survey_element['variable']] < float(survey_element['min']): + errors.append("'%s' value %s is too small (must be at least %s)." % + (survey_element['variable'], data[survey_element['variable']], survey_element['min'])) + if 'max' in survey_element and survey_element['max'] not in ["", None] and data[survey_element['variable']] > float(survey_element['max']): + errors.append("'%s' value %s is too large (must be no more than %s)." % + (survey_element['variable'], data[survey_element['variable']], survey_element['max'])) + elif survey_element['type'] == 'multiselect': + if survey_element['variable'] in data: + if type(data[survey_element['variable']]) != list: + errors.append("'%s' value is expected to be a list." % survey_element['variable']) + else: + for val in data[survey_element['variable']]: + if val not in survey_element['choices']: + errors.append("Value %s for '%s' expected to be one of %s." % (val, survey_element['variable'], + survey_element['choices'])) + elif survey_element['type'] == 'multiplechoice': + if survey_element['variable'] in data: + if data[survey_element['variable']] not in survey_element['choices']: + errors.append("Value %s for '%s' expected to be one of %s." % (data[survey_element['variable']], + survey_element['variable'], + survey_element['choices'])) + return errors + def survey_variable_validation(self, data): errors = [] if not self.survey_enabled: @@ -155,62 +217,7 @@ class SurveyJobTemplateMixin(models.Model): if 'description' not in self.survey_spec: errors.append("'description' missing from survey spec.") for survey_element in self.survey_spec.get("spec", []): - if survey_element['variable'] not in data and \ - survey_element['required']: - errors.append("'%s' value missing" % survey_element['variable']) - elif survey_element['type'] in ["textarea", "text", "password"]: - if survey_element['variable'] in data: - if type(data[survey_element['variable']]) not in (str, unicode): - errors.append("Value %s for '%s' expected to be a string." % (data[survey_element['variable']], - survey_element['variable'])) - continue - if 'min' in survey_element and survey_element['min'] not in ["", None] and len(data[survey_element['variable']]) < int(survey_element['min']): - errors.append("'%s' value %s is too small (length is %s must be at least %s)." % - (survey_element['variable'], data[survey_element['variable']], len(data[survey_element['variable']]), survey_element['min'])) - if 'max' in survey_element and survey_element['max'] not in ["", None] and len(data[survey_element['variable']]) > int(survey_element['max']): - errors.append("'%s' value %s is too large (must be no more than %s)." % - (survey_element['variable'], data[survey_element['variable']], survey_element['max'])) - elif survey_element['type'] == 'integer': - if survey_element['variable'] in data: - if type(data[survey_element['variable']]) != int: - errors.append("Value %s for '%s' expected to be an integer." % (data[survey_element['variable']], - survey_element['variable'])) - continue - if 'min' in survey_element and survey_element['min'] not in ["", None] and survey_element['variable'] in data and \ - data[survey_element['variable']] < int(survey_element['min']): - errors.append("'%s' value %s is too small (must be at least %s)." % - (survey_element['variable'], data[survey_element['variable']], survey_element['min'])) - if 'max' in survey_element and survey_element['max'] not in ["", None] and survey_element['variable'] in data and \ - data[survey_element['variable']] > int(survey_element['max']): - errors.append("'%s' value %s is too large (must be no more than %s)." % - (survey_element['variable'], data[survey_element['variable']], survey_element['max'])) - elif survey_element['type'] == 'float': - if survey_element['variable'] in data: - if type(data[survey_element['variable']]) not in (float, int): - errors.append("Value %s for '%s' expected to be a numeric type." % (data[survey_element['variable']], - survey_element['variable'])) - continue - if 'min' in survey_element and survey_element['min'] not in ["", None] and data[survey_element['variable']] < float(survey_element['min']): - errors.append("'%s' value %s is too small (must be at least %s)." % - (survey_element['variable'], data[survey_element['variable']], survey_element['min'])) - if 'max' in survey_element and survey_element['max'] not in ["", None] and data[survey_element['variable']] > float(survey_element['max']): - errors.append("'%s' value %s is too large (must be no more than %s)." % - (survey_element['variable'], data[survey_element['variable']], survey_element['max'])) - elif survey_element['type'] == 'multiselect': - if survey_element['variable'] in data: - if type(data[survey_element['variable']]) != list: - errors.append("'%s' value is expected to be a list." % survey_element['variable']) - else: - for val in data[survey_element['variable']]: - if val not in survey_element['choices']: - errors.append("Value %s for '%s' expected to be one of %s." % (val, survey_element['variable'], - survey_element['choices'])) - elif survey_element['type'] == 'multiplechoice': - if survey_element['variable'] in data: - if data[survey_element['variable']] not in survey_element['choices']: - errors.append("Value %s for '%s' expected to be one of %s." % (data[survey_element['variable']], - survey_element['variable'], - survey_element['choices'])) + errors += self._survey_element_validation(survey_element, data) return errors From 45c3a389d45b457d2f2b20ec2158e169d65cb039 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Sun, 26 Mar 2017 22:43:46 -0400 Subject: [PATCH 12/23] add test, restore old behavior in api test --- awx/main/tests/functional/api/test_survey_spec.py | 2 +- awx/main/tests/unit/models/test_survey_models.py | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/awx/main/tests/functional/api/test_survey_spec.py b/awx/main/tests/functional/api/test_survey_spec.py index 1aff4b8eea..f954538973 100644 --- a/awx/main/tests/functional/api/test_survey_spec.py +++ b/awx/main/tests/functional/api/test_survey_spec.py @@ -211,7 +211,7 @@ def test_launch_with_non_empty_survey_spec_no_license(job_template_factory, post @pytest.mark.django_db @pytest.mark.survey def test_redact_survey_passwords_in_activity_stream(job_template_with_survey_passwords): - job_template_with_survey_passwords.create_unified_job(extra_vars={'secret_key':''}) + job_template_with_survey_passwords.create_unified_job() AS_record = ActivityStream.objects.filter(object1='job').all()[0] changes_dict = json.loads(AS_record.changes) extra_vars = json.loads(changes_dict['extra_vars']) diff --git a/awx/main/tests/unit/models/test_survey_models.py b/awx/main/tests/unit/models/test_survey_models.py index 584a4cc7f0..eefc5d97ab 100644 --- a/awx/main/tests/unit/models/test_survey_models.py +++ b/awx/main/tests/unit/models/test_survey_models.py @@ -4,6 +4,7 @@ import json from awx.main.tasks import RunJob from awx.main.models import ( Job, + JobTemplate, WorkflowJobTemplate ) @@ -78,6 +79,18 @@ def test_job_args_unredacted_passwords(job): assert extra_vars['secret_key'] == 'my_password' +def test_update_kwargs_survey_invalid_default(survey_spec_factory): + spec = survey_spec_factory('var2') + spec['spec'][0]['required'] = False + spec['spec'][0]['min'] = 3 + spec['spec'][0]['default'] = 1 + jt = JobTemplate(name="test-jt", survey_spec=spec, survey_enabled=True, extra_vars="var2: 2") + defaulted_extra_vars = jt._update_unified_job_kwargs() + assert 'extra_vars' in defaulted_extra_vars + # Make sure we did not set the invalid default of 1 + assert json.loads(defaulted_extra_vars['extra_vars'])['var2'] == 2 + + class TestWorkflowSurveys: def test_update_kwargs_survey_defaults(self, survey_spec_factory): "Assure that the survey default over-rides a JT variable" From fd95c1e6e4114b5f61261725b10e1354d20c43b3 Mon Sep 17 00:00:00 2001 From: Michael Abashian Date: Mon, 27 Mar 2017 11:56:08 -0400 Subject: [PATCH 13/23] Fixed permissions typo --- .../workflows/workflow-chart/workflow-chart.directive.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js b/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js index aea7cc0d62..d5fa53a776 100644 --- a/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js +++ b/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js @@ -207,7 +207,7 @@ export default [ '$state','moment', '$timeout', '$window', } function update() { - let userCanAddEdit = (scope.workflowJobTemplateObjt && scope.workflowJobTemplateObj.summary_fields && scope.workflowJobTemplateObj.summary_fields.user_capabilities && scope.workflowJobTemplateObj.summary_fields.user_capabilities.edit) || scope.canAddWorkflowJobTemplate; + let userCanAddEdit = (scope.workflowJobTemplateObj && scope.workflowJobTemplateObj.summary_fields && scope.workflowJobTemplateObj.summary_fields.user_capabilities && scope.workflowJobTemplateObj.summary_fields.user_capabilities.edit) || scope.canAddWorkflowJobTemplate; if(scope.dimensionsSet) { // Declare the nodes let nodes = tree.nodes(scope.treeData), @@ -813,7 +813,7 @@ export default [ '$state','moment', '$timeout', '$window', function add_node() { this.on("click", function(d) { - if((scope.workflowJobTemplateObjt && scope.workflowJobTemplateObj.summary_fields && scope.workflowJobTemplateObj.summary_fields.user_capabilities && scope.workflowJobTemplateObj.summary_fields.user_capabilities.edit) || scope.canAddWorkflowJobTemplate) { + if((scope.workflowJobTemplateObj && scope.workflowJobTemplateObj.summary_fields && scope.workflowJobTemplateObj.summary_fields.user_capabilities && scope.workflowJobTemplateObj.summary_fields.user_capabilities.edit) || scope.canAddWorkflowJobTemplate) { scope.addNode({ parent: d, betweenTwoNodes: false @@ -824,7 +824,7 @@ export default [ '$state','moment', '$timeout', '$window', function add_node_between() { this.on("click", function(d) { - if((scope.workflowJobTemplateObjt && scope.workflowJobTemplateObj.summary_fields && scope.workflowJobTemplateObj.summary_fields.user_capabilities && scope.workflowJobTemplateObj.summary_fields.user_capabilities.edit) || scope.canAddWorkflowJobTemplate) { + if((scope.workflowJobTemplateObj && scope.workflowJobTemplateObj.summary_fields && scope.workflowJobTemplateObj.summary_fields.user_capabilities && scope.workflowJobTemplateObj.summary_fields.user_capabilities.edit) || scope.canAddWorkflowJobTemplate) { scope.addNode({ parent: d, betweenTwoNodes: true @@ -835,7 +835,7 @@ export default [ '$state','moment', '$timeout', '$window', function remove_node() { this.on("click", function(d) { - if((scope.workflowJobTemplateObjt && scope.workflowJobTemplateObj.summary_fields && scope.workflowJobTemplateObj.summary_fields.user_capabilities && scope.workflowJobTemplateObj.summary_fields.user_capabilities.edit) || scope.canAddWorkflowJobTemplate) { + if((scope.workflowJobTemplateObj && scope.workflowJobTemplateObj.summary_fields && scope.workflowJobTemplateObj.summary_fields.user_capabilities && scope.workflowJobTemplateObj.summary_fields.user_capabilities.edit) || scope.canAddWorkflowJobTemplate) { scope.deleteNode({ nodeToDelete: d }); From d4955599adf63a9b01d39b9489542c968c57ae34 Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Mon, 27 Mar 2017 12:06:54 -0400 Subject: [PATCH 14/23] fix legacy standard out --- awx/ui/client/src/job-results/job-results.controller.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/awx/ui/client/src/job-results/job-results.controller.js b/awx/ui/client/src/job-results/job-results.controller.js index 73137f98d0..975c8c565e 100644 --- a/awx/ui/client/src/job-results/job-results.controller.js +++ b/awx/ui/client/src/job-results/job-results.controller.js @@ -451,13 +451,6 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy var getSkeleton = function(url) { jobResultsService.getEvents(url) .then(events => { - // old job check: if the job is complete, there is result stdout, and - // there are no job events, it's an old job - if ($scope.jobFinished) { - $scope.showLegacyJobErrorMessage = $scope.job.result_stdout.length && - !events.results.length; - } - events.results.forEach(event => { if (event.start_line === 0 && event.end_line === 0) { $scope.isOld++; From 536a3b1647a16500fe0ad4764035af6a9216980a Mon Sep 17 00:00:00 2001 From: Matthew Jones Date: Mon, 27 Mar 2017 15:40:43 -0400 Subject: [PATCH 15/23] Don't use jinja quote filter on insights username or password This fixes an issue where a user was trying to use an exclamation mark in the password. Historically we've used the quote filter but more and more we're seeing conflicts with special characters --- awx/playbooks/project_update.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/awx/playbooks/project_update.yml b/awx/playbooks/project_update.yml index c3803c7118..db66e32487 100644 --- a/awx/playbooks/project_update.yml +++ b/awx/playbooks/project_update.yml @@ -108,8 +108,8 @@ - name: update project using insights uri: url: "{{insights_url}}/r/insights/v1/maintenance?ansible=true" - user: "{{scm_username|quote}}" - password: "{{scm_password|quote}}" + user: "{{scm_username}}" + password: "{{scm_password}}" force_basic_auth: yes when: scm_type == 'insights' register: insights_output From bfbeef18e6b4df6851a6f59955e5687a359f8316 Mon Sep 17 00:00:00 2001 From: Michael Abashian Date: Wed, 29 Mar 2017 12:04:48 -0400 Subject: [PATCH 16/23] Navigate back to the jobDetails state when the user clicks outside the host event modal. --- .../host-event/host-event-modal.partial.html | 4 ++-- .../job-results/host-event/host-event.controller.js | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/awx/ui/client/src/job-results/host-event/host-event-modal.partial.html b/awx/ui/client/src/job-results/host-event/host-event-modal.partial.html index 2023129573..e48908f3ff 100644 --- a/awx/ui/client/src/job-results/host-event/host-event-modal.partial.html +++ b/awx/ui/client/src/job-results/host-event/host-event-modal.partial.html @@ -9,7 +9,7 @@ {{event.host_name}} - @@ -64,7 +64,7 @@
- +
diff --git a/awx/ui/client/src/job-results/host-event/host-event.controller.js b/awx/ui/client/src/job-results/host-event/host-event.controller.js index 5059140f00..ae10bccc4d 100644 --- a/awx/ui/client/src/job-results/host-event/host-event.controller.js +++ b/awx/ui/client/src/job-results/host-event/host-event.controller.js @@ -45,6 +45,12 @@ return $scope.hostResults.indexOf(result[0]); }; + $scope.closeHostEvent = function() { + // Unbind the listener so it doesn't fire when we close the modal via navigation + $('#HostEvent').off('hidden.bs.modal'); + $state.go('jobDetail'); + }; + var init = function(){ hostEvent.event_name = hostEvent.event; $scope.event = _.cloneDeep(hostEvent); @@ -98,6 +104,10 @@ } } $('#HostEvent').modal('show'); + + $('#HostEvent').on('hidden.bs.modal', function () { + $scope.closeHostEvent(); + }); }; init(); }]; From 83919682ea16e7786c9399209f8eb6b63374b3b9 Mon Sep 17 00:00:00 2001 From: Shane McDonald Date: Wed, 29 Mar 2017 10:57:34 -0400 Subject: [PATCH 17/23] Add clean-dist target --- Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index c45d70324b..6a46e6e22f 100644 --- a/Makefile +++ b/Makefile @@ -225,11 +225,13 @@ clean-tmp: clean-venv: rm -rf venv/ +clean-dist: + rm -rf dist + # Remove temporary build files, compiled Python files. -clean: clean-rpm clean-deb clean-ui clean-tar clean-packer clean-bundle +clean: clean-rpm clean-deb clean-ui clean-tar clean-packer clean-bundle clean-dist rm -rf awx/public rm -rf awx/lib/site-packages - rm -rf dist/* rm -rf awx/job_status rm -rf awx/job_output rm -rf reports From 93dda555f107d73ddbd13ba35eb570b38842726d Mon Sep 17 00:00:00 2001 From: Shane McDonald Date: Wed, 29 Mar 2017 11:08:03 -0400 Subject: [PATCH 18/23] Get offline pip installs working I had to pull the git urls out of the main requirements files because in order to install offline (--no-index), we need pip to install from local package archives rather than cloning repo. The weird `cat` thing going on in the Makefile is because we need to install everything as part of a single `pip install` transaction. Without this, installing only requirements_git.txt will result in dependencies getting unintentionally updated. --- Makefile | 12 ++++++++++-- requirements/requirements.in | 5 ----- requirements/requirements.txt | 5 ----- requirements/requirements_ansible.in | 1 - requirements/requirements_ansible.txt | 1 - requirements/requirements_ansible_git.txt | 1 + requirements/requirements_git.txt | 5 +++++ tools/docker-compose/Dockerfile | 2 ++ 8 files changed, 18 insertions(+), 14 deletions(-) create mode 100644 requirements/requirements_ansible_git.txt create mode 100644 requirements/requirements_git.txt diff --git a/Makefile b/Makefile index 6a46e6e22f..7a6a85ad38 100644 --- a/Makefile +++ b/Makefile @@ -284,7 +284,11 @@ virtualenv_tower: fi requirements_ansible: virtualenv_ansible - $(VENV_BASE)/ansible/bin/pip install $(PIP_OPTIONS) --ignore-installed --no-binary $(SRC_ONLY_PKGS) -r requirements/requirements_ansible.txt + if [[ "$(PIP_OPTIONS)" == *"--no-index"* ]]; then \ + cat requirements/requirements_ansible.txt requirements/requirements_ansible_local.txt | $(VENV_BASE)/ansible/bin/pip install $(PIP_OPTIONS) --ignore-installed -r /dev/stdin ; \ + else \ + cat requirements/requirements_ansible.txt requirements/requirements_ansible_git.txt | $(VENV_BASE)/ansible/bin/pip install $(PIP_OPTIONS) --ignore-installed -r /dev/stdin ; \ + fi $(VENV_BASE)/ansible/bin/pip uninstall --yes -r requirements/requirements_ansible_uninstall.txt requirements_ansible_dev: @@ -294,7 +298,11 @@ requirements_ansible_dev: # Install third-party requirements needed for Tower's environment. requirements_tower: virtualenv_tower - $(VENV_BASE)/tower/bin/pip install $(PIP_OPTIONS) --ignore-installed --no-binary $(SRC_ONLY_PKGS) -r requirements/requirements.txt + if [[ "$(PIP_OPTIONS)" == *"--no-index"* ]]; then \ + cat requirements/requirements.txt requirements/requirements_local.txt | $(VENV_BASE)/tower/bin/pip install $(PIP_OPTIONS) --ignore-installed -r /dev/stdin ; \ + else \ + cat requirements/requirements.txt requirements/requirements_git.txt | $(VENV_BASE)/tower/bin/pip install $(PIP_OPTIONS) --ignore-installed -r /dev/stdin ; \ + fi $(VENV_BASE)/tower/bin/pip uninstall --yes -r requirements/requirements_tower_uninstall.txt requirements_tower_dev: diff --git a/requirements/requirements.in b/requirements/requirements.in index 19d222e315..2ba5b1fa9a 100644 --- a/requirements/requirements.in +++ b/requirements/requirements.in @@ -1,8 +1,3 @@ --e git+https://github.com/ansible/ansiconv.git@tower_1.0.0#egg=ansiconv --e git+https://github.com/ansible/django-jsonbfield@fix-sqlite_serialization#egg=jsonbfield --e git+https://github.com/ansible/django-qsstats-magic.git@tower_0.7.2#egg=django-qsstats-magic --e git+https://github.com/ansible/dm.xmlsec.binding.git@master#egg=dm.xmlsec.binding --e git+https://github.com/chrismeyersfsu/pyrax@tower#egg=pyrax apache-libcloud==1.3.0 appdirs==1.4.2 asgi-amqp==0.4.1 diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 1ff0a862cb..57b04b3e98 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -4,11 +4,6 @@ # # pip-compile --output-file requirements/requirements.txt requirements/requirements.in # -git+https://github.com/ansible/ansiconv.git@tower_1.0.0#egg=ansiconv -git+https://github.com/ansible/django-qsstats-magic.git@tower_0.7.2#egg=django-qsstats-magic -git+https://github.com/ansible/dm.xmlsec.binding.git@master#egg=dm.xmlsec.binding -git+https://github.com/ansible/django-jsonbfield@fix-sqlite_serialization#egg=jsonbfield -git+https://github.com/chrismeyersfsu/pyrax@tower#egg=pyrax adal==0.4.3 # via msrestazure amqp==1.4.9 # via kombu anyjson==0.3.3 # via kombu diff --git a/requirements/requirements_ansible.in b/requirements/requirements_ansible.in index c5479b85d9..13c7823172 100644 --- a/requirements/requirements_ansible.in +++ b/requirements/requirements_ansible.in @@ -1,4 +1,3 @@ --e git+https://github.com/chrismeyersfsu/pyrax@tower#egg=pyrax apache-libcloud==1.3.0 azure==2.0.0rc6 backports.ssl-match-hostname==3.5.0.1 diff --git a/requirements/requirements_ansible.txt b/requirements/requirements_ansible.txt index 763dc872cb..ef2138c312 100644 --- a/requirements/requirements_ansible.txt +++ b/requirements/requirements_ansible.txt @@ -4,7 +4,6 @@ # # pip-compile --output-file requirements/requirements_ansible.txt requirements/requirements_ansible.in # -git+https://github.com/chrismeyersfsu/pyrax@tower#egg=pyrax adal==0.4.3 # via msrestazure amqp==1.4.9 # via kombu anyjson==0.3.3 # via kombu diff --git a/requirements/requirements_ansible_git.txt b/requirements/requirements_ansible_git.txt new file mode 100644 index 0000000000..d71c0f11ad --- /dev/null +++ b/requirements/requirements_ansible_git.txt @@ -0,0 +1 @@ +git+https://github.com/chrismeyersfsu/pyrax@tower#egg=pyrax diff --git a/requirements/requirements_git.txt b/requirements/requirements_git.txt new file mode 100644 index 0000000000..8439a821cb --- /dev/null +++ b/requirements/requirements_git.txt @@ -0,0 +1,5 @@ +git+https://github.com/ansible/ansiconv.git@tower_1.0.0#egg=ansiconv +git+https://github.com/ansible/django-qsstats-magic.git@tower_0.7.2#egg=django-qsstats-magic +git+https://github.com/ansible/dm.xmlsec.binding.git@master#egg=dm.xmlsec.binding +git+https://github.com/ansible/django-jsonbfield@fix-sqlite_serialization#egg=jsonbfield +git+https://github.com/chrismeyersfsu/pyrax@tower#egg=pyrax diff --git a/tools/docker-compose/Dockerfile b/tools/docker-compose/Dockerfile index 4a78226a3a..ef0854bcc7 100644 --- a/tools/docker-compose/Dockerfile +++ b/tools/docker-compose/Dockerfile @@ -3,7 +3,9 @@ FROM centos:7 ADD Makefile /tmp/Makefile RUN mkdir /tmp/requirements ADD requirements/requirements.txt \ +requirements/requirements_git.txt \ requirements/requirements_ansible.txt \ +requirements/requirements_ansible_git.txt \ requirements/requirements_dev.txt \ requirements/requirements_ansible_uninstall.txt \ requirements/requirements_tower_uninstall.txt \ From d9e65823ad57ffa2cf4d0f80f183adbb4c7631f0 Mon Sep 17 00:00:00 2001 From: Shane McDonald Date: Wed, 29 Mar 2017 11:09:38 -0400 Subject: [PATCH 19/23] Update brew-srpm target to generate local requirements files This looks pretty hairy, but essentially it's just re-invoking `pip download` with `--no-deps` so that I can record the name of the local archives that are created. --- Makefile | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 7a6a85ad38..99e7303f24 100644 --- a/Makefile +++ b/Makefile @@ -723,21 +723,21 @@ rpm-build/$(SDIST_TAR_FILE): rpm-build dist/$(SDIST_TAR_FILE) tar-build/$(SETUP_ rpmtar: sdist rpm-build/$(SDIST_TAR_FILE) -brewrpmtar: rpm-build/python-deps.tar.gz rpmtar +brewrpmtar: rpm-build/python-deps.tar.gz requirements/requirements_local.txt requirements/requirements_ansible_local.txt rpmtar rpm-build/python-deps.tar.gz: requirements/vendor rpm-build tar czf rpm-build/python-deps.tar.gz requirements/vendor requirements/vendor: - pip download \ + cat requirements/requirements.txt requirements/requirements_git.txt | pip download \ --no-binary=:all: \ - --requirement=requirements/requirements_ansible.txt \ + --requirement=/dev/stdin \ --dest=$@ \ --exists-action=i - pip download \ + cat requirements/requirements_ansible.txt requirements/requirements_ansible_git.txt | pip download \ --no-binary=:all: \ - --requirement=requirements/requirements.txt \ + --requirement=/dev/stdin \ --dest=$@ \ --exists-action=i @@ -747,6 +747,21 @@ requirements/vendor: --dest=$@ \ --exists-action=i +requirements/requirements_local.txt: + @echo "This is going to take a while..." + pip download \ + --requirement=requirements/requirements_git.txt \ + --no-deps \ + --exists-action=w \ + --dest=requirements/vendor 2>/dev/null | sed -n 's/^\s*Saved\s*//p' > $@ + +requirements/requirements_ansible_local.txt: + pip download \ + --requirement=requirements/requirements_ansible_git.txt \ + --no-deps \ + --exists-action=w \ + --dest=requirements/vendor 2>/dev/null | sed -n 's/^\s*Saved\s*//p' > $@ + rpm-build/$(RPM_NVR).src.rpm: /etc/mock/$(MOCK_CFG).cfg $(MOCK_BIN) -r $(MOCK_CFG) --resultdir rpm-build --buildsrpm --spec rpm-build/$(NAME).spec --sources rpm-build \ --define "tower_version $(VERSION)" --define "tower_release $(RELEASE)" $(SCL_DEFINES) From 8c7947b1a89dffde5744002053a1d9d34766b144 Mon Sep 17 00:00:00 2001 From: Shane McDonald Date: Wed, 29 Mar 2017 11:10:05 -0400 Subject: [PATCH 20/23] Remove requirements/vendor on make clean. --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 99e7303f24..6f39d93a0b 100644 --- a/Makefile +++ b/Makefile @@ -236,6 +236,7 @@ clean: clean-rpm clean-deb clean-ui clean-tar clean-packer clean-bundle clean-di rm -rf awx/job_output rm -rf reports rm -f awx/awx_test.sqlite3 + rm -rf requirements/vendor rm -rf tmp mkdir tmp rm -rf build $(NAME)-$(VERSION) *.egg-info From 8bab0a14ef301ac639255236cb4b8451cc5f6a3a Mon Sep 17 00:00:00 2001 From: Shane McDonald Date: Wed, 29 Mar 2017 11:15:45 -0400 Subject: [PATCH 21/23] Add requirements/vendor to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 57e2baf042..48c5934377 100644 --- a/.gitignore +++ b/.gitignore @@ -108,6 +108,7 @@ reports *.results local/ *.mo +requirements/vendor # AWX python libs populated by requirements.txt awx/lib/.deps_built From eeed969a560ce4283c818fbcd3928b4f9be65866 Mon Sep 17 00:00:00 2001 From: Matthew Jones Date: Wed, 29 Mar 2017 16:41:42 -0400 Subject: [PATCH 22/23] Make sure the insight playbook fetch doesn't quote user and pass --- awx/playbooks/project_update.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/awx/playbooks/project_update.yml b/awx/playbooks/project_update.yml index db66e32487..bfabd47a98 100644 --- a/awx/playbooks/project_update.yml +++ b/awx/playbooks/project_update.yml @@ -124,8 +124,8 @@ get_url: url: "{{insights_url}}/r/insights/v3/maintenance/{{item.maintenance_id}}/playbook" dest: "{{project_path|quote}}/{{item.name}}-{{item.maintenance_id}}.yml" - url_username: "{{scm_username|quote}}" - url_password: "{{scm_password|quote}}" + url_username: "{{scm_username}}" + url_password: "{{scm_password}}" force_basic_auth: yes force: yes when: scm_type == 'insights' and item.name != None @@ -136,8 +136,8 @@ get_url: url: "{{insights_url}}/r/insights/v3/maintenance/{{item.maintenance_id}}/playbook" dest: "{{project_path|quote}}/insights-plan-{{item.maintenance_id}}.yml" - url_username: "{{scm_username|quote}}" - url_password: "{{scm_password|quote}}" + url_username: "{{scm_username}}" + url_password: "{{scm_password}}" force_basic_auth: yes force: yes when: scm_type == 'insights' and item.name == None From 5d8e4d04199d3616425f4a36d3805b803e3c60f9 Mon Sep 17 00:00:00 2001 From: Shane McDonald Date: Thu, 30 Mar 2017 13:08:48 -0400 Subject: [PATCH 23/23] Add back SRC_ONLY_PKGS Ooops. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 6f39d93a0b..3d378c60c9 100644 --- a/Makefile +++ b/Makefile @@ -288,7 +288,7 @@ requirements_ansible: virtualenv_ansible if [[ "$(PIP_OPTIONS)" == *"--no-index"* ]]; then \ cat requirements/requirements_ansible.txt requirements/requirements_ansible_local.txt | $(VENV_BASE)/ansible/bin/pip install $(PIP_OPTIONS) --ignore-installed -r /dev/stdin ; \ else \ - cat requirements/requirements_ansible.txt requirements/requirements_ansible_git.txt | $(VENV_BASE)/ansible/bin/pip install $(PIP_OPTIONS) --ignore-installed -r /dev/stdin ; \ + cat requirements/requirements_ansible.txt requirements/requirements_ansible_git.txt | $(VENV_BASE)/ansible/bin/pip install $(PIP_OPTIONS) --no-binary $(SRC_ONLY_PKGS) --ignore-installed -r /dev/stdin ; \ fi $(VENV_BASE)/ansible/bin/pip uninstall --yes -r requirements/requirements_ansible_uninstall.txt @@ -302,7 +302,7 @@ requirements_tower: virtualenv_tower if [[ "$(PIP_OPTIONS)" == *"--no-index"* ]]; then \ cat requirements/requirements.txt requirements/requirements_local.txt | $(VENV_BASE)/tower/bin/pip install $(PIP_OPTIONS) --ignore-installed -r /dev/stdin ; \ else \ - cat requirements/requirements.txt requirements/requirements_git.txt | $(VENV_BASE)/tower/bin/pip install $(PIP_OPTIONS) --ignore-installed -r /dev/stdin ; \ + cat requirements/requirements.txt requirements/requirements_git.txt | $(VENV_BASE)/tower/bin/pip install $(PIP_OPTIONS) --no-binary $(SRC_ONLY_PKGS) --ignore-installed -r /dev/stdin ; \ fi $(VENV_BASE)/tower/bin/pip uninstall --yes -r requirements/requirements_tower_uninstall.txt