Commit Graph
34982 Commits
Author SHA1 Message Date
Dirk Julich f506ccab45 AAP-85334: Normalize formatting with ruff format
Pure formatting commit — no logic changes. Run `ruff format` across
awx/ and awxkit/ (awx_collection excluded).
2026-08-20 18:35:23 +02:00
Dirk JulichandClaude Opus 4.6 4b73e1264b AAP-85334: Replace Black + Flake8 with Ruff
Migrate Python linting and formatting toolchain from Black (formatter)
and Flake8 (linter) to Ruff, which provides both in a single tool.

- pyproject.toml: remove [tool.black], add [tool.ruff] config sections
- tox.ini: replace black+flake8 deps/commands with ruff in linters env
- awxkit/tox.ini: replace flake8 with ruff in lint env
- Makefile: replace `black` target with `format` + `lint`, keep `black`
  as legacy alias, update `check` and `api-lint`
- pre-commit.sh: use `ruff format --check`, honor AWX_IGNORE_RUFF
- requirements_dev.txt: swap black/flake8 for ruff
- Dockerfile.j2: install ruff instead of black
- CONTRIBUTING.md: update tool references
- Fix noqa comments to use # separator instead of ; (ruff requirement)
- Add noqa: F841 for false positive in awxkit/cli/__init__.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-20 18:35:23 +02:00
Chris Meyers b84931a49b AAP-87883: Use shared lock for project copy when no sync is needed (#16587)
Use shared lock for project copy when no sync is needed

When a job template runs and no project sync is required (the common
case for slice jobs), multiple jobs were serializing behind an exclusive
lock even though they were only reading the shared project directory to
copy it.

Introduce read/write lock semantics using fcntl LOCK_SH/LOCK_EX:

- acquire_lock gains an exclusive= parameter (default True, preserving
  existing behavior for all callers that don't pass it)
- sync_and_copy always acquires LOCK_SH first, then refreshes DB state
  and calls get_sync_needs() under the lock — preserving the invariant
  that the sync check always happens under a lock (no TOCTOU window)
- If sync is needed, LOCK_SH is released and LOCK_EX is acquired before
  proceeding; sync_and_copy_without_lock re-checks internally
- project.refresh_from_db() is guarded by project.pk to handle unsaved
  model instances used in unit tests

This allows slice jobs backed by the same project to copy the project
tree concurrently instead of sequentially. Measured improvement on a
200MB/54k-file project with 8 slice jobs: 140.5s total lock wait → 0s,
wall time 42.6s → 16.9s (2.5x).

Assisted-by: Claude Code via Google Vertex AI <cmeyers+claude@redhat.com>
2026-08-20 09:10:29 -04:00
Liam Allen 405c56e2bc AAP-78319 Fix path traversal in awxkit YAML (#16608)
* Fix path traversal in awxkit YAML

* coderabbitai feedback
2026-08-20 12:18:32 +01:00
Dirk Julich 413dfed16c Bump aiohttp version (#16607) 2026-08-20 10:34:26 +02:00
red-hat-konflux-kflux-prd-rh02[bot] e9bf05c550 Update quay.io/aap-ci/tekton-catalog/pipeline/test/aap-api-tests:0.1 Docker digest to 94fb1b4 (#16600)
Signed-off-by: red-hat-konflux-kflux-prd-rh02 <190377777+red-hat-konflux-kflux-prd-rh02[bot]@users.noreply.github.com>
2026-08-19 17:07:59 -04:00
Alan RomingerandClaude Opus 4.6 70f42d2b7d AAP-87084 — Revert UnifiedJob queryset to DAB APIs, scope actor role filter (#16599)
* AAP-87084 — Revert UnifiedJob queryset to DAB APIs, scope actor role filter

The previous patch (7242fe89) inlined all DAB RBAC internals into
UnifiedJobAccess.filtered_queryset to pre-compute role IDs and avoid
repeated subqueries.  While it fixed the UNION regression from c84575c,
it created 70 lines of duplicated logic inconsistent with the
UnifiedJobTemplate path and fetched ALL user roles regardless of
relevance.

This commit:

- Reverts filtered_queryset to the clean post-f8fa690 form that
  delegates to DAB's access_ids_qs / accessible_pk_qs APIs
- Adds _scoped_actor_role_filter() as a temporary shim that pre-filters
  RoleUserAssignment by content_type_id, auto-including Org and Team CTs
  for inherited permissions and NULL for global roles
- Wires the scoped filter into UnifiedJobTemplate.accessible_pk_qs
- Adds tests for custom global roles, mixed permissions, and unrelated
  role exclusion

The shim duplicates logic planned for DAB's _actor_role_filter
content_type_ids parameter; once that merges, replace and delete.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Remove unused ContentType and RoleUserAssignment imports to fix flake8

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* AAP-87084 — Remove _scoped_actor_role_filter shim, use DAB _actor_role_filter directly

The scoped content_type_ids parameter now lives in DAB's
_actor_role_filter. The AWX shim and its hardcoded Org/Team CT
inclusions are no longer needed. The unified job accessible_pk_qs
uses the unscoped _actor_role_filter for correctness.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-18 15:29:54 -04:00
Stevenson Michel d6675e67e0 [Devel] Fix Migration From Old system_auditor to New platform_auditor (#16582)
* Fixed migration from old to new rbac for sys auditor

* Added first migration

* Added testing for migrations and fix

* Removed attempts to remove platform auditor assignments

* Fixed migration test failures
2026-08-18 09:03:46 -04:00
Peter Braun 85891b8d30 revert 78a55b25ec and 64dc097914 (#16597) 2026-08-18 13:19:12 +02:00
Ryan Williams 6257ba9a7f fix: disable mintmaker to run pipeline-migration-tool (#16598)
When mintmaker bumps tekton resources it is failing on running the
pipeline-migration-tool. This runs as a post upgrade task and fails when
trying to run against tekton bundles. Disabling this to allow CI checks
to pass and merge updated pipeline bundles.

Signed-off-by: Ryan Williams <3375653+ryankwilliams@users.noreply.github.com>
2026-08-17 20:57:03 +00:00
red-hat-konflux-kflux-prd-rh02[bot] 505e354526 Update quay.io/aap-ci/tekton-catalog/pipeline/test/aap-api-tests:0.1 Docker digest to 33add02 (#16524)
Signed-off-by: red-hat-konflux-kflux-prd-rh02 <190377777+red-hat-konflux-kflux-prd-rh02[bot]@users.noreply.github.com>
Co-authored-by: red-hat-konflux-kflux-prd-rh02[bot] <190377777+red-hat-konflux-kflux-prd-rh02[bot]@users.noreply.github.com>
2026-08-17 16:08:52 -04:00
Chris SantiagoandClaude Opus 4.6 3fea070420 Update x-ai-description for 44 tools to improve LLM tool selection (#16579)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-14 15:11:47 +02:00
Peter Braun 693a5820ae fix: do not delete any of the jwt managed roles during cleanup (#16590)
fix: do not delete and of the jwt managed roles during cleanup
2026-08-14 13:20:01 +02:00
Dirk JulichandClaude Opus 4.6 d759ff160e AAP-87829 — Make composite index migration idempotent for upgrade paths (#16585)
Use SeparateDatabaseAndState with CREATE INDEX IF NOT EXISTS so the
migration succeeds when the index was already created manually (via
awx-manage create_host_summary_index or raw SQL workaround).

Forward-port of tower#7895 (stable-2.7).

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-13 13:42:05 -04:00
Dirk JulichandClaude Opus 4.6 587b816793 AAP-87549 — Add management command to create JobHostSummary composite index (#16586)
Adds `create_host_summary_index` management command that creates the
composite index (host_id, id DESC) on main_jobhostsummary using
CREATE INDEX CONCURRENTLY.

The command is idempotent: it checks for existing valid/invalid indexes,
drops invalid indexes from failed prior attempts, and skips if the index
already exists. Disables statement_timeout for the session to avoid
killing the index build on large tables where ALTER ROLE timeout is set.

Forward-port of tower#7893 (stable-2.6).

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-13 13:41:48 -04:00
Dirk JulichandClaude Opus 4.6 7242fe89ae AAP-87084 — Replace UNION with OR + pre-computed role set in unified job RBAC query (#16577)
* AAP-87084 — Replace UNION with OR + pre-computed role set in unified job RBAC query

The UNION ALL approach introduced by AAP-81173 forces PostgreSQL to
materialize all accessible job IDs across four RBAC branches before
any filtering or LIMIT can apply. This causes a 10x per-call regression
on the unified jobs list (36ms → 366ms) and a 3x regression on the
dashboard date-bucketed aggregation (88.9ms → 261.5ms).

Pre-compute the user's role IDs once as a Python list and pass them as
literal parameters to OR-based RoleEvaluation filters. This eliminates
the 4x redundant roleuserassignment subquery scans and restores
single-pass filtering with early LIMIT exit.

Resolves: AAP-87084, AAP-87087

* Assert RBAC query list is non-empty before checking for UNION

Ensures the test does not pass vacuously if no RBAC query is captured.

* Add test for singleton permission shortcut branches

Exercises the UJT, inventory, and org auditor singleton permission
paths in filtered_queryset() to increase coverage on new code.

* Add test for team-granted permissions in unified job list

Verifies that a user who can view a job template only through a team
assignment (not direct) sees the corresponding unified jobs.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-11 21:40:01 +02:00
Dirk Julich c0aedc6e37 fix: bump daphne to 4.2.2 (#16574) 2026-08-07 18:11:56 +02:00
Alan RomingerandClaude Opus 4.6 3f04ed4707 [AAP-82668] Skip old RBAC sync on cascade-deleted assignments (#16559)
* [AAP-82668] Skip old RBAC sync on cascade-deleted assignments

When a non-RBAC parent (e.g. Organization) is deleted, Django cascades
to RoleUserAssignment/RoleTeamAssignment and fires post_delete for each.
The sync_assignments_to_old_rbac_delete handler would then do 3-4
FK/GFK queries per assignment to sync removals to the old Role model —
entirely redundant since the old Role M2M tables cascade-delete from the
same content object.

Use Django's post_delete `origin` kwarg (4.1+) to detect this: when
origin is a Model instance whose app_label is not dab_rbac, the delete
is a cascade from a non-RBAC parent and the sync is skipped.

Measured on 150 teams / 321 users / 48K assignments:
  Baseline:      78.8s
  With fix:      20.4s (via service-index endpoint)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-03 14:43:03 -04:00
Alan RomingerandClaude Opus 4.6 4996c91d12 AAP-85067 Old RBAC sync regression tests for bulk claims (#16565)
* test: add regression tests for old RBAC sync after bulk claims

Tests that save_user_claims (which uses bulk_create, skipping signals)
correctly syncs old Role.members when run through AwxJWTAuthentication.
Covers add, remove, and multi-org/team scenarios.

* test: call process_permissions instead of internal _sync_old_rbac

Tests now exercise the public AwxJWTAuthentication.process_permissions()
API with the JWT layer mocked, rather than calling the private
_sync_old_rbac method directly.

The test_bulk_claims_skips_old_rbac_signals test asserts that
save_user_claims does NOT populate old Role.members via signals.
On current DAB, save_user_claims uses the serial give_permission
path which fires signals normally, so old Role.members IS populated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-03 14:39:55 -04:00
Dirk JulichandClaude Opus 4.6 c84575c215 AAP-81173 — Replace 4-way OR with UNION in unified job list RBAC query (#16555)
* AAP-81173 — Replace 4-way OR with UNION in unified job list RBAC query

The UnifiedJobAccess.filtered_queryset() method used a 4-way OR to
determine which unified jobs a user can see. Under load, the OR forces
PostgreSQL to evaluate all four branches in a single plan, preventing
branch-specific index optimization and causing 35 hours of DB time per
30-minute Scale Lab window.

Split each RBAC branch (template read_role, inventory update, ad-hoc
command, org auditor) into separate querysets combined with UNION, giving
the planner an independent optimal plan per branch. The UNION result is
wrapped in pk__in= for compatibility with BaseAccess.get_queryset()
prefetch_related and the workflowapproval filter.

This follows the same pattern proven in AAP-83319 (team list UNION fix).

* AAP-81173 — Add UnifiedJobPagination to prevent COUNT regression

The pk__in UNION pattern used for RBAC filtering forces the large
outer table as the driving table for COUNT(*), requiring PostgreSQL
to materialize all subquery result sets.  On large deployments this
produces catastrophic query times (see AAP-83773 for the identical
issue on activity_stream).

Override the paginator count to use an unfiltered
UnifiedJob.objects.count() — the over-count is acceptable for
pagination UI.  Also fix test_unified_job_list_rando_sees_nothing
to assert on results length instead of count, since count is now
unfiltered.

* AAP-81173 — Add unit tests for UnifiedJobPagination coverage

Cover the UnifiedJobPaginator.count cached property and the
count_disabled branch in UnifiedJobPagination.paginate_queryset
to satisfy SonarCloud's 90% new-code coverage gate.

* AAP-81173 — Address review feedback: save/restore paginator class, format consistency

- Fix Pagination.paginate_queryset() to save/restore django_paginator_class
  instead of hardcoding DjangoPaginator in the finally block. This lets
  subclasses set the class attribute without needing to override the method.
- Remove UnifiedJobPagination.paginate_queryset() override — now only needs
  to set django_paginator_class = UnifiedJobPaginator as a class attribute.
- Format by_org_auditor consistently with the other three UNION branches.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-03 16:27:46 +02:00
Hao LiuandClaude Opus 4.6 9d67c302fd Exclude .tox from flake8 linter scope (#16566)
awxkit has its own tox.ini and when `tox -e linters` runs, awxkit/.tox/
can get populated with a py3 virtualenv. Since flake8 is configured to
scan the `awxkit` directory, it recurses into
awxkit/.tox/py3/lib/python3.12/site-packages/ and reports hundreds of
false positives (F405, E265, E266) from third-party packages like PyYAML.

Adding .tox to the flake8 exclude list prevents this — matching the
existing exclusion of `env` for virtualenvs.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-08-03 09:25:46 +02:00
1e8944676b Suppress SonarQube S7498 for Ansible collection modules (#16532)
dict() with keyword arguments is idiomatic for Ansible module
argument_spec definitions. Suppress the "use literal instead of
constructor" rule (python:S7498) for awx_collection/plugins/modules/.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Jake Jackson <jljacks93@gmail.com>
2026-07-31 13:17:13 -04:00
Peter BraunandLiam Allen bee4470fb9 fix: prefer scm_revision as the cache_id if available (#16564)
* fix: prefer scm_revision as the cache_id if available

* add tests

* fix: make sure galaxy requirements are always updated when a new revision is pulled

* make sure existing project cache id is used when available

---------

Co-authored-by: Liam Allen <lallen@redhat.com>
2026-07-31 09:20:18 +00:00
10f2f11fe2 [devel][forwardport] AAP-83773 — Use unfiltered count for activity stream pagination (#16557)
* AAP-83773 — Use unfiltered count for activity stream pagination

Forward-port of tower#7606 (stable-2.6).

The RBAC-filtered COUNT(*) on activity_stream takes ~36 min per call
on large tables (713K rows) due to the pk__in subquery shape introduced
by the AAP-81860 LEFT JOIN fix.  Replace with an unfiltered table count
for pagination -- an approximate over-count is harmless for UI page
navigation while the actual page results remain RBAC-filtered.

* AAP-83773 — Add unit tests for ActivityStreamPagination

Cover ActivityStreamPaginator and ActivityStreamPagination to satisfy
SonarCloud coverage gate on new lines in awx/api/pagination.py.

* AAP-83773 — Save/restore django_paginator_class in base Pagination

Address review feedback from lallen92: use a save/restore pattern in
Pagination.paginate_queryset() so subclasses only need to set the class
attribute. Remove the now-redundant paginate_queryset override from
ActivityStreamPagination.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Liam Allen <lallen@redhat.com>
2026-07-31 11:16:06 +02:00
Dirk JulichandClaude Opus 4.6 2a28b80ec6 AAP-84057: Set PostgreSQL statement_timeout on web worker DB connections (#16558)
* AAP-84057: Set PostgreSQL statement_timeout on web worker DB connections

When uwsgi's harakiri kills a worker, the PostgreSQL backend continues
running the query indefinitely.  These abandoned queries accumulate and
create resource contention for all other queries.

Add a connection_created signal handler that sets statement_timeout on
new DB connections.  Under uwsgi, the timeout is auto-derived from the
harakiri value (minus 5s margin so PostgreSQL cancels the query before
uwsgi kills the worker).  Outside uwsgi (task workers, migrations),
no timeout is applied.  A manual DATABASE_STATEMENT_TIMEOUT setting
is available as a fallback for non-uwsgi deployments.

* Remove timeout value caching because it brings no significant gains

* Use proportional margin between statement_timeout and harakiri timeout

* Fix zero-harakiri test to patch fake uwsgi module instead of None

* Refactor statement_timeout from signal handler to connection string

Move statement_timeout configuration from a connection_created signal
handler (extra SQL round-trip per connection) to a dynaconf merge
function that sets it via the libpq OPTIONS connection string parameter.
This mirrors the existing merge_application_name() pattern and
eliminates the SET statement on every new connection.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-31 10:27:33 +02:00
Elijah DeLee bf6a5f6b21 AAP-36932 Batch-delete JobHostSummary before cleanup to avoid invalid memory alloc (#16563)
Django's cascade collector materializes all JobHostSummary IDs into a
single UPDATE ... IN (...) to SET_NULL on Host.last_job_host_summary.
With many jobs x hosts this exceeds PostgreSQL's max memory allocation
size for a single query (1GB).

Pre-delete JHS rows and clear Host FK references in chunks of 1000 job
IDs using raw SQL before Django's .delete() runs, so the cascade
collector finds nothing to collect.

Assisted-by: Claude Code via Google Vertex AI
2026-07-30 10:13:35 -04:00
Dirk JulichandClaude Opus 4.6 f1a3e13df7 AAP-83319 — Replace OR with UNION in team list RBAC query (#16554)
The TeamAccess.filtered_queryset() method combined two access paths
(org membership and direct read permission) using Q(…) | Q(…), which
PostgreSQL could not optimize — it scanned all teams for each OR
branch. Replacing the OR with UNION lets each branch use the
RoleEvaluation 3-column index independently. The UNION result is
wrapped in pk__in= so the outer queryset remains compatible with
BaseAccess.get_queryset()'s select_related() call.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-29 20:44:17 +02:00
Rodrigo Toshiaki Horie 78a55b25ec [AAP-82221] Skip reverse sync during post-migrate role definition setup (#16561)
AAP-82221 — Skip reverse sync during post-migrate role definition setup

The dab_post_migrate signal handler added in 64dc097914 calls
setup_managed_role_definitions on every awx-manage migrate run.
During fresh installs this deletes stale managed role definitions
and triggers a reverse sync to the gateway, which returns 423
Locked because migrate_service_data has not finished yet. The
installer retries 5 times but each attempt fires the same signal
and hits the same 423, failing the controller init.
2026-07-27 15:25:03 -03:00
54e5c948fe AAP-76460 Prioritize REDHAT_CANDLEPIN_HOST over rhsm.conf for subscription validation (#16543)
* AAP-76460 — Prioritize REDHAT_CANDLEPIN_HOST over rhsm.conf for subscription validation

When REDHAT_CANDLEPIN_HOST is explicitly configured (Satellite/disconnected
environments), use it as the subscription host instead of falling back to
rhsm.conf. This fixes subscription loading failures in containerized
deployments where rhsm.conf defaults to subscription.rhsm.redhat.com but
the system cannot reach it.

Also guard against double port-appending in get_satellite_subs when the
host URL already includes a port.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Liam Allen <lallen@redhat.com>

* Optimize HostList API: conditional DISTINCT + composite index on JobHostSummary (#16530)

AAP-81517 — Optimize HostList API: conditional DISTINCT + composite index

Make .distinct() conditional on host_filter being set — without it the
RBAC IN subquery on a direct FK cannot produce duplicates, so DISTINCT
is pure overhead. Add composite index (host_id, id DESC) on
main_jobhostsummary so the with_latest_summary_id() correlated subquery
can use an index-only top-1 scan instead of scanning and sorting.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Fix: Increase awx-operator molecule timeout to 20m (#16534)

The awx-operator molecule kind test intermittently times out after
15 minutes on GitHub Actions runners, causing flaky CI failures.
Bump the bash-level timeout from 15m to 20m (step-level
timeout-minutes: 60 is unchanged).

Closes: AAP-81583

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* AAP-81860 — Eliminate LEFT OUTER JOINs in ActivityStream RBAC query (#16533)

* AAP-81860 — Eliminate LEFT OUTER JOINs in ActivityStream RBAC query

ActivityStreamAccess.filtered_queryset() used field-traversal Q objects
(e.g. Q(host__inventory__in=...)) across 18 M2M relationships, which
Django translates into LEFT OUTER JOINs. This forces PostgreSQL to join
all intermediary tables before filtering, making it the #2 DB consumer
at 483s in a 10-minute Scale Lab window.

Replace all M2M field traversals with pk__in subqueries using .through
intermediary tables, following the same pattern proven in AAP-81082
(28% improvement for unified job RBAC). This changes the SQL from
LEFT OUTER JOIN to IN (SELECT ...) semi-joins, allowing PostgreSQL to
skip the unconditional joins. Also:

- Remove .distinct() (no longer needed without M2M JOINs)
- Remove unnecessary if-truthy checks that evaluated subqueries as
  boolean before including them (5 extra DB roundtrips per request)
- Migrate accessible_pk_qs(user, 'read_role') to access_ids_qs(user,
  'view') for direct DAB RBAC API usage

* Use .exists() instead of queryset truth-test for auditing_orgs guard

Avoids evaluating the full queryset when checking whether the user has
auditing orgs — .exists() issues a cheaper SELECT 1 … LIMIT 1 instead.

* Restore .exists() guards for resource-type Q branches

Re-add conditional guards around inventory, credential, project,
job template, workflow job template, and team Q branches. Uses
.exists() (cheap SELECT 1 LIMIT 1) instead of the old queryset
truth-test to avoid unnecessary query evaluation while still
preventing generation of an overly complex query when the user
has no access to a given resource type.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* chore: Upgrade kubernetes package to 36.0.2

* feat: remove extra_vars from jobs and unified_jobs list endpoint. Add… (#16461)

feat: add exclude query parameter to unified_jobs and jobs list endpoints

Allow clients to exclude heavy fields (artifacts, extra_vars) from list
responses via ?exclude=artifacts,extra_vars. These fields are now included
by default instead of being stripped.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* AAP-80457 - Fix credential_input_source to handle state: absent when target crede… (#16523)

Fix credential_input_source to handle state: absent when target credential is missing

The credential_input_source module would error when trying to delete a
credential input source (state: absent) if the target credential didn't
exist. This breaks idempotent playbook runs where cleanup tasks assume
resources may already be gone.

The fix only applies to state: absent; state: present still correctly
fails when the target credential doesn't exist.

Co-authored-by: Liam Allen <lallen@redhat.com>

* Add additional test coverage

* get_satellite_subs() reads verify from rhsm.conf, never from REDHAT_CANDLEPIN_VERIFY.

* Address coderabbit feedback

---------

Signed-off-by: Liam Allen <lallen@redhat.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Dirk Julich <djulich@redhat.com>
Co-authored-by: Rodrigo Toshiaki Horie <rodrigo.horie@hotmail.com>
Co-authored-by: Adrià Sala <22398818+adrisala@users.noreply.github.com>
Co-authored-by: Peter Braun <pbraun@redhat.com>
Co-authored-by: Nick Meyer <nick.a.meyer@icloud.com>
2026-07-27 14:24:44 +01:00
tvo 8812569f92 Add stable branch-cut checklist reference to release process docs (#16535) 2026-07-22 16:10:01 -06:00
Lila Yasin 64dc097914 [AAP-82221] Add member_organization to specialized Organization *Admin roles (#16545) 2026-07-22 15:25:43 -04:00
fcc1aa56d1 Balance controller node selection during capacity ties (#16417)
* Balance controller node selection during capacity ties

When multiple control-plane instances have equal remaining capacity,
the instance selection algorithm always picks the first instance in
iteration order. This causes burst workloads to concentrate all job
management on a single controller pod while others remain idle.

Add a tie-breaking criterion: when would_be_remaining capacity is
equal, prefer the instance with fewer jobs_running. This distributes
the control overhead (event processing, callbacks, output streaming)
more evenly across available controller pods during burst scenarios.

The change is backwards-compatible: when capacity clearly differs,
the existing "most remaining capacity" logic dominates unchanged.

Signed-off-by: Alexey Masolov <amasolov@redhat.com>
Signed-off-by: Alexey Masolov <alexey.masolov@gmail.com>
Made-with: Cursor

* Track jobs_running on controller node for container-group tasks

Address review feedback: container-group tasks only set controller_node
(execution_node is empty), so the previous consume_capacity call on the
control path never incremented jobs_running. This meant the tie-breaker
could not differentiate between nodes.

Now pass job_impact=True on the control path when the controller is not
also the execution node (avoiding double-counting for hybrid nodes).

Also improve test coverage:
- Fix test case to prove capacity dominates over jobs_running
- Add dedicated tests for container-group controller distribution
- Add test verifying equal-capacity tie-breaking behaviour

Signed-off-by: Alexey Masolov <amasolov@redhat.com>
Signed-off-by: Alexey Masolov <alexey.masolov@gmail.com>
Made-with: Cursor

* Add docstrings to consume_capacity and fit_task_to_most_remaining_capacity_instance

Address CodeRabbit docstring coverage warning by documenting the two
methods modified in this PR. No functional changes.

Signed-off-by: Alexey Masolov <amasolov@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Signed-off-by: Alexey Masolov <amasolov@redhat.com>
Signed-off-by: Alexey Masolov <alexey.masolov@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Liam Allen <lallen@redhat.com>
2026-07-21 15:46:47 +01:00
Peter Braun d8e7a711d0 fix: delete inventory hosts in batches to avoid memory exhaustion (#16547)
* fix: delete inventory hosts in batches to avoid memory exhaustion

* update tests

* fix failing test
2026-07-21 11:38:44 +00:00
9acf3d1887 AAP-73393 - Remove skip_tags character limit by converting to TextField (#16552)
* Optimize HostList API: conditional DISTINCT + composite index on JobHostSummary (#16530)

AAP-81517 — Optimize HostList API: conditional DISTINCT + composite index

Make .distinct() conditional on host_filter being set — without it the
RBAC IN subquery on a direct FK cannot produce duplicates, so DISTINCT
is pure overhead. Add composite index (host_id, id DESC) on
main_jobhostsummary so the with_latest_summary_id() correlated subquery
can use an index-only top-1 scan instead of scanning and sorting.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Fix: Increase awx-operator molecule timeout to 20m (#16534)

The awx-operator molecule kind test intermittently times out after
15 minutes on GitHub Actions runners, causing flaky CI failures.
Bump the bash-level timeout from 15m to 20m (step-level
timeout-minutes: 60 is unchanged).

Closes: AAP-81583

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* AAP-81860 — Eliminate LEFT OUTER JOINs in ActivityStream RBAC query (#16533)

* AAP-81860 — Eliminate LEFT OUTER JOINs in ActivityStream RBAC query

ActivityStreamAccess.filtered_queryset() used field-traversal Q objects
(e.g. Q(host__inventory__in=...)) across 18 M2M relationships, which
Django translates into LEFT OUTER JOINs. This forces PostgreSQL to join
all intermediary tables before filtering, making it the #2 DB consumer
at 483s in a 10-minute Scale Lab window.

Replace all M2M field traversals with pk__in subqueries using .through
intermediary tables, following the same pattern proven in AAP-81082
(28% improvement for unified job RBAC). This changes the SQL from
LEFT OUTER JOIN to IN (SELECT ...) semi-joins, allowing PostgreSQL to
skip the unconditional joins. Also:

- Remove .distinct() (no longer needed without M2M JOINs)
- Remove unnecessary if-truthy checks that evaluated subqueries as
  boolean before including them (5 extra DB roundtrips per request)
- Migrate accessible_pk_qs(user, 'read_role') to access_ids_qs(user,
  'view') for direct DAB RBAC API usage

* Use .exists() instead of queryset truth-test for auditing_orgs guard

Avoids evaluating the full queryset when checking whether the user has
auditing orgs — .exists() issues a cheaper SELECT 1 … LIMIT 1 instead.

* Restore .exists() guards for resource-type Q branches

Re-add conditional guards around inventory, credential, project,
job template, workflow job template, and team Q branches. Uses
.exists() (cheap SELECT 1 LIMIT 1) instead of the old queryset
truth-test to avoid unnecessary query evaluation while still
preventing generation of an overly complex query when the user
has no access to a given resource type.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* chore: Upgrade kubernetes package to 36.0.2

* feat: remove extra_vars from jobs and unified_jobs list endpoint. Add… (#16461)

feat: add exclude query parameter to unified_jobs and jobs list endpoints

Allow clients to exclude heavy fields (artifacts, extra_vars) from list
responses via ?exclude=artifacts,extra_vars. These fields are now included
by default instead of being stripped.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* AAP-80457 - Fix credential_input_source to handle state: absent when target crede… (#16523)

Fix credential_input_source to handle state: absent when target credential is missing

The credential_input_source module would error when trying to delete a
credential input source (state: absent) if the target credential didn't
exist. This breaks idempotent playbook runs where cleanup tasks assume
resources may already be gone.

The fix only applies to state: absent; state: present still correctly
fails when the target credential doesn't exist.

Co-authored-by: Liam Allen <lallen@redhat.com>

* AAP-83163 — Eliminate LEFT JOIN fan-out in user list RBAC query (#16546)

Replace legacy Role M2M traversal in UserAccess.filtered_queryset()
with DAB's visible_users(), which queries organizational membership
through RoleUserAssignment subqueries instead of LEFT OUTER JOINs
through the objectrole/roleevaluation tables.

Also replace role__in=actor.has_roles.all() in can_admin() with
RoleEvaluation._actor_role_filter() to avoid the objectrole JOIN
in per-object permission checks.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* alter_skip_tags_to_textfield

* Bump migration number

---------

Co-authored-by: Dirk Julich <djulich@redhat.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Rodrigo Toshiaki Horie <rodrigo.horie@hotmail.com>
Co-authored-by: Adrià Sala <22398818+adrisala@users.noreply.github.com>
Co-authored-by: Peter Braun <pbraun@redhat.com>
Co-authored-by: Nick Meyer <nick.a.meyer@icloud.com>
2026-07-21 10:00:21 +01:00
Dirk JulichandClaude Opus 4.6 7a7a6224c0 AAP-82745 — Replace O(N) correlated subqueries in org list with bulk aggregation (#16548)
* AAP-82745 — Replace O(N) correlated subqueries in org list with bulk aggregation

The org list endpoint computed per-org user and admin counts using two
correlated subqueries against roleuserassignment — one per org per role
type. At scale this was the #1 worst query in the customer DB (677ms
mean, 336K calls, 3,801 min total DB time).

Replace with a single flat conditional-aggregation query that scans
roleuserassignment once regardless of org count. Also simplify the
detail view to use direct .count() calls and add a query count
regression test.

* Remove dead code: org_counts is always populated after refactor

* Address review feedback on detail view and test

- Detail view: combine two .count() calls into single .aggregate()
  with conditional counts, matching the list-view pattern
- Use .update() in else branch for consistency with if branch
- Remove redundant setup_managed_roles fixture (transitive via
  organization_resource_creator)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-20 15:41:20 +02:00
Dirk JulichandClaude Opus 4.6 ea14ee1563 AAP-83163 — Eliminate LEFT JOIN fan-out in user list RBAC query (#16546)
Replace legacy Role M2M traversal in UserAccess.filtered_queryset()
with DAB's visible_users(), which queries organizational membership
through RoleUserAssignment subqueries instead of LEFT OUTER JOINs
through the objectrole/roleevaluation tables.

Also replace role__in=actor.has_roles.all() in can_admin() with
RoleEvaluation._actor_role_filter() to avoid the objectrole JOIN
in per-object permission checks.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-17 19:24:13 +02:00
Nick MeyerandLiam Allen 354fa35860 AAP-80457 - Fix credential_input_source to handle state: absent when target crede… (#16523)
Fix credential_input_source to handle state: absent when target credential is missing

The credential_input_source module would error when trying to delete a
credential input source (state: absent) if the target credential didn't
exist. This breaks idempotent playbook runs where cleanup tasks assume
resources may already be gone.

The fix only applies to state: absent; state: present still correctly
fails when the target credential doesn't exist.

Co-authored-by: Liam Allen <lallen@redhat.com>
2026-07-09 13:42:06 +01:00
Peter BraunandClaude Opus 4.6 d0576d7823 feat: remove extra_vars from jobs and unified_jobs list endpoint. Add… (#16461)
feat: add exclude query parameter to unified_jobs and jobs list endpoints

Allow clients to exclude heavy fields (artifacts, extra_vars) from list
responses via ?exclude=artifacts,extra_vars. These fields are now included
by default instead of being stripped.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-07-08 17:05:41 +02:00
Adrià Sala 9d5bf22f90 chore: Upgrade kubernetes package to 36.0.2 2026-07-08 16:02:07 +02:00
Dirk JulichandClaude Opus 4.6 f3b04125a6 AAP-81860 — Eliminate LEFT OUTER JOINs in ActivityStream RBAC query (#16533)
* AAP-81860 — Eliminate LEFT OUTER JOINs in ActivityStream RBAC query

ActivityStreamAccess.filtered_queryset() used field-traversal Q objects
(e.g. Q(host__inventory__in=...)) across 18 M2M relationships, which
Django translates into LEFT OUTER JOINs. This forces PostgreSQL to join
all intermediary tables before filtering, making it the #2 DB consumer
at 483s in a 10-minute Scale Lab window.

Replace all M2M field traversals with pk__in subqueries using .through
intermediary tables, following the same pattern proven in AAP-81082
(28% improvement for unified job RBAC). This changes the SQL from
LEFT OUTER JOIN to IN (SELECT ...) semi-joins, allowing PostgreSQL to
skip the unconditional joins. Also:

- Remove .distinct() (no longer needed without M2M JOINs)
- Remove unnecessary if-truthy checks that evaluated subqueries as
  boolean before including them (5 extra DB roundtrips per request)
- Migrate accessible_pk_qs(user, 'read_role') to access_ids_qs(user,
  'view') for direct DAB RBAC API usage

* Use .exists() instead of queryset truth-test for auditing_orgs guard

Avoids evaluating the full queryset when checking whether the user has
auditing orgs — .exists() issues a cheaper SELECT 1 … LIMIT 1 instead.

* Restore .exists() guards for resource-type Q branches

Re-add conditional guards around inventory, credential, project,
job template, workflow job template, and team Q branches. Uses
.exists() (cheap SELECT 1 LIMIT 1) instead of the old queryset
truth-test to avoid unnecessary query evaluation while still
preventing generation of an overly complex query when the user
has no access to a given resource type.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-07 16:52:11 +02:00
Rodrigo Toshiaki HorieandClaude Opus 4.6 1bd07b981a Fix: Increase awx-operator molecule timeout to 20m (#16534)
The awx-operator molecule kind test intermittently times out after
15 minutes on GitHub Actions runners, causing flaky CI failures.
Bump the bash-level timeout from 15m to 20m (step-level
timeout-minutes: 60 is unchanged).

Closes: AAP-81583

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-07-06 19:51:21 +00:00
Dirk JulichandClaude Opus 4.6 41545cfcf0 Optimize HostList API: conditional DISTINCT + composite index on JobHostSummary (#16530)
AAP-81517 — Optimize HostList API: conditional DISTINCT + composite index

Make .distinct() conditional on host_filter being set — without it the
RBAC IN subquery on a direct FK cannot produce duplicates, so DISTINCT
is pure overhead. Add composite index (host_id, id DESC) on
main_jobhostsummary so the with_latest_summary_id() correlated subquery
can use an index-only top-1 scan instead of scanning and sorting.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-06 17:01:20 +02:00
Seth FosterandClaude Opus 4.6 72a1922a9c Replace realistic looking credentials in subscriptions module examples (#16531)
Replace hardcoded credentials in subscriptions module examples

The EXAMPLES block contained values that looked like real client
credentials. Replace them with obvious placeholders and add a
username/password example.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-02 15:52:26 -04:00
Dirk Julich f8fa690de3 AAP-81082 — Eliminate LEFT OUTER JOINs in unified job RBAC query (#16527)
* AAP-81082 Eliminate LEFT OUTER JOINs in unified job RBAC query

UnifiedJobAccess.filtered_queryset() used field-traversal Q objects
(inventoryupdate__inventory_source__inventory__id__in=...) which Django
translates into LEFT OUTER JOINs. This forces PostgreSQL to scan all
rows in main_unifiedjob before filtering — at scale, 99.84% are
discarded. Replace with pk__in subqueries that generate IN (SELECT ...)
instead, allowing PostgreSQL to skip the unconditional join. EXPLAIN
ANALYZE shows a 28% reduction in execution time (496ms -> 355ms), with
larger gains expected under concurrent load.

* AAP-81082 Eliminate LEFT OUTER JOINs in RBAC filtered_queryset methods

Replace field-traversal Q objects with pk__in subqueries across all Access
classes that query polymorphic or M2M tables, preventing Django from
generating unconditional LEFT OUTER JOINs. Also migrate legacy
_accessible_pk_qs / accessible_pk_qs calls to DAB RBAC access_ids_qs.

Affected: UnifiedJobAccess, UnifiedJobTemplateAccess, JobAccess,
JobEventAccess, LabelAccess.

* Fix docstring
2026-07-01 13:18:09 +02:00
Hamzah Yousuf 8ab5deb54a FIX: Refactor formatted raw SQL in unified_jobs result_stdout_raw_handle (#16522)
* Refactor result_stdout_raw_handle to use parameterized COPY SQL.
Replace f-string SQL construction with psycopg.sql composables and bound
parameters so security scans no longer flag formatted raw SQL in the
unified jobs stdout path.
Fix sqlite_copy mock rendering for psycopg3 SQL composables.

* Fix sqlite_copy mock without psycopg SQL internals.
Load stdout from the first populated event table instead of rendering
psycopg composables, which use version-specific private attributes.

* Use sql.Literal in COPY query for Django cursor.copy compatibility.
Django's cursor.copy() does not forward bind parameters to psycopg,
which caused stdout API 500s against real PostgreSQL.
2026-06-25 13:49:22 +00:00
Rodrigo Toshiaki HorieandClaude Opus 4.6 843f23f4cb Fix SonarCloud security rating: remove user-controlled data from sqlite filepath (#16516)
* Fix SonarCloud security rating by removing user-controlled data from sqlite filepath

Replace os.path.basename(sys.argv[0]) with a hardcoded 'unknown' fallback
in RecordedQueryLog.write() to eliminate path injection via CLI arguments.
This resolves SonarCloud rule pythonsecurity:S8706 and helps restore the
AWX security rating from C to A.

Closes: AAP-80006

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Remove unused sys import from test_db.py

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-25 10:08:54 -03:00
Lila YasinandClaude Opus 4.6 6d665dda33 Fix awxkit tox test failure caused by root pytest.ini config bleed (#16520)
Forward-port of ansible/tower#7537 for the devel branch.

When running awxkit's tox tests, pytest picks up the root pytest.ini
which pulls in pytest-django options (--reuse-db, --nomigrations,
DJANGO_SETTINGS_MODULE) and Django-specific filterwarnings.  Since the
awxkit tox environment does not install Django or pytest-django, these
cause test collection to fail.

The root cause is that pytest.ini has absolute priority in pytest's
config discovery — it searches all ancestor directories for pytest.ini
before falling back to tox.ini's [pytest] section.  A [pytest] section
in awxkit/tox.ini alone cannot prevent the root config from being used.

Fix by:
- Adding awxkit/pytest.ini to act as the primary config boundary
  (pytest.ini has the highest priority in config discovery, so its
  presence in awxkit/ stops the upward search before reaching root)
- Adding explicit `test` path argument to the pytest command in
  awxkit/tox.ini so pytest discovers tests correctly
- Adding `testpaths` and `python_files` to the [pytest] section in
  awxkit/tox.ini as a secondary config boundary
- Adding awxkit/conftest.py that registers the Django-specific CLI
  options and INI keys as harmless no-ops, as a further safety net

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-24 10:42:23 -04:00
Stevenson Michel 17dc7f898a Fix: handle cursor-paginated responses in get_one() (#16521)
* Fix: handle cursor-paginated API responses in get_one()

DAB PR #1025 switched role_team_assignments and role_user_assignments
endpoints from PageNumberPagination to CursorPagination. Cursor
pagination returns {results, next, previous} without a count field,
causing get_one() to fail with "The endpoint did not provide count
and results".

When the response includes results but no count, infer count from
len(results). Also guard get_all_endpoint() against missing count.
2026-06-24 09:56:26 -04:00
Seth FosterandClaude Opus 4.6 f25e436bef Do not mark node READY when health check reports zero cpu or memory (#16511)
Instances reporting cpu=0 or memory=0 with no errors would be
transitioned to READY state. Treat zero cpu/memory as an error
so the node stays offline until a valid health check is received.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-22 10:23:52 -04:00
Dave 67048a609a feat(analytics): log request_id, account_number, org_id from ingress API response (#16512)
* feat(analytics): ANSTRAT-2268 log request_id, account_number, org_id from ingress API response

Log the ingress API success response fields (request_id, account_number,
org_id) in the controller task log when gather_analytics tarballs are
uploaded. This enables support engineers to trace uploads through to
Kibana without source code modifications.

* style(analytics): use f-strings in _log_shipping_response to match codebase conventions

* style(analytics): fix black formatting in test assertions
2026-06-22 10:03:57 -04:00