540 lines
17 KiB
Go
540 lines
17 KiB
Go
package k8s
|
|
|
|
import (
|
|
"testing"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/api/resource"
|
|
)
|
|
|
|
var testCfg = Config{
|
|
Domain: "spinoff.dev",
|
|
Registry: "10.22.0.56:30500",
|
|
GoldenImage: "dev-golden:v2",
|
|
VPNGatewayNS: "claw-system",
|
|
VPNGatewaySecret: "vpn-gateway-secrets",
|
|
AnthropicKey: "br_test_anthropic",
|
|
OpenAIKey: "br_test_openai",
|
|
ForgejoURL: "http://forgejo.dev-infra.svc:3000",
|
|
}
|
|
|
|
var testOpts = PodOpts{
|
|
User: "alice",
|
|
Pod: "main",
|
|
Tools: "go@1.25,rust@1.94",
|
|
Task: "build a web server",
|
|
CPUReq: "2",
|
|
CPULimit: "4",
|
|
MemReq: "4Gi",
|
|
MemLimit: "8Gi",
|
|
VPNKey: "test-vpn-key",
|
|
AnthropicKey: "br_test_anthropic",
|
|
OpenAIKey: "br_test_openai",
|
|
}
|
|
|
|
func TestPVCTemplate(t *testing.T) {
|
|
pvc := PVCTemplate("alice", "main")
|
|
|
|
if pvc.Name != "workspace-main" {
|
|
t.Errorf("expected name workspace-main, got %s", pvc.Name)
|
|
}
|
|
if pvc.Namespace != "dev-alice" {
|
|
t.Errorf("expected namespace dev-alice, got %s", pvc.Namespace)
|
|
}
|
|
if len(pvc.Spec.AccessModes) != 1 || pvc.Spec.AccessModes[0] != corev1.ReadWriteOnce {
|
|
t.Errorf("expected ReadWriteOnce access mode")
|
|
}
|
|
if *pvc.Spec.StorageClassName != "longhorn" {
|
|
t.Errorf("expected longhorn storage class, got %s", *pvc.Spec.StorageClassName)
|
|
}
|
|
storage := pvc.Spec.Resources.Requests[corev1.ResourceStorage]
|
|
if storage.Cmp(resource.MustParse("20Gi")) != 0 {
|
|
t.Errorf("expected 20Gi storage, got %s", storage.String())
|
|
}
|
|
if pvc.Labels["app"] != "dev-pod" {
|
|
t.Errorf("expected label app=dev-pod, got %s", pvc.Labels["app"])
|
|
}
|
|
if pvc.Labels["podname"] != "main" {
|
|
t.Errorf("expected label podname=main, got %s", pvc.Labels["podname"])
|
|
}
|
|
}
|
|
|
|
func TestPVCTemplatePerPod(t *testing.T) {
|
|
tests := []struct {
|
|
user string
|
|
pod string
|
|
wantPVC string
|
|
}{
|
|
{"alice", "main", "workspace-main"},
|
|
{"bob", "build1", "workspace-build1"},
|
|
{"alice", "runner-42", "workspace-runner-42"},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.user+"/"+tt.pod, func(t *testing.T) {
|
|
pvc := PVCTemplate(tt.user, tt.pod)
|
|
if pvc.Name != tt.wantPVC {
|
|
t.Errorf("expected PVC name %s, got %s", tt.wantPVC, pvc.Name)
|
|
}
|
|
if pvc.Labels["podname"] != tt.pod {
|
|
t.Errorf("expected podname label %s, got %s", tt.pod, pvc.Labels["podname"])
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPVCName(t *testing.T) {
|
|
if got := PVCName("main"); got != "workspace-main" {
|
|
t.Errorf("expected workspace-main, got %s", got)
|
|
}
|
|
if got := PVCName("build1"); got != "workspace-build1" {
|
|
t.Errorf("expected workspace-build1, got %s", got)
|
|
}
|
|
}
|
|
|
|
func TestPodTemplate(t *testing.T) {
|
|
pod := PodTemplate(testCfg, testOpts)
|
|
|
|
t.Run("metadata", func(t *testing.T) {
|
|
if pod.Name != "dev-pod-main" {
|
|
t.Errorf("expected name dev-pod-main, got %s", pod.Name)
|
|
}
|
|
if pod.Namespace != "dev-alice" {
|
|
t.Errorf("expected namespace dev-alice, got %s", pod.Namespace)
|
|
}
|
|
if pod.Labels["app"] != "dev-pod" {
|
|
t.Errorf("expected label app=dev-pod, got %s", pod.Labels["app"])
|
|
}
|
|
if pod.Labels["podname"] != "main" {
|
|
t.Errorf("expected label podname=main, got %s", pod.Labels["podname"])
|
|
}
|
|
})
|
|
|
|
t.Run("host_aliases", func(t *testing.T) {
|
|
if len(pod.Spec.HostAliases) != 1 {
|
|
t.Fatalf("expected 1 host alias, got %d", len(pod.Spec.HostAliases))
|
|
}
|
|
ha := pod.Spec.HostAliases[0]
|
|
if ha.IP != "127.0.0.1" {
|
|
t.Errorf("expected IP 127.0.0.1, got %s", ha.IP)
|
|
}
|
|
if len(ha.Hostnames) != 2 {
|
|
t.Fatalf("expected 2 hostnames, got %d", len(ha.Hostnames))
|
|
}
|
|
if ha.Hostnames[0] != "anthropic.internal" || ha.Hostnames[1] != "openai.internal" {
|
|
t.Errorf("unexpected hostnames: %v", ha.Hostnames)
|
|
}
|
|
})
|
|
|
|
t.Run("containers_count", func(t *testing.T) {
|
|
if len(pod.Spec.Containers) != 3 {
|
|
t.Fatalf("expected 3 containers, got %d", len(pod.Spec.Containers))
|
|
}
|
|
})
|
|
|
|
t.Run("dev_container", func(t *testing.T) {
|
|
dev := pod.Spec.Containers[0]
|
|
if dev.Name != "dev" {
|
|
t.Errorf("expected container name dev, got %s", dev.Name)
|
|
}
|
|
if dev.Image != "10.22.0.56:30500/dev-golden:v2" {
|
|
t.Errorf("expected golden image, got %s", dev.Image)
|
|
}
|
|
if dev.ImagePullPolicy != corev1.PullAlways {
|
|
t.Errorf("expected PullAlways, got %s", dev.ImagePullPolicy)
|
|
}
|
|
if len(dev.Ports) != 5 {
|
|
t.Errorf("expected 5 ports, got %d", len(dev.Ports))
|
|
}
|
|
|
|
// Check resource requests
|
|
cpuReq := dev.Resources.Requests[corev1.ResourceCPU]
|
|
if cpuReq.Cmp(resource.MustParse("2")) != 0 {
|
|
t.Errorf("expected cpu req 2, got %s", cpuReq.String())
|
|
}
|
|
memLim := dev.Resources.Limits[corev1.ResourceMemory]
|
|
if memLim.Cmp(resource.MustParse("8Gi")) != 0 {
|
|
t.Errorf("expected mem limit 8Gi, got %s", memLim.String())
|
|
}
|
|
|
|
// Check env vars
|
|
envMap := make(map[string]string)
|
|
for _, e := range dev.Env {
|
|
envMap[e.Name] = e.Value
|
|
}
|
|
if envMap["TASK_DESCRIPTION"] != "build a web server" {
|
|
t.Errorf("unexpected TASK_DESCRIPTION: %s", envMap["TASK_DESCRIPTION"])
|
|
}
|
|
if envMap["DEV_TOOLS"] != "go@1.25,rust@1.94" {
|
|
t.Errorf("unexpected DEV_TOOLS: %s", envMap["DEV_TOOLS"])
|
|
}
|
|
if envMap["TTYD_BASE_PATH"] != "/@alice/main" {
|
|
t.Errorf("unexpected TTYD_BASE_PATH: %s", envMap["TTYD_BASE_PATH"])
|
|
}
|
|
if envMap["DEV_EXTERNAL_HOST"] != "spinoff.dev" {
|
|
t.Errorf("unexpected DEV_EXTERNAL_HOST: %s", envMap["DEV_EXTERNAL_HOST"])
|
|
}
|
|
if envMap["FORGEJO_URL"] != "http://forgejo.dev-infra.svc:3000" {
|
|
t.Errorf("unexpected FORGEJO_URL: %s", envMap["FORGEJO_URL"])
|
|
}
|
|
|
|
// Check envFrom references dev-secrets
|
|
if len(dev.EnvFrom) != 1 {
|
|
t.Fatalf("expected 1 envFrom, got %d", len(dev.EnvFrom))
|
|
}
|
|
if dev.EnvFrom[0].SecretRef.Name != "dev-secrets" {
|
|
t.Errorf("expected envFrom dev-secrets, got %s", dev.EnvFrom[0].SecretRef.Name)
|
|
}
|
|
|
|
// Check volume mount
|
|
if len(dev.VolumeMounts) != 1 || dev.VolumeMounts[0].MountPath != "/home/dev/workspace" {
|
|
t.Errorf("expected workspace mount at /home/dev/workspace")
|
|
}
|
|
})
|
|
|
|
t.Run("ai_proxy_container", func(t *testing.T) {
|
|
proxy := pod.Spec.Containers[1]
|
|
if proxy.Name != "ai-proxy" {
|
|
t.Errorf("expected container name ai-proxy, got %s", proxy.Name)
|
|
}
|
|
if proxy.Image != "nginx:alpine" {
|
|
t.Errorf("expected nginx:alpine, got %s", proxy.Image)
|
|
}
|
|
if len(proxy.VolumeMounts) != 2 {
|
|
t.Errorf("expected 2 volume mounts, got %d", len(proxy.VolumeMounts))
|
|
}
|
|
for _, vm := range proxy.VolumeMounts {
|
|
if !vm.ReadOnly {
|
|
t.Errorf("expected read-only mount for %s", vm.Name)
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("ipip_sidecar_container", func(t *testing.T) {
|
|
ipip := pod.Spec.Containers[2]
|
|
if ipip.Name != "ipip-sidecar" {
|
|
t.Errorf("expected container name ipip-sidecar, got %s", ipip.Name)
|
|
}
|
|
if ipip.Image != "10.22.0.56:30500/claw-ipip-tunnel:dev" {
|
|
t.Errorf("expected ipip tunnel image, got %s", ipip.Image)
|
|
}
|
|
if ipip.SecurityContext == nil || !*ipip.SecurityContext.Privileged {
|
|
t.Error("expected privileged security context")
|
|
}
|
|
|
|
envMap := make(map[string]string)
|
|
for _, e := range ipip.Env {
|
|
if e.ValueFrom == nil {
|
|
envMap[e.Name] = e.Value
|
|
}
|
|
}
|
|
if envMap["POD_ID"] != "dev-alice-main" {
|
|
t.Errorf("unexpected POD_ID: %s", envMap["POD_ID"])
|
|
}
|
|
if envMap["VPN_GATEWAY_HOST"] != "vpn-gateway.claw-system.svc" {
|
|
t.Errorf("unexpected VPN_GATEWAY_HOST: %s", envMap["VPN_GATEWAY_HOST"])
|
|
}
|
|
|
|
// Check VPN_GATEWAY_KEY comes from secret ref
|
|
var vpnKeyEnv *corev1.EnvVar
|
|
for i := range ipip.Env {
|
|
if ipip.Env[i].Name == "VPN_GATEWAY_KEY" {
|
|
vpnKeyEnv = &ipip.Env[i]
|
|
break
|
|
}
|
|
}
|
|
if vpnKeyEnv == nil || vpnKeyEnv.ValueFrom == nil || vpnKeyEnv.ValueFrom.SecretKeyRef == nil {
|
|
t.Fatal("expected VPN_GATEWAY_KEY from secret ref")
|
|
}
|
|
if vpnKeyEnv.ValueFrom.SecretKeyRef.Name != "dev-secrets" {
|
|
t.Errorf("expected secret name dev-secrets, got %s", vpnKeyEnv.ValueFrom.SecretKeyRef.Name)
|
|
}
|
|
})
|
|
|
|
t.Run("volumes", func(t *testing.T) {
|
|
if len(pod.Spec.Volumes) != 3 {
|
|
t.Fatalf("expected 3 volumes, got %d", len(pod.Spec.Volumes))
|
|
}
|
|
volumeNames := make(map[string]bool)
|
|
for _, v := range pod.Spec.Volumes {
|
|
volumeNames[v.Name] = true
|
|
}
|
|
for _, name := range []string{"workspace", "ai-proxy-config", "ai-proxy-secrets"} {
|
|
if !volumeNames[name] {
|
|
t.Errorf("missing volume %s", name)
|
|
}
|
|
}
|
|
// Verify workspace volume references per-pod PVC
|
|
wsVol := pod.Spec.Volumes[0]
|
|
if wsVol.PersistentVolumeClaim.ClaimName != "workspace-main" {
|
|
t.Errorf("expected workspace PVC claim workspace-main, got %s", wsVol.PersistentVolumeClaim.ClaimName)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestServiceTemplate(t *testing.T) {
|
|
svc := ServiceTemplate("alice", "main")
|
|
|
|
if svc.Name != "dev-pod-main-svc" {
|
|
t.Errorf("expected name dev-pod-main-svc, got %s", svc.Name)
|
|
}
|
|
if svc.Namespace != "dev-alice" {
|
|
t.Errorf("expected namespace dev-alice, got %s", svc.Namespace)
|
|
}
|
|
if svc.Spec.Type != corev1.ServiceTypeClusterIP {
|
|
t.Errorf("expected ClusterIP, got %s", svc.Spec.Type)
|
|
}
|
|
if len(svc.Spec.Ports) != 5 {
|
|
t.Errorf("expected 5 ports, got %d", len(svc.Spec.Ports))
|
|
}
|
|
|
|
portMap := make(map[string]int32)
|
|
for _, p := range svc.Spec.Ports {
|
|
portMap[p.Name] = p.Port
|
|
}
|
|
expected := map[string]int32{
|
|
"ttyd": 7681, "ssh": 22, "forgejo": 3000,
|
|
"vscode": 8080, "ralphex-gerrit": 8090,
|
|
}
|
|
for name, port := range expected {
|
|
if portMap[name] != port {
|
|
t.Errorf("expected port %s=%d, got %d", name, port, portMap[name])
|
|
}
|
|
}
|
|
|
|
if svc.Spec.Selector["app"] != "dev-pod" || svc.Spec.Selector["podname"] != "main" {
|
|
t.Errorf("unexpected selector: %v", svc.Spec.Selector)
|
|
}
|
|
}
|
|
|
|
func TestIngressTemplate(t *testing.T) {
|
|
objs := IngressTemplate("alice", "main", "spinoff.dev")
|
|
|
|
if len(objs) != 3 {
|
|
t.Fatalf("expected 3 objects (ingress + 2 middleware), got %d", len(objs))
|
|
}
|
|
|
|
t.Run("ingress_route", func(t *testing.T) {
|
|
ir := objs[0]
|
|
if ir.GetKind() != "IngressRoute" {
|
|
t.Errorf("expected kind IngressRoute, got %s", ir.GetKind())
|
|
}
|
|
if ir.GetName() != "dev-pod-main-ingress" {
|
|
t.Errorf("expected name dev-pod-main-ingress, got %s", ir.GetName())
|
|
}
|
|
if ir.GetNamespace() != "dev-alice" {
|
|
t.Errorf("expected namespace dev-alice, got %s", ir.GetNamespace())
|
|
}
|
|
|
|
spec := ir.Object["spec"].(map[string]interface{})
|
|
routes := spec["routes"].([]interface{})
|
|
if len(routes) != 5 {
|
|
t.Errorf("expected 5 routes, got %d", len(routes))
|
|
}
|
|
|
|
// Check first route is ttyd (no explicit priority — Traefik v3 auto-calculates)
|
|
r0 := routes[0].(map[string]interface{})
|
|
if _, hasPriority := r0["priority"]; hasPriority {
|
|
t.Errorf("expected no explicit priority, got %v", r0["priority"])
|
|
}
|
|
if r0["match"] != "Host(`spinoff.dev`) && PathPrefix(`/@alice/main/`)" {
|
|
t.Errorf("unexpected match: %s", r0["match"])
|
|
}
|
|
|
|
// Check last route has strip prefix middleware
|
|
r4 := routes[4].(map[string]interface{})
|
|
middlewares := r4["middlewares"].([]interface{})
|
|
if len(middlewares) != 2 {
|
|
t.Errorf("expected 2 middlewares for ralphex route, got %d", len(middlewares))
|
|
}
|
|
})
|
|
|
|
t.Run("basic_auth_middleware", func(t *testing.T) {
|
|
mw := objs[1]
|
|
if mw.GetKind() != "Middleware" {
|
|
t.Errorf("expected kind Middleware, got %s", mw.GetKind())
|
|
}
|
|
if mw.GetName() != "spinoff-basic-auth" {
|
|
t.Errorf("expected name spinoff-basic-auth, got %s", mw.GetName())
|
|
}
|
|
})
|
|
|
|
t.Run("strip_prefix_middleware", func(t *testing.T) {
|
|
mw := objs[2]
|
|
if mw.GetName() != "strip-dev-main-ralphex-prefix" {
|
|
t.Errorf("expected name strip-dev-main-ralphex-prefix, got %s", mw.GetName())
|
|
}
|
|
spec := mw.Object["spec"].(map[string]interface{})
|
|
stripPrefix := spec["stripPrefix"].(map[string]interface{})
|
|
prefixes := stripPrefix["prefixes"].([]interface{})
|
|
if len(prefixes) != 1 || prefixes[0] != "/@alice/main/ralphex" {
|
|
t.Errorf("unexpected prefixes: %v", prefixes)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestSecretsTemplate(t *testing.T) {
|
|
devSec, aiSec := SecretsTemplate("alice", "vpn-key-123", "br_anthropic_real", "br_openai_real", "forgejo-tok-abc", "tskey-auth-abc123")
|
|
|
|
t.Run("dev_secrets", func(t *testing.T) {
|
|
if devSec.Name != "dev-secrets" {
|
|
t.Errorf("expected name dev-secrets, got %s", devSec.Name)
|
|
}
|
|
if devSec.Namespace != "dev-alice" {
|
|
t.Errorf("expected namespace dev-alice, got %s", devSec.Namespace)
|
|
}
|
|
if devSec.StringData["ANTHROPIC_API_KEY"] != "sk-devpod" {
|
|
t.Errorf("expected dummy anthropic key, got %s", devSec.StringData["ANTHROPIC_API_KEY"])
|
|
}
|
|
if devSec.StringData["ANTHROPIC_BASE_URL"] != "http://anthropic.internal" {
|
|
t.Errorf("unexpected base url: %s", devSec.StringData["ANTHROPIC_BASE_URL"])
|
|
}
|
|
if devSec.StringData["BASEROUTE_OPENAI_KEY"] != "sk-devpod" {
|
|
t.Errorf("expected dummy openai key, got %s", devSec.StringData["BASEROUTE_OPENAI_KEY"])
|
|
}
|
|
if devSec.StringData["VPN_GATEWAY_KEY"] != "vpn-key-123" {
|
|
t.Errorf("expected vpn key vpn-key-123, got %s", devSec.StringData["VPN_GATEWAY_KEY"])
|
|
}
|
|
if devSec.StringData["FORGEJO_TOKEN"] != "forgejo-tok-abc" {
|
|
t.Errorf("expected forgejo token forgejo-tok-abc, got %s", devSec.StringData["FORGEJO_TOKEN"])
|
|
}
|
|
if devSec.StringData["TAILSCALE_AUTHKEY"] != "tskey-auth-abc123" {
|
|
t.Errorf("expected tailscale key tskey-auth-abc123, got %s", devSec.StringData["TAILSCALE_AUTHKEY"])
|
|
}
|
|
})
|
|
|
|
t.Run("ai_proxy_secrets", func(t *testing.T) {
|
|
if aiSec.Name != "ai-proxy-secrets" {
|
|
t.Errorf("expected name ai-proxy-secrets, got %s", aiSec.Name)
|
|
}
|
|
if aiSec.Namespace != "dev-alice" {
|
|
t.Errorf("expected namespace dev-alice, got %s", aiSec.Namespace)
|
|
}
|
|
if aiSec.StringData["anthropic-key"] != "br_anthropic_real" {
|
|
t.Errorf("expected real anthropic key, got %s", aiSec.StringData["anthropic-key"])
|
|
}
|
|
if aiSec.StringData["openai-key"] != "br_openai_real" {
|
|
t.Errorf("expected real openai key, got %s", aiSec.StringData["openai-key"])
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestSecretsTemplate_NoTailscaleKey(t *testing.T) {
|
|
devSec, _ := SecretsTemplate("alice", "vpn-key-123", "br_anthropic_real", "br_openai_real", "forgejo-tok-abc", "")
|
|
if _, exists := devSec.StringData["TAILSCALE_AUTHKEY"]; exists {
|
|
t.Error("TAILSCALE_AUTHKEY should not be present when tailscale key is empty")
|
|
}
|
|
}
|
|
|
|
func TestNetworkPolicyTemplate(t *testing.T) {
|
|
np := NetworkPolicyTemplate("alice")
|
|
|
|
if np.Name != "dev-pod-network-policy" {
|
|
t.Errorf("expected name dev-pod-network-policy, got %s", np.Name)
|
|
}
|
|
if np.Namespace != "dev-alice" {
|
|
t.Errorf("expected namespace dev-alice, got %s", np.Namespace)
|
|
}
|
|
if len(np.Spec.PolicyTypes) != 2 {
|
|
t.Errorf("expected 2 policy types, got %d", len(np.Spec.PolicyTypes))
|
|
}
|
|
|
|
t.Run("ingress_rules", func(t *testing.T) {
|
|
if len(np.Spec.Ingress) != 1 {
|
|
t.Fatalf("expected 1 ingress rule, got %d", len(np.Spec.Ingress))
|
|
}
|
|
from := np.Spec.Ingress[0].From
|
|
if len(from) != 1 {
|
|
t.Fatalf("expected 1 from peer, got %d", len(from))
|
|
}
|
|
nsLabel := from[0].NamespaceSelector.MatchLabels["kubernetes.io/metadata.name"]
|
|
if nsLabel != "kube-system" {
|
|
t.Errorf("expected kube-system, got %s", nsLabel)
|
|
}
|
|
})
|
|
|
|
t.Run("egress_rules", func(t *testing.T) {
|
|
if len(np.Spec.Egress) != 3 {
|
|
t.Fatalf("expected 3 egress rules, got %d", len(np.Spec.Egress))
|
|
}
|
|
|
|
// VPN gateway rule
|
|
vpnRule := np.Spec.Egress[0]
|
|
if len(vpnRule.To) != 1 {
|
|
t.Fatalf("expected 1 to peer in vpn rule, got %d", len(vpnRule.To))
|
|
}
|
|
nsLabel := vpnRule.To[0].NamespaceSelector.MatchLabels["kubernetes.io/metadata.name"]
|
|
if nsLabel != "claw-system" {
|
|
t.Errorf("expected claw-system, got %s", nsLabel)
|
|
}
|
|
podLabel := vpnRule.To[0].PodSelector.MatchLabels["app"]
|
|
if podLabel != "vpn-gateway" {
|
|
t.Errorf("expected vpn-gateway, got %s", podLabel)
|
|
}
|
|
|
|
// dev-infra rule
|
|
devInfraRule := np.Spec.Egress[1]
|
|
nsLabel = devInfraRule.To[0].NamespaceSelector.MatchLabels["kubernetes.io/metadata.name"]
|
|
if nsLabel != "dev-infra" {
|
|
t.Errorf("expected dev-infra, got %s", nsLabel)
|
|
}
|
|
|
|
// DNS rule
|
|
dnsRule := np.Spec.Egress[2]
|
|
if len(dnsRule.Ports) != 2 {
|
|
t.Errorf("expected 2 DNS ports, got %d", len(dnsRule.Ports))
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestAIProxyConfigMapTemplate(t *testing.T) {
|
|
cm := AIProxyConfigMapTemplate("alice")
|
|
|
|
if cm.Name != "ai-proxy-config" {
|
|
t.Errorf("expected name ai-proxy-config, got %s", cm.Name)
|
|
}
|
|
if cm.Namespace != "dev-alice" {
|
|
t.Errorf("expected namespace dev-alice, got %s", cm.Namespace)
|
|
}
|
|
if _, ok := cm.Data["nginx.conf.template"]; !ok {
|
|
t.Error("missing nginx.conf.template in ConfigMap data")
|
|
}
|
|
if _, ok := cm.Data["entrypoint.sh"]; !ok {
|
|
t.Error("missing entrypoint.sh in ConfigMap data")
|
|
}
|
|
}
|
|
|
|
func TestPodTemplateWithDifferentUsers(t *testing.T) {
|
|
tests := []struct {
|
|
user string
|
|
pod string
|
|
wantNS string
|
|
wantPod string
|
|
wantPath string
|
|
}{
|
|
{"bob", "dev", "dev-bob", "dev-pod-dev", "/@bob/dev"},
|
|
{"team-alpha", "staging", "dev-team-alpha", "dev-pod-staging", "/@team-alpha/staging"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.user+"/"+tt.pod, func(t *testing.T) {
|
|
opts := PodOpts{
|
|
User: tt.user, Pod: tt.pod,
|
|
CPUReq: "1", CPULimit: "2", MemReq: "1Gi", MemLimit: "2Gi",
|
|
}
|
|
pod := PodTemplate(testCfg, opts)
|
|
if pod.Namespace != tt.wantNS {
|
|
t.Errorf("expected namespace %s, got %s", tt.wantNS, pod.Namespace)
|
|
}
|
|
if pod.Name != tt.wantPod {
|
|
t.Errorf("expected pod name %s, got %s", tt.wantPod, pod.Name)
|
|
}
|
|
// Check TTYD_BASE_PATH
|
|
dev := pod.Spec.Containers[0]
|
|
for _, e := range dev.Env {
|
|
if e.Name == "TTYD_BASE_PATH" && e.Value != tt.wantPath {
|
|
t.Errorf("expected TTYD_BASE_PATH %s, got %s", tt.wantPath, e.Value)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|