Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 6 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ To backport a merged PR to a release branch, comment `/cherry-pick release-X.Y`

## Further Reading

- [Architecture Overview](docs/architecture.md) — system components, reconciliation flow, CRD lifecycle
- [Release Process](docs/release.md) — branching model, versioning, automated tag management
- [Gitea Integration](docs/development/gitea-integration.md) — e2e test infrastructure details
- [Architecture Overview](docs/architecture.md) - system components, reconciliation flow, CRD lifecycle
- [Release Process](docs/release.md) - branching model, versioning, automated tag management
- [Gitea Integration](docs/development/gitea-integration.md) - e2e test infrastructure details

## Development

Expand Down Expand Up @@ -96,9 +96,9 @@ make update-codegen
```

This runs three sub-targets:
- `generate` — DeepCopy and DeepCopyInto methods
- `manifests` — CRDs, ClusterRoles, and webhook configurations
- `gen-mocks` — Mock implementations via [mockery](https://github.com/vektra/mockery)
- `generate` - DeepCopy and DeepCopyInto methods
- `manifests` - CRDs, ClusterRoles, and webhook configurations
- `gen-mocks` - Mock implementations via [mockery](https://github.com/vektra/mockery)

## Go Modules

Expand Down
34 changes: 17 additions & 17 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,40 @@ The operator does **not** handle initial deployment. Functions must first be dep

## Components

- **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.
- **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.

- **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`.
- **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`.

- **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).
- **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).

- **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.
- **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.

## CRD: Function

Defined in `api/v1alpha1/function_types.go`. A `Function` resource represents a deployed serverless function that the operator should monitor.

**Spec** (user-provided):
- `repository.url` — Git repository containing the function source
- `repository.revision` — Branch name or full ref to build (optional, defaults to the repo's default branch)
- `repository.dir` — Directory within the repo that holds the function (for monorepos)
- `repository.authSecretRef` — Secret for private repo authentication
- `registry.authSecretRef` — Secret for container registry authentication
- `autoUpdateMiddleware` — Override operator default (optional)
- `repository.url` - Git repository containing the function source
- `repository.revision` - Branch name or full ref to build (optional, defaults to the repo's default branch)
- `repository.dir` - Directory within the repo that holds the function (for monorepos)
- `repository.authSecretRef` - Secret for private repo authentication
- `registry.authSecretRef` - Secret for container registry authentication
- `autoUpdateMiddleware` - Override operator default (optional)

**Status** (operator-managed):
- `git` — Resolved revision, observed commit, last check time
- `deployment` — Current image, build time, deployer, runtime
- `middleware` — Current/available versions, auto-update config, rebuild state
- `service` — URL and readiness of the underlying Knative Service
- `conditions` — Standard Kubernetes conditions (see below)
- `history` — Last 20 reconciliation events
- `git` - Resolved revision, observed commit, last check time
- `deployment` - Current image, build time, deployer, runtime
- `middleware` - Current/available versions, auto-update config, rebuild state
- `service` - URL and readiness of the underlying Knative Service
- `conditions` - Standard Kubernetes conditions (see below)
- `history` - Last 20 reconciliation events

## Reconciliation Flow

```mermaid
flowchart TD
start["Reconcile()"] --> get["Get Function CR"]
get -->|Not found| ignore["Exit — already deleted"]
get -->|Not found| ignore["Exit - already deleted"]
get -->|Found| tracker["Create StatusTracker"]
tracker --> prepare

Expand Down
42 changes: 21 additions & 21 deletions docs/development/gitea-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,37 +44,37 @@ BeforeEach(func() {
### Available Helper Methods

**RepositoryProvider Interface:**
- `CreateUser(username, password, email string) (cleanup func(), err error)` — Create user with cleanup function
- `DeleteUser(username string) error` — Delete a user and all their data
- `CreateRandomUser() (username, password, email string, cleanup func(), err error)` — Create user with random credentials
- `CreateRepo(owner, name string, private bool) (url string, cleanup func(), err error)` — Create repository
- `DeleteRepo(owner, name string) error` — Delete a repository
- `CreateRandomRepo(owner string, private bool) (name, url string, cleanup func(), err error)` — Create repo with random name
- `CreateAccessToken(username, password, tokenName string) (string, error)` — Generate access token
- `CreateSSHKey(username, password, title, publicKey string) error` — Register SSH public key for user
- `SSHRepoURL(owner, repo string) (string, error)` — Get SSH URL for a repository
- `CreateUser(username, password, email string) (cleanup func(), err error)` - Create user with cleanup function
- `DeleteUser(username string) error` - Delete a user and all their data
- `CreateRandomUser() (username, password, email string, cleanup func(), err error)` - Create user with random credentials
- `CreateRepo(owner, name string, private bool) (url string, cleanup func(), err error)` - Create repository
- `DeleteRepo(owner, name string) error` - Delete a repository
- `CreateRandomRepo(owner string, private bool) (name, url string, cleanup func(), err error)` - Create repo with random name
- `CreateAccessToken(username, password, tokenName string) (string, error)` - Generate access token
- `CreateSSHKey(username, password, title, publicKey string) error` - Register SSH public key for user
- `SSHRepoURL(owner, repo string) (string, error)` - Get SSH URL for a repository

Note: `CreateUser`, `CreateRepo`, and `CreateRandomRepo` return cleanup functions that call `DeleteUser`/`DeleteRepo` internally. Prefer `DeferCleanup(cleanup)` over calling the delete methods directly.

**Git Helper Functions:**
- `InitializeRepoWithFunction(url, user, pass, lang string, opts ...RepoOption) (repoDir string, err error)` — Clone, init function, push
- `CommitAndPush(repoDir, msg, file string, otherFiles ...string) error` — Commit and push files
- `InitializeRepoWithFunction(url, user, pass, lang string, opts ...RepoOption) (repoDir string, err error)` - Clone, init function, push
- `CommitAndPush(repoDir, msg, file string, otherFiles ...string) error` - Commit and push files

`InitializeRepoWithFunction` accepts functional options:
- `WithSubDir(subDir string)` — Place the function in a subdirectory (for monorepo testing)
- `WithCliVersion(version string)` — Use a specific func CLI version to initialize the function
- `WithSubDir(subDir string)` - Place the function in a subdirectory (for monorepo testing)
- `WithCliVersion(version string)` - Use a specific func CLI version to initialize the function

**func CLI Helper Functions:**
- `RunFunc(command string, args ...string) (string, error)` — Run the current/latest func CLI
- `RunFuncWithVersion(version, command string, args ...string) (string, error)` — Run a specific func CLI version (downloads and caches automatically)
- `RunFuncDeploy(functionDir string, opts ...FuncDeployOption) (string, error)` — Deploy a function with retry logic
- `RunFunc(command string, args ...string) (string, error)` - Run the current/latest func CLI
- `RunFuncWithVersion(version, command string, args ...string) (string, error)` - Run a specific func CLI version (downloads and caches automatically)
- `RunFuncDeploy(functionDir string, opts ...FuncDeployOption) (string, error)` - Deploy a function with retry logic

`RunFuncDeploy` accepts functional options:
- `WithNamespace(namespace string)` — Target namespace
- `WithBuilder(builder string)` — Builder to use (e.g. `pack`, `s2i`)
- `WithDeployer(deployer string)` — Deployer to use (e.g. `knative`, `keda`)
- `WithDeployCliVersion(version string)` — Use a specific func CLI version
- `WithEnvVars(envVars map[string]string)` — Set environment variables for the deploy command
- `WithNamespace(namespace string)` - Target namespace
- `WithBuilder(builder string)` - Builder to use (e.g. `pack`, `s2i`)
- `WithDeployer(deployer string)` - Deployer to use (e.g. `knative`, `keda`)
- `WithDeployCliVersion(version string)` - Use a specific func CLI version
- `WithEnvVars(envVars map[string]string)` - Set environment variables for the deploy command

Defaults for `RunFuncDeploy` are read from environment variables: `REGISTRY` (or `REGISTRY_URL`), `REGISTRY_INSECURE`, `DEFAULT_BUILDER`, `DEFAULT_DEPLOYER`.

Expand Down
6 changes: 3 additions & 3 deletions docs/objectbucket-notifications-adapter-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ These settings can be changed at runtime by modifying the ConfigMap. The adapter
| `RADOSGW_ADAPTER_ID` | `rgw-adapter` | Identifier used in the S3 bucket notification configuration for RadosGW-managed OBCs |
| `RADOSGW_ADAPTER_TOPIC_ARN` | `arn:aws:sns:ocs-storagecluster-cephobjectstore::rgw-adapter-notifications` | RadosGW SNS TopicArn used in put-bucket-notification calls |
| `RADOSGW_ADAPTER_STORAGECLASS_PATTERN` | `.*ceph-rgw$` | Regex matched against OBC `spec.storageClassName` to classify as RadosGW-managed |
| `NOTIFICATIONS_MODE` | value of `--notifications-mode` (`http`) | `http` or `kafka` — selects how the adapter receives NooBaa/RadosGW notifications. Switching modes restarts the notification runner. |
| `NOTIFICATIONS_MODE` | value of `--notifications-mode` (`http`) | `http` or `kafka` - selects how the adapter receives NooBaa/RadosGW notifications. Switching modes restarts the notification runner. |
| `KAFKA_BROKERS` | value of `--kafka-brokers` | Comma-separated list of Kafka broker addresses (required for Kafka mode). Changing it gracefully restarts the Kafka consumer. |
| `KAFKA_NOTIFICATIONS_TOPICS` | value of `--kafka-notifications-topics` | Comma-separated list of Kafka topics to consume notifications from (required for Kafka mode). Changing it gracefully restarts the Kafka consumer. |
| `KAFKA_NOTIFICATIONS_GROUP_ID` | value of `--kafka-notifications-group-id` | Consumer group ID for consuming notifications (required for Kafka mode). Changing it gracefully restarts the Kafka consumer. |
Expand Down Expand Up @@ -138,7 +138,7 @@ When any of them change, the adapter:

This means you can, for example, switch the adapter from `http` to `kafka` mode, point the
consumer at different brokers, subscribe to different topics, change the consumer group ID,
or rotate the Kafka credentials — all without restarting the pod.
or rotate the Kafka credentials - all without restarting the pod.

The runner is restarted only when a change actually affects it: changes to unrelated
ConfigMap keys (e.g. adapter IDs) do not restart it, and a Kafka credential change only
Expand Down Expand Up @@ -166,7 +166,7 @@ To rotate Kafka credentials without restarting the adapter pod:
1. Update the Kafka Secret with new credentials in place, **or** update the ConfigMap to
reference a new secret via `KAFKA_SECRET`.
2. The adapter watches both the ConfigMap and the referenced Kafka Secret, so it detects the
change automatically, rebuilds the Kafka configuration, and — if Kafka is in use —
change automatically, rebuilds the Kafka configuration, and - if Kafka is in use -
gracefully restarts its Kafka producer/consumer so the new credentials take effect
immediately.

Expand Down
2 changes: 1 addition & 1 deletion docs/plans/2026-04-23-reconcile-state-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,4 +208,4 @@ Helpers populate the state struct and return plain errors. They never call `Mark
- **Pro**: All condition logic in one place, easy to read the full status story
- **Pro**: Helpers are pure data gatherers, easy to test
- **Pro**: Mid-reconcile flushes use the same mechanism
- **Con**: State struct and `syncStatus` must be kept in sync with helpers — two places to update when adding new status fields
- **Con**: State struct and `syncStatus` must be kept in sync with helpers - two places to update when adding new status fields
10 changes: 5 additions & 5 deletions docs/plans/2026-04-24-refactor-handleMiddlewareUpdate-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
2. **Decision logic** - should we redeploy or not?
3. **Status bookkeeping** - ~15 status field assignments scattered throughout

This makes the method hard to follow. There's also a redundant `Describe` call — the second one (line 321) re-fetches data that only changes after a redeploy.
This makes the method hard to follow. There's also a redundant `Describe` call - the second one (line 321) re-fetches data that only changes after a redeploy.

## Design

Expand Down Expand Up @@ -69,10 +69,10 @@ The second `Describe` call only happens inside the deploy branch (where it's act

### Cleanup

`isMiddlewareLatest` is no longer needed — the version comparison happens inside `checkMiddlewareState` using data from the single `Describe` call.
`isMiddlewareLatest` is no longer needed - the version comparison happens inside `checkMiddlewareState` using data from the single `Describe` call.

## Decisions

- **Keep status field duplication across switch cases** — each case is self-contained and readable top-to-bottom
- **`autoUpdate` lives as a field on `middlewareOutdated`** rather than being a third type — the two outdated cases share the same data
- **All types are unexported** — this is controller-internal
- **Keep status field duplication across switch cases** - each case is self-contained and readable top-to-bottom
- **`autoUpdate` lives as a field on `middlewareOutdated`** rather than being a third type - the two outdated cases share the same data
- **All types are unexported** - this is controller-internal
20 changes: 10 additions & 10 deletions docs/plans/2026-04-29-ssh-url-support-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ Add support for SSH repository URLs in the Function CR, allowing users to specif
- For `http`/`https` scheme: existing token/username-password logic unchanged.

**Auth secret fields (SSH):**
- `sshPrivateKey` (required for private repos) — PEM-encoded private key
- `sshPrivateKeyPassword` (optional) — passphrase for encrypted keys
- `known_hosts` (optional) — known_hosts file content for host key verification
- `sshPrivateKey` (required for private repos) - PEM-encoded private key
- `sshPrivateKeyPassword` (optional) - passphrase for encrypted keys
- `known_hosts` (optional) - known_hosts file content for host key verification

### 2. Unit Tests (`internal/git/manager_test.go`)

Expand All @@ -48,20 +48,20 @@ New test file covering `getClientOptions`:
### 3. E2E Test Utilities

**`test/utils/gitea.go`:**
- `GetSSHEndpoint()` — reads `ssh` key from `gitea-endpoint` ConfigMap
- `CreateSSHKey(username, password, title, publicKey string)` — registers SSH public key via Gitea SDK `CreatePublicKey()`
- `SSHRepoURL(owner, repo string)` — builds SCP-style URL from SSH endpoint
- `GetSSHEndpoint()` - reads `ssh` key from `gitea-endpoint` ConfigMap
- `CreateSSHKey(username, password, title, publicKey string)` - registers SSH public key via Gitea SDK `CreatePublicKey()`
- `SSHRepoURL(owner, repo string)` - builds SCP-style URL from SSH endpoint

**`test/utils/git.go`:**
- `WithSSHKey(privateKeyPath string)` option — configures `InitializeRepoWithFunction` to clone/push via SSH using `GIT_SSH_COMMAND` with the provided private key
- `WithSSHKey(privateKeyPath string)` option - configures `InitializeRepoWithFunction` to clone/push via SSH using `GIT_SSH_COMMAND` with the provided private key

### 4. E2E Tests (`test/e2e/func_deploy_test.go`)

Three new test cases under a new `Context("with an SSH repository URL", ...)`:

1. **Public repo with SSH URL** — Create public repo, push via HTTP, create Function CR with SSH URL, verify function becomes ready.
2. **Private repo with SSH key auth** — Generate SSH keypair, register public key in Gitea, create Secret with `sshPrivateKey`, create Function CR with SSH URL + authSecretRef, verify function becomes ready.
3. **Private repo without auth secret** — Create private repo, create Function CR with SSH URL but no authSecretRef, verify function fails with auth error.
1. **Public repo with SSH URL** - Create public repo, push via HTTP, create Function CR with SSH URL, verify function becomes ready.
2. **Private repo with SSH key auth** - Generate SSH keypair, register public key in Gitea, create Secret with `sshPrivateKey`, create Function CR with SSH URL + authSecretRef, verify function becomes ready.
3. **Private repo without auth secret** - Create private repo, create Function CR with SSH URL but no authSecretRef, verify function fails with auth error.

### 5. README Updates

Expand Down
4 changes: 2 additions & 2 deletions docs/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ The project uses [semantic versioning](https://semver.org/) with a `v` prefix: `

## Branch Model

- **`main`** — Active development. All PRs target `main`.
- **`release-{MAJOR}.{MINOR}`** — Maintenance branches for each minor version (e.g. `release-0.3`). Created when a minor version is ready to ship.
- **`main`** - Active development. All PRs target `main`.
- **`release-{MAJOR}.{MINOR}`** - Maintenance branches for each minor version (e.g. `release-0.3`). Created when a minor version is ready to ship.

## Creating a New Minor Release

Expand Down
2 changes: 1 addition & 1 deletion test/utils/func.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ func ensureFuncVersion(version string) (string, error) {
}
defer syscall.Flock(int(lockFile.Fd()), syscall.LOCK_UN) //nolint:errcheck

// Re-check after acquiring the lock — another process may have finished the download
// Re-check after acquiring the lock - another process may have finished the download
if _, err := os.Stat(funcBinary); err == nil {
return funcBinary, nil
}
Expand Down
Loading