mcp-maigret
by BurtTheCoder
package main
import (
"context"
"fmt"
"os"
"os/exec"
"path"
"testing"
"time"
"github.com/strowk/foxy-contexts/pkg/foxytest"
"github.com/strowk/mcp-k8s-go/internal/k8s"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestListContexts(t *testing.T) {
ts, err := foxytest.Read("testdata/k8s_contexts")
if err != nil {
t.Fatal(err)
}
os.Setenv("KUBECONFIG", "./testdata/k8s_contexts/kubeconfig")
defer os.Unsetenv("KUBECONFIG")
ts.WithExecutable("go", []string{"run", "main.go"})
cntrl := foxytest.NewTestRunner(t)
ts.Run(cntrl)
ts.AssertNoErrors(cntrl)
}
func TestLists(t *testing.T) {
ts, err := foxytest.Read("testdata")
if err != nil {
t.Fatal(err)
}
ts.WithLogging()
ts.WithExecutable("go", []string{"run", "main.go"})
cntrl := foxytest.NewTestRunner(t)
ts.Run(cntrl)
ts.AssertNoErrors(cntrl)
}
const k3dClusterName = "mcp-k8s-integration-test"
func TestInK3dCluster(t *testing.T) {
testSuites := []string{
"testdata/with_k3d",
"internal/k8s/apps/v1/deployment",
"internal/k8s/core/v1/pod",
}
withK3dCluster(t, k3dClusterName, func() {
nginxImage := "nginx:1.27.3"
busyboxImage := "busybox:1.37.0"
preloadImage(t, nginxImage, k3dClusterName)
preloadImage(t, busyboxImage, k3dClusterName)
createTestNamespace(t, "test")
createPod(t, "nginx", nginxImage)
createPod(t, "busybox", busyboxImage, "--", "sh", "-c", "echo HELLO ; tail -f /dev/null")
createPodService(t, "nginx", "nginx-headless", "None")
for _, suite := range testSuites {
ts, err := foxytest.Read(suite)
if err != nil {
t.Fatal(err)
}
manifestsFolder := fmt.Sprintf("%s/test_manifests", suite)
// if exists, apply manifests specific to particular testsuite
if _, err := os.Stat(manifestsFolder); err == nil {
namespaceName := fmt.Sprintf("test-%s", path.Base(suite))
createTestNamespace(t, namespaceName)
cmd := exec.Command("kubectl", "apply", "-f", manifestsFolder)
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
t.Fatal(err)
}
}
ts.WithLogging()
ts.WithExecutable("go", []string{"run", "main.go"})
cntrl := foxytest.NewTestRunner(t)
ts.Run(cntrl)
ts.AssertNoErrors(cntrl)
}
})
}
const kubeconfigPath = "testdata/with_k3d/kubeconfig"
func withK3dCluster(t *testing.T, name string, fn func()) {
t.Helper()
cmd := exec.Command("k3d", "cluster", "delete", name)
cmd.Stderr = os.Stderr
err := cmd.Run() // precleanup if cluster has leaked from previous test
if err != nil {
t.Logf("error in preclean: %v", err)
}
defer deleteK3dCluster(t, name)
t.Log("creating k3d cluster", name)
createK3dCluster(t, name)
saveKubeconfig(t, name)
t.Log("waiting till k3d cluster is ready")
waitForClusterReady(t)
fn()
}
func createTestNamespace(t *testing.T, name string) {
t.Helper()
cmd := exec.Command("kubectl", "create", "namespace", name)
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
t.Fatal(err)
}
}
func createPod(t *testing.T, name string, image string, args ...string) {
t.Helper()
allargs := []string{"-n", "test", "run", name, "--image=" + image, "--restart=Never"}
allargs = append(allargs, args...)
cmd := exec.Command("kubectl", allargs...)
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
t.Fatal(err)
}
t.Logf("waiting for pod %s to be running", name)
// wait for pod to be running
cmd = exec.Command("kubectl", "-n", "test", "wait", "--for=condition=Ready", "--timeout=5m", "pod", name)
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
t.Fatal(err)
}
}
func createPodService(t *testing.T, podName string, serviceName string, clusterIp string) {
t.Helper()
cmd := exec.Command("kubectl", "expose", "-n", "test", "pod", podName, "--port", "80", "--target-port", "80", "--name", serviceName, "--cluster-ip", clusterIp)
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
t.Fatal(err)
}
}
func createK3dCluster(t *testing.T, name string) {
t.Helper()
cmd := exec.Command("k3d", "cluster", "create", name, "--wait", "--no-lb", "--timeout", "5m")
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
t.Fatal(err)
}
saveKubeconfig(t, name)
}
func saveKubeconfig(t *testing.T, name string) {
os.Setenv("KUBECONFIG", kubeconfigPath)
// write kubeconfig to file
data, err := exec.Command("k3d", "kubeconfig", "get", name).Output()
if err != nil {
t.Fatal(err)
}
err = os.WriteFile(kubeconfigPath, data, 0644)
if err != nil {
t.Fatal(err)
}
}
func waitForClusterReady(t *testing.T) {
// wait till all kube-system pods are running
clients, err := k8s.GetKubeClientset()
if err != nil {
t.Fatal(err)
}
timeout := time.After(5 * time.Minute)
ticker := time.NewTicker(5 * time.Second)
t.Log("waiting for kube-system pods to be created")
waiting:
for {
select {
case <-timeout:
t.Fatal("timed out waiting for kube-system pods to be created")
case <-ticker.C:
pods, err := clients.CoreV1().Pods("kube-system").List(context.Background(), metav1.ListOptions{})
if err != nil {
t.Fatal(err)
}
if len(pods.Items) > 0 {
break waiting
}
}
}
// This is temporarily disabled, as it makes tests slower, while we actually don't need it at the moment
// t.Log("waiting for kube-system pods to start")
// cmd := exec.Command("kubectl", "wait", "--for=condition=Ready", "--timeout=5m", "pod", "--all", "-n", "kube-system")
// cmd.Stderr = os.Stderr
// err = cmd.Run()
// if err != nil {
// t.Fatal(err)
// }
}
func deleteK3dCluster(t *testing.T, name string) {
t.Helper()
t.Log("deleting k3d cluster", name)
cmd := exec.Command("k3d", "cluster", "delete", name)
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
t.Error(err)
}
t.Log("removing kubeconfig file")
// remove kubeconfig file
err = os.Remove(kubeconfigPath)
if err != nil {
t.Error(err)
}
}
// preloadImage pulls the image and imports it into the k3d cluster
// this is needed to speed up the tests, as repeated runs would reuse
// the image from the local docker cache
func preloadImage(t *testing.T, image string, clusterName string) {
t.Helper()
t.Log("preloading image", image)
cmd := exec.Command("docker", "pull", image)
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
t.Fatal(err)
}
cmd = exec.Command("k3d", "image", "import", image, "-c", clusterName)
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
t.Fatal(err)
}
}