diff --git a/README.md b/README.md index 606c906..4487787 100644 --- a/README.md +++ b/README.md @@ -739,15 +739,22 @@ git push origin v0.2.0 ``` CI ([`.github/workflows/release.yml`](.github/workflows/release.yml)) builds and pushes: -- `quay.io/amobrem/pulse-operator:v0.2.0` + `:latest` (the operator image, from `Dockerfile`) -- `quay.io/amobrem/pulse-operator-bundle:v0.2.0` + `:latest` (the OLM bundle, from `Dockerfile.bundle`) - -Before the bundle image is built, CI runs [`scripts/bump-bundle-version.py`](scripts/bump-bundle-version.py) against the pushed tag, which updates the CSV's `metadata.name`, `spec.version`, `metadata.annotations.containerImage`, and the manager Deployment's `image` to match — and sets `spec.replaces` to whatever CSV name was current before the bump, extending the upgrade graph by one step. This runs in CI only; the version bump is never committed back to `main`, so `bundle/manifests/pulse-operator.clusterserviceversion.yaml` on `main` always reflects the most recently *released* version, not a preview of the next one. - -The **catalog** image (`pulse-operator-catalog`, used by the CatalogSource in -[Install via OLM](#install-via-olm)) is *not* built by this workflow — publish it -manually via [Build the catalog image](#build-the-catalog-image) above after -tagging a release. +- `quay.io/amobrem/pulse-operator:` + `:latest` (the operator image, from `Dockerfile`) +- `quay.io/amobrem/pulse-operator-bundle:` + `:latest` (the OLM bundle, from `Dockerfile.bundle`) +- `quay.io/amobrem/pulse-operator-catalog:` + `:latest` (the file-based OLM catalog, from `Dockerfile.catalog`) + +Before the bundle image is built, CI runs [`scripts/bump-bundle-version.py`](scripts/bump-bundle-version.py) against the pushed tag, which updates the CSV's `metadata.name`, `spec.version`, `metadata.annotations.containerImage`, and the manager Deployment's `image` to match — and sets `spec.replaces` to whatever CSV name was current before the bump, extending the upgrade graph by one step. This runs in CI only; the version bump is never committed back to `main`, so the checked-in CSV may lag the release; verify the published bundle rather than inferring its version from `main`. + +The workflow renders available released bundles into the **catalog** image +(`pulse-operator-catalog`, used by the CatalogSource in [Install via OLM](#install-via-olm)), +and publishes it with the operator and bundle. The current bundle +must render successfully; unavailable historical bundles are skipped and +`replaces` is rewired to available predecessors. Verify the resulting upgrade +graph and all three published image digests before deployment. + +Before tagging, update `internal/controller/compat.go` +`OperatorVersion` to the tag without `v`; the release workflow rejects a mismatch. +These image publications do not themselves create a GitHub Release. Requires `QUAY_USERNAME` and `QUAY_TOKEN` as GitHub repository secrets. diff --git a/docs/index.html b/docs/index.html index 679d7a9..a84b574 100644 --- a/docs/index.html +++ b/docs/index.html @@ -3,10 +3,10 @@ - Pulse Operator // AI SRE for OpenShift — One CR, Full Stack - + Pulse Operator — Documentation + - + -
- - -
- ⚙️ -

PULSE OPERATOR

-
v0.1.0 // OLM-READY // 7 RECONCILERS // OPENSHIFT 4.12+ // MIT
-

Deploy the complete AI SRE stack on OpenShift from a single custom resource. Agent, UI, PostgreSQL, monitoring — fully lifecycle-managed.

- -
- -
- - -
-
7
Reconcilers
-
1
Custom Resource
-
5
Trust Levels
-
∞
Drift Repair
-
0
Orphans on Delete
-
OLM
OperatorHub Ready
-
- -
- - -
-

// WHAT THE OPERATOR MANAGES

-

One CR. Seven reconcilers. Zero manual wiring.

-
-
-

Agent ALWAYS ON

-

ClusterRole with configurable RBAC scope, WS token Secret (32-char hex, never rotated), 1Gi memory PVC (gated — Deployment only starts after PVC is Bound), Deployment + Service.

-
-
-

PostgreSQL DATA SAFE

-

UBI 9 postgresql-15 StatefulSet with RHSCL env vars. pg-data PVC is retained on CR delete — data survives operator upgrades. Headless + ClusterIP Services. pg-auth Secret generated once.

-
-
-

UI + OAuth Proxy AUTO-DETECT

-

nginx Deployment serving the React SPA. oauth-proxy sidecar with TLS on :8443. Route hostname auto-detected from cluster ingress config. OAuthClient redirect URI patched after Route is admitted.

-
-
-

Monitoring OPTIONAL

-

ServiceMonitor targeting agent /metrics. PrometheusRule with PulseAgentDown, PulsePostgreSQLDown, and PulseUIDown alert rules. Toggle with monitoring.enabled: true.

-
-
-

MCP Server OPTIONAL

-

Model Context Protocol server for extending the agent with custom tools. Deployed as a separate Deployment + Service. Toggle with agent.mcp.enabled: true.

-
-
-

Network Policies ALWAYS ON

-

UI ingress restricted to OCP router + Prometheus only. PostgreSQL ingress restricted to agent pods only. Zero trust by default — NetworkPolicy created for every CR.

-
-
-

Cluster Auto-Detection

-

On first reconcile: reads ingress domain from ingresses.config.openshift.io/cluster, oauth-proxy image digest from ImageStream, ACM availability from namespace existence. Cached with sync.Once — zero API calls after warmup.

-
-
-

Finalizer SAFE DELETE

-

pulse.ai/cleanup finalizer removes all cluster-scoped resources (ClusterRole, ClusterRoleBinding, OAuthClient) when the CR is deleted. No orphans, ever.

-
-
-
- -
- - -
-

// QUICK START — OLM

-

Install via OLM so the operator appears in Installed Operators and gets auto-upgrades.

-
-# 1. Add the CatalogSource -oc apply -f - <<EOF -apiVersion: operators.coreos.com/v1alpha1 -kind: CatalogSource -metadata: - name: pulse-operator-catalog - namespace: openshift-marketplace -spec: - sourceType: grpc - image: quay.io/amobrem/pulse-operator-catalog:latest - displayName: Pulse Operator -EOF - -# 2. Create namespace + AI backend secret -oc new-project openshiftpulse -oc create secret generic gcp-sa-key --from-file=key.json=./sa-key.json -n openshiftpulse - -# 3. Subscribe -oc apply -f - <<EOF -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: pulse-operator - namespace: openshiftpulse -spec: - channel: alpha - name: pulse-operator - source: pulse-operator-catalog - sourceNamespace: openshift-marketplace -EOF - -# 4. Deploy your Pulse instance -oc apply -f - <<EOF -apiVersion: pulse.ai/v1alpha1 -kind: OpenShiftPulse -metadata: - name: pulse - namespace: openshiftpulse -spec: - vertexAI: - projectId: my-gcp-project - region: us-east5 - credentialSecret: gcp-sa-key - agent: - image: quay.io/amobrem/pulse-agent:latest - trustLevel: 2 - mcp: - enabled: true - monitoring: - enabled: true -EOF - -# 5. Get your Pulse URL -oc get route -n openshiftpulse -o jsonpath='{.items[0].spec.host}' -
-
- -
- - -
-

// TRUST LEVELS

-

Progressive autonomy. You decide how much the agent can act without asking.

-
-
-
0
-
Observe
-
Read-only. Answers questions, no actions.
-
-
-
1
-
Suggest
-
Proposes actions, user approves each one.
-
-
-
2
-
Confirm
-
Default. One confirmation per action.
-
-
-
3
-
Auto-Safe
-
Low-risk auto-fix. One batch confirm.
-
-
-
4
-
Autonomous
-
Full auto. No confirmation required.
-
-
-
- -
- - -
-

// ARCHITECTURE

-

Operator layout. Every managed resource is CR-scoped — multiple CRs coexist on the same cluster.

-
-pulse-operator-system/pulse-operator-manager (1 replica, leader-elected) -└── OpenShiftPulseReconciler - ├── pulse.ai/cleanup finalizer ← removes cluster-scoped resources on CR delete - │ - ├── AgentReconciler - │ ├── {ns}/{name}-openshift-sre-agent (ServiceAccount + ClusterRole + ClusterRoleBinding) - │ ├── {ns}/{name}-ws-token (Secret — random 32-char hex) - │ ├── {ns}/{name}-openshift-sre-agent-memory (PVC 1Gi RWO — gates Deployment) - │ └── {ns}/{name}-openshift-sre-agent (Deployment + Service :8080) - │ - ├── PostgreSQLReconciler - │ ├── {ns}/{name}-pg-auth (Secret — POSTGRESQL_* RHSCL vars) - │ ├── {ns}/{name}-openshift-sre-agent-postgresql (StatefulSet + pg-data PVC) - │ └── {ns}/{name}-openshift-sre-agent-postgresql[-headless] (Services) - │ - ├── UIReconciler - │ ├── {ns}/{name}-openshiftpulse (ServiceAccount + ClusterRole) - │ ├── {ns}/{name}-oauth-secrets (Secret — client-secret + cookie-secret) - │ ├── {ns}/{name}-nginx (ConfigMap — nginx.conf, root /opt/app-root/src) - │ ├── {ns}/{name}-openshiftpulse (Deployment: nginx + oauth-proxy TLS :8443) - │ ├── {ns}/{name}-openshiftpulse (Route — reencrypt, OCP assigns hostname) - │ └── openshiftpulse-{ns}-{name} (OAuthClient — cluster-scoped) - │ - ├── MonitoringReconciler [spec.monitoring.enabled] - │ ├── {ns}/{name}-openshift-sre-agent (ServiceMonitor → /metrics) - │ └── {ns}/{name}-openshiftpulse (PrometheusRule — 3 alert rules) - │ - ├── MCPReconciler [spec.agent.mcp.enabled] - │ └── {ns}/{name}-mcp-server (Deployment + Service :8001) - │ - └── NetworkPolicyReconciler - ├── {ns}/{name}-openshiftpulse (UI: ingress router + Prometheus only) - └── {ns}/{name}-pg-access (PG: ingress from agent pods only) -
-
- -
- - -
-

// FULL CR SPEC

-

Every field. All defaults. Copy-paste ready.

-
-apiVersion: pulse.ai/v1alpha1 -kind: OpenShiftPulse -metadata: - name: pulse - namespace: openshiftpulse -spec: - - # ── AI Backend — pick one ──────────────────────────────────────────── - - vertexAI: - projectId: my-gcp-project # GCP project ID - region: us-east5 # Vertex AI region - credentialSecret: gcp-sa-key # Secret with key.json - - # anthropicApiKey: # alternative to vertexAI - # existingSecret: anthropic-api-key # Secret with ANTHROPIC_API_KEY - - # ── Agent ──────────────────────────────────────────────────────────── - - agent: - image: quay.io/amobrem/pulse-agent:latest - trustLevel: 2 # 0=observe 1=suggest 2=confirm 3=auto-safe 4=autonomous - allowWriteOperations: false # add delete(pods), patch(deploy) to ClusterRole - allowSecretAccess: false # add get/list/watch(secrets) to ClusterRole - resources: # corev1.ResourceRequirements - requests: - cpu: 100m - memory: 256Mi - mcp: - enabled: false # deploy MCP server sidecar for tool extension - - # ── UI ─────────────────────────────────────────────────────────────── - - ui: - image: quay.io/amobrem/openshiftpulse:latest - replicas: 2 - oauthProxyImage: "" # auto-detected from cluster ImageStream when empty - resources: {} - - # ── Database ───────────────────────────────────────────────────────── - - database: - storageSize: 5Gi # PVC size — cannot shrink after creation - storageClass: "" # cluster default if empty - image: "" # UBI9 postgresql-15 if empty - - # ── Monitoring ─────────────────────────────────────────────────────── - - monitoring: - enabled: true # ServiceMonitor + PrometheusRule alerts -
-
- -
- - -
-

// CR STATUS

-

Watch the operator bring up the stack.

-
-# Watch real-time -oc get openshiftpulse pulse -n openshiftpulse -w - -NAME PHASE AGENTHEALTHY DBREADY UIAVAILABLE ROUTEHOST -pulse Installing false false false -pulse Installing false true false -pulse Installing true true false -pulse Running true true true pulse-openshiftpulse.apps.cluster.example.com -
-
-status: - phase: Running # Pending | Installing | Running | Degraded - agentHealthy: true # agent Deployment has ≥1 ready replica - databaseReady: true # PostgreSQL StatefulSet has ≥1 ready replica - uiAvailable: true # Route hostname assigned by OCP router - routeHost: pulse-openshiftpulse.apps.cluster.example.com - observedGeneration: 3 - conditions: - - type: Ready - status: "True" - reason: AllComponentsReady - observedGeneration: 3 -
-
- -
- - -
-

// EXAMPLE CONFIGURATIONS

-

Copy-paste starting points for common scenarios.

- -

// MINIMAL — Anthropic API key

-
-spec: - anthropicApiKey: - existingSecret: anthropic-api-key - agent: - image: quay.io/amobrem/pulse-agent:latest - trustLevel: 2 -
- -

// FULL — MCP + write operations + custom resources

-
-spec: - vertexAI: - projectId: my-gcp-project - region: us-east5 - credentialSecret: gcp-sa-key - agent: - image: quay.io/amobrem/pulse-agent:latest - trustLevel: 3 - allowWriteOperations: true - allowSecretAccess: true - mcp: - enabled: true - resources: - requests: - cpu: 200m - memory: 512Mi - limits: - cpu: 1 - memory: 1Gi - ui: - image: quay.io/amobrem/openshiftpulse:latest - replicas: 3 - database: - storageSize: 20Gi - storageClass: gp3-csi - monitoring: - enabled: true -
- -

// UNINSTALL — CR deletion triggers finalizer cleanup

-
-# Delete CR — finalizer removes ClusterRoles + OAuthClient -oc delete openshiftpulse pulse -n openshiftpulse -oc delete namespace openshiftpulse - -# PostgreSQL PVCs are RETAINED — data survives operator deletion -# Re-create the CR to reattach to existing data -
-
- -
- - -
-

// RELATED

-

The full Pulse ecosystem.

- -
- -
- - -
-

One CR. The full stack.

-

Pair with Pulse Agent and Pulse UI for the complete AI SRE experience on OpenShift.

- -
- - - + +
+
+

Pulse Operator

+
CURRENT IMPLEMENTATION // INDEPENDENT OPERATOR VERSION
+

Deploy and reconcile Pulse through an OpenShiftPulse custom resource.

+ +
+

What you can do

Capabilities depend on configuration, permissions and the deployed image pair. Consult current source and operational guides.

Reconcile the installation

The custom resource configures UI, agent, PostgreSQL and optional MCP/Temporal components. Inspect the current CRD and README for supported fields and defaults.

+

Manage image compatibility

Agent and UI deploy as a matched pair. Operator versioning is independent; an optional minimum operator version checks the running operator build, not database schema compatibility.

+

Publish OLM artifacts

The tag workflow builds operator, bundle and file-based catalog images. It updates the bundle CSV for the tag and renders available release history into the catalog.

+

Inspect real cluster prerequisites

The explicit-context acceptance runner observes component ownership, rollout readiness, image IDs and PostgreSQL policy. Optional disposable-namespace probes test actual connectivity and selected mutations.

+

Readiness and recovery evidence

A ready installation is a prerequisite, not evidence that Pulse diagnosed or repaired an incident. The runner reports incomplete overall acceptance even when its requested observations pass. Real browser, provider, authorization and recovery evidence must be collected separately.

Crashloop, failed rollout and pending-pod acceptance must preserve diagnostic observations, the exact approved target/change, execution results and fresh recovery measurements. Rejected or unverifiable operations stay unresolved.

+

Operational guides

These links open current repository documentation. Historical plans and designs describe proposals, not current operational guarantees.

Install and configure

+

Custom resource schema

+

Cluster acceptance guide

+

Acceptance runner

+

Release workflow

+
+