DESIGN_DOC.md¶
System: Jupyter Notebook Validator Operator
Version: 1.0.9
Status: Accepted
Audience: Architects, implementers, reviewers
Voice: STE100
Related requirements: docs/adrs/, README.md
1. Introduction and goals¶
The Jupyter Notebook Validator Operator is a Kubernetes-native operator that automates Jupyter notebook validation in MLOps workflows. It clones a notebook from a Git repository, executes it inside an isolated pod with Papermill, and compares the output against a golden notebook. The operator also validates notebooks against deployed ML models on platforms such as KServe, OpenShift AI, and vLLM.
The operator is built with the Operator SDK (v1.37.0) and Go 1.24. It runs on OpenShift 4.20, 4.21, and 4.22 (certified per ADR-056) and on vanilla Kubernetes 1.31 and later.
1.1 Quality goals¶
| ID | Goal | Scenario |
|---|---|---|
| QG-1 | Isolation | Each validation job runs in its own pod. One failure does not affect another job. |
| QG-2 | Observability | Prometheus metrics, structured JSON logs, and OpenShift Console dashboards expose every stage of the reconciliation loop. |
| QG-3 | Portability | The same CRD and controller run on OpenShift and vanilla Kubernetes. Build strategy selection is automatic. |
| QG-4 | Security | Credentials are injected from Kubernetes Secrets or ESO. Logs are sanitized to remove sensitive values. The operator runs as non-root in a distroless container. |
| QG-5 | Extensibility | New build backends and model serving platforms can be added without changes to the controller. |
1.2 Stakeholders¶
| Stakeholder | Expectation |
|---|---|
| MLOps engineer | Submit a NotebookValidationJob CR and receive a pass or fail result with cell-level detail. |
| Platform administrator | Install the operator with Helm or OLM and monitor it with Prometheus. |
| Data scientist | Run notebooks against a deployed model and confirm inference accuracy within tolerance. |
| Contributor | Understand the architecture, run tests locally, and add a new build strategy or platform detector. |
1.3 Reading guide¶
This document follows the arc42 template. Not every section is relevant to every reader. Start here based on your role:
- Data scientist / notebook author: Read Section 1 (goals) and Section 3 (context and scope) to understand what the operator does. Read Section 6 (runtime view) to see what happens when you submit a validation job. Skip Sections 4, 5, and 8-11 unless you plan to contribute code.
- Platform administrator: Read Section 7 (deployment view) for installation and resource layout. Read Section 8 (crosscutting concepts) for security and observability. Read Section 2 (constraints) for supported platforms and versions.
- Operator contributor: Read all sections. Start with Section 5 (building block view) and Section 6 (runtime view) to understand the code structure, then work outward.
flowchart TD
mlops([MLOps Engineer]) --> operator[Jupyter Notebook\nValidator Operator]
admin([Platform Admin]) --> operator
operator --> k8s[Kubernetes API]
operator --> git[Git Repository]
operator --> model[ML Model Endpoint]
2. Constraints¶
- Language: Go 1.24 with controller-runtime v0.17.
- Platforms: OpenShift 4.20 through 4.22 (certified). Kubernetes 1.31 and later. OpenShift 4.19 is best-effort only (ADR-056).
- Build tools: Operator SDK v1.37.0, Kustomize, Helm 3, OLM.
- Container base:
gcr.io/distroless/static:nonroot. The operator binary runs as UID 65532. - License: Apache License 2.0.
- CI: GitHub Actions with golangci-lint v2.11.4, Ginkgo for E2E tests, Kind and live OpenShift clusters.
3. Context and scope¶
The operator watches NotebookValidationJob custom resources. When a CR is created, the controller clones a Git repository, creates a validation pod, monitors its execution, and updates the CR status.
In scope:
- Git clone (HTTPS, SSH, git@host:path)
- Papermill execution in an isolated pod
- Golden notebook comparison with configurable tolerances
- Model validation against nine serving platforms
- Build integration (S2I, Tekton) for dependency resolution
- Prometheus metrics and Grafana dashboards
Out of scope: - Notebook authoring or editing - Model training or deployment - Cluster provisioning
flowchart TB
user([MLOps Engineer])
cr[NotebookValidationJob CR]
ctrl[Operator Controller]
git[(Git Repository)]
pod[Validation Pod]
model[ML Model Endpoint]
prom[Prometheus]
secrets[(Kubernetes Secrets)]
user --> cr
cr --> ctrl
ctrl --> git
ctrl --> pod
ctrl --> model
ctrl --> secrets
pod -->|metrics| prom
ctrl -->|metrics| prom
4. Solution strategy¶
The design follows the Kubernetes operator pattern. A single controller reconciles NotebookValidationJob resources through a deterministic state machine.
Key architectural decisions (58 ADRs in docs/adrs/):
- ADR-001: Operator SDK v1.37.0 with controller-runtime.
- ADR-003: Single CRD (
NotebookValidationJob) atv1alpha1. - ADR-009: Git credentials from Kubernetes Secrets with HTTPS and SSH support.
- ADR-020: Model-aware validation with auto-detection of nine serving platforms.
- ADR-023: S2I build integration for OpenShift.
- ADR-037: State machine with phases: Initializing, Building, BuildComplete, ValidationRunning, Succeeded, Failed.
- ADR-041: Exit code validation and developer safety framework.
- ADR-056: Certified support window: OpenShift 4.20, 4.21, 4.22.
5. Building block view¶
| Building block | Responsibility | Key ADRs |
|---|---|---|
api/v1alpha1/ |
CRD type definitions, webhook validation, deep copy | ADR-003 |
cmd/main.go |
Entrypoint. Registers schemes, starts manager. | ADR-001 |
internal/controller/ |
Reconciliation loop and helper modules | ADR-037 |
pkg/build/ |
Pluggable build strategy interface (S2I, Tekton) | ADR-023, ADR-028 |
pkg/platform/ |
ML platform detection and model resolution | ADR-020 |
pkg/logging/ |
Log sanitization for credential scrubbing | ADR-009 |
pkg/errors/ |
Smart error messages with knowledge base | ADR-030 |
helm/ |
Helm chart for Kubernetes and OpenShift deployment | ADR-004 |
config/ |
Kustomize overlays, RBAC, CRD, webhooks, monitoring | ADR-005, ADR-010 |
bundle/ |
OLM bundle for OperatorHub distribution | ADR-007 |
flowchart TB
subgraph operator [Jupyter Notebook Validator Operator]
main[cmd/main.go]
api[api/v1alpha1]
ctrl[internal/controller]
git_h[git_helper]
paper_h[papermill_helper]
comp_h[comparison_helper]
model_h[model_validation_helper]
build_h[build_integration_helper]
pod_h[pod_log_helper]
fail_h[pod_failure_analyzer]
cond_h[condition_helper]
metrics[metrics]
end
subgraph packages [pkg/]
build_pkg[build/strategy]
s2i[build/s2i_strategy]
tekton[build/tekton_strategy]
platform[platform/detector]
resolver[platform/model_resolver]
logging[logging/sanitize]
errors_pkg[errors/smart_error]
end
main --> ctrl
ctrl --> api
ctrl --> git_h
ctrl --> paper_h
ctrl --> comp_h
ctrl --> model_h
ctrl --> build_h
ctrl --> pod_h
ctrl --> fail_h
ctrl --> cond_h
ctrl --> metrics
build_h --> build_pkg
build_pkg --> s2i
build_pkg --> tekton
model_h --> platform
model_h --> resolver
ctrl --> logging
ctrl --> errors_pkg
5.1 Directory tree¶
.
├── api/v1alpha1/ # CRD types, webhook, deep copy
├── cmd/main.go # Operator entrypoint
├── internal/controller/
│ ├── notebookvalidationjob_controller.go # Main reconciler
│ ├── git_helper.go # Git clone (HTTPS, SSH)
│ ├── papermill_helper.go # Validation pod creation
│ ├── comparison_helper.go # Golden notebook diff
│ ├── model_validation_helper.go # Model inference checks
│ ├── build_integration_helper.go # S2I and Tekton builds
│ ├── pod_log_helper.go # Pod log collection
│ ├── pod_failure_analyzer.go # Failure diagnosis
│ ├── condition_helper.go # Status condition updates
│ ├── metrics.go # Prometheus metrics
│ ├── constants.go # Phase and condition constants
│ └── argocd/ # Argo CD sync wave integration
├── pkg/
│ ├── build/
│ │ ├── strategy.go # Strategy interface and registry
│ │ ├── s2i_strategy.go # OpenShift S2I builds
│ │ ├── tekton_strategy.go # Tekton Pipeline builds
│ │ └── openshiftai.go # OpenShift AI workbench
│ ├── platform/
│ │ ├── detector.go # Platform auto-detection
│ │ └── model_resolver.go # Model endpoint resolution
│ ├── logging/
│ │ └── sanitize.go # Credential scrubbing
│ └── errors/
│ └── smart_error.go # Context-aware error messages
├── config/ # Kustomize manifests
├── helm/ # Helm chart
├── bundle/ # OLM bundle
└── test/ # E2E and integration tests
6. Runtime view¶
6.1 Standard validation (no build)¶
sequenceDiagram
participant U as MLOps Engineer
participant K as Kubernetes API
participant C as Controller
participant G as Git Repository
participant P as Validation Pod
U->>K: Create NotebookValidationJob
K->>C: Reconcile event
C->>K: Set phase = Initializing
C->>G: Clone repository
G-->>C: Notebook files
C->>K: Create validation pod
K->>P: Start pod (Papermill)
C->>K: Set phase = ValidationRunning
P-->>K: Pod completes
C->>K: Collect pod logs
C->>K: Set phase = Succeeded
6.2 Validation with build¶
sequenceDiagram
participant C as Controller
participant B as Build System
participant K as Kubernetes API
participant P as Validation Pod
C->>K: Detect requirements.txt
C->>B: Create build (S2I or Tekton)
C->>K: Set phase = Building
B-->>K: Build completes
C->>K: Set phase = BuildComplete
C->>K: Create pod with built image
C->>K: Set phase = ValidationRunning
P-->>K: Pod completes
C->>K: Set phase = Succeeded
6.3 Job lifecycle¶
stateDiagram-v2
[*] --> Initializing: CR created
Initializing --> Building: requirements.txt found
Initializing --> ValidationRunning: no build needed
Building --> BuildComplete: build succeeds
Building --> Failed: build error
BuildComplete --> ValidationRunning: pod created
ValidationRunning --> Succeeded: pod exit 0
ValidationRunning --> Failed: pod error or timeout
Succeeded --> [*]
Failed --> [*]
7. Deployment view¶
flowchart LR
subgraph cluster [Kubernetes / OpenShift Cluster]
subgraph ns_op [jupyter-validator-system]
deploy[Controller Manager\nDeployment]
webhook[Webhook Service]
cert[cert-manager Certificate]
sa[ServiceAccount]
end
subgraph ns_user [User Namespace]
cr[NotebookValidationJob CR]
pod[Validation Pod]
secret[Git Credentials Secret]
end
subgraph ns_models [Model Namespace]
isvc[InferenceService]
end
prom[Prometheus]
end
deploy --> cr
deploy --> pod
deploy --> secret
deploy --> isvc
deploy --> prom
cert --> webhook
Runtime details:
- The controller manager runs as a single-replica Deployment.
- Leader election prevents split-brain when multiple replicas are configured.
- The webhook service validates and defaults
NotebookValidationJobCRs. - cert-manager issues TLS certificates for the webhook endpoint.
- Prometheus scrapes the
/metricsendpoint on port 8080. - The validation pod runs in the same namespace as the CR.
Installation methods:
| Method | Command |
|---|---|
| Helm | helm install jupyter-validator helm/jupyter-notebook-validator-operator |
| Kustomize | kubectl apply -k config/default |
| OLM | Install from OperatorHub catalog |
8. Crosscutting concepts¶
- Logging and tracing: Structured JSON logs via
controller-runtime/pkg/log. All credential values are scrubbed bypkg/logging/sanitize.gobefore output. - Authentication: Git credentials from Kubernetes Secrets (HTTPS username/password or SSH private key). ESO and Vault integration for dynamic secret rotation (ADR-016, ADR-017).
- Error handling: Smart error messages with a YAML knowledge base (
pkg/errors/knowledge_base.yaml). Pod failure analysis with context-aware diagnosis. Maximum three retries per reconciliation loop. - Configuration: The
NotebookValidationJobCRD carries all configuration. The operator reads no external config files. Default values are set by the mutating webhook. - Extensibility: New build strategies implement the
build.Strategyinterface and register with thebuild.Registry. New model platforms add aPlatformDefinitionentry inpkg/platform/detector.go. - Metrics: Nine Prometheus metrics cover reconciliation duration, validation counts, Git clone performance, active pods, model validation timing, health checks, prediction results, and platform detection.
- RBAC: ClusterRole with least-privilege access to pods, secrets, builds, pipelines, and inference services. Validation pods use a dedicated ServiceAccount.
9. Architectural decisions¶
This project maintains 58 ADRs in docs/adrs/. The following are the most significant.
ADR-001: Operator Framework and SDK Version¶
Status: Accepted
Decision: Use Operator SDK v1.37.0 with controller-runtime.
Consequences: Mature Go-based reconciliation. Helm and OLM packaging. Operator Lifecycle Manager support for OperatorHub.
ADR-003: CRD Schema Design¶
Status: Accepted
Decision: Single CRD NotebookValidationJob at API version v1alpha1 under mlops.mlops.dev.
Consequences: One resource covers notebook source, pod config, golden comparison, model validation, and build config. Webhook validation enforces field constraints.
ADR-020: Model-Aware Validation¶
Status: Accepted
Decision: Auto-detect model serving platforms by probing Kubernetes API groups.
Consequences: Supports KServe, OpenShift AI, vLLM, TorchServe, TensorFlow Serving, Triton, Ray Serve, Seldon, and BentoML without user configuration.
ADR-037: Build-Validation Sequencing¶
Status: Accepted
Decision: Six-phase state machine: Initializing, Building, BuildComplete, ValidationRunning, Succeeded, Failed.
Consequences: Clear lifecycle tracking. Each phase maps to a Kubernetes condition. Backward-compatible Pending and Running aliases.
ADR-056: OpenShift Support Window¶
Status: Accepted
Decision: Certify OpenShift 4.20, 4.21, and 4.22. Drop 4.18 (EoL). 4.19 is best-effort.
Consequences: Three-version rolling window. CSV annotations target v4.20-v4.22. Release branches follow the pattern release-4.XX.
The complete list is in docs/adrs/README.md.
10. Quality requirements¶
| ID | Requirement | Risk | Verify |
|---|---|---|---|
| NFR-001 | Reconciliation completes within 60 seconds for jobs without builds. | Medium | Prometheus histogram reconciliation_duration_seconds |
| NFR-002 | The operator runs as non-root (UID 65532) in a distroless container. | Low | Dockerfile, Pod Security Standards audit |
| NFR-003 | Credential values never appear in logs or status fields. | High | pkg/logging/sanitize_test.go |
| NFR-004 | The operator handles concurrent validation jobs without resource conflicts. | Medium | ADR-052, integration tests |
| NFR-005 | Build PVCs are unique per job to prevent data corruption in concurrent builds. | Medium | ADR-040, pkg/build/tekton_strategy.go |
| NFR-006 | All API changes pass webhook validation before persistence. | Low | api/v1alpha1/notebookvalidationjob_webhook_test.go |
11. Risks and technical debt¶
| Risk | Owner | Mitigation |
|---|---|---|
API is v1alpha1. Breaking changes require a migration path before v1. |
Maintainer | ADR-003 documents the version strategy. |
| S2I support depends on OpenShift Build API. Not available on vanilla Kubernetes. | Contributor | Tekton strategy provides a Kubernetes-native alternative. |
| Nine platform detectors probe the API server on each reconciliation. | Contributor | Cache detection results. Add a TTL-based refresh (tracked as tech debt). |
| Golden notebook comparison is cell-by-cell. Large notebooks increase memory use. | Contributor | Add streaming comparison for notebooks above a configurable size. |
| Argo CD integration is early-stage. Sync wave annotations may conflict with other controllers. | Maintainer | ADR-049 documents the approach. Feature is optional and disabled by default. |
12. Glossary¶
| Term | Meaning |
|---|---|
| CRD | Custom Resource Definition. A Kubernetes API extension. |
| CR | Custom Resource. An instance of a CRD. |
| Papermill | A Python tool that executes Jupyter notebooks and parameterizes them. |
| Golden notebook | A reference notebook with known-good outputs for comparison. |
| S2I | Source-to-Image. An OpenShift build strategy that creates container images from source code. |
| Tekton | A Kubernetes-native CI/CD framework based on pipelines and tasks. |
| KServe | A Kubernetes-native model serving platform for inference workloads. |
| OLM | Operator Lifecycle Manager. Manages operator installation and upgrades on OpenShift. |
| ESO | External Secrets Operator. Syncs secrets from external providers into Kubernetes. |
| ADR | Architecture Decision Record. Documents a significant design choice with context and consequences. |
| SCC | Security Context Constraint. An OpenShift resource that controls pod security permissions. |
| Reconciliation | The controller-runtime loop that drives the actual cluster state toward the desired state. |
| Distroless | A minimal container base image that contains only the application binary and its runtime dependencies. |
| CSV | ClusterServiceVersion. An OLM resource that describes an operator version for OperatorHub. |
Required figures¶
This document includes the following figures:
- Context flowchart (section 3)
- Building-block flowchart (section 5)
- Sequence diagram: standard validation (section 6.1)
- Sequence diagram: validation with build (section 6.2)
- State diagram: job lifecycle (section 6.3)
- Deployment flowchart (section 7)
The operator does not have a user-facing UI. It is controlled entirely through kubectl and Kubernetes CRs. No wireframes are applicable.