Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
188 commits
Select commit Hold shift + click to select a range
67e973a
Merge pull request #1 from rhpds/main
stencell May 15, 2026
2a51e4f
Update BACKLOG.md
stencell May 15, 2026
e60d143
rcars: Add infrastructure-aware catalog metadata (Session 1)
stencell Jun 12, 2026
26fce05
rcars: Sync workloads and ACL groups in CLI refresh command
stencell Jun 12, 2026
acc9d94
rcars: Move alembic into src/api/ and wire up Ansible migrate task
stencell Jun 12, 2026
4a1c955
docs: Update alembic references after move to src/api/
stencell Jun 12, 2026
dfa4592
rcars: Fix alembic env.py to use SQLAlchemy engine with psycopg3
stencell Jun 12, 2026
613ca77
rcars: Add faceted search API, workload scanner, pipeline integration…
stencell Jun 12, 2026
bb784d1
docs: Add infrastructure metadata docs, move schema to reference page
stencell Jun 12, 2026
edcc8c7
rcars: Add infrastructure metadata to Browse and Admin UI (Session 3)
stencell Jun 12, 2026
2465026
rcars: Update backlog and worklog for 2026-06-12 session
stencell Jun 12, 2026
695cdef
build(deps): bump esbuild, @vitejs/plugin-react and vite
dependabot[bot] Jun 14, 2026
5d55026
docs: Add Browse page redesign spec and contextual sidebar backlog item
stencell Jun 15, 2026
e9e4f27
docs: Add Browse page redesign implementation plan
stencell Jun 15, 2026
5264637
db: Add list_catalog_items_filtered with server-side search and infra…
stencell Jun 15, 2026
aa56b6f
catalog: Extend GET /catalog with search, stage, infra, and content_f…
stencell Jun 15, 2026
cf1f637
frontend: Add filter components, pagination, and updated API client
stencell Jun 15, 2026
ddad0f9
browse: Rewrite with server-side filtering, collapsible panel, and pa…
stencell Jun 15, 2026
093e5f2
browse: Fix workload dropdown clipped by filter panel overflow
stencell Jun 15, 2026
a0686ca
admin: Reorganize Catalog page with stat cards and mapping management
stencell Jun 15, 2026
efda9f0
admin: Add tabbed navigation to Catalog page
stencell Jun 15, 2026
b3462fc
admin: Fix maintenance pipeline description to include workload scan …
stencell Jun 15, 2026
3899367
admin: Merge Workers page into Sync & Analysis tab
stencell Jun 15, 2026
5599bab
docs: Update BACKLOG and WORKLOG for 2026-06-15 session
stencell Jun 15, 2026
b54b49a
recommender: Fix case-insensitive acronym matching in query expansion
stencell Jun 15, 2026
2377639
frontend: Fix rec card copy/paste by scoping click handler to header
stencell Jun 15, 2026
18fc518
database: Add curated_duration_min column to showroom_analysis
stencell Jun 15, 2026
352ec2e
catalog: Add curator endpoint for curated duration
stencell Jun 15, 2026
ede7d12
recommender: Thread duration_source through pipeline and serialization
stencell Jun 15, 2026
fbda7b1
frontend: Add duration labels to recommendation cards
stencell Jun 15, 2026
c80f1e5
frontend: Redesign Best Fit button with bold outline treatment
stencell Jun 15, 2026
48289ee
browse: Add curator duration input field
stencell Jun 15, 2026
04e6a30
docs: Add spec and plan for rec card duration + best fit changes
stencell Jun 15, 2026
2be4287
recommender: Fix dataclass field ordering for duration_source
stencell Jun 15, 2026
ae1e8a9
browse: Show duration source label in analysis summary
stencell Jun 15, 2026
974421f
frontend: Improve no-results message to suggest adding more context
stencell Jun 15, 2026
48b9f7b
prompts: Fix overall_assessment formatting — one pick per line, no label
stencell Jun 15, 2026
4a5591d
frontend: Strip Response label and ensure section breaks in assessment
stencell Jun 15, 2026
6525841
ansible: Make recommend worker replicas configurable
stencell Jun 15, 2026
8857175
nginx: Enable HTTP/1.1 upstream for concurrent SSE connections
stencell Jun 15, 2026
1e813aa
recommender: Run sync LLM calls in thread pool for concurrent queries
stencell Jun 15, 2026
21ad911
docs: Update CLAUDE.md, WORKLOG, and BACKLOG for duration + concurren…
stencell Jun 15, 2026
17a2ce7
docs: Update web guide and operations for duration labels and concurr…
stencell Jun 15, 2026
35390c8
rcars: Add content overlap detection for duplicate lab identification
stencell Jun 15, 2026
bbc32fc
docs: Add dev deployment testing guideline to CLAUDE.md
stencell Jun 15, 2026
50fc63b
docs: Document content overlap detection feature
stencell Jun 15, 2026
ec42856
rcars: Exclude sibling pairs from overlap detection, add --tags update
stencell Jun 15, 2026
4a55cb7
rcars: Deduplicate by content_hash before computing similarity
stencell Jun 15, 2026
01ee7ba
rcars: Add compute-similarity CLI command, remove alert popup
stencell Jun 15, 2026
491791f
rcars: Fix URL-based dedup, update all user-facing documentation
stencell Jun 15, 2026
2f93efa
rcars: Two-pass dedup for overlap — URL grouping + content_hash bridging
stencell Jun 15, 2026
64aab33
rcars: Simplify overlap to prod-vs-prod only
stencell Jun 15, 2026
67655aa
rcars: Add stage selector to overlap detection
stencell Jun 15, 2026
d13a36b
rcars: Move Overlap to new Content Analysis nav section
stencell Jun 15, 2026
2cc8660
docs: Comprehensive overlap detection documentation
stencell Jun 15, 2026
811d27f
docs: Update WORKLOG and BACKLOG for overlap detection session
stencell Jun 15, 2026
8ba7bb6
docs: Add retirement analysis integration spec and implementation plan
stencell Jun 15, 2026
bbec942
docs: Update BACKLOG and WORKLOG for retirement analysis session
stencell Jun 15, 2026
f7bb455
rcars: Remediate code review findings across API, frontend, and docs
stencell Jun 15, 2026
de0e6c9
rcars: Clean up recommendation card layout
stencell Jun 15, 2026
33065b3
rcars: Restructure RecCard layout for clarity
stencell Jun 15, 2026
e21883b
rcars: Unify RecCard layout with consistent two-column rows
stencell Jun 15, 2026
8b13220
docs: Update WORKLOG for code review + RecCard cleanup session
stencell Jun 15, 2026
90bff6c
Merge pull request #8 from rhpds/main
stencell Jun 15, 2026
58eb07c
ansible: Move TokenReview binding to mgmt-rbac bootstrap
stencell Jun 15, 2026
50a368a
ansible: Fix migration race condition during rollouts
stencell Jun 15, 2026
4833b2a
frontend: Upgrade Node.js base image from 20 to 22 LTS
stencell Jun 16, 2026
b144fa5
Merge pull request #7 from rhpds/dependabot/npm_and_yarn/src/frontend…
stencell Jun 16, 2026
5d6cb5e
build(deps): bump react-router and react-router-dom in /src/frontend
dependabot[bot] Jun 16, 2026
f8319a6
build(deps-dev): bump js-yaml from 4.1.1 to 4.2.0 in /src/frontend
dependabot[bot] Jun 16, 2026
55c6216
Merge pull request #9 from rhpds/dependabot/npm_and_yarn/src/frontend…
stencell Jun 16, 2026
245f4f6
Merge pull request #11 from rhpds/dependabot/npm_and_yarn/src/fronten…
stencell Jun 16, 2026
0dac87d
ansible: Fix migration pod lookup using wrong label selector
stencell Jun 16, 2026
127ed35
recommender: Add User-Agent header to event page fetcher
stencell Jun 16, 2026
b58eb69
Merge pull request #12 from rhpds/main
stencell Jun 16, 2026
802f378
rcars: Add retirement analysis integration with RHDP reporting data
stencell Jun 16, 2026
221ac2a
rcars: Simplify content format labels to demo and hands-on lab
stencell Jun 16, 2026
781b0a6
ansible: Add reporting MCP env vars to scan worker deployment
stencell Jun 16, 2026
cc1c8b2
frontend: Add search filter to content overlap page
stencell Jun 16, 2026
f5d99a5
rcars: Fix pipeline step labels and add babydev migration to backlog
stencell Jun 16, 2026
6ef2725
rcars: Fix reporting sync KeyError and clarify pipeline messages
stencell Jun 16, 2026
c992d49
rcars: Add LiteMaaS as primary LLM provider with Vertex fallback
stencell Jun 16, 2026
fbe42cf
frontend: Redesign retirement page with sticky headers and proper tab…
stencell Jun 16, 2026
89f69de
frontend: Unify Content Analysis pages with shared design system
stencell Jun 16, 2026
38a3657
admin: Simplify reporting sync card to status, items, orphans, last s…
stencell Jun 16, 2026
f83905d
frontend: Fix retirement table width, number formatting, and name links
stencell Jun 16, 2026
c9058c9
frontend: Full overlap summaries, new-tab Browse links, wider name co…
stencell Jun 16, 2026
6c05b03
frontend: Default retirement sort to lowest score first
stencell Jun 16, 2026
19ab46a
backlog: Add time window filter to retirement scoring item
stencell Jun 16, 2026
d4a0aeb
database: Guard provider index in SCHEMA_SQL against missing column
stencell Jun 16, 2026
e2e8905
rcars: Code review remediation — secrets, defensive checks, cleanup
stencell Jun 16, 2026
ebebda1
Merge pull request #13 from rhpds/main
stencell Jun 16, 2026
6be7f73
docs: Update WORKLOG and BACKLOG for 2026-06-16 session
stencell Jun 16, 2026
82ed2bb
backlog: Add migration race condition bug
stencell Jun 16, 2026
99709ab
reporting_sync: Fix sales date filter to use opportunity close date
stencell Jun 17, 2026
307014c
cli: Fix sales_rows KeyError in reporting-db sync output
stencell Jun 17, 2026
d832257
admin: Improve LLM Provider and Reporting Sync status cards
stencell Jun 17, 2026
d242a98
admin: Rename Analysis/Scanning to Content scan/Workload scan
stencell Jun 17, 2026
f936f8e
admin: Fix LLM model labels — Analysis, Triage, Rationale
stencell Jun 17, 2026
6f4e1cf
reporting_sync: Filter to PROD env + real users only
stencell Jun 17, 2026
8dbabc9
ansible: Bump API memory to 1Gi/4Gi to prevent OOM on sync
stencell Jun 17, 2026
bb38efa
reporting_sync: Switch to provisions_summary materialized view
stencell Jun 17, 2026
57dc038
retirement: Percentile scoring, exclusions, Prod/Without Prod tabs
stencell Jun 17, 2026
6d52810
docs: Break system-design.md into focused architecture pages
stencell Jun 17, 2026
7109b83
docs: Rewrite overview to reflect current RCARS capabilities
stencell Jun 17, 2026
4cd2e3b
docs: Fix incorrect claim that only prod namespace is synced
stencell Jun 17, 2026
f9e3026
docs: Clarify that RCARS tracks both Showroom and workload data
stencell Jun 17, 2026
6ec61cb
docs: Add infrastructure metadata to Catalog Reader extraction list
stencell Jun 17, 2026
696437f
docs: Fix 'Zero Touch Virtual CIs' to 'ZTE items'
stencell Jun 17, 2026
c358025
docs: Simplify to 'Zero Touch items'
stencell Jun 17, 2026
c59e3a4
docs: Clarify workload extraction vs scanner distinction
stencell Jun 17, 2026
fddf3fe
docs: Expand vector embeddings section with cross-page links
stencell Jun 17, 2026
917e707
docs: Replace schema tables/ER diagram with focused embeddings section
stencell Jun 17, 2026
44a09db
docs: Clarify worker scaling and add reporting sync to pipeline
stencell Jun 17, 2026
8a005ee
retirement: Fix stat card counts and make Without Prod clickable
stencell Jun 17, 2026
9e77b18
retirement: Add age filter pills to Without Prod tab
stencell Jun 17, 2026
497187b
retirement: Remove unused summary state (TS6133 build error)
stencell Jun 17, 2026
f820e69
docs: Remove local dev auth bypass from system design
stencell Jun 17, 2026
b388aa5
docs: Add introductions to architecture pages, fix scan diagram
stencell Jun 17, 2026
92ed4d1
docs: Rewrite Step 4 with actual prompt fields and example output
stencell Jun 17, 2026
54622b8
docs: Rewrite Step 6 embeddings with clear explanation and structure
stencell Jun 17, 2026
e99a60f
docs: Move dedup after Step 7, add change detection section
stencell Jun 17, 2026
3eebc28
docs: Rewrite recommendation engine with expanded explanations
stencell Jun 17, 2026
7fabc42
docs: Clarify triage vs rationale data, format acronym table
stencell Jun 17, 2026
84b0e2a
backlog: Add lower overlap threshold item for broader detection
stencell Jun 17, 2026
339fc83
docs: Explain 75-point max and add three scoring examples
stencell Jun 17, 2026
4a8c3f6
docs: Add config, CLI, and API reference to architecture pages
stencell Jun 17, 2026
f7c0f0a
docs: Update WORKLOG for 2026-06-17 session handoff
stencell Jun 17, 2026
0ce9313
reporting_sync: Fix stale scoring by cleaning items not in current sync
stencell Jun 17, 2026
79982b2
reporting_sync: Also remove non-catalog items during orphan cleanup
stencell Jun 17, 2026
646884e
retirement: Add time window filter with per-quarter breakdowns
stencell Jun 17, 2026
e06aef4
analysis: Fix sort key for display_name and clean up import
stencell Jun 17, 2026
7508d71
reporting_sync: Fix cost quarterly query timeout
stencell Jun 17, 2026
e16bcf8
reporting_sync: Increase timeout for cost quarterly query
stencell Jun 17, 2026
d3174be
reporting_sync: Handle date objects in retirement score age calculation
stencell Jun 17, 2026
7e9dbb6
docs: Update worklog and backlog for scoring fix + time window filter
stencell Jun 17, 2026
fe56f3d
retirement: Aggressive scoring, include all catalog items, window fil…
stencell Jun 17, 2026
18cee90
retirement: Backfill catalog items and restore catalog filter
stencell Jun 17, 2026
9ceaf59
database: Fix dict_row access in get_catalog_base_names
stencell Jun 17, 2026
c2e06a3
retirement: Remove activity filter — all current catalog items always…
stencell Jun 17, 2026
e3fb780
docs: Update worklog and backlog for full retirement session
stencell Jun 17, 2026
3e64fd2
retirement: Fix code review findings — COALESCE, test params, sort UX
stencell Jun 17, 2026
054fc71
Merge pull request #14 from rhpds/main
stencell Jun 17, 2026
6c32c73
docs: Rewrite README with current capabilities and doc links
stencell Jun 17, 2026
d2f2741
ansible: Fix migration race condition and reporting MCP on API pod
stencell Jun 17, 2026
9179420
ansible: Replace k8s_info rollout checks with oc rollout status
stencell Jun 17, 2026
9a4713e
reporting_sync: Amortize all-environment costs across prod provisions
stencell Jun 18, 2026
2ed1764
docs: Document cost methodology, time windows, and catalog backfill
stencell Jun 18, 2026
b883636
backlog: Triage and reorganize — clear completed, set next priorities
stencell Jun 18, 2026
d5f47d7
admin: Replace score breakdown with data coverage on Reporting Sync card
stencell Jun 18, 2026
bb74b38
backlog: Add soft-delete as top priority, reorder active items
stencell Jun 18, 2026
22a06ee
recommender: Detect bare www. URLs in advisor queries
stencell Jun 18, 2026
760dfd6
database: Soft-delete catalog items instead of purging on CRD removal
stencell Jun 18, 2026
f445f1c
docs: Add soft-delete session to worklog, mark backlog item complete
stencell Jun 18, 2026
3072a08
docs: Document soft-delete pattern across architecture pages
stencell Jun 18, 2026
da738af
reporting: Exclude fully-retired items from sync scoring and dashboard
stencell Jun 18, 2026
fd3f1c7
docs: Update worklog with retirement exclusion fix
stencell Jun 18, 2026
a76b56f
reporting: Compute windowed scores against full peer pool, not filter…
stencell Jun 18, 2026
b871ba9
reporting: Tighten retirement scoring for low-activity items
stencell Jun 18, 2026
c818781
rcars: Code review remediation — 9 findings from PR #15
stencell Jun 18, 2026
a741619
Merge pull request #15 from rhpds/main
stencell Jun 18, 2026
32cd686
recommender: Add usage-aware scoring boost after triage
stencell Jun 18, 2026
d7c00e0
frontend: Remove cost/provision from rec cards, rename to deployments
stencell Jun 18, 2026
1c0ea81
Merge pull request #16 from rhpds/main
stencell Jun 18, 2026
891e4bf
frontend: Replace sales impact hover tooltip with clickable info icon
stencell Jun 18, 2026
6776e66
backlog: Add published/base CI merge for retirement dashboard
stencell Jun 18, 2026
621d42b
ansible: Remove github_token and source secret (repo is now public)
stencell Jun 18, 2026
4240091
reporting_sync: Merge published/base CI pairs in retirement analysis
stencell Jun 19, 2026
00dd005
database: Use DISTINCT ON for deterministic published/base mapping
stencell Jun 19, 2026
c0a9846
WORKLOG: Add 2026-06-19 session notes
stencell Jun 19, 2026
cacf647
rcars: Fix LLM fallback, score capping, and deploy reliability
stencell Jun 22, 2026
4773637
docs: Overhaul web guide, deployment, CLI reference, and operations docs
stencell Jun 22, 2026
fe770b2
ansible: Fix smoke test result parsing for advisor API response
stencell Jun 22, 2026
f7c4dc1
rcars: Address PR review findings
stencell Jun 22, 2026
d7d0296
rcars: Filter overlap report by stage to prevent cross-stage mixing
stencell Jun 22, 2026
d1e18bf
Redact secrets and credentials from tracked files
bbethell-1 Jun 22, 2026
06a2841
Security: add input sanitization for XSS, SSRF, and SQL injection
bbethell-1 Jun 22, 2026
5fb9987
fix: Update link for web guide
agonzalezrh Jun 22, 2026
e3e5530
Merge pull request #28 from rhpds/fix/secrets-cleanup
bbethell-1 Jun 22, 2026
53c66a6
Merge pull request #36 from rhpds/agonzalezrh-patch-1
bbethell-1 Jun 22, 2026
50dae60
Merge pull request #31 from rhpds/fix/input-sanitization
bbethell-1 Jun 22, 2026
b6b1336
Generate embeddings from catalog metadata for items without Showroom …
bbethell-1 Jun 22, 2026
609fad8
Add light mode theme toggle
bbethell-1 Jun 22, 2026
07dfb4e
Merge pull request #42 from rhpds/fix/light-mode
bbethell-1 Jun 22, 2026
98439a5
Merge pull request #41 from rhpds/fix/metadata-embeddings
bbethell-1 Jun 22, 2026
68ed6d3
Revert "Add light mode theme toggle"
stencell Jun 22, 2026
00b2c2f
Merge pull request #43 from rhpds/revert-42-fix/light-mode
stencell Jun 22, 2026
f660796
Revert "Revert "Add light mode theme toggle""
bbethell-1 Jun 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ venv/

# Data (never commit)
data/
!src/api/rcars/data/
*.db
*.sqlite

Expand Down Expand Up @@ -48,4 +49,4 @@ ansible/vars/dev.yml
ansible/vars/prod.yml
node_modules
tsconfig.tsbuildinfo
OPERATIONS.md
/OPERATIONS.md
103 changes: 62 additions & 41 deletions BACKLOG.md

Large diffs are not rendered by default.

294 changes: 37 additions & 257 deletions CLAUDE.md

Large diffs are not rendered by default.

52 changes: 34 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,43 @@
# RCARS — RHDP Content Advisory & Recommendation System

Recommendation engine that matches RHDP catalog items to events, booth opportunities,
and field requests. Analyzes Showroom content and uses semantic search + LLM reasoning
to recommend the best assets for any given use case.
AI-powered content intelligence for the Red Hat Demo Platform. RCARS reads every lab and demo in the RHDP catalog, understands what each one teaches, and uses that understanding to help teams find the right content, detect duplicate material, and identify items that should be retired.

## Quick Start
## What It Does

```bash
# Install dependencies
pip install -e ".[dev]"
- **Recommendations** — Ask a question in plain English ("what should we show at a Kubernetes conference?") and get a ranked list of catalog items with rationales
- **Content Overlap Detection** — Pairwise semantic comparison across the catalog to surface duplicate or near-duplicate labs
- **Retirement Analysis** — Scores every catalog item based on provisions, sales impact, cost, and age to identify retirement candidates
- **Infrastructure Metadata** — Extracts workload mappings, cloud providers, and platform details from AgnosticD v2 configurations
- **Catalog Browse** — Filterable view of all catalog items with curator controls for content management

## Architecture

Four deployments on OpenShift: React frontend, FastAPI API, arq workers (scan + recommend), PostgreSQL with pgvector. LLM analysis via LiteMaaS (primary) with Vertex AI fallback. Nightly pipeline handles catalog refresh, stale detection, workload scanning, content similarity, and reporting sync.

# Set up PostgreSQL (local dev)
podman run -d --name rcars-db -p 5432:5432 \
-e POSTGRESQL_USER=rcars -e POSTGRESQL_PASSWORD=dev \
-e POSTGRESQL_DATABASE=rcars \
registry.redhat.io/rhel9/postgresql-16:latest
## Documentation

# Configure
export RCARS_DATABASE_URL="postgresql://rcars:dev@localhost:5432/rcars"
Full documentation is published at **[rhpds.github.io/rcars](https://rhpds.github.io/rcars/)**.

# Refresh catalog from Babylon CRDs (requires oc login)
rcars refresh
- [Overview](https://rhpds.github.io/rcars/overview/) — What RCARS is and how it works
- [Web Guide](https://rhpds.github.io/rcars/user/web-guide/) — Using the web interface
- [CLI Guide](https://rhpds.github.io/rcars/admin/cli-guide/) — Command-line reference
- [System Design](https://rhpds.github.io/rcars/architecture/system-design/) — Architecture and data model
- [Operations](https://rhpds.github.io/rcars/admin/operations/) — Deployment, monitoring, and maintenance
- [Development](https://rhpds.github.io/rcars/admin/development/) — Local setup and contributing

# Check status
rcars status
## Deployment

RCARS is deployed to OpenShift via Ansible. See `ansible/` for playbooks and `ansible/vars/common.yml` for shared configuration. Environment-specific vars (`dev.yml`, `prod.yml`) are gitignored.

```bash
ansible-playbook ansible/deploy.yml -e env=dev --tags deploy
```

## Local Development

```bash
./dev-services.sh start # PostgreSQL, Redis, API, workers, frontend
./dev-services.sh stop
```

See the [Development guide](https://rhpds.github.io/rcars/admin/development/) for full setup instructions.
576 changes: 576 additions & 0 deletions WORKLOG.md

Large diffs are not rendered by default.

109 changes: 73 additions & 36 deletions ansible/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
# ansible-playbook ansible/deploy.yml -e env=dev --tags builds # Build all
# ansible-playbook ansible/deploy.yml -e env=dev --tags apply # App manifests only (no build)
# ansible-playbook ansible/deploy.yml -e env=dev --tags migrate # Just run migrations
# ansible-playbook ansible/deploy.yml -e env=dev --tags update # Build all + migrate (correct order)
# ansible-playbook ansible/deploy.yml -e env=dev --tags smoke-test # Advisor end-to-end test only
#
# Prerequisites:
# - ansible-galaxy collection install -r ansible/requirements.yml
Expand Down Expand Up @@ -98,8 +100,8 @@
ansible.builtin.include_tasks:
file: tasks/apply-infra.yml
apply:
tags: [deploy]
tags: [deploy]
tags: [deploy, apply]
tags: [deploy, apply]

- name: Apply app manifests
ansible.builtin.include_tasks:
Expand All @@ -119,54 +121,89 @@
ansible.builtin.include_tasks:
file: tasks/build-frontend.yml
apply:
tags: [build-frontend]
tags: [build-frontend]
tags: [build-frontend, update]
tags: [build-frontend, update]

- name: Build API + workers only
ansible.builtin.include_tasks:
file: tasks/build-api.yml
apply:
tags: [build-api]
tags: [build-api]
tags: [build-api, update]
tags: [build-api, update]

- name: Run database migrations
block:
- name: Wait for ready app pod
kubernetes.core.k8s_info:
- name: Wait for API rollout to complete
ansible.builtin.command:
cmd: >-
oc rollout status deployment/{{ app_name }}-api
-n {{ target_namespace }}
--kubeconfig={{ kubeconfig | expanduser }}
--timeout=300s
changed_when: false

- name: Find running API pod
ansible.builtin.command:
cmd: >-
oc get pods -n {{ target_namespace }}
--kubeconfig={{ kubeconfig | expanduser }}
-l app={{ app_name }},component=api
--field-selector=status.phase=Running
--no-headers -o custom-columns=NAME:.metadata.name
register: app_pods
until: app_pods.stdout_lines | default([]) | length > 0
retries: 10
delay: 10

- name: Select migration target pod
ansible.builtin.set_fact:
migrate_pod: "{{ app_pods.stdout_lines[0] }}"

- name: Run database schema setup (create tables)
kubernetes.core.k8s_exec:
kubeconfig: "{{ kubeconfig }}"
kind: Pod
namespace: "{{ target_namespace }}"
label_selectors:
- "app={{ app_name }}"
- "component=api"
register: app_pods
until: >-
app_pods.resources | default([]) | length > 0 and
(app_pods.resources
| selectattr('status.phase', 'eq', 'Running')
| selectattr('status.containerStatuses', 'defined')
| list | length > 0) and
(app_pods.resources
| selectattr('status.phase', 'eq', 'Running')
| selectattr('status.containerStatuses', 'defined')
| first).status.containerStatuses[0].ready | bool
retries: 30
delay: 15
failed_when: app_pods.resources | default([]) | length == 0

- name: Run database schema setup
pod: "{{ migrate_pod }}"
command: rcars init-db
register: schema_result
changed_when: false

- name: Run alembic migrations (alter existing tables)
kubernetes.core.k8s_exec:
kubeconfig: "{{ kubeconfig }}"
namespace: "{{ target_namespace }}"
pod: >-
{{ (app_pods.resources
| selectattr('status.phase', 'eq', 'Running')
| selectattr('status.containerStatuses', 'defined')
| first).metadata.name }}
command: rcars status
pod: "{{ migrate_pod }}"
command: alembic upgrade head
register: migrate_result
changed_when: false
tags: [deploy, migrate]
changed_when: "'Running upgrade' in (migrate_result.stdout | default(''))"

- name: Get expected alembic head from code
kubernetes.core.k8s_exec:
kubeconfig: "{{ kubeconfig }}"
namespace: "{{ target_namespace }}"
pod: "{{ migrate_pod }}"
command: alembic heads --resolve-dependencies
register: expected_version

- name: Get current alembic version from database
kubernetes.core.k8s_exec:
kubeconfig: "{{ kubeconfig }}"
namespace: "{{ target_namespace }}"
pod: "{{ migrate_pod }}"
command: alembic current
register: actual_version
until: expected_version.stdout | default('') | trim in (actual_version.stdout | default(''))
retries: 6
delay: 30
failed_when: expected_version.stdout | default('') | trim not in (actual_version.stdout | default(''))
tags: [deploy, migrate, update]

- name: Advisor smoke test (end-to-end LLM verification)
ansible.builtin.include_tasks:
file: tasks/smoke-test.yml
apply:
tags: [deploy, apply, smoke-test]
tags: [deploy, apply, smoke-test]

post_tasks:
- name: Get pod status
Expand Down
18 changes: 0 additions & 18 deletions ansible/tasks/apply-infra.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,6 @@
state: present
apply: true

- name: Ensure RCARS API can validate SA tokens (TokenReview access)
kubernetes.core.k8s:
kubeconfig: "{{ kubeconfig }}"
state: present
definition:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: "rcars-token-review-{{ env }}"
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- kind: ServiceAccount
name: default
namespace: "{{ target_namespace }}"

- name: Clean up rendered infra manifests
ansible.builtin.file:
path: "/tmp/rcars-{{ env }}-infra.yaml"
Expand Down
21 changes: 7 additions & 14 deletions ansible/tasks/build-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,11 @@
| last).status.phase in ['Failed', 'Error', 'Cancelled']
changed_when: false

- name: Wait for API rollout
kubernetes.core.k8s_info:
kubeconfig: "{{ kubeconfig }}"
api_version: apps/v1
kind: Deployment
namespace: "{{ target_namespace }}"
name: "{{ app_name }}-api"
register: api_rollout
until: >-
api_rollout.resources | default([]) | length > 0 and
(api_rollout.resources[0].status.readyReplicas | default(0) | int) ==
(api_rollout.resources[0].spec.replicas | default(1) | int)
retries: 20
delay: 10
- name: Wait for new API rollout to complete
ansible.builtin.command:
cmd: >-
oc rollout status deployment/{{ app_name }}-api
-n {{ target_namespace }}
--kubeconfig={{ kubeconfig | expanduser }}
--timeout=300s
changed_when: false
18 changes: 18 additions & 0 deletions ansible/tasks/mgmt-rbac.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,24 @@
name: "{{ mgmt_service_account.name }}"
namespace: "{{ target_namespace }}"

- name: Ensure RCARS API can validate SA tokens (TokenReview access)
kubernetes.core.k8s:
kubeconfig: "{{ kubeconfig }}"
state: present
definition:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: "rcars-token-review-{{ env }}"
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- kind: ServiceAccount
name: default
namespace: "{{ target_namespace }}"

# ── Long-lived token + kubeconfig generation ──────────────────────────────────

- name: Ensure long-lived token Secret exists
Expand Down
94 changes: 94 additions & 0 deletions ansible/tasks/smoke-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
---
# End-to-end advisor smoke test — verifies the full pipeline:
# vector search → LLM triage → LLM rationale → recommendations
#
# Runs a simple query against the advisor API from inside the cluster
# and checks that results come back without errors.

- name: Wait for recommend-worker rollout
ansible.builtin.command:
cmd: >-
oc rollout status deployment/{{ app_name }}-recommend-worker
-n {{ target_namespace }}
--kubeconfig={{ kubeconfig | expanduser }}
--timeout=120s
changed_when: false

- name: Wait for API rollout
ansible.builtin.command:
cmd: >-
oc rollout status deployment/{{ app_name }}-api
-n {{ target_namespace }}
--kubeconfig={{ kubeconfig | expanduser }}
--timeout=120s
changed_when: false

- name: Find running API pod
ansible.builtin.command:
cmd: >-
oc get pods -n {{ target_namespace }}
--kubeconfig={{ kubeconfig | expanduser }}
-l app={{ app_name }},component=api
--field-selector=status.phase=Running
--no-headers -o custom-columns=NAME:.metadata.name
register: smoke_api_pods
until: smoke_api_pods.stdout_lines | default([]) | length > 0
retries: 10
delay: 5

- name: Select API pod for smoke test
ansible.builtin.set_fact:
smoke_pod: "{{ smoke_api_pods.stdout_lines[0] }}"

- name: Submit advisor query
kubernetes.core.k8s_exec:
kubeconfig: "{{ kubeconfig }}"
namespace: "{{ target_namespace }}"
pod: "{{ smoke_pod }}"
command: >-
curl -sf --max-time 30 -X POST http://localhost:{{ app_port }}/api/v1/advisor/query
-H 'Content-Type: application/json'
-H 'X-Forwarded-Email: smoke-test@redhat.com'
-d '{"query": "OpenShift getting started demo", "include_zt": false}'
register: smoke_query
failed_when: smoke_query.rc != 0

- name: Parse query job ID
ansible.builtin.set_fact:
smoke_job_id: "{{ (smoke_query.stdout | from_json).job_id }}"

- name: "Poll advisor result (job {{ smoke_job_id }})"
kubernetes.core.k8s_exec:
kubeconfig: "{{ kubeconfig }}"
namespace: "{{ target_namespace }}"
pod: "{{ smoke_pod }}"
command: >-
curl -sf --max-time 30 http://localhost:{{ app_port }}/api/v1/advisor/query/{{ smoke_job_id }}/result
-H 'X-Forwarded-Email: smoke-test@redhat.com'
register: smoke_result
until: >-
(smoke_result.stdout | from_json).status | default('') in ['complete', 'failed']
retries: 30
delay: 5
failed_when: smoke_result.rc != 0

- name: Parse smoke test result
ansible.builtin.set_fact:
smoke_parsed: "{{ smoke_result.stdout | from_json }}"
smoke_candidates: "{{ (smoke_result.stdout | from_json).result.candidates | default([]) }}"

- name: Verify advisor returned results
ansible.builtin.assert:
that:
- smoke_parsed.status == 'complete'
- smoke_candidates | length > 0
fail_msg: >-
Advisor smoke test FAILED.
Status: {{ smoke_parsed.status | default('unknown') }}.
Error: {{ smoke_parsed.error | default('none') }}.
Candidates: {{ smoke_candidates | length }}.
This usually means the LLM provider (LiteMaaS/Vertex) is unreachable or
returned an authentication error. Check recommend-worker logs:
oc logs deployment/{{ app_name }}-recommend-worker -n {{ target_namespace }} --tail=50
success_msg: >-
Advisor smoke test PASSED — {{ smoke_candidates | length }} candidates returned.
Loading