A Kubernetes controller that automatically assigns namespaces to Rancher Projects based on the appOwner label.
This controller watches for namespaces with an appOwner label and automatically assigns them to the corresponding Rancher Project. For example, if a namespace has appOwner=DevOps, the controller will find the "DevOps" project in Rancher and assign the namespace to it by adding the necessary labels and annotations.
Architecture: The operator runs on the Rancher management cluster and manages namespaces across all downstream clusters that are registered with Rancher.
- The controller watches all namespaces across all clusters registered with Rancher (via Rancher's management API)
- When a namespace (on any downstream cluster) has the
appOwnerlabel, it searches for a Rancher Project with a matching display name - Once found (or created if it doesn't exist), it adds the following to the namespace:
- Label:
field.cattle.io/projectId: <project-id> - Label:
field.cattle.io/clusterId: <cluster-id>(if available) - Annotation:
field.cattle.io/projectId: <project-id>
- Label:
Important Notes:
- The operator uses Rancher's management API (
management.cattle.io/v3) to access resources across all managed clusters - This API is only available on the Rancher management cluster, which is why the operator must be deployed there
- The operator can manage namespaces on any downstream cluster that is registered with Rancher
- Namespace updates are applied directly to the downstream clusters via Rancher's cluster API proxy
IMPORTANT: This operator must be deployed on the Rancher management cluster (where Rancher itself is running), NOT on downstream clusters that have the Rancher agent (cattle) installed.
- Rancher management cluster (where Rancher is deployed)
- Access to Rancher's management API (management.cattle.io/v3) - only available on the management cluster
- RBAC permissions to:
- List, watch, get, update, and patch namespaces (on downstream clusters via Rancher)
- List, watch, and create Rancher Projects (management.cattle.io/v3)
Deployment Location: Deploy this operator on your Rancher management cluster, not on downstream clusters.
# Install on the Rancher management cluster
helm install qn-rancher-operator ./charts/qn-rancher-operator \
--set image.repository=ghcr.io/quiknode-labs/qn-rancher-operator \
--set image.tag=latest \
--namespace qn-rancher-operator-system \
--create-namespace# Build the binary
make build
# Or build the Docker image
make docker-build
# Deploy to cluster
make deployNote: Update the image in config/manager/deployment.yaml to point to your container registry before deploying.
# Create namespace
kubectl create namespace qn-rancher-operator-system
# Apply RBAC
kubectl apply -f config/rbac/
# Apply deployment (update image first)
kubectl apply -f config/manager/deployment.yaml# Check if the controller is running
kubectl get pods -n qn-rancher-operator-system
# Check logs
kubectl logs -n qn-rancher-operator-system deployment/qn-rancher-operator-controller-managerSimply add the appOwner label to any namespace:
kubectl label namespace my-namespace appOwner=DevOpsThe controller will automatically:
- Detect the label
- Find the "DevOps" project in Rancher
- Assign the namespace to that project
You can verify the assignment by checking the namespace labels:
kubectl get namespace my-namespace -o yamlYou should see:
field.cattle.io/projectId: <project-id>field.cattle.io/clusterId: <cluster-id>(if available)
The controller searches for Rancher Projects by:
- Display Name: Checks
spec.displayNamefield - Labels: Searches common label patterns like:
project.cattle.io/namecattle.io/projectNamefield.cattle.io/projectName
- Annotations: Checks annotations for project name
The matching is case-insensitive.
The controller can be configured via command-line flags:
--metrics-bind-address: Address for metrics endpoint (default::8080)--health-probe-bind-address: Address for health probe (default::8081)--leader-elect: Enable leader election (default:false)
# Run the controller locally (requires kubeconfig)
make run# Build binary
make build
# Build Docker image
make docker-build IMG=your-registry/qn-rancher-operator:tag
# Push Docker image
make docker-push IMG=your-registry/qn-rancher-operator:tag# Run tests
make testIf the controller can't find Rancher Projects, check:
-
Deployment Location: Ensure the operator is deployed on the Rancher management cluster, not on a downstream cluster. The management API (
management.cattle.io/v3) is only available on the management cluster. -
RBAC Permissions: Ensure the service account has permissions to list projects:
kubectl auth can-i list projects.management.cattle.io --as=system:serviceaccount:qn-rancher-operator-system:qn-rancher-operator-controller-manager
-
Rancher API Access: Verify that the management API is accessible from the cluster. If you're on a downstream cluster, you won't have access to this API.
-
Project Names: Ensure the project display name matches the
appOwnerlabel exactly (case-insensitive)
-
Check controller logs:
kubectl logs -n qn-rancher-operator-system deployment/qn-rancher-operator-controller-manager
-
Verify the namespace has the
appOwnerlabel:kubectl get namespace <namespace-name> --show-labels
-
Check if the namespace is already assigned to a project (controller skips already-assigned namespaces)
helm uninstall qn-rancher-operator --namespace qn-rancher-operator-systemmake undeploykubectl delete -f config/manager/deployment.yaml
kubectl delete -f config/rbac/
kubectl delete -f config/namespace.yamlThis repository includes a GitHub Actions workflow (.github/workflows/build-and-publish.yml) that automatically:
- Builds Docker images on push to main/master branches
- Publishes images to GitHub Container Registry (ghcr.io)
- Tags images with version tags, branch names, and SHA
- Supports multi-architecture builds (amd64, arm64)
The workflow is triggered on:
- Push to main/master branches
- Push of version tags (v*)
- Pull requests (builds but doesn't push)
- Manual workflow dispatch
Images are published to: ghcr.io/quiknode-labs/qn-rancher-operator
[Add your license here]