Compare commits

..

1 Commits

Author SHA1 Message Date
Sandra McCann
8c4be1c529 Cherrypick recent docs prs to docs branch (#15477)
* Update  docs replacements to AWX (#15349)

Update replacements to AWX

Signed-off-by: Sandra McCann <samccann@redhat.com>
(cherry picked from commit 9979fc659e)

* Remove remnants of controller terms from quickstart docs (#15350)

Remove remnants of controller terms from quickstart

Signed-off-by: Sandra McCann <samccann@redhat.com>
(cherry picked from commit 864a30e3d4)

* Remove references to translated versions of the docs (#15354)

remove references to translated versions of the docs

Signed-off-by: Sandra McCann <samccann@redhat.com>
Co-authored-by: TVo <thavo@redhat.com>
(cherry picked from commit 5f42db67e6)

* update terminology (#15357)

* update terminology

Replace some instances of Tower with AWX and remove some references to
enterprise left over from the migration of RST content from the
Automation Controller docs.

* Update docs/docsite/rst/userguide/overview.rst

Co-authored-by: TVo <thavo@redhat.com>

---------

Co-authored-by: TVo <thavo@redhat.com>
(cherry picked from commit f1448fced1)

* Replaced all references of downstream docs to upstream docs (#15388)

* Replaced all references of downstream docs to upstream docs.

* Update README.md

Co-authored-by: Don Naro <dnaro@redhat.com>

* Update README.md.j2

Co-authored-by: Don Naro <dnaro@redhat.com>

* Update README.md.j2

Co-authored-by: Don Naro <dnaro@redhat.com>

* Incorpor'd review feedback from @oraNod and @samccann

* Updated with agreed link (for now) until further change is needed.

---------

Co-authored-by: Don Naro <dnaro@redhat.com>
(cherry picked from commit 018f235a64)

* Re-do PR #14685 for alt-text inventories. (#15394)

(cherry picked from commit 6d0c47fdd0)

* Docs: add Communication guide (#15469)

* Docs: add Communication guide

* Update docs/docsite/rst/contributor/communication.rst

Co-authored-by: Don Naro <dnaro@redhat.com>

* Update docs/docsite/rst/contributor/communication.rst

---------

Co-authored-by: Don Naro <dnaro@redhat.com>
(cherry picked from commit 79c1921ea4)

---------

Co-authored-by: Don Naro <dnaro@redhat.com>
Co-authored-by: TVo <thavo@redhat.com>
Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>
2024-08-29 12:26:30 +01:00
89 changed files with 304 additions and 827 deletions

View File

@@ -24,7 +24,7 @@ runs:
- name: Pre-pull latest devel image to warm cache
shell: bash
run: docker pull -q ghcr.io/${OWNER_LC}/awx_devel:${{ github.base_ref }}
run: docker pull ghcr.io/${OWNER_LC}/awx_devel:${{ github.base_ref }}
- name: Build image for current source checkout
shell: bash

View File

@@ -57,6 +57,16 @@ runs:
awx-manage update_password --username=admin --password=password
EOSH
- name: Build UI
# This must be a string comparison in composite actions:
# https://github.com/actions/runner/issues/2238
if: ${{ inputs.build-ui == 'true' }}
shell: bash
run: |
docker exec -i tools_awx_1 sh <<-EOSH
make ui-devel
EOSH
- name: Get instance data
id: data
shell: bash

View File

@@ -38,9 +38,7 @@ jobs:
- name: ui-test-general
command: make ui-test-general
steps:
- uses: actions/checkout@v4
with:
show-progress: false
- uses: actions/checkout@v3
- name: Build awx_devel image for running checks
uses: ./.github/actions/awx_devel_image
@@ -54,9 +52,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
with:
show-progress: false
- uses: actions/checkout@v3
- uses: ./.github/actions/run_awx_devel
id: awx
@@ -74,15 +70,13 @@ jobs:
DEBUG_OUTPUT_DIR: /tmp/awx_operator_molecule_test
steps:
- name: Checkout awx
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
show-progress: false
path: awx
- name: Checkout awx-operator
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
show-progress: false\
repository: ansible/awx-operator
path: awx-operator
@@ -136,9 +130,7 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
with:
show-progress: false
- uses: actions/checkout@v3
# The containers that GitHub Actions use have Ansible installed, so upgrade to make sure we have the latest version.
- name: Upgrade ansible-core
@@ -162,9 +154,7 @@ jobs:
- name: r-z0-9
regex: ^[r-z0-9]
steps:
- uses: actions/checkout@v4
with:
show-progress: false
- uses: actions/checkout@v3
- uses: ./.github/actions/run_awx_devel
id: awx
@@ -210,9 +200,7 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
with:
show-progress: false
- uses: actions/checkout@v3
- name: Upgrade ansible-core
run: python3 -m pip install --upgrade ansible-core

View File

@@ -1,57 +0,0 @@
---
name: django-ansible-base requirements update
on:
workflow_dispatch:
schedule:
- cron: '0 6 * * *' # once an day @ 6 AM
permissions:
pull-requests: write
contents: write
jobs:
dab-pin-newest:
if: (github.repository_owner == 'ansible' && endsWith(github.repository, 'awx')) || github.event_name != 'schedule'
runs-on: ubuntu-latest
steps:
- id: dab-release
name: Get current django-ansible-base release version
uses: pozetroninc/github-action-get-latest-release@2a61c339ea7ef0a336d1daa35ef0cb1418e7676c # v0.8.0
with:
owner: ansible
repo: django-ansible-base
excludes: prerelease, draft
- name: Check out respository code
uses: actions/checkout@v4
- id: dab-pinned
name: Get current django-ansible-base pinned version
run:
echo "version=$(requirements/django-ansible-base-pinned-version.sh)" >> "$GITHUB_OUTPUT"
- name: Update django-ansible-base pinned version to upstream release
run:
requirements/django-ansible-base-pinned-version.sh -s ${{ steps.dab-release.outputs.release }}
- name: Create Pull Request
uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v6
with:
base: devel
branch: bump-django-ansible-base
title: Bump django-ansible-base to ${{ steps.dab-release.outputs.release }}
body: |
##### SUMMARY
Automated .github/workflows/dab-release.yml
django-ansible-base upstream released version == ${{ steps.dab-release.outputs.release }}
requirements_git.txt django-ansible-base pinned version == ${{ steps.dab-pinned.outputs.version }}
##### ISSUE TYPE
- Bug, Docs Fix or other nominal change
##### COMPONENT NAME
- API
commit-message: |
Update django-ansible-base version to ${{ steps.dab-pinned.outputs.version }}
add-paths:
requirements/requirements_git.txt

View File

@@ -35,9 +35,7 @@ jobs:
exit 0
if: matrix.build-targets.image-name == 'awx' && !endsWith(github.repository, '/awx')
- uses: actions/checkout@v4
with:
show-progress: false
- uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
@@ -62,14 +60,16 @@ jobs:
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Setup node and npm for the new UI build
- name: Setup node and npm
uses: actions/setup-node@v2
with:
node-version: '18'
node-version: '16.13.1'
if: matrix.build-targets.image-name == 'awx'
- name: Prebuild new UI for awx image (to speed up build process)
- name: Prebuild UI for awx image (to speed up build process)
run: |
sudo apt-get install gettext
make ui-release
make ui-next
if: matrix.build-targets.image-name == 'awx'

View File

@@ -8,9 +8,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
with:
show-progress: false
- uses: actions/checkout@v3
- name: install tox
run: pip install tox

View File

@@ -30,10 +30,7 @@ jobs:
timeout-minutes: 20
name: Label Issue - Community
steps:
- uses: actions/checkout@v4
with:
show-progress: false
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- name: Install python requests
run: pip install requests

View File

@@ -29,10 +29,7 @@ jobs:
timeout-minutes: 20
name: Label PR - Community
steps:
- uses: actions/checkout@v4
with:
show-progress: false
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- name: Install python requests
run: pip install requests

View File

@@ -32,9 +32,7 @@ jobs:
echo "TAG_NAME=${{ github.event.release.tag_name }}" >> $GITHUB_ENV
- name: Checkout awx
uses: actions/checkout@v4
with:
show-progress: false
uses: actions/checkout@v3
- name: Get python version from Makefile
run: echo py_version=`make PYTHON_VERSION` >> $GITHUB_ENV

View File

@@ -45,22 +45,19 @@ jobs:
exit 0
- name: Checkout awx
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
show-progress: false
path: awx
- name: Checkout awx-operator
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
show-progress: false
repository: ${{ github.repository_owner }}/awx-operator
path: awx-operator
- name: Checkout awx-logos
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
show-progress: false
repository: ansible/awx-logos
path: awx-logos
@@ -89,14 +86,17 @@ jobs:
run: |
cp ../awx-logos/awx/ui/client/assets/* awx/ui/public/static/media/
- name: Setup node and npm for new UI build
- name: Setup node and npm
uses: actions/setup-node@v2
with:
node-version: '18'
node-version: '16.13.1'
- name: Prebuild new UI for awx image (to speed up build process)
- name: Prebuild UI for awx image (to speed up build process)
working-directory: awx
run: make ui-next
run: |
sudo apt-get install gettext
make ui-release
make ui-next
- name: Set build env variables
run: |
@@ -136,9 +136,9 @@ jobs:
- name: Pulling images for test deployment with awx-operator
# awx operator molecue test expect to kind load image and buildx exports image to registry and not local
run: |
docker pull -q ${AWX_OPERATOR_TEST_IMAGE}
docker pull -q ${AWX_EE_TEST_IMAGE}
docker pull -q ${AWX_TEST_IMAGE}:${AWX_TEST_VERSION}
docker pull ${AWX_OPERATOR_TEST_IMAGE}
docker pull ${AWX_EE_TEST_IMAGE}
docker pull ${AWX_TEST_IMAGE}:${AWX_TEST_VERSION}
- name: Run test deployment with awx-operator
working-directory: awx-operator

View File

@@ -13,9 +13,7 @@ jobs:
steps:
- name: Checkout branch
uses: actions/checkout@v4
with:
show-progress: false
uses: actions/checkout@v3
- name: Update PR Body
env:

View File

@@ -18,9 +18,7 @@ jobs:
packages: write
contents: read
steps:
- uses: actions/checkout@v4
with:
show-progress: false
- uses: actions/checkout@v3
- name: Get python version from Makefile
run: echo py_version=`make PYTHON_VERSION` >> $GITHUB_ENV
@@ -36,7 +34,7 @@ jobs:
- name: Pre-pull image to warm build cache
run: |
docker pull -q ghcr.io/${{ github.repository_owner }}/awx_devel:${GITHUB_REF##*/} || :
docker pull ghcr.io/${{ github.repository_owner }}/awx_devel:${GITHUB_REF##*/} || :
- name: Build image
run: |

View File

@@ -67,7 +67,7 @@ If you're not using Docker for Mac, or Docker for Windows, you may need, or choo
#### Frontend Development
See [the ansible-ui development documentation](https://github.com/ansible/ansible-ui/blob/main/CONTRIBUTING.md).
See [the ui development documentation](awx/ui/CONTRIBUTING.md).
#### Fork and clone the AWX repo
@@ -121,7 +121,7 @@ If it has someone assigned to it then that person is the person responsible for
**NOTES**
> Issue assignment will only be done for maintainers of the project. If you decide to work on an issue, please feel free to add a comment in the issue to let others know that you are working on it; but know that we will accept the first pull request from whomever is able to fix an issue. Once your PR is accepted we can add you as an assignee to an issue upon request.
> Issue assignment will only be done for maintainers of the project. If you decide to work on an issue, please feel free to add a comment in the issue to let others know that you are working on it; but know that we will accept the first pull request from whomever is able to fix an issue. Once your PR is accepted we can add you as an assignee to an issue upon request.
> If you work in a part of the codebase that is going through active development, your changes may be rejected, or you may be asked to `rebase`. A good idea before starting work is to have a discussion with us in the `#ansible-awx` channel on irc.libera.chat, or on the [mailing list](https://groups.google.com/forum/#!forum/awx-project).
@@ -132,7 +132,7 @@ If it has someone assigned to it then that person is the person responsible for
At this time we do not accept PRs for adding additional language translations as we have an automated process for generating our translations. This is because translations require constant care as new strings are added and changed in the code base. Because of this the .po files are overwritten during every translation release cycle. We also can't support a lot of translations on AWX as its an open source project and each language adds time and cost to maintain. If you would like to see AWX translated into a new language please create an issue and ask others you know to upvote the issue. Our translation team will review the needs of the community and see what they can do around supporting additional language.
If you find an issue with an existing translation, please see the [Reporting Issues](#reporting-issues) section to open an issue and our translation team will work with you on a resolution.
If you find an issue with an existing translation, please see the [Reporting Issues](#reporting-issues) section to open an issue and our translation team will work with you on a resolution.
## Submitting Pull Requests
@@ -161,7 +161,7 @@ Sometimes it might take us a while to fully review your PR. We try to keep the `
When your PR is initially submitted the checks will not be run until a maintainer allows them to be. Once a maintainer has done a quick review of your work the PR will have the linter and unit tests run against them via GitHub Actions, and the status reported in the PR.
## Reporting Issues
We welcome your feedback, and encourage you to file an issue when you run into a problem. But before opening a new issues, we ask that you please view our [Issues guide](./ISSUES.md).
## Getting Help

View File

@@ -502,7 +502,13 @@ ui-test-general:
$(NPM_BIN) run --prefix awx/ui pretest
$(NPM_BIN) run --prefix awx/ui/ test-general --runInBand
# NOTE: The make target ui-next is imported from awx/ui_next/Makefile
HEADLESS ?= no
ifeq ($(HEADLESS), yes)
dist/$(SDIST_TAR_FILE):
else
dist/$(SDIST_TAR_FILE): $(UI_BUILD_FLAG_FILE) ui-next
endif
$(PYTHON) -m build -s
ln -sf $(SDIST_TAR_FILE) dist/awx.tar.gz

View File

@@ -35,7 +35,4 @@ We ask all of our community members and contributors to adhere to the [Ansible c
Get Involved
------------
We welcome your feedback and ideas. Here's how to reach us with feedback and questions:
- Join the [Ansible AWX channel on Matrix](https://matrix.to/#/#awx:ansible.com)
- Join the [Ansible Community Forum](https://forum.ansible.com)
We welcome your feedback and ideas. See the [AWX Communication guide](https://ansible.readthedocs.io/projects/awx/en/latest/contributor/communication.html) to learn how to join the conversation.

View File

@@ -103,7 +103,7 @@ class Metadata(metadata.SimpleMetadata):
default = field.get_default()
if type(default) is UUID:
default = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
if field.field_name == 'TOWER_URL_BASE' and default == 'https://platformhost':
if field.field_name == 'TOWER_URL_BASE' and default == 'https://towerhost':
default = '{}://{}'.format(self.request.scheme, self.request.get_host())
field_info['default'] = default
except serializers.SkipField:

View File

@@ -2,12 +2,6 @@
- hosts: all
become: yes
tasks:
- name: Create the receptor group
group:
{% verbatim %}
name: "{{ receptor_group }}"
{% endverbatim %}
state: present
- name: Create the receptor user
user:
{% verbatim %}

View File

@@ -1400,9 +1400,7 @@ class ExecutionEnvironmentAccess(BaseAccess):
def filtered_queryset(self):
return ExecutionEnvironment.objects.filter(
Q(organization__in=Organization.accessible_pk_qs(self.user, 'read_role'))
| Q(organization__isnull=True)
| Q(id__in=ExecutionEnvironment.access_ids_qs(self.user, 'change'))
Q(organization__in=Organization.accessible_pk_qs(self.user, 'read_role')) | Q(organization__isnull=True)
).distinct()
@check_superuser
@@ -1421,13 +1419,11 @@ class ExecutionEnvironmentAccess(BaseAccess):
else:
if self.user not in obj.organization.execution_environment_admin_role:
raise PermissionDenied
if not self.check_related('organization', Organization, data, obj=obj, role_field='execution_environment_admin_role'):
return False
# Special case that check_related does not catch, org users can not remove the organization from the EE
if data and ('organization' in data or 'organization_id' in data):
if (not data.get('organization')) and (not data.get('organization_id')):
if data and 'organization' in data:
new_org = get_object_from_data('organization', Organization, data, obj=obj)
if not new_org or self.user not in new_org.execution_environment_admin_role:
return False
return True
return self.check_related('organization', Organization, data, obj=obj, role_field='execution_environment_admin_role')
def can_delete(self, obj):
if obj.managed:
@@ -2104,18 +2100,15 @@ class WorkflowJobTemplateAccess(NotificationAttachMixin, BaseAccess):
if not self.check_related('organization', Organization, data, role_field='workflow_admin_role', mandatory=True):
if data.get('organization', None) is None:
if self.save_messages:
self.messages['organization'] = [_('An organization is required to create a workflow job template for normal user')]
self.messages['organization'] = [_('An organization is required to create a workflow job template for normal user')]
return False
if not self.check_related('inventory', Inventory, data, role_field='use_role'):
if self.save_messages:
self.messages['inventory'] = [_('You do not have use_role to the inventory')]
self.messages['inventory'] = [_('You do not have use_role to the inventory')]
return False
if not self.check_related('execution_environment', ExecutionEnvironment, data, role_field='read_role'):
if self.save_messages:
self.messages['execution_environment'] = [_('You do not have read_role to the execution environment')]
self.messages['execution_environment'] = [_('You do not have read_role to the execution environment')]
return False
return True

View File

@@ -66,8 +66,10 @@ class FixedSlidingWindow:
class RelayWebsocketStatsManager:
def __init__(self, local_hostname):
def __init__(self, event_loop, local_hostname):
self._local_hostname = local_hostname
self._event_loop = event_loop
self._stats = dict()
self._redis_key = BROADCAST_WEBSOCKET_REDIS_KEY_NAME
@@ -92,10 +94,7 @@ class RelayWebsocketStatsManager:
self.start()
def start(self):
self.async_task = asyncio.get_running_loop().create_task(
self.run_loop(),
name='RelayWebsocketStatsManager.run_loop',
)
self.async_task = self._event_loop.create_task(self.run_loop())
return self.async_task
@classmethod

View File

@@ -929,16 +929,6 @@ register(
category_slug='debug',
)
register(
'RECEPTOR_KEEP_WORK_ON_ERROR',
field_class=fields.BooleanField,
label=_('Keep receptor work on error'),
default=False,
help_text=_('Prevent receptor work from being released on when error is detected'),
category=('Debug'),
category_slug='debug',
)
def logging_validate(serializer, attrs):
if not serializer.instance or not hasattr(serializer.instance, 'LOG_AGGREGATOR_HOST') or not hasattr(serializer.instance, 'LOG_AGGREGATOR_TYPE'):

View File

@@ -43,7 +43,6 @@ STANDARD_INVENTORY_UPDATE_ENV = {
}
CAN_CANCEL = ('new', 'pending', 'waiting', 'running')
ACTIVE_STATES = CAN_CANCEL
ERROR_STATES = ('error',)
MINIMAL_EVENTS = set(['playbook_on_play_start', 'playbook_on_task_start', 'playbook_on_stats', 'EOF'])
CENSOR_VALUE = '************'
ENV_BLOCKLIST = frozenset(

View File

@@ -2,7 +2,6 @@
# All Rights Reserved
from django.core.management.base import BaseCommand
from django.db import transaction
from crum import impersonate
from awx.main.models import User, Organization, Project, Inventory, CredentialType, Credential, Host, JobTemplate
from awx.main.signals import disable_computed_fields
@@ -14,12 +13,6 @@ class Command(BaseCommand):
help = 'Creates a preload tower data if there is none.'
def handle(self, *args, **kwargs):
# Wrap the operation in an atomic block, so we do not on accident
# create the organization but not create the project, etc.
with transaction.atomic():
self._handle()
def _handle(self):
changed = False
# Create a default organization as the first superuser found.
@@ -50,11 +43,10 @@ class Command(BaseCommand):
ssh_type = CredentialType.objects.filter(namespace='ssh').first()
c, _ = Credential.objects.get_or_create(
credential_type=ssh_type, name='Demo Credential', inputs={'username': getattr(superuser, 'username', 'null')}, created_by=superuser
credential_type=ssh_type, name='Demo Credential', inputs={'username': superuser.username}, created_by=superuser
)
if superuser:
c.admin_role.members.add(superuser)
c.admin_role.members.add(superuser)
public_galaxy_credential, _ = Credential.objects.get_or_create(
name='Ansible Galaxy',

View File

@@ -1,151 +0,0 @@
# Copyright (c) 2015 Ansible, Inc.
# All Rights Reserved
# Django
from django.core.management.base import BaseCommand
from django.db import connection
import json
import re
class Command(BaseCommand):
"""
Emits some simple statistics suitable for external monitoring
"""
help = 'Run queries that provide an overview of the performance of the system over a given period of time'
def add_arguments(self, parser):
parser.add_argument('--since', action='store', dest='days', type=str, default="1", help='Max days to look back to for data')
parser.add_argument('--limit', action='store', dest='limit', type=str, default="10", help='Max number of records for database queries (LIMIT)')
def execute_query(self, query):
with connection.cursor() as cursor:
cursor.execute(query)
rows = cursor.fetchall()
return rows
def jsonify(self, title, keys, values, query):
result = []
query = re.sub('\n', ' ', query)
query = re.sub('\s{2,}', ' ', query)
for value in values:
result.append(dict(zip(keys, value)))
return {title: result, 'count': len(values), 'query': query}
def jobs_pending_duration(self, days, limit):
"""Return list of jobs sorted by time in pending within configured number of days (within limit)"""
query = f"""
SELECT name, id AS job_id, unified_job_template_id, created, started - created AS pending_duration
FROM main_unifiedjob
WHERE finished IS NOT null
AND started IS NOT null
AND cancel_flag IS NOT true
AND created > NOW() - INTERVAL '{days} days'
AND started - created > INTERVAL '0 seconds'
ORDER BY pending_duration DESC
LIMIT {limit};"""
values = self.execute_query(query)
return self.jsonify(
title='completed_or_started_jobs_by_pending_duration',
keys=('job_name', 'job_id', 'unified_job_template_id', 'job_created', 'pending_duration'),
values=values,
query=query,
)
def times_of_day_pending_more_than_X_min(self, days, limit, minutes_pending):
"""Return list of jobs sorted by time in pending within configured number of days (within limit)"""
query = f"""
SELECT
date_trunc('hour', created) as day_and_hour,
COUNT(created) as count_jobs_pending_greater_than_{minutes_pending}_min
FROM main_unifiedjob
WHERE started IS NOT NULL
AND started - created > INTERVAL '{minutes_pending} minutes'
AND created > NOW() - INTERVAL '{days} days'
GROUP BY date_trunc('hour', created)
ORDER BY count_jobs_pending_greater_than_{minutes_pending}_min DESC
LIMIT {limit};"""
values = self.execute_query(query)
return self.jsonify(
title=f'times_of_day_pending_more_than_{minutes_pending}',
keys=('day_and_hour', f'count_jobs_pending_more_than_{minutes_pending}_min'),
values=values,
query=query,
)
def pending_jobs_details(self, days, limit):
"""Return list of jobs that are in pending and list details such as reasons they may be blocked, within configured number of days and limit."""
query = f"""
SELECT DISTINCT ON(A.id) A.name, A.id, A.unified_job_template_id, A.created, NOW() - A.created as pending_duration, F.allow_simultaneous, B.current_job_id as current_ujt_job, I.to_unifiedjob_id as dependency_job_id, A.dependencies_processed
FROM main_unifiedjob A
LEFT JOIN (
SELECT C.id, C.current_job_id FROM main_unifiedjobtemplate as C
) B
ON A.unified_job_template_id = B.id
LEFT JOIN main_job F ON A.id = F.unifiedjob_ptr_id
LEFT JOIN (
SELECT * FROM main_unifiedjob_dependent_jobs as G
RIGHT JOIN main_unifiedjob H ON G.to_unifiedjob_id = H.id
) I
ON A.id = I.from_unifiedjob_id
WHERE A.status = 'pending'
AND A.created > NOW() - INTERVAL '{days} days'
ORDER BY id DESC
LIMIT {limit};"""
values = self.execute_query(query)
return self.jsonify(
title='pending_jobs_details',
keys=(
'job_name',
'job_id',
'unified_job_template_id',
'job_created',
'pending_duration',
'allow_simultaneous',
'current_ujt_job',
'dependency_job_id',
'dependencies_processed',
),
values=values,
query=query,
)
def jobs_by_FUNC_event_processing_time(self, func, days, limit):
"""Return list of jobs sorted by MAX job event procesing time within configured number of days (within limit)"""
if func not in ('MAX', 'MIN', 'AVG', 'SUM'):
raise RuntimeError('Only able to asses job events grouped by job with MAX, MIN, AVG, SUM functions')
query = f"""SELECT job_id, {func}(A.modified - A.created) as job_event_processing_delay_{func}, B.name, B.created, B.finished, B.controller_node, B.execution_node
FROM main_jobevent A
RIGHT JOIN (
SELECT id, created, name, finished, controller_node, execution_node FROM
main_unifiedjob
WHERE created > NOW() - INTERVAL '{days} days'
AND created IS NOT null
AND finished IS NOT null
AND id IS NOT null
AND name IS NOT null
) B
ON A.job_id=B.id
WHERE A.job_id is not null
GROUP BY job_id, B.name, B.created, B.finished, B.controller_node, B.execution_node
ORDER BY job_event_processing_delay_{func} DESC
LIMIT {limit};"""
values = self.execute_query(query)
return self.jsonify(
title=f'jobs_by_{func}_event_processing',
keys=('job_id', f'{func}_job_event_processing_delay', 'job_name', 'job_created_time', 'job_finished_time', 'controller_node', 'execution_node'),
values=values,
query=query,
)
def handle(self, *args, **options):
items = []
for func in ('MAX', 'MIN', 'AVG'):
items.append(self.jobs_by_FUNC_event_processing_time(func, options['days'], options['limit']))
items.append(self.jobs_pending_duration(options['days'], options['limit']))
items.append(self.pending_jobs_details(options['days'], options['limit']))
items.append(self.times_of_day_pending_more_than_X_min(options['days'], options['limit'], minutes_pending=10))
self.stdout.write(json.dumps(items, indent=4, sort_keys=True, default=str))

View File

@@ -277,6 +277,7 @@ def setup_managed_role_definitions(apps, schema_editor):
to_create = {
'object_admin': '{cls.__name__} Admin',
'org_admin': 'Organization Admin',
'org_audit': 'Organization Audit',
'org_children': 'Organization {cls.__name__} Admin',
'special': '{cls.__name__} {action}',
}
@@ -333,19 +334,12 @@ def setup_managed_role_definitions(apps, schema_editor):
for perm in special_perms:
action = perm.codename.split('_')[0]
view_perm = Permission.objects.get(content_type=ct, codename__startswith='view_')
perm_list = [perm, view_perm]
# Handle special-case where adhoc role also listed use permission
if action == 'adhoc':
for other_perm in object_perms:
if other_perm.codename == 'use_inventory':
perm_list.append(other_perm)
break
managed_role_definitions.append(
get_or_create_managed(
to_create['special'].format(cls=cls, action=action.title()),
f'Has {action} permissions to a single {cls._meta.verbose_name}',
ct,
perm_list,
[perm, view_perm],
RoleDefinition,
)
)
@@ -361,40 +355,18 @@ def setup_managed_role_definitions(apps, schema_editor):
)
)
# Special "organization action" roles
audit_permissions = [perm for perm in org_perms if perm.codename.startswith('view_')]
audit_permissions.append(Permission.objects.get(codename='audit_organization'))
managed_role_definitions.append(
get_or_create_managed(
'Organization Audit',
'Has permission to view all objects inside of a single organization',
org_ct,
audit_permissions,
RoleDefinition,
if 'org_audit' in to_create:
audit_permissions = [perm for perm in org_perms if perm.codename.startswith('view_')]
audit_permissions.append(Permission.objects.get(codename='audit_organization'))
managed_role_definitions.append(
get_or_create_managed(
to_create['org_audit'].format(cls=Organization),
'Has permission to view all objects inside of a single organization',
org_ct,
audit_permissions,
RoleDefinition,
)
)
)
org_execute_permissions = {'view_jobtemplate', 'execute_jobtemplate', 'view_workflowjobtemplate', 'execute_workflowjobtemplate', 'view_organization'}
managed_role_definitions.append(
get_or_create_managed(
'Organization Execute',
'Has permission to execute all runnable objects in the organization',
org_ct,
[perm for perm in org_perms if perm.codename in org_execute_permissions],
RoleDefinition,
)
)
org_approval_permissions = {'view_organization', 'view_workflowjobtemplate', 'approve_workflowjobtemplate'}
managed_role_definitions.append(
get_or_create_managed(
'Organization Approval',
'Has permission to approve any workflow steps within a single organization',
org_ct,
[perm for perm in org_perms if perm.codename in org_approval_permissions],
RoleDefinition,
)
)
unexpected_role_definitions = RoleDefinition.objects.filter(managed=True).exclude(pk__in=[rd.pk for rd in managed_role_definitions])
for role_definition in unexpected_role_definitions:

View File

@@ -321,14 +321,13 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
raise ValueError('{} is not a dynamic input field'.format(field_name))
def validate_role_assignment(self, actor, role_definition):
if self.organization:
if isinstance(actor, User):
if actor.is_superuser or Organization.access_qs(actor, 'member').filter(id=self.organization.id).exists():
return
if isinstance(actor, Team):
if actor.organization == self.organization:
return
raise DRFValidationError({'detail': _(f"You cannot grant credential access to a {actor._meta.object_name} not in the credentials' organization")})
if isinstance(actor, User):
if actor.is_superuser or Organization.access_qs(actor, 'change').filter(id=self.organization.id).exists():
return
if isinstance(actor, Team):
if actor.organization == self.organization:
return
raise DRFValidationError({'detail': _(f"You cannot grant credential access to a {actor._meta.object_name} not in the credentials' organization")})
class CredentialType(CommonModelNameNotUnique):

View File

@@ -66,3 +66,7 @@ class ExecutionEnvironment(CommonModel):
if actor._meta.model_name == 'user' and (not actor.has_obj_perm(self.organization, 'view')):
raise ValidationError({'user': _('User must have view permission to Execution Environment organization')})
if actor._meta.model_name == 'team':
organization_cls = self._meta.get_field('organization').related_model
if self.orgaanization not in organization_cls.access_qs(actor, 'view'):
raise ValidationError({'team': _('Team must have view permission to Execution Environment organization')})

View File

@@ -396,11 +396,11 @@ class JobNotificationMixin(object):
'verbosity': 0,
},
'job_friendly_name': 'Job',
'url': 'https://platformhost/#/jobs/playbook/1010',
'url': 'https://towerhost/#/jobs/playbook/1010',
'approval_status': 'approved',
'approval_node_name': 'Approve Me',
'workflow_url': 'https://platformhost/#/jobs/workflow/1010',
'job_metadata': """{'url': 'https://platformhost/$/jobs/playbook/13',
'workflow_url': 'https://towerhost/#/jobs/workflow/1010',
'job_metadata': """{'url': 'https://towerhost/$/jobs/playbook/13',
'traceback': '',
'status': 'running',
'started': '2019-08-07T21:46:38.362630+00:00',

View File

@@ -689,15 +689,9 @@ def sync_parents_to_new_rbac(instance, action, model, pk_set, reverse, **kwargs)
for role_id in pk_set:
if reverse:
try:
child_role = Role.objects.get(id=role_id)
except Role.DoesNotExist:
continue
child_role = Role.objects.get(id=role_id)
else:
try:
parent_role = Role.objects.get(id=role_id)
except Role.DoesNotExist:
continue
parent_role = Role.objects.get(id=role_id)
# To a fault, we want to avoid running this if triggered from implicit_parents management
# we only want to do anything if we know for sure this is a non-implicit team role

View File

@@ -405,11 +405,10 @@ class AWXReceptorJob:
finally:
# Make sure to always release the work unit if we established it
if self.unit_id is not None and settings.RECEPTOR_RELEASE_WORK:
if settings.RECPETOR_KEEP_WORK_ON_ERROR and getattr(res, 'status', 'error') == 'error':
try:
receptor_ctl.simple_command(f"work release {self.unit_id}")
except Exception:
logger.exception(f"Error releasing work unit {self.unit_id}.")
try:
receptor_ctl.simple_command(f"work release {self.unit_id}")
except Exception:
logger.exception(f"Error releasing work unit {self.unit_id}.")
def _run_internal(self, receptor_ctl):
# Create a socketpair. Where the left side will be used for writing our payload

View File

@@ -54,7 +54,7 @@ from awx.main.models import (
Job,
convert_jsonfields,
)
from awx.main.constants import ACTIVE_STATES, ERROR_STATES
from awx.main.constants import ACTIVE_STATES
from awx.main.dispatch.publish import task
from awx.main.dispatch import get_task_queuename, reaper
from awx.main.utils.common import ignore_inventory_computed_fields, ignore_inventory_group_removal
@@ -685,8 +685,6 @@ def awx_receptor_workunit_reaper():
unit_ids = [id for id in receptor_work_list]
jobs_with_unreleased_receptor_units = UnifiedJob.objects.filter(work_unit_id__in=unit_ids).exclude(status__in=ACTIVE_STATES)
if settings.RECEPTOR_KEEP_WORK_ON_ERROR:
jobs_with_unreleased_receptor_units = jobs_with_unreleased_receptor_units.exclude(status__in=ERROR_STATES)
for job in jobs_with_unreleased_receptor_units:
logger.debug(f"{job.log_format} is not active, reaping receptor work unit {job.work_unit_id}")
receptor_ctl.simple_command(f"work cancel {job.work_unit_id}")
@@ -706,10 +704,7 @@ def awx_k8s_reaper():
logger.debug("Checking for orphaned k8s pods for {}.".format(group))
pods = PodManager.list_active_jobs(group)
time_cutoff = now() - timedelta(seconds=settings.K8S_POD_REAPER_GRACE_PERIOD)
reap_job_candidates = UnifiedJob.objects.filter(pk__in=pods.keys(), finished__lte=time_cutoff).exclude(status__in=ACTIVE_STATES)
if settings.RECEPTOR_KEEP_WORK_ON_ERROR:
reap_job_candidates = reap_job_candidates.exclude(status__in=ERROR_STATES)
for job in reap_job_candidates:
for job in UnifiedJob.objects.filter(pk__in=pods.keys(), finished__lte=time_cutoff).exclude(status__in=ACTIVE_STATES):
logger.debug('{} is no longer active, reaping orphaned k8s pod'.format(job.log_format))
try:
pm = PodManager(job)
@@ -985,15 +980,5 @@ def periodic_resource_sync():
if acquired is False:
logger.debug("Not running periodic_resource_sync, another task holds lock")
return
logger.debug("Running periodic resource sync")
executor = SyncExecutor()
executor.run()
for key, item_list in executor.results.items():
if not item_list or key == 'noop':
continue
# Log creations and conflicts
if len(item_list) > 10 and settings.LOG_AGGREGATOR_LEVEL != 'DEBUG':
logger.info(f'Periodic resource sync {key}, first 10 items:\n{item_list[:10]}')
else:
logger.info(f'Periodic resource sync {key}:\n{item_list}')
SyncExecutor().run()

File diff suppressed because one or more lines are too long

View File

@@ -68,17 +68,13 @@ def test_assign_managed_role(admin_user, alice, rando, inventory, post, setup_ma
@pytest.mark.django_db
def test_assign_custom_delete_role(admin_user, rando, inventory, delete, patch):
# TODO: just a delete_inventory, without change_inventory
rd, _ = RoleDefinition.objects.get_or_create(
name='inventory-delete',
permissions=['delete_inventory', 'view_inventory', 'change_inventory'],
content_type=ContentType.objects.get_for_model(Inventory),
name='inventory-delete', permissions=['delete_inventory', 'view_inventory'], content_type=ContentType.objects.get_for_model(Inventory)
)
rd.give_permission(rando, inventory)
inv_id = inventory.pk
inv_url = reverse('api:inventory_detail', kwargs={'pk': inv_id})
# TODO: eventually this will be valid test, for now ignore
# patch(url=inv_url, data={"description": "new"}, user=rando, expect=403)
patch(url=inv_url, data={"description": "new"}, user=rando, expect=403)
delete(url=inv_url, user=rando, expect=202)
assert Inventory.objects.get(id=inv_id).pending_deletion
@@ -132,7 +128,7 @@ def test_assign_credential_to_user_of_another_org(setup_managed_roles, credentia
rd = RoleDefinition.objects.get(name="Credential Admin")
credential.organization = organization
credential.save(update_fields=['organization'])
assert credential.organization not in Organization.access_qs(rando, 'member')
assert credential.organization not in Organization.access_qs(rando, 'change')
url = django_reverse('roleuserassignment-list')
resp = post(url=url, data={"user": rando.id, "role_definition": rd.id, "object_id": credential.id}, user=admin_user, expect=400)
assert "You cannot grant credential access to a User not in the credentials' organization" in str(resp.data)
@@ -143,7 +139,7 @@ def test_assign_credential_to_user_of_another_org(setup_managed_roles, credentia
post(url=url, data={"user": rando.id, "role_definition": rd.id, "object_id": credential.id}, user=admin_user, expect=201)
# can assign credential to org_admin
assert credential.organization in Organization.access_qs(org_admin, 'member')
assert credential.organization in Organization.access_qs(org_admin, 'change')
post(url=url, data={"user": org_admin.id, "role_definition": rd.id, "object_id": credential.id}, user=admin_user, expect=201)

View File

@@ -1,5 +1,4 @@
from unittest import mock
import json
import pytest
@@ -7,13 +6,11 @@ from django.contrib.contenttypes.models import ContentType
from crum import impersonate
from awx.main.fields import ImplicitRoleField
from awx.main.models.rbac import get_role_from_object_role, give_creator_permissions, get_role_codenames, get_role_definition
from awx.main.models.rbac import get_role_from_object_role, give_creator_permissions
from awx.main.models import User, Organization, WorkflowJobTemplate, WorkflowJobTemplateNode, Team
from awx.api.versioning import reverse
from ansible_base.rbac.models import RoleUserAssignment, RoleDefinition
from ansible_base.rbac import permission_registry
@pytest.mark.django_db
@@ -27,7 +24,6 @@ from ansible_base.rbac import permission_registry
'auditor_role',
'read_role',
'execute_role',
'approval_role',
'notification_admin_role',
],
)
@@ -43,37 +39,6 @@ def test_round_trip_roles(organization, rando, role_name, setup_managed_roles):
assert old_role.id == getattr(organization, role_name).id
@pytest.mark.django_db
@pytest.mark.parametrize('model', sorted(permission_registry.all_registered_models, key=lambda cls: cls._meta.model_name))
def test_role_migration_matches(request, model, setup_managed_roles):
fixture_name = model._meta.verbose_name.replace(' ', '_')
obj = request.getfixturevalue(fixture_name)
role_ct = 0
for field in obj._meta.get_fields():
if isinstance(field, ImplicitRoleField):
if field.name == 'read_role':
continue # intentionally left as "Compat" roles
role_ct += 1
old_role = getattr(obj, field.name)
old_codenames = set(get_role_codenames(old_role))
rd = get_role_definition(old_role)
new_codenames = set(rd.permissions.values_list('codename', flat=True))
# all the old roles should map to a non-Compat role definition
if 'Compat' not in rd.name:
model_rds = RoleDefinition.objects.filter(content_type=ContentType.objects.get_for_model(obj))
rd_data = {}
for rd in model_rds:
rd_data[rd.name] = list(rd.permissions.values_list('codename', flat=True))
assert (
'Compat' not in rd.name
), f'Permissions for old vs new roles did not match.\nold {field.name}: {old_codenames}\nnew:\n{json.dumps(rd_data, indent=2)}'
assert new_codenames == set(old_codenames)
# In the old system these models did not have object-level roles, all others expect some model roles
if model._meta.model_name not in ('notificationtemplate', 'executionenvironment'):
assert role_ct > 0
@pytest.mark.django_db
def test_role_naming(setup_managed_roles):
qs = RoleDefinition.objects.filter(content_type=ContentType.objects.get(model='jobtemplate'), name__endswith='dmin')

View File

@@ -3,7 +3,7 @@ import pytest
from django.contrib.contenttypes.models import ContentType
from awx.main.access import ExecutionEnvironmentAccess
from awx.main.models import ExecutionEnvironment, Organization, Team
from awx.main.models import ExecutionEnvironment, Organization
from awx.main.models.rbac import get_role_codenames
from awx.api.versioning import reverse
@@ -63,11 +63,6 @@ def check_user_capabilities(get, setup_managed_roles):
# ___ begin tests ___
@pytest.mark.django_db
def test_any_user_can_view_global_ee(control_plane_execution_environment, rando):
assert ExecutionEnvironmentAccess(rando).can_read(control_plane_execution_environment)
@pytest.mark.django_db
def test_managed_ee_not_assignable(control_plane_execution_environment, ee_rd, rando, admin_user, post):
url = django_reverse('roleuserassignment-list')
@@ -82,21 +77,6 @@ def test_org_member_required_for_assignment(org_ee, ee_rd, rando, admin_user, po
assert 'User must have view permission to Execution Environment organization' in str(r.data)
@pytest.mark.django_db
def test_team_can_have_permission(org_ee, ee_rd, rando, admin_user, post):
org2 = Organization.objects.create(name='a different team')
team = Team.objects.create(name='a team', organization=org2)
team.member_role.members.add(rando)
assert org_ee not in ExecutionEnvironmentAccess(rando).get_queryset() # user can not view the EE
url = django_reverse('roleteamassignment-list')
# can give object roles to the team now
post(url, {'role_definition': ee_rd.pk, 'team': team.id, 'object_id': org_ee.pk}, user=admin_user, expect=201)
assert rando.has_obj_perm(org_ee, 'change')
assert org_ee in ExecutionEnvironmentAccess(rando).get_queryset() # user can view the EE now
@pytest.mark.django_db
def test_give_object_permission_to_ee(org_ee, ee_rd, org_member, check_user_capabilities):
access = ExecutionEnvironmentAccess(org_member)
@@ -105,29 +85,11 @@ def test_give_object_permission_to_ee(org_ee, ee_rd, org_member, check_user_capa
check_user_capabilities(org_member, org_ee, {'edit': False, 'delete': False, 'copy': False})
ee_rd.give_permission(org_member, org_ee)
assert access.can_change(org_ee, {'name': 'new', 'organization': org_ee.organization.id})
assert access.can_change(org_ee, {'name': 'new'})
check_user_capabilities(org_member, org_ee, {'edit': True, 'delete': True, 'copy': False})
@pytest.mark.django_db
def test_need_related_organization_access(org_ee, ee_rd, org_member):
org2 = Organization.objects.create(name='another organization')
ee_rd.give_permission(org_member, org_ee)
org2.member_role.members.add(org_member)
access = ExecutionEnvironmentAccess(org_member)
assert access.can_change(org_ee, {'name': 'new', 'organization': org_ee.organization})
assert access.can_change(org_ee, {'name': 'new', 'organization': org_ee.organization.id})
assert not access.can_change(org_ee, {'name': 'new', 'organization': org2.id})
assert not access.can_change(org_ee, {'name': 'new', 'organization': org2})
# User can make the change if they have relevant permission to the new organization
org_ee.organization.execution_environment_admin_role.members.add(org_member)
org2.execution_environment_admin_role.members.add(org_member)
assert access.can_change(org_ee, {'name': 'new', 'organization': org2.id})
assert access.can_change(org_ee, {'name': 'new', 'organization': org2})
@pytest.mark.django_db
@pytest.mark.parametrize('style', ['new', 'old'])
def test_give_org_permission_to_ee(org_ee, organization, org_member, check_user_capabilities, style, org_ee_rd):
@@ -141,8 +103,5 @@ def test_give_org_permission_to_ee(org_ee, organization, org_member, check_user_
else:
organization.execution_environment_admin_role.members.add(org_member)
assert access.can_change(org_ee, {'name': 'new', 'organization': organization.id})
assert access.can_change(org_ee, {'name': 'new'})
check_user_capabilities(org_member, org_ee, {'edit': True, 'delete': True, 'copy': True})
# Extra check, user can not remove the EE from the organization
assert not access.can_change(org_ee, {'name': 'new', 'organization': None})

View File

@@ -48,17 +48,3 @@ def test_org_resource_role(ext_auth, organization, rando, org_admin):
assert access.can_attach(organization, rando, 'member_role.members') == ext_auth
organization.member_role.members.add(rando)
assert access.can_unattach(organization, rando, 'member_role.members') == ext_auth
@pytest.mark.django_db
def test_delete_org_while_workflow_active(workflow_job_template):
'''
Delete org while workflow job is active (i.e. changing status)
'''
assert workflow_job_template.organization # sanity check
wj = workflow_job_template.create_unified_job() # status should be new
workflow_job_template.organization.delete()
wj.refresh_from_db()
assert wj.status != 'pending' # sanity check
wj.status = 'pending' # status needs to change in order to trigger workflow_job_template.save()
wj.save(update_fields=['status'])

View File

@@ -17,13 +17,13 @@ def advisory_lock(*args, lock_session_timeout_milliseconds=0, **kwargs):
with connection.cursor() as cur:
idle_in_transaction_session_timeout = cur.execute('SHOW idle_in_transaction_session_timeout').fetchone()[0]
idle_session_timeout = cur.execute('SHOW idle_session_timeout').fetchone()[0]
cur.execute(f"SET idle_in_transaction_session_timeout = '{lock_session_timeout_milliseconds}'")
cur.execute(f"SET idle_session_timeout = '{lock_session_timeout_milliseconds}'")
cur.execute(f"SET idle_in_transaction_session_timeout = {lock_session_timeout_milliseconds}")
cur.execute(f"SET idle_session_timeout = {lock_session_timeout_milliseconds}")
with django_pglocks_advisory_lock(*args, **kwargs) as internal_lock:
yield internal_lock
if lock_session_timeout_milliseconds > 0:
with connection.cursor() as cur:
cur.execute(f"SET idle_in_transaction_session_timeout = '{idle_in_transaction_session_timeout}'")
cur.execute(f"SET idle_session_timeout = '{idle_session_timeout}'")
cur.execute(f"SET idle_in_transaction_session_timeout = {idle_in_transaction_session_timeout}")
cur.execute(f"SET idle_session_timeout = {idle_session_timeout}")
else:
yield True

View File

@@ -47,6 +47,7 @@ class WebsocketRelayConnection:
verify_ssl: bool = settings.BROADCAST_WEBSOCKET_VERIFY_CERT,
):
self.name = name
self.event_loop = asyncio.get_event_loop()
self.stats = stats
self.remote_host = remote_host
self.remote_port = remote_port
@@ -109,10 +110,7 @@ class WebsocketRelayConnection:
self.stats.record_connection_lost()
def start(self):
self.async_task = asyncio.get_running_loop().create_task(
self.connect(),
name=f"WebsocketRelayConnection.connect.{self.name}",
)
self.async_task = self.event_loop.create_task(self.connect())
return self.async_task
def cancel(self):
@@ -123,10 +121,7 @@ class WebsocketRelayConnection:
# metrics messages
# the "metrics" group is not subscribed to in the typical fashion, so we
# just explicitly create it
producer = asyncio.get_running_loop().create_task(
self.run_producer("metrics", websocket, "metrics"),
name="WebsocketRelayConnection.run_producer.metrics",
)
producer = self.event_loop.create_task(self.run_producer("metrics", websocket, "metrics"))
self.producers["metrics"] = {"task": producer, "subscriptions": {"metrics"}}
async for msg in websocket:
self.stats.record_message_received()
@@ -148,10 +143,7 @@ class WebsocketRelayConnection:
name = f"{self.remote_host}-{group}"
origin_channel = payload['origin_channel']
if not self.producers.get(name):
producer = asyncio.get_running_loop().create_task(
self.run_producer(name, websocket, group),
name=f"WebsocketRelayConnection.run_producer.{name}",
)
producer = self.event_loop.create_task(self.run_producer(name, websocket, group))
self.producers[name] = {"task": producer, "subscriptions": {origin_channel}}
logger.debug(f"Producer {name} started.")
else:
@@ -305,7 +297,9 @@ class WebSocketRelayManager(object):
pass
async def run(self):
self.stats_mgr = RelayWebsocketStatsManager(self.local_hostname)
event_loop = asyncio.get_running_loop()
self.stats_mgr = RelayWebsocketStatsManager(event_loop, self.local_hostname)
self.stats_mgr.start()
database_conf = deepcopy(settings.DATABASES['default'])
@@ -329,10 +323,7 @@ class WebSocketRelayManager(object):
)
await async_conn.set_autocommit(True)
on_ws_heartbeat_task = asyncio.get_running_loop().create_task(
self.on_ws_heartbeat(async_conn),
name="WebSocketRelayManager.on_ws_heartbeat",
)
on_ws_heartbeat_task = event_loop.create_task(self.on_ws_heartbeat(async_conn))
# Establishes a websocket connection to /websocket/relay on all API servers
while True:

View File

@@ -828,7 +828,7 @@ MANAGE_ORGANIZATION_AUTH = True
DISABLE_LOCAL_AUTH = False
# Note: This setting may be overridden by database settings.
TOWER_URL_BASE = "https://platformhost"
TOWER_URL_BASE = "https://towerhost"
INSIGHTS_URL_BASE = "https://example.org"
INSIGHTS_AGENT_MIME = 'application/example'
@@ -1009,7 +1009,6 @@ AWX_RUNNER_KEEPALIVE_SECONDS = 0
# Delete completed work units in receptor
RECEPTOR_RELEASE_WORK = True
RECPETOR_KEEP_WORK_ON_ERROR = False
# K8S only. Use receptor_log_level on AWX spec to set this properly
RECEPTOR_LOG_LEVEL = 'info'

View File

@@ -64,7 +64,7 @@
<div class="col-sm-6">
</div>
<div class="col-sm-6 footer-copyright">
Copyright &copy; 2024 <a href="http://www.redhat.com" target="_blank">Red Hat</a>, Inc. All Rights Reserved.
Copyright &copy; 2021 <a href="http://www.redhat.com" target="_blank">Red Hat</a>, Inc. All Rights Reserved.
</div>
</div>
</div>

View File

@@ -59,7 +59,7 @@ function ActivityStream() {
{
page: 1,
page_size: 20,
order_by: '-id',
order_by: '-timestamp',
},
['id', 'page', 'page_size']
);

View File

@@ -89,7 +89,7 @@
"LC_ALL": "en_US.UTF-8",
"MFLAGS": "-w",
"OLDPWD": "/awx_devel",
"AWX_HOST": "https://platformhost",
"AWX_HOST": "https://towerhost",
"HOSTNAME": "awx",
"LANGUAGE": "en_US:en",
"SDB_HOST": "0.0.0.0",

View File

@@ -89,7 +89,7 @@
"LC_ALL": "en_US.UTF-8",
"MFLAGS": "-w",
"OLDPWD": "/awx_devel",
"AWX_HOST": "https://platformhost",
"AWX_HOST": "https://towerhost",
"HOSTNAME": "awx",
"LANGUAGE": "en_US:en",
"SDB_HOST": "0.0.0.0",

View File

@@ -164,7 +164,7 @@
"ANSIBLE_RETRY_FILES_ENABLED": "False",
"MAX_EVENT_RES": "700000",
"ANSIBLE_CALLBACK_PLUGINS": "/awx_devel/awx/plugins/callback",
"AWX_HOST": "https://platformhost",
"AWX_HOST": "https://towerhost",
"ANSIBLE_SSH_CONTROL_PATH_DIR": "/tmp/awx_2_a4b1afiw/cp",
"ANSIBLE_STDOUT_CALLBACK": "awx_display"
},

View File

@@ -16,7 +16,7 @@ describe('<AzureAD />', () => {
SettingsAPI.readCategory.mockResolvedValue({
data: {
SOCIAL_AUTH_AZUREAD_OAUTH2_CALLBACK_URL:
'https://platformhost/sso/complete/azuread-oauth2/',
'https://towerhost/sso/complete/azuread-oauth2/',
SOCIAL_AUTH_AZUREAD_OAUTH2_KEY: 'mock key',
SOCIAL_AUTH_AZUREAD_OAUTH2_SECRET: '$encrypted$',
SOCIAL_AUTH_AZUREAD_OAUTH2_ORGANIZATION_MAP: {},

View File

@@ -22,7 +22,7 @@ describe('<AzureADDetail />', () => {
SettingsAPI.readCategory.mockResolvedValue({
data: {
SOCIAL_AUTH_AZUREAD_OAUTH2_CALLBACK_URL:
'https://platformhost/sso/complete/azuread-oauth2/',
'https://towerhost/sso/complete/azuread-oauth2/',
SOCIAL_AUTH_AZUREAD_OAUTH2_KEY: 'mock key',
SOCIAL_AUTH_AZUREAD_OAUTH2_SECRET: '$encrypted$',
SOCIAL_AUTH_AZUREAD_OAUTH2_ORGANIZATION_MAP: {},
@@ -62,7 +62,7 @@ describe('<AzureADDetail />', () => {
assertDetail(
wrapper,
'Azure AD OAuth2 Callback URL',
'https://platformhost/sso/complete/azuread-oauth2/'
'https://towerhost/sso/complete/azuread-oauth2/'
);
assertDetail(wrapper, 'Azure AD OAuth2 Key', 'mock key');
assertDetail(wrapper, 'Azure AD OAuth2 Secret', 'Encrypted');

View File

@@ -22,7 +22,7 @@ describe('<AzureADEdit />', () => {
SettingsAPI.readCategory.mockResolvedValue({
data: {
SOCIAL_AUTH_AZUREAD_OAUTH2_CALLBACK_URL:
'https://platformhost/sso/complete/azuread-oauth2/',
'https://towerhost/sso/complete/azuread-oauth2/',
SOCIAL_AUTH_AZUREAD_OAUTH2_KEY: 'mock key',
SOCIAL_AUTH_AZUREAD_OAUTH2_SECRET: '$encrypted$',
SOCIAL_AUTH_AZUREAD_OAUTH2_ORGANIZATION_MAP: {},

View File

@@ -19,7 +19,7 @@ describe('<GitHub />', () => {
SettingsAPI.readCategory.mockResolvedValueOnce({
data: {
SOCIAL_AUTH_GITHUB_CALLBACK_URL:
'https://platformhost/sso/complete/github/',
'https://towerhost/sso/complete/github/',
SOCIAL_AUTH_GITHUB_KEY: 'mock github key',
SOCIAL_AUTH_GITHUB_SECRET: '$encrypted$',
SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP: null,
@@ -29,7 +29,7 @@ describe('<GitHub />', () => {
SettingsAPI.readCategory.mockResolvedValueOnce({
data: {
SOCIAL_AUTH_GITHUB_ORG_CALLBACK_URL:
'https://platformhost/sso/complete/github-org/',
'https://towerhost/sso/complete/github-org/',
SOCIAL_AUTH_GITHUB_ORG_KEY: '',
SOCIAL_AUTH_GITHUB_ORG_SECRET: '$encrypted$',
SOCIAL_AUTH_GITHUB_ORG_NAME: '',
@@ -40,7 +40,7 @@ describe('<GitHub />', () => {
SettingsAPI.readCategory.mockResolvedValueOnce({
data: {
SOCIAL_AUTH_GITHUB_TEAM_CALLBACK_URL:
'https://platformhost/sso/complete/github-team/',
'https://towerhost/sso/complete/github-team/',
SOCIAL_AUTH_GITHUB_TEAM_KEY: 'OAuth2 key (Client ID)',
SOCIAL_AUTH_GITHUB_TEAM_SECRET: '$encrypted$',
SOCIAL_AUTH_GITHUB_TEAM_ID: 'team_id',
@@ -51,7 +51,7 @@ describe('<GitHub />', () => {
SettingsAPI.readCategory.mockResolvedValueOnce({
data: {
SOCIAL_AUTH_GITHUB_ENTERPRISE_CALLBACK_URL:
'https://platformhost/sso/complete/github-enterprise/',
'https://towerhost/sso/complete/github-enterprise/',
SOCIAL_AUTH_GITHUB_ENTERPRISE_URL: 'https://localhost/url',
SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL: 'https://localhost/apiurl',
SOCIAL_AUTH_GITHUB_ENTERPRISE_KEY: 'ent_key',
@@ -63,7 +63,7 @@ describe('<GitHub />', () => {
SettingsAPI.readCategory.mockResolvedValueOnce({
data: {
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_CALLBACK_URL:
'https://platformhost/sso/complete/github-enterprise-org/',
'https://towerhost/sso/complete/github-enterprise-org/',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_URL: 'https://localhost/url',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_API_URL: 'https://localhost/apiurl',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_KEY: 'ent_org_key',
@@ -76,7 +76,7 @@ describe('<GitHub />', () => {
SettingsAPI.readCategory.mockResolvedValueOnce({
data: {
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_CALLBACK_URL:
'https://platformhost/sso/complete/github-enterprise-team/',
'https://towerhost/sso/complete/github-enterprise-team/',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_URL: 'https://localhost/url',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_API_URL: 'https://localhost/apiurl',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_KEY: 'ent_team_key',

View File

@@ -22,8 +22,7 @@ jest.mock('../../../../api');
const mockDefault = {
data: {
SOCIAL_AUTH_GITHUB_CALLBACK_URL:
'https://platformhost/sso/complete/github/',
SOCIAL_AUTH_GITHUB_CALLBACK_URL: 'https://towerhost/sso/complete/github/',
SOCIAL_AUTH_GITHUB_KEY: 'mock github key',
SOCIAL_AUTH_GITHUB_SECRET: '$encrypted$',
SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP: null,
@@ -33,7 +32,7 @@ const mockDefault = {
const mockOrg = {
data: {
SOCIAL_AUTH_GITHUB_ORG_CALLBACK_URL:
'https://platformhost/sso/complete/github-org/',
'https://towerhost/sso/complete/github-org/',
SOCIAL_AUTH_GITHUB_ORG_KEY: '',
SOCIAL_AUTH_GITHUB_ORG_SECRET: '$encrypted$',
SOCIAL_AUTH_GITHUB_ORG_NAME: '',
@@ -44,7 +43,7 @@ const mockOrg = {
const mockTeam = {
data: {
SOCIAL_AUTH_GITHUB_TEAM_CALLBACK_URL:
'https://platformhost/sso/complete/github-team/',
'https://towerhost/sso/complete/github-team/',
SOCIAL_AUTH_GITHUB_TEAM_KEY: 'OAuth2 key (Client ID)',
SOCIAL_AUTH_GITHUB_TEAM_SECRET: '$encrypted$',
SOCIAL_AUTH_GITHUB_TEAM_ID: 'team_id',
@@ -55,7 +54,7 @@ const mockTeam = {
const mockEnterprise = {
data: {
SOCIAL_AUTH_GITHUB_ENTERPRISE_CALLBACK_URL:
'https://platformhost/sso/complete/github-enterprise/',
'https://towerhost/sso/complete/github-enterprise/',
SOCIAL_AUTH_GITHUB_ENTERPRISE_URL: 'https://localhost/enterpriseurl',
SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL: 'https://localhost/enterpriseapi',
SOCIAL_AUTH_GITHUB_ENTERPRISE_KEY: 'foobar',
@@ -67,7 +66,7 @@ const mockEnterprise = {
const mockEnterpriseOrg = {
data: {
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_CALLBACK_URL:
'https://platformhost/sso/complete/github-enterprise-org/',
'https://towerhost/sso/complete/github-enterprise-org/',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_URL: 'https://localhost/orgurl',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_API_URL: 'https://localhost/orgapi',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_KEY: 'foobar',
@@ -80,7 +79,7 @@ const mockEnterpriseOrg = {
const mockEnterpriseTeam = {
data: {
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_CALLBACK_URL:
'https://platformhost/sso/complete/github-enterprise-team/',
'https://towerhost/sso/complete/github-enterprise-team/',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_URL: 'https://localhost/teamurl',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_API_URL: 'https://localhost/teamapi',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_KEY: 'foobar',
@@ -144,7 +143,7 @@ describe('<GitHubDetail />', () => {
assertDetail(
wrapper,
'GitHub OAuth2 Callback URL',
'https://platformhost/sso/complete/github/'
'https://towerhost/sso/complete/github/'
);
assertDetail(wrapper, 'GitHub OAuth2 Key', 'mock github key');
assertDetail(wrapper, 'GitHub OAuth2 Secret', 'Encrypted');
@@ -219,7 +218,7 @@ describe('<GitHubDetail />', () => {
assertDetail(
wrapper,
'GitHub Organization OAuth2 Callback URL',
'https://platformhost/sso/complete/github-org/'
'https://towerhost/sso/complete/github-org/'
);
assertDetail(wrapper, 'GitHub Organization OAuth2 Key', 'Not configured');
assertDetail(wrapper, 'GitHub Organization OAuth2 Secret', 'Encrypted');
@@ -270,7 +269,7 @@ describe('<GitHubDetail />', () => {
assertDetail(
wrapper,
'GitHub Team OAuth2 Callback URL',
'https://platformhost/sso/complete/github-team/'
'https://towerhost/sso/complete/github-team/'
);
assertDetail(wrapper, 'GitHub Team OAuth2 Key', 'OAuth2 key (Client ID)');
assertDetail(wrapper, 'GitHub Team OAuth2 Secret', 'Encrypted');
@@ -317,7 +316,7 @@ describe('<GitHubDetail />', () => {
assertDetail(
wrapper,
'GitHub Enterprise OAuth2 Callback URL',
'https://platformhost/sso/complete/github-enterprise/'
'https://towerhost/sso/complete/github-enterprise/'
);
assertDetail(
wrapper,
@@ -344,7 +343,7 @@ describe('<GitHubDetail />', () => {
});
});
describe('Enterprise Organization', () => {
describe('Enterprise Org', () => {
let wrapper;
beforeAll(async () => {
@@ -377,7 +376,7 @@ describe('<GitHubDetail />', () => {
assertDetail(
wrapper,
'GitHub Enterprise Organization OAuth2 Callback URL',
'https://platformhost/sso/complete/github-enterprise-org/'
'https://towerhost/sso/complete/github-enterprise-org/'
);
assertDetail(
wrapper,
@@ -446,7 +445,7 @@ describe('<GitHubDetail />', () => {
assertDetail(
wrapper,
'GitHub Enterprise Team OAuth2 Callback URL',
'https://platformhost/sso/complete/github-enterprise-team/'
'https://towerhost/sso/complete/github-enterprise-team/'
);
assertDetail(
wrapper,
@@ -477,4 +476,23 @@ describe('<GitHubDetail />', () => {
);
});
});
describe('Redirect', () => {
test('should render redirect when user navigates to erroneous category', async () => {
let wrapper;
useRouteMatch.mockImplementation(() => ({
url: '/settings/github/foo/details',
path: '/settings/github/:category/details',
params: { category: 'foo' },
}));
await act(async () => {
wrapper = mountWithContexts(
<SettingsProvider value={mockAllOptions.actions}>
<GitHubDetail />
</SettingsProvider>
);
});
await waitForElement(wrapper, 'Redirect');
});
});
});

View File

@@ -22,7 +22,7 @@ describe('<GitHubEnterpriseEdit />', () => {
SettingsAPI.readCategory.mockResolvedValue({
data: {
SOCIAL_AUTH_GITHUB_ENTERPRISE_CALLBACK_URL:
'https://platformhost/sso/complete/github-enterprise/',
'https://towerhost/sso/complete/github-enterprise/',
SOCIAL_AUTH_GITHUB_ENTERPRISE_URL: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_KEY: '',

View File

@@ -22,7 +22,7 @@ describe('<GitHubEnterpriseOrgEdit />', () => {
SettingsAPI.readCategory.mockResolvedValue({
data: {
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_CALLBACK_URL:
'https://platformhost/sso/complete/github-enterprise-org/',
'https://towerhost/sso/complete/github-enterprise-org/',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_URL: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_API_URL: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_KEY: '',

View File

@@ -22,7 +22,7 @@ describe('<GitHubEnterpriseTeamEdit />', () => {
SettingsAPI.readCategory.mockResolvedValue({
data: {
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_CALLBACK_URL:
'https://platformhost/sso/complete/github-enterprise-team/',
'https://towerhost/sso/complete/github-enterprise-team/',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_URL: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_API_URL: '',
SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_KEY: '',

View File

@@ -22,7 +22,7 @@ describe('<GitHubOrgEdit />', () => {
SettingsAPI.readCategory.mockResolvedValue({
data: {
SOCIAL_AUTH_GITHUB_ORG_CALLBACK_URL:
'https://platformhost/sso/complete/github-org/',
'https://towerhost/sso/complete/github-org/',
SOCIAL_AUTH_GITHUB_ORG_KEY: '',
SOCIAL_AUTH_GITHUB_ORG_SECRET: '$encrypted$',
SOCIAL_AUTH_GITHUB_ORG_NAME: '',

View File

@@ -22,7 +22,7 @@ describe('<GitHubTeamEdit />', () => {
SettingsAPI.readCategory.mockResolvedValue({
data: {
SOCIAL_AUTH_GITHUB_TEAM_CALLBACK_URL:
'https://platformhost/sso/complete/github-team/',
'https://towerhost/sso/complete/github-team/',
SOCIAL_AUTH_GITHUB_TEAM_KEY: 'OAuth2 key (Client ID)',
SOCIAL_AUTH_GITHUB_TEAM_SECRET: '$encrypted$',
SOCIAL_AUTH_GITHUB_TEAM_ID: 'team_id',

View File

@@ -16,7 +16,7 @@ describe('<GoogleOAuth2 />', () => {
SettingsAPI.readCategory.mockResolvedValue({
data: {
SOCIAL_AUTH_GOOGLE_OAUTH2_CALLBACK_URL:
'https://platformhost/sso/complete/google-oauth2/',
'https://towerhost/sso/complete/google-oauth2/',
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY: 'mock key',
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET: '$encrypted$',
SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_DOMAINS: [

View File

@@ -22,7 +22,7 @@ describe('<GoogleOAuth2Detail />', () => {
SettingsAPI.readCategory.mockResolvedValue({
data: {
SOCIAL_AUTH_GOOGLE_OAUTH2_CALLBACK_URL:
'https://platformhost/sso/complete/google-oauth2/',
'https://towerhost/sso/complete/google-oauth2/',
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY: 'mock key',
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET: '$encrypted$',
SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_DOMAINS: [
@@ -68,7 +68,7 @@ describe('<GoogleOAuth2Detail />', () => {
assertDetail(
wrapper,
'Google OAuth2 Callback URL',
'https://platformhost/sso/complete/google-oauth2/'
'https://towerhost/sso/complete/google-oauth2/'
);
assertDetail(wrapper, 'Google OAuth2 Key', 'mock key');
assertDetail(wrapper, 'Google OAuth2 Secret', 'Encrypted');

View File

@@ -22,7 +22,7 @@ describe('<GoogleOAuth2Edit />', () => {
SettingsAPI.readCategory.mockResolvedValue({
data: {
SOCIAL_AUTH_GOOGLE_OAUTH2_CALLBACK_URL:
'https://platformhost/sso/complete/google-oauth2/',
'https://towerhost/sso/complete/google-oauth2/',
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY: 'mock key',
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET: '$encrypted$',
SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_DOMAINS: [

View File

@@ -26,7 +26,7 @@ describe('<MiscSystemDetail />', () => {
ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC: false,
ORG_ADMINS_CAN_SEE_ALL_USERS: true,
MANAGE_ORGANIZATION_AUTH: true,
TOWER_URL_BASE: 'https://platformhost',
TOWER_URL_BASE: 'https://towerhost',
REMOTE_HOST_HEADERS: [],
PROXY_IP_ALLOWED_LIST: [],
CSRF_TRUSTED_ORIGINS: [],
@@ -94,7 +94,7 @@ describe('<MiscSystemDetail />', () => {
'Automation Analytics upload URL',
'https://example.com'
);
assertDetail(wrapper, 'Base URL of the service', 'https://platformhost');
assertDetail(wrapper, 'Base URL of the service', 'https://towerhost');
assertDetail(wrapper, 'Gather data for Automation Analytics', 'Off');
assertDetail(
wrapper,

View File

@@ -15,10 +15,8 @@ describe('<SAML />', () => {
beforeEach(() => {
SettingsAPI.readCategory.mockResolvedValue({
data: {
SOCIAL_AUTH_SAML_CALLBACK_URL:
'https://platformhost/sso/complete/saml/',
SOCIAL_AUTH_SAML_METADATA_URL:
'https://platformhost/sso/metadata/saml/',
SOCIAL_AUTH_SAML_CALLBACK_URL: 'https://towerhost/sso/complete/saml/',
SOCIAL_AUTH_SAML_METADATA_URL: 'https://towerhost/sso/metadata/saml/',
SOCIAL_AUTH_SAML_SP_ENTITY_ID: '',
SOCIAL_AUTH_SAML_SP_PUBLIC_CERT: '',
SOCIAL_AUTH_SAML_SP_PRIVATE_KEY: '',

View File

@@ -21,10 +21,8 @@ describe('<SAMLDetail />', () => {
beforeEach(() => {
SettingsAPI.readCategory.mockResolvedValue({
data: {
SOCIAL_AUTH_SAML_CALLBACK_URL:
'https://platformhost/sso/complete/saml/',
SOCIAL_AUTH_SAML_METADATA_URL:
'https://platformhost/sso/metadata/saml/',
SOCIAL_AUTH_SAML_CALLBACK_URL: 'https://towerhost/sso/complete/saml/',
SOCIAL_AUTH_SAML_METADATA_URL: 'https://towerhost/sso/metadata/saml/',
SOCIAL_AUTH_SAML_SP_ENTITY_ID: 'mock_id',
SOCIAL_AUTH_SAML_SP_PUBLIC_CERT: 'mock_cert',
SOCIAL_AUTH_SAML_SP_PRIVATE_KEY: '',
@@ -73,12 +71,12 @@ describe('<SAMLDetail />', () => {
assertDetail(
wrapper,
'SAML Assertion Consumer Service (ACS) URL',
'https://platformhost/sso/complete/saml/'
'https://towerhost/sso/complete/saml/'
);
assertDetail(
wrapper,
'SAML Service Provider Metadata URL',
'https://platformhost/sso/metadata/saml/'
'https://towerhost/sso/metadata/saml/'
);
assertDetail(wrapper, 'SAML Service Provider Entity ID', 'mock_id');
assertVariableDetail(

View File

@@ -22,10 +22,8 @@ describe('<SAMLEdit />', () => {
SettingsAPI.readCategory.mockResolvedValue({
data: {
SAML_AUTO_CREATE_OBJECTS: true,
SOCIAL_AUTH_SAML_CALLBACK_URL:
'https://platformhost/sso/complete/saml/',
SOCIAL_AUTH_SAML_METADATA_URL:
'https://platformhost/sso/metadata/saml/',
SOCIAL_AUTH_SAML_CALLBACK_URL: 'https://towerhost/sso/complete/saml/',
SOCIAL_AUTH_SAML_METADATA_URL: 'https://towerhost/sso/metadata/saml/',
SOCIAL_AUTH_SAML_SP_ENTITY_ID: 'mock_id',
SOCIAL_AUTH_SAML_SP_PUBLIC_CERT: 'mock_cert',
SOCIAL_AUTH_SAML_SP_PRIVATE_KEY: '$encrypted$',

View File

@@ -117,10 +117,6 @@ function TroubleshootingEdit() {
name="RECEPTOR_RELEASE_WORK"
config={debug.RECEPTOR_RELEASE_WORK}
/>
<BooleanField
name="RECEPTOR_KEEP_WORK_ON_ERROR"
config={debug.RECEPTOR_KEEP_WORK_ON_ERROR}
/>
{submitError && <FormSubmitError error={submitError} />}
{revertError && <FormSubmitError error={revertError} />}
</FormColumnLayout>

View File

@@ -1,6 +1,5 @@
{
"AWX_CLEANUP_PATHS": false,
"AWX_REQUEST_PROFILE": false,
"RECEPTOR_RELEASE_WORK": false,
"RECEPTOR_KEEP_WORK_ON_ERROR": false
}
"RECEPTOR_RELEASE_WORK": false
}

View File

@@ -830,15 +830,6 @@
"category_slug": "debug",
"default": true
},
"RECEPTOR_KEEP_WORK_ON_ERROR": {
"type": "boolean",
"required": false,
"label": "Keep receptor work on error",
"help_text": "Prevent receptor work from being released on when error is detected",
"category": "Debug",
"category_slug": "debug",
"default": false
},
"SESSION_COOKIE_AGE": {
"type": "integer",
"required": true,
@@ -5182,14 +5173,6 @@
"category_slug": "debug",
"defined_in_file": false
},
"RECEPTOR_KEEP_WORK_ON_ERROR": {
"type": "boolean",
"label": "Keep receptor work on error",
"help_text": "Prevent receptor work from being released on when error is detected",
"category": "Debug",
"category_slug": "debug",
"defined_in_file": false
},
"SESSION_COOKIE_AGE": {
"type": "integer",
"label": "Idle Time Force Log Out",

View File

@@ -91,7 +91,6 @@
"slirp4netns:enable_ipv6=true"
],
"RECEPTOR_RELEASE_WORK": true,
"RECEPTOR_KEEP_WORK_ON_ERROR": false,
"SESSION_COOKIE_AGE": 1800,
"SESSIONS_PER_USER": -1,
"DISABLE_LOCAL_AUTH": false,

View File

@@ -35,7 +35,7 @@ ui-next/src/build: $(UI_NEXT_DIR)/src/build/awx
## True target for ui-next/src/build. Build ui_next from source.
$(UI_NEXT_DIR)/src/build/awx: $(UI_NEXT_DIR)/src $(UI_NEXT_DIR)/src/node_modules/webpack
@echo "=== Building ui_next ==="
@cd $(UI_NEXT_DIR)/src && PRODUCT="$(PRODUCT)" PUBLIC_PATH=/static/awx/ ROUTE_PREFIX=/ npm run build:awx
@cd $(UI_NEXT_DIR)/src && PRODUCT="$(PRODUCT)" PUBLIC_PATH=/static/awx/ ROUTE_PREFIX=/ui_next npm run build:awx
@mv $(UI_NEXT_DIR)/src/build/awx/index.html $(UI_NEXT_DIR)/src/build/awx/index_awx.html
.PHONY: ui-next/src

View File

@@ -1,3 +1,5 @@
from django.conf import settings
from django.http import Http404
from django.urls import re_path
from django.views.generic.base import TemplateView
@@ -5,6 +7,12 @@ from django.views.generic.base import TemplateView
class IndexView(TemplateView):
template_name = 'index_awx.html'
def get_context_data(self, **kwargs):
if settings.UI_NEXT is False:
raise Http404()
return super().get_context_data(**kwargs)
app_name = 'ui_next'

View File

@@ -18,6 +18,8 @@ def get_urlpatterns(prefix=None):
prefix = f'/{prefix}/'
urlpatterns = [
re_path(r'', include('awx.ui.urls', namespace='ui')),
re_path(r'^ui_next/.*', include('awx.ui_next.urls', namespace='ui_next')),
path(f'api{prefix}', include('awx.api.urls', namespace='api')),
]
@@ -34,9 +36,6 @@ def get_urlpatterns(prefix=None):
re_path(r'^(?:api/)?500.html$', handle_500),
re_path(r'^csp-violation/', handle_csp_violation),
re_path(r'^login/', handle_login_redirect),
# want api/v2/doesnotexist to return a 404, not match the ui_next urls,
# so use a negative lookahead assertion here
re_path(r'^(?!api/|sso/).*', include('awx.ui_next.urls', namespace='ui_next')),
]
if settings.SETTINGS_MODULE == 'awx.settings.development':

View File

@@ -13,10 +13,7 @@ Likewise content in this guide can be removed or replaced if it applies to funct
**Join us online**
We talk about AWX documentation on Matrix at `#docs:ansible.im <https://matrix.to/#/#docs:ansible.im>`_ and on libera IRC at ``#ansible-docs`` if you ever want to join us and chat about the docs!
You can also find lots of AWX discussion and get answers to questions at `forum.ansible.com <https://forum.ansible.com/>`_.
Need help or want to discuss AWX including the documentation? See the :ref:`Communication guide<communication>` to learn how to join the conversation!
.. toctree::
:maxdepth: 2

View File

@@ -320,43 +320,6 @@ Items surrounded by ``{}`` will be substituted when the log error is generated.
- **error**: The error message returned by the API or, if no error is specified, the HTTP status as text
.. _logging-api-otel:
OTel configuration with AWX
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can integrate OTel with AWX by configuring logging manually to point to your OTel collector. To do this, add the following codeblock in your `settings file <https://github.com/ansible/awx/blob/devel/tools/docker-compose/ansible/roles/sources/templates/local_settings.py.j2#L50>`_ (``local_settings.py.j2``):
.. code-block:: python
LOGGING['handlers']['otel'] |= {
'class': 'awx.main.utils.handlers.OTLPHandler',
'endpoint': 'http://otel:4317',
}
# Add otel log handler to all log handlers where propagate is False
for name in LOGGING['loggers'].keys():
if not LOGGING['loggers'][name].get('propagate', True):
handler = LOGGING['loggers'][name].get('handlers', [])
if 'otel' not in handler:
LOGGING['loggers'][name].get('handlers', []).append('otel')
# Everything without explicit propagate=False ends up logging to 'awx' so add it
handler = LOGGING['loggers']['awx'].get('handlers', [])
if 'otel' not in handler:
LOGGING['loggers']['awx'].get('handlers', []).append('otel')
Edit ``'endpoint': 'http://otel:4317',`` to point to your OTel collector.
To see it working in the dev environment, set the following:
::
OTEL=true GRAFANA=true LOKI=true PROMETHEUS=true make docker-compose
Then go to `http://localhost:3001 <http://localhost:3001>`_ to access Grafana and see the logs.
Troubleshoot Logging
---------------------

View File

@@ -0,0 +1,39 @@
.. _communication:
Communication
=============
We welcome your feedback, questions and ideas. Here's how to reach the community.
.. _code_of_conduct:
Code of Conduct
---------------
All communication and interactions in the Ansible Community are governed by our :ref:`code_of_conduct`. Please read and abide by it!
Reach out to our community team at `codeofconduct@ansible.com <mailto:codeofconduct@ansible.com>`_ if you have any questions or need assistance.
.. _forum:
Forum
-----
Join the `Ansible Forum <https://forum.ansible.com>`_ as a single starting point and our default communication platform for questions and help, development discussions, events, and much more. `Register <https://forum.ansible.com/signup?>`_ to join the community. Search by categories and tags to find interesting topics or start a new one; subscribe only to topics you need!
* `Get Help <https://forum.ansible.com/c/help/6>`_: get help or help others. Please add appropriate tags if you start new discussions, for example `awx`, `ee`, and `documentation`.
* `Posts tagged with 'awx' <https://forum.ansible.com/tag/awx>`_: subscribe to participate in project/technology-related conversations. There are other related tags in the forum you can use.
* `Bullhorn newsletter <https://docs.ansible.com/ansible/devel/community/communication.html#the-bullhorn>`_: used to announce releases and important changes.
* `Social Spaces <https://forum.ansible.com/c/chat/4>`_: gather and interact with fellow enthusiasts.
* `News & Announcements <https://forum.ansible.com/c/news/5>`_: track project-wide announcements including social events.
For more information on the forum navigation, see `Navigating the Ansible forum <https://forum.ansible.com/t/navigating-the-ansible-forum-tags-categories-and-concepts/39>`_ post.
Matrix
------
For real-time interactions, conversations in the AWX community happen over the Matrix protocol in the following channels:
* `#awx:ansible.com <https://matrix.to/#/#awx:ansible.com>`_: AWX project-related discussions.
* `#docs:ansible.im <https://matrix.to/#/#docs:ansible.im>`_: Ansible and AWX documentation-related discussions.
For more information, see the community-hosted `Matrix FAQ <https://hackmd.io/@ansible-community/community-matrix-faq>`_.

View File

@@ -10,13 +10,14 @@ There are so many ways you can contribute to AWX.
**Join us online**
You can chat with us and ask questions on Matrix at `#awx:ansible.com <https://matrix.to/#/#awx:ansible.com>`_ or visit the `Ansible Community Forum <https://forum.ansible.com/c/project/7/>`_ to find contributor resources.
Need help or want to discuss AWX including the documentation? See the :ref:`Communication guide<communication>` to learn how to join the conversation!
.. toctree::
:maxdepth: 2
:numbered:
intro
communication
setting_up
work_items
report_issues
report_issues

View File

@@ -4,6 +4,4 @@ Introduction
Hi there! We're excited to have you as a contributor.
Have questions about this document or anything not covered here? Come chat with us and ask questions on Matrix at `#awx:ansible.com <https://matrix.to/#/#awx:ansible.com>`_.
Also visit the `Ansible Community Forum <https://forum.ansible.com/c/project/7/>`_ to find contributor resources where you can also submit your questions or concerns.
Get started with joining the community! See the :ref:`Communication guide<communication>` to learn how.

View File

@@ -9,14 +9,13 @@ as possible. Version information, and an accurate reproducing scenario are criti
Be sure to attach the ``component:docs`` label to your issue. These labels are determined by the template data. Please use the template and fill it out as accurately as possible.
Please don't use the issue tracker as a way to ask how to do something. Instead, discuss it on on the `Ansible Community Forum <https://forum.ansible.com/c/project/7/>`_, or you can chat with us and ask questions on Matrix at `#awx:ansible.com <https://matrix.to/#/#awx:ansible.com>`_.
Please don't use the issue tracker as a way to ask how to do something. Instead, discuss it on on the Ansible Forum, or you can chat with us and ask questions on Matrix. See the :ref:`Communication guide<communication>` for details.
Before opening a new issue, please use the issue search feature to see if what you're experiencing has already been reported. If you have any extra detail to provide, please comment. Otherwise, rather than posting a "me too" comment, please consider giving it a `"thumbs up" <https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comment>`_ to give us an indication of the severity of the problem.
See `How issues are resolved <https://github.com/ansible/awx/blob/devel/ISSUES.md#how-issues-are-resolved>`_ for more information about the triaging and resolution process.
Getting help
-------------
If you require additional assistance, join the discussions on the `Ansible Community Forum <https://forum.ansible.com/c/project/7/>`_. Specify with tags ``#documentation`` and ``#awx`` to narrow down the area(s) of interest. For more information on tags, see `Navigating the Ansible forum — Tags, Categories, and Concepts <https://forum.ansible.com/t/navigating-the-ansible-forum-tags-categories-and-concepts/39>`_. You may also reach out to us and ask questions on Matrix at `#awx:ansible.com <https://matrix.to/#/#awx:ansible.com>`_.
See the :ref:`Communication guide<communication>` to learn how to get help.

View File

@@ -18,19 +18,18 @@ Fixing and updating the documentation are always appreciated, so reviewing the b
Things to know prior to submitting revisions
----------------------------------------------
- Please follow the `Ansible code of conduct <http://docs.ansible.com/ansible/latest/community/code_of_conduct.html>`_ in all your interactions with the community.
- All doc revisions or additions are done through pull requests against the ``devel`` branch.
- You must use ``git commit --signoff`` for any commit to be merged, and agree that usage of ``--signoff`` constitutes agreement with the terms of `DCO 1.1 <https://github.com/ansible/awx/blob/devel/DCO_1_1.md>`_.
- Take care to make sure no merge commits are in the submission, and use ``git rebase`` vs ``git merge`` for this reason.
- If collaborating with someone else on the same branch, consider using ``--force-with-lease`` instead of ``--force``. This will prevent you from accidentally overwriting commits pushed by someone else. For more information, see `git push docs <https://git-scm.com/docs/git-push#git-push---force-with-leaseltrefnamegt>`_.
- If submitting a large doc change, it's a good idea to join the `Ansible Community Forum <https://forum.ansible.com/c/project/7/>`_, and talk about what you would like to do or add first. Use the ``#documentation`` and ``#awx`` tags to help notify relevant people of the topic. This not only helps everyone know what's going on, it also helps save time and effort, if the community decides some changes are needed. For more information on tags, see `Navigating the Ansible forum — Tags, Categories, and Concepts <https://forum.ansible.com/t/navigating-the-ansible-forum-tags-categories-and-concepts/39>`_.
- We ask all of our community members and contributors to adhere to the `Ansible code of conduct <http://docs.ansible.com/ansible/latest/community/code_of_conduct.html>`_. If you have questions, or need assistance, please reach out to our community team at `codeofconduct@ansible.com <mailto:codeofconduct@ansible.com>`_.
- If submitting a large doc change, it's a good idea to join the :ref:`Ansible Forum<forum>`, and talk about what you would like to do or add first. Use the ``#documentation`` and ``#awx`` tags to help notify relevant people of the topic. This not only helps everyone know what's going on, it also helps save time and effort, if the community decides some changes are needed.
.. Note::
- Issue assignment will only be done for maintainers of the project. If you decide to work on an issue, please feel free to add a comment in the issue to let others know that you are working on it; but know that we will accept the first pull request from whomever is able to fix an issue. Once your PR is accepted we can add you as an assignee to an issue upon request.
- If you work in a part of the docs that is going through active development, your changes may be rejected, or you may be asked to `rebase`. A good idea before starting work is to have a discussion with us and ask questions on Matrix at `#awx:ansible.com <https://matrix.to/#/#awx:ansible.com>`_ or discuss your ideas on the `Ansible Community Forum <https://forum.ansible.com/c/project/7/>`_.
- If you work in a part of the docs that is going through active development, your changes may be rejected, or you may be asked to `rebase`. A good idea before starting work is to have a :ref:`discussion with the community<communication>`.
- If you find an issue with the functions of the UI or API, please see the `Reporting Issues <https://github.com/ansible/awx/blob/devel/CONTRIBUTING.md#reporting-issues>`_ section to open an issue.

View File

@@ -12,9 +12,7 @@ Likewise content in this guide can be removed or replaced if it applies to funct
**Join us online**
We talk about AWX documentation on Matrix at `#docs:ansible.im <https://matrix.to/#/#docs:ansible.im>`_ and on libera IRC at ``#ansible-docs`` if you ever want to join us and chat about the docs!
You can also find lots of AWX discussion and get answers to questions at `forum.ansible.com <https://forum.ansible.com/>`_.
Need help or want to discuss AWX including the documentation? See the :ref:`Communication guide<communication>` to learn how to join the conversation!
.. toctree::
:maxdepth: 2

View File

@@ -12,9 +12,7 @@ Likewise content in this guide can be removed or replaced if it applies to funct
**Join us online**
We talk about AWX documentation on Matrix at `#docs:ansible.im <https://matrix.to/#/#docs:ansible.im>`_ if you ever want to join us and chat about the docs!
You can also find lots of AWX discussion and get answers to questions on the `Ansible Community Forum <https://forum.ansible.com/c/project/7/>`_.
Need help or want to discuss AWX including the documentation? See the :ref:`Communication guide<communication>` to learn how to join the conversation!
.. toctree::
:maxdepth: 2
@@ -25,4 +23,4 @@ You can also find lots of AWX discussion and get answers to questions on the `An
known_issues
supported_locales
.. include:: ../common/copyright.rst
.. include:: ../common/copyright.rst

View File

@@ -94,7 +94,7 @@ Field lookups may also be used for more advanced queries, by appending the looku
The following field lookups are supported:
- ``exact``: Exact match (default lookup if not specified, refer to the following note for more information).
- ``exact``: Exact match (default lookup if not specified).
- ``iexact``: Case-insensitive version of exact.
- ``contains``: Field contains value.
- ``icontains``: Case-insensitive version of contains.
@@ -122,18 +122,3 @@ Filtering based on the requesting user's level of access by query string paramet
- ``role_level``: Level of role to filter on, such as ``admin_role``
.. note::
Previous releases of AWX returned queries with **__exact** results by default, but you may find that the latest versions are returning a larger subset instead. As a workaround, set the ``limit`` to ``?limit__exact`` for the default filter. For example, ``/api/v2/jobs/?limit__exact=example.domain.com`` results in:
::
{
"count": 1,
"next": null,
"previous": null,
"results": [
...
.. this note is generically written for AWX. For downstream, the change started in AAP 2.0 so we can be more specific if necessary.

View File

@@ -12,9 +12,7 @@ Likewise content in this guide can be removed or replaced if it applies to funct
**Join us online**
We talk about AWX documentation on Matrix at `#docs:ansible.im <https://matrix.to/#/#docs:ansible.im>`_ and on libera IRC at ``#ansible-docs`` if you ever want to join us and chat about the docs!
You can also find lots of AWX discussion and get answers to questions at `forum.ansible.com <https://forum.ansible.com/>`_.
Need help or want to discuss AWX including the documentation? See the :ref:`Communication guide<communication>` to learn how to join the conversation!
.. toctree::
:maxdepth: 2

View File

@@ -12,9 +12,7 @@ Likewise content in this guide can be removed or replaced if it applies to funct
**Join us online**
We talk about AWX documentation on Matrix at `#docs:ansible.im <https://matrix.to/#/#docs:ansible.im>`_ and on libera IRC at ``#ansible-docs`` if you ever want to join us and chat about the docs!
You can also find lots of AWX discussion and get answers to questions at `forum.ansible.com <https://forum.ansible.com/>`_.
Need help or want to discuss AWX including the documentation? See the :ref:`Communication guide<communication>` to learn how to join the conversation!
.. toctree::
:maxdepth: 2

View File

@@ -13,9 +13,7 @@ Likewise content in this guide can be removed or replaced if it applies to funct
**Join us online**
We talk about AWX documentation on Matrix at `#docs:ansible.im <https://matrix.to/#/#docs:ansible.im>`_ and on libera IRC at ``#ansible-docs`` if you ever want to join us and chat about the docs!
You can also find lots of AWX discussion and get answers to questions at `forum.ansible.com <https://forum.ansible.com/>`_.
Need help or want to discuss AWX including the documentation? See the :ref:`Communication guide<communication>` to learn how to join the conversation!
.. toctree::
:maxdepth: 2

View File

@@ -264,7 +264,7 @@ This will create an `httpbin` service reachable from the AWX container at `http:
The Grafana notification type allows you to create Grafana annotations. Details about this feature of Grafana are available at http://docs.grafana.org/reference/annotations/. In order to allow AWX to add annotations, an API Key needs to be created in Grafana. Note that the created annotations are region events with start and endtime of the associated AWX Job. The annotation description is also provided by the subject of the associated AWX Job, for example:
```
Job #1 'Ping Macbook' succeeded: https://platformhost/#/jobs/playbook/1
Job #1 'Ping Macbook' succeeded: https://towerhost/#/jobs/playbook/1
```
The configurable options of the Grafana notification type are:

View File

@@ -18,7 +18,7 @@ X-API-Query-Time: 0.004s
X-API-Time: 0.026s
{
"SOCIAL_AUTH_GITHUB_TEAM_CALLBACK_URL": "https://platformhost/sso/complete/github-team/",
"SOCIAL_AUTH_GITHUB_TEAM_CALLBACK_URL": "https://towerhost/sso/complete/github-team/",
"SOCIAL_AUTH_GITHUB_TEAM_KEY": "",
"SOCIAL_AUTH_GITHUB_TEAM_SECRET": "",
"SOCIAL_AUTH_GITHUB_TEAM_ID": "",

View File

@@ -1,77 +0,0 @@
#!/bin/bash
set +x
# CONSTANTS
export REGEX_LEFT='https://github.com/ansible/django-ansible-base@'
export REGEX_RIGHT='#egg=django-ansible-base'
# GLOBALS
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
REQ_FILE=$SCRIPT_DIR/requirements_git.txt
# Pin Function
DESIRED_VERSION=''
Pin()
{
export DESIRED_VERSION
perl -p -i -e 's/\Q$ENV{REGEX_LEFT}\E(.*?)\Q$ENV{REGEX_RIGHT}\E/$ENV{REGEX_LEFT}$ENV{DESIRED_VERSION}$ENV{REGEX_RIGHT}/g' $REQ_FILE
}
# Current Function
Current()
{
REQUIREMENTS_LINE=$(grep django-ansible-base $REQ_FILE)
echo "$REQUIREMENTS_LINE" | perl -nE 'say $1 if /\Q$ENV{REGEX_LEFT}\E(.*?)\Q$ENV{REGEX_RIGHT}\E/'
}
Help()
{
# Display Help
echo ""
echo "Help:"
echo ""
echo "Interact with django-ansible-base in $REQ_FILE."
echo "By default, output the current django-ansible-base pinned version."
echo
echo "Syntax: scriptTemplate [-s|h|v]"
echo "options:"
echo "s Set django-ansible-base version to pin to."
echo "h Print this Help."
echo "v Verbose mode."
echo
}
if [ $# -eq 0 ]; then
Current
exit
fi
while getopts ":hs:" option; do
case $option in
h) # display Help
Help
exit
;;
s)
DESIRED_VERSION=$OPTARG;;
:)
echo "Option -${OPTARG} requires an argument."
Help
exit 1
;;
\?) # Invalid option
echo "Error: Invalid option"
echo ""
Help
exit;;
esac
done
if [ -n "$DESIRED_VERSION" ]; then
Pin
Current
fi

View File

@@ -2,4 +2,4 @@ git+https://github.com/ansible/system-certifi.git@devel#egg=certifi
# Remove pbr from requirements.in when moving ansible-runner to requirements.in
git+https://github.com/ansible/ansible-runner.git@devel#egg=ansible-runner
git+https://github.com/ansible/python3-saml.git@devel#egg=python3-saml
django-ansible-base @ git+https://github.com/ansible/django-ansible-base@2024.7.17#egg=django-ansible-base[rest_filters,jwt_consumer,resource_registry,rbac]
django-ansible-base @ git+https://github.com/ansible/django-ansible-base@devel#egg=django-ansible-base[rest_filters,jwt_consumer,resource_registry,rbac]

View File

@@ -4,20 +4,8 @@
### DO NOT EDIT
###
{% if not headless|bool %}
# UI_next build contaienr
FROM quay.io/centos/centos:stream9 AS ui-next-builder
USER root
RUN dnf -y update && dnf install -y nodejs make git
RUN npm install -g n && n 18
COPY . /tmp/src/
WORKDIR /tmp/src/
RUN make ui-next
{% endif %}
# Build container
FROM quay.io/centos/centos:stream9 AS builder
FROM quay.io/centos/centos:stream9 as builder
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
@@ -43,6 +31,9 @@ RUN dnf -y update && dnf install -y 'dnf-command(config-manager)' && \
libffi-devel \
libtool-ltdl-devel \
make \
{% if not headless|bool %}
nodejs \
{% endif %}
nss \
openldap-devel \
# pin to older openssl, see jira AAP-23449
@@ -83,20 +74,21 @@ RUN cd /tmp && make requirements_awx
ARG VERSION
ARG SETUPTOOLS_SCM_PRETEND_VERSION
ARG HEADLESS
{% if (build_dev|bool) or (kube_dev|bool) %}
ADD requirements/requirements_dev.txt /tmp/requirements
RUN cd /tmp && make requirements_awx_dev
{% else %}
# Use the distro provided npm to bootstrap our required version of node
{% if not headless|bool %}
RUN npm install -g n && n 16.13.1
{% endif %}
# Copy source into builder, build sdist, install it into awx venv
COPY . /tmp/src/
WORKDIR /tmp/src/
{% if not headless|bool %}
COPY --from=ui-next-builder /tmp/src/awx/ui_next/build /tmp/src/awx/ui_next/build
{% endif %}
RUN make sdist && /var/lib/awx/venv/awx/bin/pip install dist/awx.tar.gz
{% if not headless|bool %}
@@ -197,7 +189,7 @@ COPY --from=builder /var/lib/awx /var/lib/awx
RUN ln -s /var/lib/awx/venv/awx/bin/awx-manage /usr/bin/awx-manage
{% if build_dev|bool %}
{%if build_dev|bool %}
COPY --from={{ receptor_image }} /usr/bin/receptor /usr/bin/receptor
RUN openssl req -nodes -newkey rsa:2048 -keyout /etc/nginx/nginx.key -out /etc/nginx/nginx.csr \

View File

@@ -208,25 +208,13 @@ awx_1 | Applying auth.0001_initial... OK
##### Clean and build the UI
Prerequisites (on your local machine)
- npm
- nodejs
Required versions listed here https://github.com/ansible/ansible-ui/blob/main/README.md
On your local machine (not in awx container)
```bash
make clean/ui-next ui-next
$ docker exec tools_awx_1 make clean-ui ui-devel
```
This will clone the ansible-ui into the `awx/ui_next/src` directory and build the static files. Then when the containers come up, awx-manage collectstatic will copy those files into the proper place.
See [the ui development documentation](../../awx/ui/README.md) for more information on using the frontend development, build, and test tooling.
You can also use `UI_NEXT_LOCAL` to build from a locally cloned ansible-ui repo.
See [the ui development documentation](https://github.com/ansible/ansible-ui/blob/main/CONTRIBUTING.md) for more information on using the frontend development, build, and test tooling.
Once migrations are completed and the UI is built, you can begin using AWX. The UI can be reached in your browser at `https://localhost:8043/`, and the API can be found at `https://localhost:8043/api/v2`.
Once migrations are completed and the UI is built, you can begin using AWX. The UI can be reached in your browser at `https://localhost:8043/#/home`, and the API can be found at `https://localhost:8043/api/v2`.
##### Create an admin user
@@ -573,11 +561,11 @@ If you have a playbook like:
var: the_secret_from_vault
```
And run it through AWX with the credential `Credential From Vault via Token Auth` tied to it, the debug should result in `this_is_the_secret_value`. If you run it through AWX with the credential `Credential From Vault via Userpass Auth`, the debug should result in `this_is_the_userpass_secret_value`.
And run it through AWX with the credential `Credential From Vault via Token Auth` tied to it, the debug should result in `this_is_the_secret_value`. If you run it through AWX with the credential `Credential From Vault via Userpass Auth`, the debug should result in `this_is_the_userpass_secret_value`.
### HashiVault with LDAP
If you wish to have your OpenLDAP container connected to the Vault container, you will first need to have the OpenLDAP container running alongside AWX and Vault.
If you wish to have your OpenLDAP container connected to the Vault container, you will first need to have the OpenLDAP container running alongside AWX and Vault.
```bash
@@ -586,7 +574,7 @@ VAULT=true LDAP=true make docker-compose
```
Similar to the above, you will need to unseal the vault before we can run the other needed playbooks.
Similar to the above, you will need to unseal the vault before we can run the other needed playbooks.
```bash
@@ -594,7 +582,7 @@ ansible-playbook tools/docker-compose/ansible/unseal_vault.yml
```
Now that the vault is unsealed, we can plumb the vault container now while passing true to enable_ldap extra var.
Now that the vault is unsealed, we can plumb the vault container now while passing true to enable_ldap extra var.
```bash
@@ -607,7 +595,7 @@ ansible-playbook tools/docker-compose/ansible/plumb_vault.yml -e enable_ldap=tru
```
This will populate your AWX instance with LDAP specific items.
This will populate your AWX instance with LDAP specific items.
- A vault LDAP Lookup Cred tied to the LDAP `awx_ldap_vault` user called `Vault LDAP Lookup Cred`
- A credential called `Credential From HashiCorp Vault via LDAP Auth` which is of the created type using the `Vault LDAP Lookup Cred` to get the secret.

View File

@@ -47,6 +47,9 @@ services:
{% if minikube_container_group|bool %}
MINIKUBE_CONTAINER_GROUP: "true"
{% endif %}
links:
- postgres
- redis_{{ container_postfix }}
networks:
- awx
- service-mesh
@@ -77,12 +80,8 @@ services:
- "~/.kube/config:/var/lib/awx/.kube/config"
- "redis_socket_{{ container_postfix }}:/var/run/redis/:rw"
privileged: true
depends_on:
postgres:
condition: service_started
redis_{{ container_postfix }}:
condition: service_started
{% if editable_dependencies | length > 0 %}
depends_on:
init_awx:
condition: service_completed_successfully
{% endif %}
@@ -198,6 +197,10 @@ services:
volumes:
- "../../docker-compose/_sources/prometheus.yml:/etc/prometheus/prometheus.yml"
- "prometheus_storage:/prometheus:rw"
links:
{% for i in range(control_plane_node_count|int) %}
- awx_{{ loop.index }}:awx{{ loop.index }} # because underscores are not valid in hostnames
{% endfor %}
{% endif %}
{% if enable_grafana|bool %}
grafana:
@@ -211,6 +214,8 @@ services:
volumes:
- "../../grafana:/etc/grafana/provisioning"
- "grafana_storage:/var/lib/grafana:rw"
links:
- prometheus
depends_on:
- prometheus
{% endif %}
@@ -313,6 +318,8 @@ services:
container_name: tools_receptor_hop
hostname: receptor-hop
command: 'receptor --config /etc/receptor/receptor.conf'
links:
- awx_1
networks:
- awx
ports:
@@ -328,6 +335,8 @@ services:
command: 'receptor --config /etc/receptor/receptor.conf'
environment:
RECEPTORCTL_SOCKET: {{ receptor_socket_file }}
links:
- receptor-hop
networks:
- awx
volumes:

View File

@@ -23,6 +23,8 @@ else
wait-for-migrations
fi
# Make sure that the UI static file directory exists, Django complains otherwise.
mkdir -p /awx_devel/awx/ui/build/static
# Make sure that the UI_NEXT statifc file directory exists, if UI_NEXT is not built yet put a placeholder file in it.
if [ ! -d "/awx_devel/awx/ui_next/build/awx" ]; then

View File

@@ -30,13 +30,13 @@ Request server configuration from Ansible Tower
Usage:
Execution using positional parameters:
$($MyInvocation.MyCommand.Name) https://example.platformhost.net 44d7507f2ead49af5fca80aa18fd24bc 38
$($MyInvocation.MyCommand.Name) https://example.towerhost.net 44d7507f2ead49af5fca80aa18fd24bc 38
Ignore self-signed certificates using named parameters:
$($MyInvocation.MyCommand.Name) -k -s https://example.platformhost.local -c 44d7507f2ead49af5fca80aa18fd24bc -t 38
$($MyInvocation.MyCommand.Name) -k -s https://example.towerhost.local -c 44d7507f2ead49af5fca80aa18fd24bc -t 38
Execution using optional extra_vars:
$($MyInvocation.MyCommand.Name) https://example.platformhost.net 44d7507f2ead49af5fca80aa18fd24bc 38 '{ key: value, dict: { key: value }}'
$($MyInvocation.MyCommand.Name) https://example.towerhost.net 44d7507f2ead49af5fca80aa18fd24bc 38 '{ key: value, dict: { key: value }}'
Options:
-help, -h Show this message