From 31c2e1a45084a9cd031c2d428e5cae9f6de48520 Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Wed, 7 Dec 2022 14:09:36 -0500 Subject: [PATCH 01/19] Only allow promote and stage to run on the awx repo --- .github/workflows/promote.yml | 1 + .github/workflows/stage.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/promote.yml b/.github/workflows/promote.yml index 820494d303..594b213e49 100644 --- a/.github/workflows/promote.yml +++ b/.github/workflows/promote.yml @@ -10,6 +10,7 @@ on: jobs: promote: + if: github.repository == 'ansible/awx' runs-on: ubuntu-latest steps: - name: Checkout awx diff --git a/.github/workflows/stage.yml b/.github/workflows/stage.yml index 042b6b7b0d..192f307d50 100644 --- a/.github/workflows/stage.yml +++ b/.github/workflows/stage.yml @@ -21,6 +21,7 @@ on: jobs: stage: + if: github.repository == 'ansible/awx' runs-on: ubuntu-latest permissions: packages: write From e2cee10767c36b659a5e26431a8f7fbf21a1c955 Mon Sep 17 00:00:00 2001 From: John Westcott IV <32551173+john-westcott-iv@users.noreply.github.com> Date: Thu, 8 Dec 2022 16:34:13 -0500 Subject: [PATCH 02/19] Update .github/workflows/promote.yml Co-authored-by: Shane McDonald --- .github/workflows/promote.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/promote.yml b/.github/workflows/promote.yml index 594b213e49..45dec2b283 100644 --- a/.github/workflows/promote.yml +++ b/.github/workflows/promote.yml @@ -10,7 +10,7 @@ on: jobs: promote: - if: github.repository == 'ansible/awx' + if: endsWith(github.repository, '/awx') runs-on: ubuntu-latest steps: - name: Checkout awx From 5d96ee084d787e18354b4eaa0fa8eec818090ad2 Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Thu, 8 Dec 2022 16:36:04 -0500 Subject: [PATCH 03/19] Adding endswith(awx) to stage --- .github/workflows/stage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stage.yml b/.github/workflows/stage.yml index 192f307d50..1e28952e0f 100644 --- a/.github/workflows/stage.yml +++ b/.github/workflows/stage.yml @@ -21,7 +21,7 @@ on: jobs: stage: - if: github.repository == 'ansible/awx' + if: endsWith(github.repository, '/awx') runs-on: ubuntu-latest permissions: packages: write From fd6605932a4d8c30bf4308cb06dca5197ae73eb4 Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Tue, 24 Jan 2023 12:02:49 -0500 Subject: [PATCH 04/19] Adding exception if unable to find the controler plane ee --- awx/main/utils/execution_environments.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/awx/main/utils/execution_environments.py b/awx/main/utils/execution_environments.py index 02e6a8b701..7b197287b3 100644 --- a/awx/main/utils/execution_environments.py +++ b/awx/main/utils/execution_environments.py @@ -1,4 +1,5 @@ import os +import logging from pathlib import Path from django.conf import settings @@ -6,8 +7,15 @@ from django.conf import settings from awx.main.models.execution_environments import ExecutionEnvironment +logger = logging.getLogger(__name__) + + def get_control_plane_execution_environment(): - return ExecutionEnvironment.objects.filter(organization=None, managed=True).first() + ee = ExecutionEnvironment.objects.filter(organization=None, managed=True).first() + if ee == None: + logger.error('Failed to find control plane ee, there are no managed EEs without organizations') + raise RuntimeError("Failed to find default control plane EE") + return ee def get_default_execution_environment(): From eb9431ee1f8433bdb479919fafa6493c935fb8c9 Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Tue, 24 Jan 2023 12:03:10 -0500 Subject: [PATCH 05/19] Fixing hard coded project --- awx/main/tasks/jobs.py | 2 +- awx/main/tests/unit/test_tasks.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/awx/main/tasks/jobs.py b/awx/main/tasks/jobs.py index a726a418c0..d453a2364b 100644 --- a/awx/main/tasks/jobs.py +++ b/awx/main/tasks/jobs.py @@ -311,7 +311,7 @@ class BaseTask(object): env['AWX_PRIVATE_DATA_DIR'] = private_data_dir if self.instance.execution_environment is None: - raise RuntimeError('The project could not sync because there is no Execution Environment.') + raise RuntimeError(f'The {self.model.__name__} could not run because there is no Execution Environment.') return env diff --git a/awx/main/tests/unit/test_tasks.py b/awx/main/tests/unit/test_tasks.py index 9a59e091d1..c3d472deb6 100644 --- a/awx/main/tests/unit/test_tasks.py +++ b/awx/main/tests/unit/test_tasks.py @@ -1972,7 +1972,7 @@ def test_project_update_no_ee(mock_me): with pytest.raises(RuntimeError) as e: task.build_env(job, {}) - assert 'The project could not sync because there is no Execution Environment' in str(e.value) + assert 'The ProjectUpdate could not run because there is no Execution Environment' in str(e.value) @pytest.mark.parametrize( From 4630757f5f75c886bbfd7c55cf0cecfab3588b2f Mon Sep 17 00:00:00 2001 From: npithonDR <123001064+npithonDR@users.noreply.github.com> Date: Thu, 9 Feb 2023 09:34:10 +0100 Subject: [PATCH 06/19] Fix error for byweekday in schedule_rruleset Fix error: ``` fatal: [localhost]: FAILED! => { "msg": "An unhandled exception occurred while running the lookup plugin 'awx.awx.schedule_rruleset'. Error was a , original message: In rule 1 byweekday must only contain values in monday, tuesday, wednesday, thursday, friday, saturday, sunday. In rule 1 byweekday must only contain values in monday, tuesday, wednesday, thursday, friday, saturday, sunday" } ``` with: ``` - name: Build a complex schedule for every monday using the rruleset plugin awx.awx.schedule: name: "Test build complex schedule" state: present unified_job_template: "template name" rrule: "{{ query('awx.awx.schedule_rruleset', '2030-04-30 10:30:45', rules=rrules, timezone='Europe/Paris' ) }}" vars: rrules: - frequency: 'day' interval: 1 byweekday: 'monday' ``` --- awx_collection/plugins/lookup/schedule_rruleset.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/awx_collection/plugins/lookup/schedule_rruleset.py b/awx_collection/plugins/lookup/schedule_rruleset.py index 6aefde48d0..d5850582b0 100644 --- a/awx_collection/plugins/lookup/schedule_rruleset.py +++ b/awx_collection/plugins/lookup/schedule_rruleset.py @@ -210,8 +210,7 @@ class LookupModule(LookupBase): def process_list(self, field_name, rule, valid_list, rule_number): return_values = [] - if isinstance(rule[field_name], list): - rule[field_name] = rule[field_name].split(',') + rule[field_name] = rule[field_name].split(',') for value in rule[field_name]: value = value.strip() if value not in valid_list: From 951eee944cf7417c4c8a98e81862579f86540727 Mon Sep 17 00:00:00 2001 From: npithonDR <123001064+npithonDR@users.noreply.github.com> Date: Mon, 13 Feb 2023 09:50:11 +0100 Subject: [PATCH 07/19] Add additional rruleset tests --- .../targets/lookup_rruleset/tasks/main.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/awx_collection/tests/integration/targets/lookup_rruleset/tasks/main.yml b/awx_collection/tests/integration/targets/lookup_rruleset/tasks/main.yml index fe5b3f755d..7f9f0271b2 100644 --- a/awx_collection/tests/integration/targets/lookup_rruleset/tasks/main.yml +++ b/awx_collection/tests/integration/targets/lookup_rruleset/tasks/main.yml @@ -95,6 +95,22 @@ - results is failed - "'In rule 2 end_on must either be an integer or in the format YYYY-MM-DD [HH:MM:SS]' in results.msg" +- name: Every Mondays + set_fact: + complex_rule: "{{ query(ruleset_plugin_name, '2022-04-30 10:30:45', rules=rrules, timezone='UTC' ) }}" + ignore_errors: True + register: results + vars: + rrules: + - frequency: 'day' + interval: 1 + byweekday: 'monday' + +- assert: + that: + - results is success + - "'DTSTART;TZID=UTC:20220430T103045 RRULE:FREQ=DAILY;BYDAY=MO;INTERVAL=1' == complex_rule" + - name: call rruleset with an invalid byweekday set_fact: From fb2647ff7bfcf29a60f1cb439c8aa10b19cb6da6 Mon Sep 17 00:00:00 2001 From: Gabe Muniz Date: Fri, 17 Feb 2023 14:17:18 -0500 Subject: [PATCH 08/19] changing the signature of workflowapprovallist included workflow approval as a read only endpoint to pass collection tests --- awx/api/views/__init__.py | 2 +- awx_collection/test/awx/test_completeness.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/awx/api/views/__init__.py b/awx/api/views/__init__.py index e81a6ebbde..609e88e155 100644 --- a/awx/api/views/__init__.py +++ b/awx/api/views/__init__.py @@ -4288,7 +4288,7 @@ class WorkflowApprovalTemplateJobsList(SubListAPIView): parent_key = 'workflow_approval_template' -class WorkflowApprovalList(ListCreateAPIView): +class WorkflowApprovalList(ListAPIView): model = models.WorkflowApproval serializer_class = serializers.WorkflowApprovalListSerializer diff --git a/awx_collection/test/awx/test_completeness.py b/awx_collection/test/awx/test_completeness.py index 43e225e4b8..451c1a61d3 100644 --- a/awx_collection/test/awx/test_completeness.py +++ b/awx_collection/test/awx/test_completeness.py @@ -16,7 +16,7 @@ import glob # Normally a read-only endpoint should not have a module (i.e. /api/v2/me) but sometimes we reuse a name # For example, we have a role module but /api/v2/roles is a read only endpoint. # This list indicates which read-only endpoints have associated modules with them. -read_only_endpoints_with_modules = ['settings', 'role', 'project_update'] +read_only_endpoints_with_modules = ['settings', 'role', 'project_update', 'workflow_approval'] # If a module should not be created for an endpoint and the endpoint is not read-only add it here # THINK HARD ABOUT DOING THIS From b5f240ce700a2f12e3ca888fb11bf71b6936c0cd Mon Sep 17 00:00:00 2001 From: Alan Rominger Date: Fri, 17 Feb 2023 15:10:59 -0500 Subject: [PATCH 09/19] Add integration test and docs for workflow_approval module --- .../plugins/modules/workflow_approval.py | 12 +++- .../targets/workflow_approval/tasks/main.yml | 57 +++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 awx_collection/tests/integration/targets/workflow_approval/tasks/main.yml diff --git a/awx_collection/plugins/modules/workflow_approval.py b/awx_collection/plugins/modules/workflow_approval.py index bb8c24dfb8..dd81eeb93c 100644 --- a/awx_collection/plugins/modules/workflow_approval.py +++ b/awx_collection/plugins/modules/workflow_approval.py @@ -57,7 +57,15 @@ extends_documentation_fragment: awx.awx.auth EXAMPLES = """ -- name: Launch a workflow with a timeout of 10 seconds +- name: Create a workflow approval node + workflow_job_template_node: + identifier: approval_test + approval_node: + name: approval_jt_name + timeout: 900 + workflow: "Test Workflow" + +- name: Launch the workflow with a timeout of 10 seconds workflow_launch: workflow_template: "Test Workflow" wait: False @@ -66,7 +74,7 @@ EXAMPLES = """ - name: Wait for approval node to activate and approve workflow_approval: workflow_job_id: "{{ workflow.id }}" - name: Approve Me + name: approval_jt_name interval: 10 timeout: 20 action: deny diff --git a/awx_collection/tests/integration/targets/workflow_approval/tasks/main.yml b/awx_collection/tests/integration/targets/workflow_approval/tasks/main.yml new file mode 100644 index 0000000000..eaf1b3bf8d --- /dev/null +++ b/awx_collection/tests/integration/targets/workflow_approval/tasks/main.yml @@ -0,0 +1,57 @@ +--- +- name: Generate a random string for names + set_fact: + test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" + test_prefix: AWX-Collection-tests-workflow_approval + +- name: Generate random names for test objects + set_fact: + org_name: "{{ test_prefix }}-org-{{ test_id }}" + approval_node_name: "{{ test_prefix }}-node-{{ test_id }}" + wfjt_name: "{{ test_prefix }}-wfjt-{{ test_id }}" + +- block: + - name: Create a new organization for test isolation + organization: + name: "{{ org_name }}" + + - name: Create a workflow job template + workflow_job_template: + name: "{{ wfjt_name }}" + organization: "{{ org_name }}" + + - name: Create approval node + workflow_job_template_node: + identifier: approval_test + approval_node: + name: "{{ approval_node_name }}" # Referenced later on + timeout: 900 + workflow: "{{ wfjt_name }}" + + # Launch and approve the workflow + - name: Launch the workflow + workflow_launch: + workflow_template: "{{ wfjt_name }}" + wait: False + register: workflow_job + + - name: Wait for approval node to activate and approve + workflow_approval: + workflow_job_id: "{{ workflow_job.id }}" + name: "{{ approval_node_name }}" + interval: 10 + timeout: 20 + action: approve + register: result + + - assert: + that: + - "result is changed" + - "result is not failed" + + always: + - name: Delete the workflow job template + workflow_job_template: + name: "{{ wfjt_name }}" + state: absent + ignore_errors: True From fe3aa6ce2bd64d33faab41dd4636379416a51821 Mon Sep 17 00:00:00 2001 From: sean-m-sullivan Date: Thu, 16 Feb 2023 21:26:41 -0500 Subject: [PATCH 10/19] fix inventory prompt on launch for workflow nodes --- .../plugins/modules/workflow_job_template.py | 35 ++++++++++++++++--- .../workflow_job_template/tasks/main.yml | 5 +++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/awx_collection/plugins/modules/workflow_job_template.py b/awx_collection/plugins/modules/workflow_job_template.py index 41c5b66573..19954877b7 100644 --- a/awx_collection/plugins/modules/workflow_job_template.py +++ b/awx_collection/plugins/modules/workflow_job_template.py @@ -183,7 +183,21 @@ options: inventory: description: - Inventory applied as a prompt, if job template prompts for inventory - type: str + type: dict + suboptions: + name: + description: + - Name Inventory to be applied to job as launch-time prompts. + type: str + organization: + description: + - Name of key for use in model for organizational reference + type: dict + suboptions: + name: + description: + - The organization of the credentials exists in. + type: str scm_branch: description: - SCM branch applied as a prompt, if job template prompts for SCM branch @@ -544,6 +558,10 @@ EXAMPLES = ''' type: job_template execution_environment: name: My EE + inventory: + name: Test inventory + organization: + name: Default related: credentials: - name: cyberark @@ -613,10 +631,6 @@ def create_workflow_nodes(module, response, workflow_nodes, workflow_id): if workflow_node['unified_job_template']['type'] != 'workflow_approval': module.fail_json(msg="Unable to Find unified_job_template: {0}".format(search_fields)) - inventory = workflow_node.get('inventory') - if inventory: - workflow_node_fields['inventory'] = module.resolve_name_to_id('inventories', inventory) - # Lookup Values for other fields for field_name in ( @@ -645,6 +659,17 @@ def create_workflow_nodes(module, response, workflow_nodes, workflow_id): 'execution_environments', name_or_id=workflow_node['execution_environment']['name'] )['id'] + # Two lookup methods are used based on a fix added in 21.11.0, and the awx export model + if 'inventory' in workflow_node: + if 'name' in workflow_node['inventory']: + inv_lookup_data = {} + if 'organization' in workflow_node['inventory']: + inv_lookup_data['organization'] = module.resolve_name_to_id('organizations', workflow_node['inventory']['organization']['name']) + workflow_node_fields['inventory'] = module.get_one( + 'inventories', name_or_id=workflow_node['inventory']['name'], data=inv_lookup_data)['id'] + else: + workflow_node_fields['inventory'] = module.get_one('inventories', name_or_id=workflow_node['inventory'])['id'] + # Set Search fields search_fields['workflow_job_template'] = workflow_node_fields['workflow_job_template'] = workflow_id diff --git a/awx_collection/tests/integration/targets/workflow_job_template/tasks/main.yml b/awx_collection/tests/integration/targets/workflow_job_template/tasks/main.yml index 1477193e6c..e5f3366cd3 100644 --- a/awx_collection/tests/integration/targets/workflow_job_template/tasks/main.yml +++ b/awx_collection/tests/integration/targets/workflow_job_template/tasks/main.yml @@ -493,6 +493,7 @@ workflow_job_template: name: "copy_{{ wfjt_name }}" organization: Default + ask_inventory_on_launch: true survey_spec: name: Basic Survey description: Basic Survey @@ -737,6 +738,10 @@ timeout: 23 execution_environment: name: "{{ ee1 }}" + inventory: + name: Test inventory + organization: + name: Default related: credentials: - name: "{{ scm_cred_name }}" From 6d3f39fe92cf860ff490cfe0af127c7c9187a7cb Mon Sep 17 00:00:00 2001 From: Alan Rominger Date: Tue, 21 Feb 2023 13:34:29 -0500 Subject: [PATCH 11/19] Give proper 401 code to user not logged in --- awx/api/generics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/awx/api/generics.py b/awx/api/generics.py index 7d21d74ee6..361506c605 100644 --- a/awx/api/generics.py +++ b/awx/api/generics.py @@ -28,7 +28,7 @@ from rest_framework import generics from rest_framework.response import Response from rest_framework import status from rest_framework import views -from rest_framework.permissions import AllowAny +from rest_framework.permissions import IsAuthenticated from rest_framework.renderers import StaticHTMLRenderer from rest_framework.negotiation import DefaultContentNegotiation @@ -822,7 +822,7 @@ def trigger_delayed_deep_copy(*args, **kwargs): class CopyAPIView(GenericAPIView): serializer_class = CopySerializer - permission_classes = (AllowAny,) + permission_classes = (IsAuthenticated,) copy_return_serializer_class = None new_in_330 = True new_in_api_v2 = True From 3a303875bb4f697269397e6d3cc9c8a379c40bf5 Mon Sep 17 00:00:00 2001 From: Hao Liu Date: Wed, 22 Feb 2023 16:18:53 -0500 Subject: [PATCH 12/19] update kind development environment instruction --- docs/development/kind.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/development/kind.md b/docs/development/kind.md index 820abae182..d3a11cc4cd 100644 --- a/docs/development/kind.md +++ b/docs/development/kind.md @@ -75,7 +75,8 @@ In the root of awx-operator: -e image_version=devel \ -e image_pull_policy=Always \ -e service_type=nodeport \ - -e namespace=awx + -e namespace=awx \ + -e nodeport_port=30080 ``` Check the operator with the following commands: From 019e6a52fe6699e4bf34c36931b739430c37b190 Mon Sep 17 00:00:00 2001 From: Hao Liu Date: Tue, 7 Feb 2023 09:51:05 -0500 Subject: [PATCH 13/19] Update project_update playbook to be compliant with ansible-lint --- awx/playbooks/project_update.yml | 189 ++++++++++++++++--------------- 1 file changed, 99 insertions(+), 90 deletions(-) diff --git a/awx/playbooks/project_update.yml b/awx/playbooks/project_update.yml index 2067e76043..7334547f97 100644 --- a/awx/playbooks/project_update.yml +++ b/awx/playbooks/project_update.yml @@ -25,42 +25,47 @@ connection: local name: Update source tree if necessary tasks: - - - name: delete project directory before update - command: "find -delete" # volume mounted, cannot delete folder itself + - name: Delete project directory before update + ansible.builtin.shell: set -o pipefail && find . -delete -print | head -2 # volume mounted, cannot delete folder itself + register: reg + changed_when: reg.stdout_lines | length > 1 args: chdir: "{{ project_path }}" tags: - delete - - block: - - name: update project using git - git: - dest: "{{project_path|quote}}" - repo: "{{scm_url}}" - version: "{{scm_branch|quote}}" - refspec: "{{scm_refspec|default(omit)}}" - force: "{{scm_clean}}" - track_submodules: "{{scm_track_submodules|default(omit)}}" - accept_hostkey: "{{scm_accept_hostkey|default(omit)}}" + - name: Update project using git + tags: + - update_git + block: + - name: Update project using git + ansible.builtin.git: + dest: "{{ project_path | quote }}" + repo: "{{ scm_url }}" + version: "{{ scm_branch | quote }}" + refspec: "{{ scm_refspec | default(omit) }}" + force: "{{ scm_clean }}" + track_submodules: "{{ scm_track_submodules | default(omit) }}" + accept_hostkey: "{{ scm_accept_hostkey | default(omit) }}" register: git_result - name: Set the git repository version - set_fact: + ansible.builtin.set_fact: scm_version: "{{ git_result['after'] }}" when: "'after' in git_result" - tags: - - update_git - - block: - - name: update project using svn - subversion: - dest: "{{project_path|quote}}" - repo: "{{scm_url|quote}}" - revision: "{{scm_branch|quote}}" - force: "{{scm_clean}}" - username: "{{scm_username|default(omit)}}" - password: "{{scm_password|default(omit)}}" + - name: Update project using svn + tags: + - update_svn + block: + - name: Update project using svn + ansible.builtin.subversion: + dest: "{{ project_path | quote }}" + repo: "{{ scm_url | quote }}" + revision: "{{ scm_branch | quote }}" + force: "{{ scm_clean }}" + username: "{{ scm_username | default(omit) }}" + password: "{{ scm_password | default(omit) }}" # must be in_place because folder pre-existing, because it is mounted in_place: true environment: @@ -68,85 +73,90 @@ register: svn_result - name: Set the svn repository version - set_fact: + ansible.builtin.set_fact: scm_version: "{{ svn_result['after'] }}" when: "'after' in svn_result" - - name: parse subversion version string properly - set_fact: - scm_version: "{{scm_version|regex_replace('^.*Revision: ([0-9]+).*$', '\\1')}}" - tags: - - update_svn + - name: Parse subversion version string properly + ansible.builtin.set_fact: + scm_version: "{{ scm_version | regex_replace('^.*Revision: ([0-9]+).*$', '\\1') }}" - - block: + + - name: Project update for Insights + tags: + - update_insights + block: - name: Ensure the project directory is present - file: - dest: "{{project_path|quote}}" + ansible.builtin.file: + dest: "{{ project_path | quote }}" state: directory + mode: '0755' - name: Fetch Insights Playbook(s) insights: - insights_url: "{{insights_url}}" - username: "{{scm_username}}" - password: "{{scm_password}}" - project_path: "{{project_path}}" - awx_license_type: "{{awx_license_type}}" - awx_version: "{{awx_version}}" + insights_url: "{{ insights_url }}" + username: "{{ scm_username }}" + password: "{{ scm_password }}" + project_path: "{{ project_path }}" + awx_license_type: "{{ awx_license_type }}" + awx_version: "{{ awx_version }}" register: results - name: Save Insights Version - set_fact: - scm_version: "{{results.version}}" + ansible.builtin.set_fact: + scm_version: "{{ results.version }}" when: results is defined - tags: - - update_insights - - block: + + - name: Update project using archive + tags: + - update_archive + block: - name: Ensure the project archive directory is present - file: - dest: "{{ project_path|quote }}/.archive" + ansible.builtin.file: + dest: "{{ project_path | quote }}/.archive" state: directory + mode: '0755' - name: Get archive from url - get_url: - url: "{{ scm_url|quote }}" - dest: "{{ project_path|quote }}/.archive/" - url_username: "{{ scm_username|default(omit) }}" - url_password: "{{ scm_password|default(omit) }}" + ansible.builtin.get_url: + url: "{{ scm_url | quote }}" + dest: "{{ project_path | quote }}/.archive/" + url_username: "{{ scm_username | default(omit) }}" + url_password: "{{ scm_password | default(omit) }}" force_basic_auth: true + mode: '0755' register: get_archive - name: Unpack archive project_archive: src: "{{ get_archive.dest }}" - project_path: "{{ project_path|quote }}" + project_path: "{{ project_path | quote }}" force: "{{ scm_clean }}" when: get_archive.changed or scm_clean register: unarchived - name: Find previous archives - find: - paths: "{{ project_path|quote }}/.archive/" + ansible.builtin.find: + paths: "{{ project_path | quote }}/.archive/" excludes: - - "{{ get_archive.dest|basename }}" + - "{{ get_archive.dest | basename }}" when: unarchived.changed register: previous_archive - name: Remove previous archives - file: + ansible.builtin.file: path: "{{ item.path }}" state: absent loop: "{{ previous_archive.files }}" - when: previous_archive.files|default([]) + when: previous_archive.files | default([]) - name: Set scm_version to archive sha1 checksum - set_fact: + ansible.builtin.set_fact: scm_version: "{{ get_archive.checksum_src }}" - tags: - - update_archive - name: Repository Version - debug: + ansible.builtin.debug: msg: "Repository Version {{ scm_version }}" tags: - update_git @@ -183,60 +193,59 @@ additional_collections_env: # These environment variables are used for installing collections, in addition to galaxy_task_env # setting the collections paths silences warnings - ANSIBLE_COLLECTIONS_PATHS: "{{projects_root}}/.__awx_cache/{{local_path}}/stage/requirements_collections" + ANSIBLE_COLLECTIONS_PATHS: "{{ projects_root }}/.__awx_cache/{{ local_path }}/stage/requirements_collections" # Put the local tmp directory in same volume as collection destination # otherwise, files cannot be moved accross volumes and will cause error - ANSIBLE_LOCAL_TEMP: "{{projects_root}}/.__awx_cache/{{local_path}}/stage/tmp" + ANSIBLE_LOCAL_TEMP: "{{ projects_root }}/.__awx_cache/{{ local_path }}/stage/tmp" tasks: - - name: Check content sync settings - block: - - debug: - msg: > - Collection and role syncing disabled. Check the AWX_ROLES_ENABLED and - AWX_COLLECTIONS_ENABLED settings and Galaxy credentials on the project's organization. - - - meta: end_play - - when: not roles_enabled|bool and not collections_enabled|bool + when: not roles_enabled | bool and not collections_enabled | bool tags: - install_roles - install_collections + block: + - name: Warn about disabled content sync + ansible.builtin.debug: + msg: > + Collection and role syncing disabled. Check the AWX_ROLES_ENABLED and + AWX_COLLECTIONS_ENABLED settings and Galaxy credentials on the project's organization. + - name: End play due to disabled content sync + ansible.builtin.meta: end_play - - name: fetch galaxy roles from requirements.(yml/yaml) - command: > + - name: Fetch galaxy roles from requirements.(yml/yaml) + ansible.builtin.command: > ansible-galaxy role install -r {{ item }} - --roles-path {{projects_root}}/.__awx_cache/{{local_path}}/stage/requirements_roles + --roles-path {{ projects_root }}/.__awx_cache/{{ local_path }}/stage/requirements_roles {{ ' -' + 'v' * ansible_verbosity if ansible_verbosity else '' }} args: - chdir: "{{project_path|quote}}" + chdir: "{{ project_path | quote }}" register: galaxy_result with_fileglob: - - "{{project_path|quote}}/roles/requirements.yaml" - - "{{project_path|quote}}/roles/requirements.yml" + - "{{ project_path | quote }}/roles/requirements.yaml" + - "{{ project_path | quote }}/roles/requirements.yml" changed_when: "'was installed successfully' in galaxy_result.stdout" environment: "{{ galaxy_task_env }}" - when: roles_enabled|bool + when: roles_enabled | bool tags: - install_roles - - name: fetch galaxy collections from collections/requirements.(yml/yaml) - command: > + - name: Fetch galaxy collections from collections/requirements.(yml/yaml) + ansible.builtin.command: > ansible-galaxy collection install -r {{ item }} - --collections-path {{projects_root}}/.__awx_cache/{{local_path}}/stage/requirements_collections + --collections-path {{ projects_root }}/.__awx_cache/{{ local_path }}/stage/requirements_collections {{ ' -' + 'v' * ansible_verbosity if ansible_verbosity else '' }} args: - chdir: "{{project_path|quote}}" + chdir: "{{ project_path | quote }}" register: galaxy_collection_result with_fileglob: - - "{{project_path|quote}}/collections/requirements.yaml" - - "{{project_path|quote}}/collections/requirements.yml" - - "{{project_path|quote}}/requirements.yaml" - - "{{project_path|quote}}/requirements.yml" + - "{{ project_path | quote }}/collections/requirements.yaml" + - "{{ project_path | quote }}/collections/requirements.yml" + - "{{ project_path | quote }}/requirements.yaml" + - "{{ project_path | quote }}/requirements.yml" changed_when: "'Installing ' in galaxy_collection_result.stdout" environment: "{{ additional_collections_env | combine(galaxy_task_env) }}" when: - "ansible_version.full is version_compare('2.9', '>=')" - - collections_enabled|bool + - collections_enabled | bool tags: - install_collections From 89e41597a6774af250bbc1d69fb409d4965920ad Mon Sep 17 00:00:00 2001 From: Hao Liu Date: Thu, 23 Feb 2023 10:46:48 -0500 Subject: [PATCH 14/19] switch from head to tail from @relrod `head` will close the input fd when it no longer needs it (or exits). find will try to write to the closed fd and somewhere along the way, it will receive SIGPIPE as a result. This is why `yes | head -5 ` doesn't run forever. --- awx/playbooks/project_update.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/playbooks/project_update.yml b/awx/playbooks/project_update.yml index 7334547f97..125b5ef312 100644 --- a/awx/playbooks/project_update.yml +++ b/awx/playbooks/project_update.yml @@ -26,7 +26,7 @@ name: Update source tree if necessary tasks: - name: Delete project directory before update - ansible.builtin.shell: set -o pipefail && find . -delete -print | head -2 # volume mounted, cannot delete folder itself + ansible.builtin.shell: set -o pipefail && find . -delete -print | tail -2 # volume mounted, cannot delete folder itself register: reg changed_when: reg.stdout_lines | length > 1 args: From 5e28f5dca162ec01d6fd32207404f66fa2276604 Mon Sep 17 00:00:00 2001 From: Alexander Pavlov Date: Tue, 17 May 2022 12:07:21 +0300 Subject: [PATCH 15/19] Remove trailing $ from websocket_urlpatterns to work with custom path to fix #12241 Signed-off-by: Alexander Pavlov --- awx/main/routing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/awx/main/routing.py b/awx/main/routing.py index 100347f64e..c96505b7e1 100644 --- a/awx/main/routing.py +++ b/awx/main/routing.py @@ -27,8 +27,8 @@ class AWXProtocolTypeRouter(ProtocolTypeRouter): websocket_urlpatterns = [ - re_path(r'websocket/$', consumers.EventConsumer.as_asgi()), - re_path(r'websocket/broadcast/$', consumers.BroadcastConsumer.as_asgi()), + re_path(r'websocket/', consumers.EventConsumer.as_asgi()), + re_path(r'websocket/broadcast/', consumers.BroadcastConsumer.as_asgi()), ] application = AWXProtocolTypeRouter( From 811ecb86735fc952de2907ed71a2f84049b07138 Mon Sep 17 00:00:00 2001 From: Alan Rominger Date: Thu, 23 Feb 2023 12:05:21 -0500 Subject: [PATCH 16/19] Follow suggestion from comment, split if NOT list --- awx_collection/plugins/lookup/schedule_rruleset.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/awx_collection/plugins/lookup/schedule_rruleset.py b/awx_collection/plugins/lookup/schedule_rruleset.py index d5850582b0..4e9732f0f2 100644 --- a/awx_collection/plugins/lookup/schedule_rruleset.py +++ b/awx_collection/plugins/lookup/schedule_rruleset.py @@ -196,7 +196,7 @@ class LookupModule(LookupBase): if isinstance(rule[field_name], int): rule[field_name] = [rule[field_name]] # If its not a list, we need to split it into a list - if isinstance(rule[field_name], list): + if not isinstance(rule[field_name], list): rule[field_name] = rule[field_name].split(',') for value in rule[field_name]: # If they have a list of strs we want to strip the str incase its space delineated @@ -210,7 +210,8 @@ class LookupModule(LookupBase): def process_list(self, field_name, rule, valid_list, rule_number): return_values = [] - rule[field_name] = rule[field_name].split(',') + if not isinstance(rule[field_name], list): + rule[field_name] = rule[field_name].split(',') for value in rule[field_name]: value = value.strip() if value not in valid_list: From 3051384f9570d5f35f96c25ecabd4e5cf158bc9f Mon Sep 17 00:00:00 2001 From: Alan Rominger Date: Thu, 23 Feb 2023 12:05:32 -0500 Subject: [PATCH 17/19] Follow suggestion from comment, split if NOT list --- awx_collection/plugins/lookup/schedule_rruleset.py | 1 + 1 file changed, 1 insertion(+) diff --git a/awx_collection/plugins/lookup/schedule_rruleset.py b/awx_collection/plugins/lookup/schedule_rruleset.py index 4e9732f0f2..b45d861db3 100644 --- a/awx_collection/plugins/lookup/schedule_rruleset.py +++ b/awx_collection/plugins/lookup/schedule_rruleset.py @@ -210,6 +210,7 @@ class LookupModule(LookupBase): def process_list(self, field_name, rule, valid_list, rule_number): return_values = [] + # If its not a list, we need to split it into a list if not isinstance(rule[field_name], list): rule[field_name] = rule[field_name].split(',') for value in rule[field_name]: From 2fca07ee4c4327fae715c6910d468d2834eb6b54 Mon Sep 17 00:00:00 2001 From: Shane McDonald Date: Thu, 23 Feb 2023 12:21:02 -0500 Subject: [PATCH 18/19] Allow serving app from non-root path in dev env Usage: $ EXTRA_SOURCES_ANSIBLE_OPTS='-e ingress_path=/awx' make docker-compose $ curl http://localhost:8013/awx/api/v2/ping/ --- Makefile | 14 +- .../roles/dockerfile/templates/Dockerfile.j2 | 5 +- .../dockerfile/templates/supervisor.conf.j2 | 2 +- .../ansible/roles/sources/defaults/main.yml | 1 + .../ansible/roles/sources/tasks/main.yml | 13 +- .../sources/templates/docker-compose.yml.j2 | 3 + .../local_settings.py.j2} | 2 + .../roles/sources/templates/nginx.conf.j2 | 108 +++++---------- .../sources/templates/nginx.locations.conf.j2 | 37 ++++++ tools/docker-compose/nginx.conf | 31 ----- tools/docker-compose/nginx.vh.default.conf | 124 ------------------ 11 files changed, 81 insertions(+), 259 deletions(-) rename tools/docker-compose/ansible/roles/sources/{files/local_settings.py => templates/local_settings.py.j2} (96%) create mode 100644 tools/docker-compose/ansible/roles/sources/templates/nginx.locations.conf.j2 delete mode 100644 tools/docker-compose/nginx.conf delete mode 100644 tools/docker-compose/nginx.vh.default.conf diff --git a/Makefile b/Makefile index 896dcf2c6c..4e402125b2 100644 --- a/Makefile +++ b/Makefile @@ -203,19 +203,7 @@ uwsgi: collectstatic @if [ "$(VENV_BASE)" ]; then \ . $(VENV_BASE)/awx/bin/activate; \ fi; \ - uwsgi -b 32768 \ - --socket 127.0.0.1:8050 \ - --module=awx.wsgi:application \ - --home=/var/lib/awx/venv/awx \ - --chdir=/awx_devel/ \ - --vacuum \ - --processes=5 \ - --harakiri=120 --master \ - --no-orphans \ - --max-requests=1000 \ - --stats /tmp/stats.socket \ - --lazy-apps \ - --logformat "%(addr) %(method) %(uri) - %(proto) %(status)" + uwsgi /etc/tower/uwsgi.ini awx-autoreload: @/awx_devel/tools/docker-compose/awx-autoreload /awx_devel/awx "$(DEV_RELOAD_COMMAND)" diff --git a/tools/ansible/roles/dockerfile/templates/Dockerfile.j2 b/tools/ansible/roles/dockerfile/templates/Dockerfile.j2 index 166f24b79f..e35234f622 100644 --- a/tools/ansible/roles/dockerfile/templates/Dockerfile.j2 +++ b/tools/ansible/roles/dockerfile/templates/Dockerfile.j2 @@ -199,11 +199,11 @@ ADD tools/ansible/roles/dockerfile/files/rsyslog.conf /var/lib/awx/rsyslog/rsysl ADD tools/ansible/roles/dockerfile/files/wait-for-migrations /usr/local/bin/wait-for-migrations ADD tools/ansible/roles/dockerfile/files/stop-supervisor /usr/local/bin/stop-supervisor +ADD tools/ansible/roles/dockerfile/files/uwsgi.ini /etc/tower/uwsgi.ini + ## File mappings {% if build_dev|bool %} ADD tools/docker-compose/launch_awx.sh /usr/bin/launch_awx.sh -ADD tools/docker-compose/nginx.conf /etc/nginx/nginx.conf -ADD tools/docker-compose/nginx.vh.default.conf /etc/nginx/conf.d/nginx.vh.default.conf ADD tools/docker-compose/start_tests.sh /start_tests.sh ADD tools/docker-compose/bootstrap_development.sh /usr/bin/bootstrap_development.sh ADD tools/docker-compose/entrypoint.sh /entrypoint.sh @@ -213,7 +213,6 @@ ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanima {% else %} ADD tools/ansible/roles/dockerfile/files/launch_awx.sh /usr/bin/launch_awx.sh ADD tools/ansible/roles/dockerfile/files/launch_awx_task.sh /usr/bin/launch_awx_task.sh -ADD tools/ansible/roles/dockerfile/files/uwsgi.ini /etc/tower/uwsgi.ini ADD {{ template_dest }}/supervisor.conf /etc/supervisord.conf ADD {{ template_dest }}/supervisor_task.conf /etc/supervisord_task.conf {% endif %} diff --git a/tools/ansible/roles/dockerfile/templates/supervisor.conf.j2 b/tools/ansible/roles/dockerfile/templates/supervisor.conf.j2 index e67b79fbe9..6cd821b9d0 100644 --- a/tools/ansible/roles/dockerfile/templates/supervisor.conf.j2 +++ b/tools/ansible/roles/dockerfile/templates/supervisor.conf.j2 @@ -30,8 +30,8 @@ environment = DEV_RELOAD_COMMAND='supervisorctl -c /etc/supervisord_task.conf restart all; supervisorctl restart tower-processes:daphne tower-processes:wsbroadcast' {% else %} command = /var/lib/awx/venv/awx/bin/uwsgi /etc/tower/uwsgi.ini -directory = /var/lib/awx {% endif %} +directory = /var/lib/awx autorestart = true startsecs = 30 stopasgroup=true diff --git a/tools/docker-compose/ansible/roles/sources/defaults/main.yml b/tools/docker-compose/ansible/roles/sources/defaults/main.yml index 9155cacfa9..cf5a65e0b6 100644 --- a/tools/docker-compose/ansible/roles/sources/defaults/main.yml +++ b/tools/docker-compose/ansible/roles/sources/defaults/main.yml @@ -9,6 +9,7 @@ control_plane_node_count: 1 minikube_container_group: false receptor_socket_file: /var/run/awx-receptor/receptor.sock receptor_image: quay.io/ansible/receptor:devel +ingress_path: / # Keys for signing work receptor_rsa_bits: 4096 diff --git a/tools/docker-compose/ansible/roles/sources/tasks/main.yml b/tools/docker-compose/ansible/roles/sources/tasks/main.yml index 5149364dd7..17ab4dc0c9 100644 --- a/tools/docker-compose/ansible/roles/sources/tasks/main.yml +++ b/tools/docker-compose/ansible/roles/sources/tasks/main.yml @@ -49,18 +49,11 @@ mode: '0600' with_items: - "database.py" + - "local_settings.py" - "websocket_secret.py" - "haproxy.cfg" - -- name: Delete old local_settings.py - file: - path: "{{ playbook_dir }}/../../../awx/settings/local_settings.py" - state: absent - -- name: Copy local_settings.py - copy: - src: "local_settings.py" - dest: "{{ sources_dest }}/local_settings.py" + - "nginx.conf" + - "nginx.locations.conf" - name: Get OS info for sdb shell: | diff --git a/tools/docker-compose/ansible/roles/sources/templates/docker-compose.yml.j2 b/tools/docker-compose/ansible/roles/sources/templates/docker-compose.yml.j2 index 60d5d44fb8..7badd37181 100644 --- a/tools/docker-compose/ansible/roles/sources/templates/docker-compose.yml.j2 +++ b/tools/docker-compose/ansible/roles/sources/templates/docker-compose.yml.j2 @@ -24,6 +24,7 @@ services: EXECUTION_NODE_COUNT: {{ execution_node_count|int }} AWX_LOGGING_MODE: stdout DJANGO_SUPERUSER_PASSWORD: {{ admin_password }} + UWSGI_MOUNT_PATH: {{ ingress_path }} {% if loop.index == 1 %} RUN_MIGRATIONS: 1 {% endif %} @@ -40,6 +41,8 @@ services: - "../../docker-compose/_sources/database.py:/etc/tower/conf.d/database.py" - "../../docker-compose/_sources/websocket_secret.py:/etc/tower/conf.d/websocket_secret.py" - "../../docker-compose/_sources/local_settings.py:/etc/tower/conf.d/local_settings.py" + - "../../docker-compose/_sources/nginx.conf:/etc/nginx/nginx.conf" + - "../../docker-compose/_sources/nginx.locations.conf:/etc/nginx/conf.d/nginx.locations.conf" - "../../docker-compose/_sources/SECRET_KEY:/etc/tower/SECRET_KEY" - "../../docker-compose/_sources/receptor/receptor-awx-{{ loop.index }}.conf:/etc/receptor/receptor.conf" - "../../docker-compose/_sources/receptor/receptor-awx-{{ loop.index }}.conf.lock:/etc/receptor/receptor.conf.lock" diff --git a/tools/docker-compose/ansible/roles/sources/files/local_settings.py b/tools/docker-compose/ansible/roles/sources/templates/local_settings.py.j2 similarity index 96% rename from tools/docker-compose/ansible/roles/sources/files/local_settings.py rename to tools/docker-compose/ansible/roles/sources/templates/local_settings.py.j2 index 552850e81c..0b4ffd0160 100644 --- a/tools/docker-compose/ansible/roles/sources/files/local_settings.py +++ b/tools/docker-compose/ansible/roles/sources/templates/local_settings.py.j2 @@ -46,3 +46,5 @@ SYSTEM_UUID = '00000000-0000-0000-0000-000000000000' BROADCAST_WEBSOCKET_PORT = 8013 BROADCAST_WEBSOCKET_VERIFY_CERT = False BROADCAST_WEBSOCKET_PROTOCOL = 'http' + +STATIC_URL = '{{ (ingress_path + '/static/').replace('//', '/') }}' diff --git a/tools/docker-compose/ansible/roles/sources/templates/nginx.conf.j2 b/tools/docker-compose/ansible/roles/sources/templates/nginx.conf.j2 index 327b59a2fe..ff7e2ab386 100644 --- a/tools/docker-compose/ansible/roles/sources/templates/nginx.conf.j2 +++ b/tools/docker-compose/ansible/roles/sources/templates/nginx.conf.j2 @@ -1,8 +1,7 @@ -#user awx; - worker_processes 1; -pid /tmp/nginx.pid; +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; events { worker_connections 1024; @@ -17,7 +16,7 @@ http { '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; - access_log /dev/stdout main; + access_log /var/log/nginx/access.log main; map $http_upgrade $connection_upgrade { default upgrade; @@ -25,41 +24,17 @@ http { } sendfile on; - #tcp_nopush on; - #gzip on; upstream uwsgi { - server 127.0.0.1:8050; - } + server localhost:8050; + } upstream daphne { - server 127.0.0.1:8051; + server localhost:8051; } - {% if ssl_certificate is defined %} server { - listen 8052 default_server; - server_name _; - - # Redirect all HTTP links to the matching HTTPS page - return 301 https://$host$request_uri; - } - {%endif %} - - server { - {% if (ssl_certificate is defined) and (ssl_certificate_key is defined) %} - listen 8053 ssl; - - ssl_certificate /etc/nginx/awxweb.pem; - ssl_certificate_key /etc/nginx/awxweb_key.pem; - {% elif (ssl_certificate is defined) and (ssl_certificate_key is not defined) %} - listen 8053 ssl; - - ssl_certificate /etc/nginx/awxweb.pem; - ssl_certificate_key /etc/nginx/awxweb.pem; - {% else %} - listen 8052 default_server; - {% endif %} + listen 8013 default_server; # If you have a domain name, this is where to add it server_name _; @@ -67,56 +42,35 @@ http { # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months) add_header Strict-Transport-Security max-age=15768000; + add_header X-Content-Type-Options nosniff; - # Protect against click-jacking https://www.owasp.org/index.php/Testing_for_Clickjacking_(OTG-CLIENT-009) - add_header X-Frame-Options "DENY"; + include /etc/nginx/conf.d/*.conf; + } - location /nginx_status { - stub_status on; - access_log off; - allow 127.0.0.1; - deny all; - } + server { + listen 8043 default_server ssl; - location /static/ { - alias /var/lib/awx/public/static/; - } + # If you have a domain name, this is where to add it + server_name _; + keepalive_timeout 65; - location /favicon.ico { alias /var/lib/awx/public/static/favicon.ico; } + ssl_certificate /etc/nginx/nginx.crt; + ssl_certificate_key /etc/nginx/nginx.key; - location /websocket { - # Pass request to the upstream alias - proxy_pass http://daphne; - # Require http version 1.1 to allow for upgrade requests - proxy_http_version 1.1; - # We want proxy_buffering off for proxying to websockets. - proxy_buffering off; - # http://en.wikipedia.org/wiki/X-Forwarded-For - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - # enable this if you use HTTPS: - proxy_set_header X-Forwarded-Proto https; - # pass the Host: header from the client for the sake of redirects - proxy_set_header Host $http_host; - # We've set the Host header, so we don't need Nginx to muddle - # about with redirects - proxy_redirect off; - # Depending on the request value, set the Upgrade and - # connection headers - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - } + ssl_session_timeout 1d; + ssl_session_cache shared:SSL:50m; + ssl_session_tickets off; - location / { - # Add trailing / if missing - rewrite ^(.*)$http_host(.*[^/])$ $1$http_host$2/ permanent; - uwsgi_read_timeout 120s; - uwsgi_pass uwsgi; - include /etc/nginx/uwsgi_params; - {%- if extra_nginx_include is defined %} - include {{ extra_nginx_include }}; - {%- endif %} - proxy_set_header X-Forwarded-Port 443; - uwsgi_param HTTP_X_FORWARDED_PORT 443; - } + # intermediate configuration. tweak to your needs. + ssl_protocols TLSv1.2; + ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; + ssl_prefer_server_ciphers on; + + # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months) + add_header Strict-Transport-Security max-age=15768000; + add_header X-Content-Type-Options nosniff; + + + include /etc/nginx/conf.d/*.conf; } } diff --git a/tools/docker-compose/ansible/roles/sources/templates/nginx.locations.conf.j2 b/tools/docker-compose/ansible/roles/sources/templates/nginx.locations.conf.j2 new file mode 100644 index 0000000000..5d1d24b2b1 --- /dev/null +++ b/tools/docker-compose/ansible/roles/sources/templates/nginx.locations.conf.j2 @@ -0,0 +1,37 @@ +location {{ (ingress_path + '/static').replace('//', '/') }} { + alias /var/lib/awx/public/static/; +} + +location {{ (ingress_path + '/favicon.ico').replace('//', '/') }} { + alias /awx_devel/awx/public/static/favicon.ico; +} + +location {{ (ingress_path + '/websocket').replace('//', '/') }} { + # Pass request to the upstream alias + proxy_pass http://daphne; + # Require http version 1.1 to allow for upgrade requests + proxy_http_version 1.1; + # We want proxy_buffering off for proxying to websockets. + proxy_buffering off; + # http://en.wikipedia.org/wiki/X-Forwarded-For + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # enable this if you use HTTPS: + proxy_set_header X-Forwarded-Proto https; + # pass the Host: header from the client for the sake of redirects + proxy_set_header Host $http_host; + # We've set the Host header, so we don't need Nginx to muddle + # about with redirects + proxy_redirect off; + # Depending on the request value, set the Upgrade and + # connection headers + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; +} + +location {{ ingress_path }} { + # Add trailing / if missing + rewrite ^(.*[^/])$ $1/ permanent; + uwsgi_read_timeout 120s; + uwsgi_pass uwsgi; + include /etc/nginx/uwsgi_params; +} diff --git a/tools/docker-compose/nginx.conf b/tools/docker-compose/nginx.conf deleted file mode 100644 index 348f93b727..0000000000 --- a/tools/docker-compose/nginx.conf +++ /dev/null @@ -1,31 +0,0 @@ -worker_processes 1; - -error_log /var/log/nginx/error.log warn; -pid /var/run/nginx.pid; - -events { - worker_connections 1024; -} - -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - server_tokens off; - - log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - - access_log /var/log/nginx/access.log main; - - map $http_upgrade $connection_upgrade { - default upgrade; - '' close; - } - - sendfile on; - #tcp_nopush on; - #gzip on; - - include /etc/nginx/conf.d/*.conf; -} diff --git a/tools/docker-compose/nginx.vh.default.conf b/tools/docker-compose/nginx.vh.default.conf deleted file mode 100644 index 649575af34..0000000000 --- a/tools/docker-compose/nginx.vh.default.conf +++ /dev/null @@ -1,124 +0,0 @@ -upstream uwsgi { - server localhost:8050; -} - -upstream daphne { - server localhost:8051; -} - -# server { -# listen 8013 default_server; -# listen [::]:8013 default_server; -# server_name _; -# return 301 https://$host:8043$request_uri; -# } - -server { - listen 8013 default_server; - - # If you have a domain name, this is where to add it - server_name _; - keepalive_timeout 65; - - # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months) - add_header Strict-Transport-Security max-age=15768000; - add_header X-Content-Type-Options nosniff; - - location /static/ { - alias /var/lib/awx/public/static/; - } - - location /favicon.ico { alias /awx_devel/awx/public/static/favicon.ico; } - - location ~ ^/websocket { - # Pass request to the upstream alias - proxy_pass http://daphne; - # Require http version 1.1 to allow for upgrade requests - proxy_http_version 1.1; - # We want proxy_buffering off for proxying to websockets. - proxy_buffering off; - # http://en.wikipedia.org/wiki/X-Forwarded-For - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - # enable this if you use HTTPS: - proxy_set_header X-Forwarded-Proto https; - # pass the Host: header from the client for the sake of redirects - proxy_set_header Host $http_host; - # We've set the Host header, so we don't need Nginx to muddle - # about with redirects - proxy_redirect off; - # Depending on the request value, set the Upgrade and - # connection headers - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - } - - location / { - # Add trailing / if missing - rewrite ^(.*[^/])$ $1/ permanent; - uwsgi_read_timeout 120s; - uwsgi_pass uwsgi; - include /etc/nginx/uwsgi_params; - } -} - -server { - listen 8043 default_server ssl; - - # If you have a domain name, this is where to add it - server_name _; - keepalive_timeout 65; - - ssl_certificate /etc/nginx/nginx.crt; - ssl_certificate_key /etc/nginx/nginx.key; - - ssl_session_timeout 1d; - ssl_session_cache shared:SSL:50m; - ssl_session_tickets off; - - # intermediate configuration. tweak to your needs. - ssl_protocols TLSv1.2; - ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; - ssl_prefer_server_ciphers on; - - # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months) - add_header Strict-Transport-Security max-age=15768000; - add_header X-Content-Type-Options nosniff; - - location /static/ { - alias /var/lib/awx/public/static/; - access_log off; - sendfile off; - } - - location /favicon.ico { alias /awx_devel/awx/public/static/favicon.ico; } - - location ~ ^/websocket { - # Pass request to the upstream alias - proxy_pass http://daphne; - # Require http version 1.1 to allow for upgrade requests - proxy_http_version 1.1; - # We want proxy_buffering off for proxying to websockets. - proxy_buffering off; - # http://en.wikipedia.org/wiki/X-Forwarded-For - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - # enable this if you use HTTPS: - proxy_set_header X-Forwarded-Proto https; - # pass the Host: header from the client for the sake of redirects - proxy_set_header Host $http_host; - # We've set the Host header, so we don't need Nginx to muddle - # about with redirects - proxy_redirect off; - # Depending on the request value, set the Upgrade and - # connection headers - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - } - - location / { - # Add trailing / if missing - rewrite ^(.*[^/])$ $1/ permanent; - uwsgi_read_timeout 120s; - uwsgi_pass uwsgi; - include /etc/nginx/uwsgi_params; - } -} From 9669b9dd2ff0cefddcfb9dbc3557a6f72fba5f42 Mon Sep 17 00:00:00 2001 From: Hao Liu Date: Mon, 27 Feb 2023 08:23:27 -0500 Subject: [PATCH 19/19] Revert project_update.yml Due to problem found in testing reverting https://github.com/ansible/awx/commit/019e6a52fe6699e4bf34c36931b739430c37b190 --- awx/playbooks/project_update.yml | 187 +++++++++++++++---------------- 1 file changed, 89 insertions(+), 98 deletions(-) diff --git a/awx/playbooks/project_update.yml b/awx/playbooks/project_update.yml index 125b5ef312..2067e76043 100644 --- a/awx/playbooks/project_update.yml +++ b/awx/playbooks/project_update.yml @@ -25,47 +25,42 @@ connection: local name: Update source tree if necessary tasks: - - name: Delete project directory before update - ansible.builtin.shell: set -o pipefail && find . -delete -print | tail -2 # volume mounted, cannot delete folder itself - register: reg - changed_when: reg.stdout_lines | length > 1 + + - name: delete project directory before update + command: "find -delete" # volume mounted, cannot delete folder itself args: chdir: "{{ project_path }}" tags: - delete - - name: Update project using git - tags: - - update_git - block: - - name: Update project using git - ansible.builtin.git: - dest: "{{ project_path | quote }}" - repo: "{{ scm_url }}" - version: "{{ scm_branch | quote }}" - refspec: "{{ scm_refspec | default(omit) }}" - force: "{{ scm_clean }}" - track_submodules: "{{ scm_track_submodules | default(omit) }}" - accept_hostkey: "{{ scm_accept_hostkey | default(omit) }}" + - block: + - name: update project using git + git: + dest: "{{project_path|quote}}" + repo: "{{scm_url}}" + version: "{{scm_branch|quote}}" + refspec: "{{scm_refspec|default(omit)}}" + force: "{{scm_clean}}" + track_submodules: "{{scm_track_submodules|default(omit)}}" + accept_hostkey: "{{scm_accept_hostkey|default(omit)}}" register: git_result - name: Set the git repository version - ansible.builtin.set_fact: + set_fact: scm_version: "{{ git_result['after'] }}" when: "'after' in git_result" - - - name: Update project using svn tags: - - update_svn - block: - - name: Update project using svn - ansible.builtin.subversion: - dest: "{{ project_path | quote }}" - repo: "{{ scm_url | quote }}" - revision: "{{ scm_branch | quote }}" - force: "{{ scm_clean }}" - username: "{{ scm_username | default(omit) }}" - password: "{{ scm_password | default(omit) }}" + - update_git + + - block: + - name: update project using svn + subversion: + dest: "{{project_path|quote}}" + repo: "{{scm_url|quote}}" + revision: "{{scm_branch|quote}}" + force: "{{scm_clean}}" + username: "{{scm_username|default(omit)}}" + password: "{{scm_password|default(omit)}}" # must be in_place because folder pre-existing, because it is mounted in_place: true environment: @@ -73,90 +68,85 @@ register: svn_result - name: Set the svn repository version - ansible.builtin.set_fact: + set_fact: scm_version: "{{ svn_result['after'] }}" when: "'after' in svn_result" - - name: Parse subversion version string properly - ansible.builtin.set_fact: - scm_version: "{{ scm_version | regex_replace('^.*Revision: ([0-9]+).*$', '\\1') }}" - - - - name: Project update for Insights + - name: parse subversion version string properly + set_fact: + scm_version: "{{scm_version|regex_replace('^.*Revision: ([0-9]+).*$', '\\1')}}" tags: - - update_insights - block: + - update_svn + + - block: - name: Ensure the project directory is present - ansible.builtin.file: - dest: "{{ project_path | quote }}" + file: + dest: "{{project_path|quote}}" state: directory - mode: '0755' - name: Fetch Insights Playbook(s) insights: - insights_url: "{{ insights_url }}" - username: "{{ scm_username }}" - password: "{{ scm_password }}" - project_path: "{{ project_path }}" - awx_license_type: "{{ awx_license_type }}" - awx_version: "{{ awx_version }}" + insights_url: "{{insights_url}}" + username: "{{scm_username}}" + password: "{{scm_password}}" + project_path: "{{project_path}}" + awx_license_type: "{{awx_license_type}}" + awx_version: "{{awx_version}}" register: results - name: Save Insights Version - ansible.builtin.set_fact: - scm_version: "{{ results.version }}" + set_fact: + scm_version: "{{results.version}}" when: results is defined - - - - name: Update project using archive tags: - - update_archive - block: + - update_insights + + - block: - name: Ensure the project archive directory is present - ansible.builtin.file: - dest: "{{ project_path | quote }}/.archive" + file: + dest: "{{ project_path|quote }}/.archive" state: directory - mode: '0755' - name: Get archive from url - ansible.builtin.get_url: - url: "{{ scm_url | quote }}" - dest: "{{ project_path | quote }}/.archive/" - url_username: "{{ scm_username | default(omit) }}" - url_password: "{{ scm_password | default(omit) }}" + get_url: + url: "{{ scm_url|quote }}" + dest: "{{ project_path|quote }}/.archive/" + url_username: "{{ scm_username|default(omit) }}" + url_password: "{{ scm_password|default(omit) }}" force_basic_auth: true - mode: '0755' register: get_archive - name: Unpack archive project_archive: src: "{{ get_archive.dest }}" - project_path: "{{ project_path | quote }}" + project_path: "{{ project_path|quote }}" force: "{{ scm_clean }}" when: get_archive.changed or scm_clean register: unarchived - name: Find previous archives - ansible.builtin.find: - paths: "{{ project_path | quote }}/.archive/" + find: + paths: "{{ project_path|quote }}/.archive/" excludes: - - "{{ get_archive.dest | basename }}" + - "{{ get_archive.dest|basename }}" when: unarchived.changed register: previous_archive - name: Remove previous archives - ansible.builtin.file: + file: path: "{{ item.path }}" state: absent loop: "{{ previous_archive.files }}" - when: previous_archive.files | default([]) + when: previous_archive.files|default([]) - name: Set scm_version to archive sha1 checksum - ansible.builtin.set_fact: + set_fact: scm_version: "{{ get_archive.checksum_src }}" + tags: + - update_archive - name: Repository Version - ansible.builtin.debug: + debug: msg: "Repository Version {{ scm_version }}" tags: - update_git @@ -193,59 +183,60 @@ additional_collections_env: # These environment variables are used for installing collections, in addition to galaxy_task_env # setting the collections paths silences warnings - ANSIBLE_COLLECTIONS_PATHS: "{{ projects_root }}/.__awx_cache/{{ local_path }}/stage/requirements_collections" + ANSIBLE_COLLECTIONS_PATHS: "{{projects_root}}/.__awx_cache/{{local_path}}/stage/requirements_collections" # Put the local tmp directory in same volume as collection destination # otherwise, files cannot be moved accross volumes and will cause error - ANSIBLE_LOCAL_TEMP: "{{ projects_root }}/.__awx_cache/{{ local_path }}/stage/tmp" + ANSIBLE_LOCAL_TEMP: "{{projects_root}}/.__awx_cache/{{local_path}}/stage/tmp" tasks: + - name: Check content sync settings - when: not roles_enabled | bool and not collections_enabled | bool - tags: - - install_roles - - install_collections block: - - name: Warn about disabled content sync - ansible.builtin.debug: + - debug: msg: > Collection and role syncing disabled. Check the AWX_ROLES_ENABLED and AWX_COLLECTIONS_ENABLED settings and Galaxy credentials on the project's organization. - - name: End play due to disabled content sync - ansible.builtin.meta: end_play - - name: Fetch galaxy roles from requirements.(yml/yaml) - ansible.builtin.command: > + - meta: end_play + + when: not roles_enabled|bool and not collections_enabled|bool + tags: + - install_roles + - install_collections + + - name: fetch galaxy roles from requirements.(yml/yaml) + command: > ansible-galaxy role install -r {{ item }} - --roles-path {{ projects_root }}/.__awx_cache/{{ local_path }}/stage/requirements_roles + --roles-path {{projects_root}}/.__awx_cache/{{local_path}}/stage/requirements_roles {{ ' -' + 'v' * ansible_verbosity if ansible_verbosity else '' }} args: - chdir: "{{ project_path | quote }}" + chdir: "{{project_path|quote}}" register: galaxy_result with_fileglob: - - "{{ project_path | quote }}/roles/requirements.yaml" - - "{{ project_path | quote }}/roles/requirements.yml" + - "{{project_path|quote}}/roles/requirements.yaml" + - "{{project_path|quote}}/roles/requirements.yml" changed_when: "'was installed successfully' in galaxy_result.stdout" environment: "{{ galaxy_task_env }}" - when: roles_enabled | bool + when: roles_enabled|bool tags: - install_roles - - name: Fetch galaxy collections from collections/requirements.(yml/yaml) - ansible.builtin.command: > + - name: fetch galaxy collections from collections/requirements.(yml/yaml) + command: > ansible-galaxy collection install -r {{ item }} - --collections-path {{ projects_root }}/.__awx_cache/{{ local_path }}/stage/requirements_collections + --collections-path {{projects_root}}/.__awx_cache/{{local_path}}/stage/requirements_collections {{ ' -' + 'v' * ansible_verbosity if ansible_verbosity else '' }} args: - chdir: "{{ project_path | quote }}" + chdir: "{{project_path|quote}}" register: galaxy_collection_result with_fileglob: - - "{{ project_path | quote }}/collections/requirements.yaml" - - "{{ project_path | quote }}/collections/requirements.yml" - - "{{ project_path | quote }}/requirements.yaml" - - "{{ project_path | quote }}/requirements.yml" + - "{{project_path|quote}}/collections/requirements.yaml" + - "{{project_path|quote}}/collections/requirements.yml" + - "{{project_path|quote}}/requirements.yaml" + - "{{project_path|quote}}/requirements.yml" changed_when: "'Installing ' in galaxy_collection_result.stdout" environment: "{{ additional_collections_env | combine(galaxy_task_env) }}" when: - "ansible_version.full is version_compare('2.9', '>=')" - - collections_enabled | bool + - collections_enabled|bool tags: - install_collections