|
| 1 | +# Architecture Overview |
| 2 | + |
| 3 | +## What This Operator Does |
| 4 | + |
| 5 | +func-operator is a Kubernetes operator that monitors serverless functions deployed with the [Knative `func` CLI](https://github.com/knative/func). Its primary job is **middleware lifecycle management**: it detects when a function's middleware is outdated and automatically rebuilds the function using the latest version. |
| 6 | + |
| 7 | +The operator does **not** handle initial deployment. Functions must first be deployed with `func deploy`. The operator then takes over ongoing maintenance. |
| 8 | + |
| 9 | +## Components |
| 10 | + |
| 11 | +- **FunctionReconciler** (`internal/controller/`) — The central controller. Watches `Function` custom resources and reconciles them. Also watches the `func-operator-controller-config` ConfigMap to re-reconcile functions when the operator-wide `autoUpdateMiddleware` default changes. Runs up to 10 concurrent reconciliations. |
| 12 | + |
| 13 | +- **FuncCliManager** (`internal/funccli/`) — Wraps the Knative `func` CLI binary. Periodically checks GitHub for new releases and downloads them (with SHA256 checksum verification and atomic install). Runs `func deploy`, `func describe`, and `func version` as subprocesses. The download logic (`DownloadAndInstall`) is shared with e2e test utilities via `internal/funccli/download.go`. |
| 14 | + |
| 15 | +- **GitManager** (`internal/git/`) — Clones function source repositories with authentication support: HTTP/HTTPS (token or basic auth) and SSH (private key with optional passphrase and known_hosts). Uses go-git for pure-Go shallow cloning (single-branch, depth 1). |
| 16 | + |
| 17 | +- **StatusTracker** (`internal/controller/status_tracker.go`) — Buffers status changes during reconciliation and persists them in a single API call at the end via `Flush()`. Supports mid-reconcile flushes for long-running operations (e.g., before a deployment starts) so users see progress. |
| 18 | + |
| 19 | +## CRD: Function |
| 20 | + |
| 21 | +Defined in `api/v1alpha1/function_types.go`. A `Function` resource represents a deployed serverless function that the operator should monitor. |
| 22 | + |
| 23 | +**Spec** (user-provided): |
| 24 | +- `repository.url` — Git repository containing the function source |
| 25 | +- `repository.branch` — Branch to track (optional, defaults to repo default) |
| 26 | +- `repository.path` — Subdirectory within the repo (for monorepos) |
| 27 | +- `repository.authSecretRef` — Secret for private repo authentication |
| 28 | +- `registry.authSecretRef` — Secret for container registry authentication |
| 29 | +- `autoUpdateMiddleware` — Override operator default (optional) |
| 30 | + |
| 31 | +**Status** (operator-managed): |
| 32 | +- `git` — Resolved branch, observed commit, last check time |
| 33 | +- `deployment` — Current image, build time, deployer, runtime |
| 34 | +- `middleware` — Current/available versions, auto-update config, rebuild state |
| 35 | +- `service` — URL and readiness of the underlying Knative Service |
| 36 | +- `conditions` — Standard Kubernetes conditions (see below) |
| 37 | +- `history` — Last 20 reconciliation events |
| 38 | + |
| 39 | +## Reconciliation Flow |
| 40 | + |
| 41 | +```mermaid |
| 42 | +flowchart TD |
| 43 | + start["Reconcile()"] --> get["Get Function CR"] |
| 44 | + get -->|Not found| ignore["Exit — already deleted"] |
| 45 | + get -->|Found| tracker["Create StatusTracker"] |
| 46 | + tracker --> prepare |
| 47 | +
|
| 48 | + subgraph prepare ["prepareSource()"] |
| 49 | + fetchsecret["Fetch auth secret<br/><i>(if configured)</i>"] |
| 50 | + fetchsecret --> clone["Clone git repository"] |
| 51 | + clone --> readmeta["Read func.yaml metadata"] |
| 52 | + readmeta --> sourceready["Mark SourceReady"] |
| 53 | + end |
| 54 | +
|
| 55 | + prepare --> describe["func describe<br/><i>(check if deployed)</i>"] |
| 56 | + describe -->|Not deployed| notdeployed["Mark NotDeployed<br/>Return"] |
| 57 | + describe -->|Deployed| checkMW["Check middleware version"] |
| 58 | +
|
| 59 | + checkMW -->|Up to date| uptodate["Update status<br/>Mark MiddlewareUpToDate"] |
| 60 | + checkMW -->|Outdated| autocheck{"Auto-update<br/>enabled?"} |
| 61 | +
|
| 62 | + autocheck -->|No| skip["Mark as intentionally<br/>not updated"] |
| 63 | + autocheck -->|Yes| flush["Flush status<br/><i>(mid-reconcile)</i>"] |
| 64 | + flush --> deploy["func deploy --remote<br/><i>(rebuild via Tekton)</i>"] |
| 65 | + deploy --> updatedesc["func describe<br/><i>(get new state)</i>"] |
| 66 | + updatedesc --> updated["Update status<br/>Mark MiddlewareUpToDate"] |
| 67 | +
|
| 68 | + uptodate --> cleanup["Remove func annotations"] |
| 69 | + skip --> cleanup |
| 70 | + updated --> cleanup |
| 71 | + cleanup --> flushfinal["StatusTracker.Flush()<br/><i>Calculate Ready, persist</i>"] |
| 72 | +``` |
| 73 | + |
| 74 | +### Conditions |
| 75 | + |
| 76 | +The operator maintains five conditions on each Function: |
| 77 | + |
| 78 | +| Condition | Meaning | |
| 79 | +|-----------|---------| |
| 80 | +| `Ready` | Overall health (calculated from all other conditions) | |
| 81 | +| `SourceReady` | Git clone and metadata read succeeded | |
| 82 | +| `Deployed` | Function exists in the cluster | |
| 83 | +| `MiddlewareUpToDate` | Function uses the latest middleware version | |
| 84 | +| `ServiceReady` | Underlying Knative Service is ready | |
| 85 | + |
| 86 | +`Ready` is automatically calculated: it is `True` only when all other conditions are `True`. |
| 87 | + |
| 88 | +## Deployment and Build |
| 89 | + |
| 90 | +The operator uses **Tekton Pipelines** for building function images. During `func deploy --remote`, the func CLI creates a Tekton PipelineRun in the function's namespace. The operator sets up the necessary RBAC (Role + RoleBinding) for the pipeline to run. |
| 91 | + |
| 92 | +Supported builders: `pack` (Cloud Native Buildpacks) and `s2i` (Source-to-Image). |
| 93 | + |
| 94 | +Supported deployers: `knative` (Knative Serving) and `raw` (plain Kubernetes Deployment), with experimental `keda` support (HTTP-based autoscaling). |
| 95 | + |
| 96 | +## Configuration |
| 97 | + |
| 98 | +The operator reads its default config from a ConfigMap named `func-operator-controller-config` in the operator's namespace: |
| 99 | + |
| 100 | +| Key | Description | Default | |
| 101 | +|-----|-------------|---------| |
| 102 | +| `autoUpdateMiddleware` | Whether to auto-rebuild functions with outdated middleware | `true` | |
| 103 | + |
| 104 | +Per-function overrides are possible via `spec.autoUpdateMiddleware` on the Function CR. |
| 105 | + |
| 106 | +## E2E Test Infrastructure |
| 107 | + |
| 108 | +E2E tests run in a KinD cluster with: |
| 109 | +- **Gitea** (in-cluster Git server) for complete test isolation |
| 110 | +- **Local container registry** with self-signed TLS |
| 111 | +- **Tekton Pipelines** for build execution |
| 112 | +- **Knative Serving** (or raw/keda deployer depending on test matrix) |
| 113 | + |
| 114 | +See [Gitea Integration](development/gitea-integration.md) for details on the test infrastructure. |
0 commit comments