194 lines
5.5 KiB
Go
194 lines
5.5 KiB
Go
package k8s
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"github.com/iliaivanov/spec-kit-remote/cmd/dev-pod-api/internal/model"
|
|
)
|
|
|
|
func defaultRunnerOpts() CreateRunnerPodOpts {
|
|
return CreateRunnerPodOpts{
|
|
User: "alice",
|
|
RunnerID: "runner-abc12345",
|
|
Tools: "go,rust",
|
|
Task: "implement feature",
|
|
RepoURL: "alice/myrepo",
|
|
Branch: "main",
|
|
CPUReq: "2",
|
|
MemReq: "4Gi",
|
|
ForgejoRunnerToken: "test-runner-token",
|
|
}
|
|
}
|
|
|
|
func TestCreateRunnerPod(t *testing.T) {
|
|
client := newTestClient()
|
|
ctx := context.Background()
|
|
|
|
if err := client.EnsureNamespace(ctx, "alice"); err != nil {
|
|
t.Fatalf("ensure namespace: %v", err)
|
|
}
|
|
|
|
opts := defaultRunnerOpts()
|
|
podName, err := client.CreateRunnerPod(ctx, opts)
|
|
if err != nil {
|
|
t.Fatalf("create runner pod: %v", err)
|
|
}
|
|
|
|
expectedPodName := model.RunnerPodName("runner-abc12345")
|
|
if podName != expectedPodName {
|
|
t.Errorf("expected pod name %q, got %q", expectedPodName, podName)
|
|
}
|
|
|
|
ns := model.NamespaceName("alice")
|
|
pod, err := client.Clientset.CoreV1().Pods(ns).Get(ctx, podName, metav1.GetOptions{})
|
|
if err != nil {
|
|
t.Fatalf("get runner pod: %v", err)
|
|
}
|
|
|
|
if pod.Labels["app"] != "dev-pod-runner" {
|
|
t.Errorf("expected app label dev-pod-runner, got %q", pod.Labels["app"])
|
|
}
|
|
if pod.Labels["runner-id"] != "runner-abc12345" {
|
|
t.Errorf("expected runner-id label runner-abc12345, got %q", pod.Labels["runner-id"])
|
|
}
|
|
if pod.Labels["user"] != "alice" {
|
|
t.Errorf("expected user label alice, got %q", pod.Labels["user"])
|
|
}
|
|
|
|
if len(pod.Spec.Containers) != 2 {
|
|
t.Fatalf("expected 2 containers (runner + ipip-sidecar), got %d", len(pod.Spec.Containers))
|
|
}
|
|
|
|
container := pod.Spec.Containers[0]
|
|
if container.Name != "runner" {
|
|
t.Errorf("expected container name runner, got %q", container.Name)
|
|
}
|
|
|
|
sidecar := pod.Spec.Containers[1]
|
|
if sidecar.Name != "ipip-sidecar" {
|
|
t.Errorf("expected sidecar name ipip-sidecar, got %q", sidecar.Name)
|
|
}
|
|
if sidecar.SecurityContext == nil || !*sidecar.SecurityContext.Privileged {
|
|
t.Error("expected ipip-sidecar to be privileged")
|
|
}
|
|
|
|
envMap := make(map[string]string)
|
|
for _, e := range container.Env {
|
|
envMap[e.Name] = e.Value
|
|
}
|
|
if envMap["RUNNER_MODE"] != "true" {
|
|
t.Error("expected RUNNER_MODE=true")
|
|
}
|
|
if envMap["RUNNER_ID"] != "runner-abc12345" {
|
|
t.Errorf("expected RUNNER_ID=runner-abc12345, got %q", envMap["RUNNER_ID"])
|
|
}
|
|
if envMap["DEV_POD_API_URL"] == "" {
|
|
t.Error("expected DEV_POD_API_URL to be set")
|
|
}
|
|
if envMap["FORGEJO_RUNNER_TOKEN"] != "test-runner-token" {
|
|
t.Errorf("expected FORGEJO_RUNNER_TOKEN=test-runner-token, got %q", envMap["FORGEJO_RUNNER_TOKEN"])
|
|
}
|
|
if envMap["RUNNER_REPO"] != "alice/myrepo" {
|
|
t.Errorf("expected RUNNER_REPO=alice/myrepo, got %q", envMap["RUNNER_REPO"])
|
|
}
|
|
if envMap["RUNNER_BRANCH"] != "main" {
|
|
t.Errorf("expected RUNNER_BRANCH=main, got %q", envMap["RUNNER_BRANCH"])
|
|
}
|
|
if envMap["DEV_TOOLS"] != "go,rust" {
|
|
t.Errorf("expected DEV_TOOLS=go,rust, got %q", envMap["DEV_TOOLS"])
|
|
}
|
|
|
|
cpuReq := container.Resources.Requests.Cpu()
|
|
if cpuReq.String() != "2" {
|
|
t.Errorf("expected cpu request 2, got %s", cpuReq.String())
|
|
}
|
|
memReq := container.Resources.Requests.Memory()
|
|
if memReq.String() != "4Gi" {
|
|
t.Errorf("expected memory request 4Gi, got %s", memReq.String())
|
|
}
|
|
}
|
|
|
|
func TestCreateRunnerPod_DefaultResources(t *testing.T) {
|
|
client := newTestClient()
|
|
ctx := context.Background()
|
|
|
|
if err := client.EnsureNamespace(ctx, "alice"); err != nil {
|
|
t.Fatalf("ensure namespace: %v", err)
|
|
}
|
|
|
|
opts := CreateRunnerPodOpts{
|
|
User: "alice",
|
|
RunnerID: "runner-defaults",
|
|
RepoURL: "alice/repo",
|
|
}
|
|
podName, err := client.CreateRunnerPod(ctx, opts)
|
|
if err != nil {
|
|
t.Fatalf("create runner pod: %v", err)
|
|
}
|
|
|
|
ns := model.NamespaceName("alice")
|
|
pod, err := client.Clientset.CoreV1().Pods(ns).Get(ctx, podName, metav1.GetOptions{})
|
|
if err != nil {
|
|
t.Fatalf("get runner pod: %v", err)
|
|
}
|
|
|
|
container := pod.Spec.Containers[0]
|
|
cpuReq := container.Resources.Requests.Cpu()
|
|
if cpuReq.String() != "2" {
|
|
t.Errorf("expected default cpu request 2, got %s", cpuReq.String())
|
|
}
|
|
memReq := container.Resources.Requests.Memory()
|
|
if memReq.String() != "4Gi" {
|
|
t.Errorf("expected default memory request 4Gi, got %s", memReq.String())
|
|
}
|
|
}
|
|
|
|
func TestDeleteRunnerPod(t *testing.T) {
|
|
client := newTestClient()
|
|
ctx := context.Background()
|
|
|
|
if err := client.EnsureNamespace(ctx, "alice"); err != nil {
|
|
t.Fatalf("ensure namespace: %v", err)
|
|
}
|
|
|
|
opts := defaultRunnerOpts()
|
|
podName, err := client.CreateRunnerPod(ctx, opts)
|
|
if err != nil {
|
|
t.Fatalf("create runner pod: %v", err)
|
|
}
|
|
|
|
if err := client.DeleteRunnerPod(ctx, "alice", podName); err != nil {
|
|
t.Fatalf("delete runner pod: %v", err)
|
|
}
|
|
|
|
ns := model.NamespaceName("alice")
|
|
pods, err := client.Clientset.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{
|
|
LabelSelector: "app=dev-pod-runner",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("list pods: %v", err)
|
|
}
|
|
if len(pods.Items) != 0 {
|
|
t.Errorf("expected 0 runner pods after delete, got %d", len(pods.Items))
|
|
}
|
|
}
|
|
|
|
func TestDeleteRunnerPod_NonExistent(t *testing.T) {
|
|
client := newTestClient()
|
|
ctx := context.Background()
|
|
|
|
err := client.DeleteRunnerPod(ctx, "alice", "nonexistent-pod")
|
|
if err != nil {
|
|
t.Fatalf("delete nonexistent pod should not fail, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRunnerPodName(t *testing.T) {
|
|
name := model.RunnerPodName("runner-abc123")
|
|
if name != "dev-pod-runner-abc123" {
|
|
t.Errorf("expected dev-pod-runner-abc123, got %s", name)
|
|
}
|
|
}
|