fix: ensureNamespace uses Get-then-Create to avoid RBAC failures

The deployer was blindly calling Namespaces().Create() which triggered
cluster-scope RBAC checks even when the namespace already existed.
Now checks with Get() first and only creates if NotFound.

Also adds namespace get/create and secrets create/update/patch
permissions to the rdev-api-deployer ClusterRole.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
jordan 2026-01-28 21:34:32 -07:00
parent 1adffbd50e
commit 043cc8c63b
2 changed files with 17 additions and 5 deletions

View File

@ -203,14 +203,18 @@ rules:
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# Namespace verification (check exists before creating)
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["get", "create"]
# Pod logs for deployment status
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
# Secrets for TLS certificates (read-only to reference existing)
# Secrets for env vars and TLS certificates
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list"]
verbs: ["get", "list", "create", "update", "patch"]
---
# ClusterRoleBinding for rdev-api deployer
apiVersion: rbac.authorization.k8s.io/v1

View File

@ -15,15 +15,23 @@ import (
"github.com/orchard9/rdev/internal/domain"
)
// ensureNamespace creates the deployment namespace if it doesn't exist.
// ensureNamespace verifies the deployment namespace exists, creating it only if needed.
func (d *Deployer) ensureNamespace(ctx context.Context) error {
_, err := d.client.CoreV1().Namespaces().Get(ctx, d.config.Namespace, metav1.GetOptions{})
if err == nil {
return nil // namespace exists
}
if !errors.IsNotFound(err) {
return err
}
// Namespace doesn't exist, try to create it
ns := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: d.config.Namespace,
},
}
_, err := d.client.CoreV1().Namespaces().Create(ctx, ns, metav1.CreateOptions{})
_, err = d.client.CoreV1().Namespaces().Create(ctx, ns, metav1.CreateOptions{})
if err != nil && !errors.IsAlreadyExists(err) {
return err
}