Skip to content

[Phase 1] Add SINGLE_NAMESPACE_MODE support to operator #2

@jeremyeder

Description

@jeremyeder

Epic: #1
Phase: 1 - Foundation & Code Changes
Dependencies: None (START HERE)
Estimate: 8 hours

Description

Add comprehensive single-namespace mode support to the operator to enable deployment in MPP environments where cluster-wide permissions are not available.

Critical Issues Addressed

This issue resolves 4 of the 8 critical showstoppers:

Implementation Tasks

1. Add Environment Variable Support

File: components/operator/main.go

Add global variables:

var (
    singleNamespaceMode bool
    storageClass        string
)

Initialize in main():

singleNamespaceMode = os.Getenv("SINGLE_NAMESPACE_MODE") == "true"
storageClass = os.Getenv("STORAGE_CLASS")
if storageClass == "" {
    storageClass = "gp3-csi"
}

if singleNamespaceMode {
    log.Printf("Running in SINGLE_NAMESPACE_MODE, watching namespace: %s", namespace)
}

2. Disable Namespace Watcher

File: components/operator/main.go (line ~83)

// Start watchers
go watchAgenticSessions()
go watchProjectSettings()

// Only watch namespaces in multi-namespace mode
if !singleNamespaceMode {
    go watchNamespaces()
} else {
    // Bootstrap single namespace resources
    if err := ensureProjectWorkspacePVC(namespace); err != nil {
        log.Fatalf("Failed to ensure workspace PVC: %v", err)
    }
    if err := ensureContentService(namespace); err != nil {
        log.Fatalf("Failed to ensure content service: %v", err)
    }
}

3. Add Namespace Filtering to Watch Functions

File: components/operator/main.go (watchAgenticSessions, line ~140)

func watchAgenticSessions() {
    gvr := getAgenticSessionResource()
    for {
        var watcher watch.Interface
        var err error
        
        if singleNamespaceMode {
            watcher, err = dynamicClient.Resource(gvr).Namespace(namespace).Watch(context.TODO(), v1.ListOptions{})
        } else {
            watcher, err = dynamicClient.Resource(gvr).Watch(context.TODO(), v1.ListOptions{})
        }
        
        if err != nil {
            log.Printf("Failed to create AgenticSession watcher: %v", err)
            time.Sleep(5 * time.Second)
            continue
        }
        
        // ... rest of logic, but skip managed namespace check in single-namespace mode
        for event := range watcher.ResultChan() {
            // ... existing event handling ...
            
            // Only check namespace labels when watching all namespaces
            if !singleNamespaceMode {
                ns := obj.GetNamespace()
                nsObj, err := k8sClient.CoreV1().Namespaces().Get(context.TODO(), ns, v1.GetOptions{})
                if err != nil {
                    log.Printf("Failed to get namespace %s: %v", ns, err)
                    continue
                }
                if nsObj.Labels["ambient-code.io/managed"] != "true" {
                    continue
                }
            }
            
            // ... handle event ...
        }
    }
}

Apply same pattern to watchProjectSettings() (line ~769)

4. Add StorageClass Parameter to PVC Creation

File: components/operator/main.go (ensureProjectWorkspacePVC, line ~533)

pvc := &corev1.PersistentVolumeClaim{
    ObjectMeta: v1.ObjectMeta{
        Name:      "ambient-workspace",
        Namespace: namespace,
        Labels:    map[string]string{"app": "ambient-workspace"},
    },
    Spec: corev1.PersistentVolumeClaimSpec{
        StorageClassName: &storageClass, // ← ADD THIS LINE
        AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
        Resources: corev1.VolumeResourceRequirements{
            Requests: corev1.ResourceList{
                corev1.ResourceStorage: resource.MustParse("5Gi"),
            },
        },
    },
}

5. Add Pod Security Context to Job Template

File: components/operator/main.go (handleAgenticSessionEvent, line ~330)

job := &batchv1.Job{
    Spec: batchv1.JobSpec{
        Template: corev1.PodTemplateSpec{
            Spec: corev1.PodSpec{
                // ← ADD POD-LEVEL SECURITY CONTEXT
                SecurityContext: &corev1.PodSecurityContext{
                    RunAsNonRoot: boolPtr(true),
                    FSGroup:      int64Ptr(1000),
                    SeccompProfile: &corev1.SeccompProfile{
                        Type: corev1.SeccompProfileTypeRuntimeDefault,
                    },
                },
                RestartPolicy: corev1.RestartPolicyNever,
                // ... rest of spec ...
            }
        }
    }
}

6. Add Pod Security Context to Content Service

File: components/operator/main.go (ensureContentService, line ~568)

Add same pod security context to content service deployment.

Testing Requirements

  • Operator starts successfully with SINGLE_NAMESPACE_MODE=true
  • Operator does NOT attempt to watch namespaces cluster-wide
  • PVC created with gp3-csi storage class
  • Job pods include pod-level security context
  • No RBAC permission errors in operator logs

Acceptance Criteria

  • Environment variables added and documented
  • Namespace watcher disabled in single-namespace mode
  • All watch functions namespace-scoped when enabled
  • StorageClass parameter added to PVC creation
  • Pod security contexts added to Job and content service
  • Operator starts without cluster-wide permissions
  • Code reviewed and tested

Files Changed

  • components/operator/main.go

Branch

feature/mpp-single-namespace-mode

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions