README
¶
OtterScale Apps Operator
A GitHub repository template for building Kubernetes operators that reconcile OtterScale custom resources. Scaffolded with Kubebuilder v4.
Quick Start
Click "Use this template" on GitHub to create a new repository, then clone it locally.
Add an API Controller
Use kubebuilder create api with the external API flags to scaffold a controller for an OtterScale resource:
kubebuilder create api \
--group addons --version v1alpha1 --kind Module \
--controller=true --resource=false \
--external-api-path=github.com/otterscale/api/addons/v1alpha1 \
--external-api-domain=otterscale.io \
--external-api-module=github.com/otterscale/api
| Flag | Purpose |
|---|---|
--controller=true |
Generate a controller for reconciliation logic |
--resource=false |
Skip CRD generation (the CRD is defined in the external API) |
--external-api-path |
Go import path of the external API types |
--external-api-domain |
API group domain (produces addons.otterscale.io) |
--external-api-module |
Go module that provides the types |
This scaffolds:
internal/controller/module_controller.go— reconciliation logicinternal/controller/module_controller_test.go— test skeleton- Registration in
cmd/main.go
Repeat for additional resources by changing --group, --version, and --kind.
After Scaffolding
go mod tidy
make manifests generate
Add Aggregate RBAC Roles
Kubebuilder does not scaffold aggregate ClusterRole files for custom resources. You need to create them manually so that the default admin, edit, and view ClusterRoles automatically inherit permissions for your resource.
For each Kind (e.g. Module), create the following files under config/rbac/:
| File | Purpose |
|---|---|
module_admin_role.yaml |
Full CRUD — aggregated into the admin ClusterRole |
module_editor_role.yaml |
Read + write — aggregated into the edit ClusterRole |
module_viewer_role.yaml |
Read-only — aggregated into the view ClusterRole |
Each file is a ClusterRole with the appropriate rbac.authorization.k8s.io/aggregate-to-* label set to "true".
Note: The
aggregate-to-admin,aggregate-to-edit, andaggregate-to-viewlabels only apply to namespaced resources, because the built-inadmin/edit/viewClusterRoles are designed for namespace-scoped access (bound viaRoleBinding). If your resource is cluster-scoped, these aggregate labels have no effect; useaggregate-to-cluster-admininstead, or create dedicatedClusterRoleBindings.
Example for a Module resource in the addons.otterscale.io API group:
config/rbac/module_admin_role.yaml
# This rule is not used by the project apps-operator itself.
# It is provided to allow the cluster admin to help manage permissions for users.
#
# Grants read-only access to addons.otterscale.io resources.
# This role is intended for users who need visibility into these resources
# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: apps-operator
app.kubernetes.io/managed-by: kustomize
rbac.authorization.k8s.io/aggregate-to-admin: "true"
name: module-admin-role
rules:
- apiGroups:
- addons.otterscale.io
resources:
- modules
verbs:
- "*"
- apiGroups:
- addons.otterscale.io
resources:
- modules/status
verbs:
- get
config/rbac/module_editor_role.yaml
# This rule is not used by the project apps-operator itself.
# It is provided to allow the cluster admin to help manage permissions for users.
#
# Grants read-only access to addons.otterscale.io resources.
# This role is intended for users who need visibility into these resources
# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: apps-operator
app.kubernetes.io/managed-by: kustomize
rbac.authorization.k8s.io/aggregate-to-edit: "true"
name: module-editor-role
rules:
- apiGroups:
- addons.otterscale.io
resources:
- modules
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- addons.otterscale.io
resources:
- modules/status
verbs:
- get
config/rbac/module_viewer_role.yaml
# This rule is not used by the project apps-operator itself.
# It is provided to allow the cluster admin to help manage permissions for users.
#
# Grants read-only access to addons.otterscale.io resources.
# This role is intended for users who need visibility into these resources
# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: apps-operator
app.kubernetes.io/managed-by: kustomize
rbac.authorization.k8s.io/aggregate-to-view: "true"
name: module-viewer-role
rules:
- apiGroups:
- addons.otterscale.io
resources:
- modules
verbs:
- get
- list
- watch
- apiGroups:
- addons.otterscale.io
resources:
- modules/status
verbs:
- get
Then register them in config/rbac/kustomization.yaml:
resources:
# For each CRD, "Admin", "Editor" and "Viewer" roles are scaffolded by
# default, aiding admins in cluster management. Those roles are
# not used by the apps-operator itself. You can comment the following lines
# if you do not want those helpers be installed with your Project.
- module_admin_role.yaml
- module_editor_role.yaml
- module_viewer_role.yaml
Versioning
The cmd/main.go file declares a version variable (defaults to "devel"). This value is injected at build time via -ldflags:
var version = "devel"
Both the Makefile and Dockerfile automatically pass VERSION (derived from git describe --tags --always) through -ldflags "-X main.version=$(VERSION)". If you add version-dependent logic (e.g. logging, health endpoints, or user-agent strings), make sure to reference version from cmd/main.go.
Note: When customizing the build process or adding a new build target, remember to include
-ldflags "-X main.version=$(VERSION)"so the binary embeds the correct version.
Development
Prerequisites
- Go 1.26+
- Docker 17.03+
- kubectl v1.11.3+
- Access to a Kubernetes cluster
Run Locally
make run
Run Tests
make test
Lint
make lint # check
make lint-fix # auto-fix
Deployment
Build & Push Image
export IMG=<registry>/<project>:tag
make docker-build docker-push IMG=$IMG
Deploy to Cluster
make deploy IMG=$IMG
Undeploy
make undeploy
CI / CD
This template includes GitHub Actions workflows out of the box:
| Workflow | Trigger | Description |
|---|---|---|
| Lint | push, PR | Runs golangci-lint |
| Tests | push, PR | Runs make test (unit tests via envtest) |
| E2E Tests | push, PR | Runs end-to-end tests on a Kind cluster |
| Publish | release published | Builds & pushes image to ghcr.io, uploads install.yaml |
| Auto Update | weekly (Tue) / manual | Checks for Kubebuilder scaffold updates |
Distribution
YAML Bundle
make build-installer IMG=<registry>/<project>:tag
Users install with:
kubectl apply -f https://raw.githubusercontent.com/<org>/<repo>/<tag>/dist/install.yaml
Helm Chart
kubebuilder edit --plugins=helm/v2-alpha
A chart will be generated under dist/chart/.
License
Copyright 2026 The OtterScale Authors.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
labels
Package labels provides shared Kubernetes recommended label constants and builder functions for all operator-managed resources.
|
Package labels provides shared Kubernetes recommended label constants and builder functions for all operator-managed resources. |
|
test
|
|