Repository navigation
Conversation
Replaced 'avasilevskii' with 'ashtarkb' in the OWNERS file.
Web dashboard for managing Fournos performance testing jobs. Provides job submission, live monitoring, scheduling, and historical results -- built with FastAPI, HTMX, and PostgreSQL. Co-authored-by: Cursor <cursoragent@cursor.com>
- Switch ClusterRoleBinding to namespace-scoped RoleBinding for least-privilege access - Add securityContext to init and dashboard containers (drop capabilities, read-only root) - Wrap blocking K8s API calls with asyncio.to_thread to avoid event-loop starvation - Add configurable K8s request timeout (K8S_REQUEST_TIMEOUT env var) - Pin dependency versions in requirements.txt - Use kustomize configMapGenerator for projects ConfigMap instead of static manifest - Add OOB HTMX swaps for live header status, completion banner, and MLflow links - Fix htmx-sse.js exponential backoff (Math.pow instead of bitwise XOR) - Deduplicate watcher archive events when phase/message unchanged - Add resilience to malformed project entries in forge_discovery - Reduce Dockerfile workers to 1 and disable Jinja2 cache_size=0 in favour of auto_reload=False - Update README with corrected deploy instructions and project structure Co-authored-by: Cursor <cursoragent@cursor.com>
- Add optional PULL_PULL_SHA field to submit form, injected into spec.env so the resolve job checks out Forge code from a specific PR - Show the MCP Gateway version field only when mcp_gateway project is selected; hide and clear it for all other projects Co-authored-by: Cursor <cursoragent@cursor.com>
- Stop the background log reader thread on client disconnect via asyncio.Event; catch QueueFull to avoid dropping the executor thread - Collect cluster filter options from the full filtered job set before paginating so the dropdown includes all clusters, not just the current page - Rename dashboard-clusterrolebinding.yaml to dashboard-rolebinding.yaml to match the actual RoleBinding kind inside Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the manual SHA input with a searchable dropdown that lists open PRs from the Forge repo (public API, no token needed). Users can filter by PR number, title or author and the HEAD SHA is filled automatically. Also sort pods by creation timestamp so they appear in execution order. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Update OWNERS file
Signed-off-by: Alberto Perdomo <aperdomo@redhat.com>
…conn-error-sync fix: Catch SSL/conn errors while synching vault
Add fournos-ui dashboard
… hardware request
This reverts commit c74f0ec.
Co-authored-by: alberto <aperdomo@redhat.com>
Allow clusterless jobs
fournos: set a 24h timeout for the task completion
config/forge/resolve_job.yaml: sync with Forge Tasks
config/forge/resolve_job.yaml: adapt after Forge Task sync
…thout lockOnly, lockOnly works as before
This code path is unreachable in practice (lockUntil is already validated at creation), and only concerns the fjob creator rather than cluster admins, so warning-level logging was too noisy per review feedback. Co-authored-by: Cursor <cursoragent@cursor.com>
Secure the dashboard with an OAuth proxy sidecar that authenticates users via OpenShift's built-in OAuth server. Add cert-manager integration for automatic Let's Encrypt certificate issuance. - Add kustomize overlay patches for OAuth proxy sidecar, service TLS, and ServiceAccount OAuth redirect annotation - Add Route, Certificate CR, and ClusterIssuer manifests - Update README with full deployment instructions and troubleshooting - Gitignore deployment-specific files (secrets, cluster config) - Replace params.env.example with oauth-cookie-secret.yaml.example Co-authored-by: Cursor <cursoragent@cursor.com>
Add a TTL field to FournosJob that defines the delay after job termination before the CR is automatically deleted. When not set, jobs are never auto-pruned (preserving current behavior). - Add spec.ttl to CRD schema (Go duration format: "12h", "7d", etc.) - Add duration parser utility (fournos/core/duration.py) - Add _gc_expired_jobs() to the operator GC loop - Add unit tests for TTL logic Co-authored-by: Cursor <cursoragent@cursor.com>
Add cluster unlock functionality to the cluster lock only mechanism
Co-authored-by: Kevin Pouget <kpouget@redhat.com>
- Add status.completionTime to CRD schema, set atomically on all terminal transitions via new set_terminal_phase() helper - Validate spec.ttl in on_create; fail immediately if unparseable - Refactor _get_completion_time to read status.completionTime with fallback to metadata.creationTimestamp for creation-time failures - Downgrade GC log for invalid/missing TTL from warning to debug - Fix indentation from accepted early-continue suggestion - Update tests to match completionTime-based logic Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…gitops fournos-ui: add OpenShift OAuth proxy and Let's Encrypt TLS
Drop the creationTimestamp fallback, require a terminal phase before reading completionTime, and stamp completionTime on create-time failures. Co-authored-by: Cursor <cursoragent@cursor.com>
Keep main's ValueError-based timestamp parsing and route those failures through set_terminal_phase so completionTime is still set. Co-authored-by: Cursor <cursoragent@cursor.com>
Kopf drops the status patch if the handler raises. Coerce a non-terminal phase to Failed, reject overflowing TTL values, and skip GC deletes that lose a resourceVersion race. Co-authored-by: Cursor <cursoragent@cursor.com>
feat: add spec.ttl for automatic job cleanup after completion
…bac-fix fix: allow TTL garbage collection to delete jobs
pyproject.toml: update to v0.4.3
…ion-sample-project Fix connectivity smoke-test sample
hacks: sync_vault_secrets: remove from fournos, move to forge
Make Fournos engine-agnostic so non-FORGE jobs can run on the platform.
Core changes:
- Introduce /opt/fournos/entrypoint contract: every engine image provides
an executable at this fixed path. The resolve Job template and Tekton
Tasks invoke it uniformly — no engine-specific logic in Fournos YAML.
- Move resolve Job template from config/forge/ to config/resolve/ (now
engine-agnostic). Delete the old FORGE-specific template.
- Update settings.py default: config/forge/ → config/resolve/.
- Update resolve.py docstring to reflect engine-agnostic semantics.
Built-in generic pipeline (config/generic/):
- New fournos-generic Pipeline + Task + runner image + RBAC.
- The runner reads spec.executionEngine.generic.{image, command, args, env}
from the FournosJob and launches the user's container as a child K8s Job.
- Users provide only a container image + FournosJob YAML. Zero Fournos
awareness needed in their project.
Optional Engine SDK (fournos/sdk/engine.py):
- FournosEngine class with @on_resolve / @on_run decorators.
- Handles all plumbing: env vars, FournosJob fetch, config extraction,
resolve/run routing, error handling.
- For advanced engines that want Python integration without raw bash.
Execution engine contract docs (docs/execution-engine-contract.md):
- Formalizes Mode 1 (generic) and Mode 2 (custom engine).
- Documents env vars, resolve phase, entrypoint requirements.
Dev/test infrastructure:
- dev/mock-resolve: updated to follow /opt/fournos/entrypoint contract.
- dev/mock-generic: new mock images + sample FournosJob for local testing.
- dev/setup.sh: builds and loads generic mock images into kind cluster.
- Makefile: deploy target now applies generic assets; new dev-test-generic.
- tekton.py: set serviceAccountName on PipelineRun taskRunTemplate.
Documentation:
- Updated README.md, Fournos_Design_Document.md to reflect multi-engine
architecture, generic pipeline, and decoupled deployment model.
Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Make Fournos engine-agnostic so any execution engine (not just FORGE) can run on the platform. Introduces two modes: a built-in generic pipeline (zero-code path) and a custom engine contract (
/opt/fournos/entrypoint).This is the Fournos-side counterpart to the FORGE entrypoint contract work (ashtarkb/forge#2).
Motivation
Previously, Fournos was tightly coupled to FORGE:
config/forge/resolve_job.yaml) contained ~90 lines of inline FORGE-specific bashdeployMakefile target applied FORGE workflowsThis meant only FORGE jobs could run on Fournos. Teams with simpler workloads (e.g. a Python benchmark script in a container) had to build a full FORGE project just to use Fournos scheduling.
Architecture
After this change, Fournos supports two modes:
Mode 1: Generic Pipeline (zero Fournos code in your project)
Users provide only a container image + FournosJob YAML:
The built-in runner creates a child K8s Job with the user's container. No SDK, no
/opt/fournos/entrypoint, no Tekton Pipeline to write.Mode 2: Custom Engine (advanced, e.g. FORGE)
Engines provide their own Tekton Pipeline/Task and a container image with
/opt/fournos/entrypoint. Full lifecycle control.spec.executionEngine.<name>is opaque to Fournos.Changes
Core: Engine-agnostic resolve template
config/forge/resolve_job.yaml(90 lines of inline FORGE bash)config/resolve/resolve_job.yaml— engine-agnostic template that just callscommand: ["/opt/fournos/entrypoint"]. The image is selected per-Pipeline viafournos.dev/resolve-imageannotation.fournos/settings.py— default path:config/forge/→config/resolve/fournos/core/resolve.py— docstring updated to reflect engine-agnostic semanticsfournos/core/tekton.py— setserviceAccountName: fournoson PipelineRun taskRunTemplateBuilt-in generic pipeline (
config/generic/)pipeline.yamlfournos-genericTekton Pipeline — referencesfournos-generic-stepTasktask.yamlfournos-generic-stepTekton Task — runs the runner imagerunner.pyspec.executionEngine.generic.*, creates child K8s Job, polls for completion, streams logs, cleans upContainerfile/opt/fournos/entrypointrbac.yamlrequirements.txtOptional Engine SDK (
fournos/sdk/engine.py)FournosEngineclass with@on_resolve/@on_rundecoratorsspec.executionEngine.<engine_name>, resolve/run routing, error handlingEngineContextdataclass withfjob_name,namespace,step,artifact_dir,fjob_specContract documentation (
docs/execution-engine-contract.md)/opt/fournos/entrypointrequirements, environment variables, resolve phase behaviorDev/test infrastructure
dev/mock-resolve/Dockerfile— now creates/opt/fournos/entrypointsymlink (matches production contract)dev/mock-resolve/resolve_job.yaml— explicitcommand: ["/opt/fournos/entrypoint"]+ missing env varsdev/mock-generic/— new directory: mock runner image, mock user image, sample FournosJob for local kind testingdev/setup.sh— +110 lines: builds/loads mock generic images, applies generic Pipeline/Task/RBAC to kindMakefile—deploytarget now applies generic assets instead of FORGE workflows; newdev-test-generictargetDocumentation updates
README.md— updated quick-start, deployment instructions, settings table, added generic pipeline docsFournos_Design_Document.md— rewritten Sections 7-8 for multi-engine architectureFile inventory
config/forge/resolve_job.yamlconfig/resolve/resolve_job.yamlconfig/generic/pipeline.yamlconfig/generic/task.yamlconfig/generic/runner.pyconfig/generic/Containerfileconfig/generic/rbac.yamlconfig/generic/requirements.txtfournos/sdk/__init__.pyfournos/sdk/engine.pydocs/execution-engine-contract.mddev/mock-generic/*(5 files)fournos/settings.pyfournos/core/resolve.pyfournos/core/tekton.pydev/mock-resolve/Dockerfiledev/mock-resolve/resolve_job.yamldev/setup.shMakefileFournos_Design_Document.mdREADME.mdKey metrics