package handler import ( "context" "encoding/json" "fmt" "os" "os/exec" "path/filepath" "reflect" "strings" "testing" "time" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/util/retry" "github.com/wencaiwulue/kubevpn/v2/pkg/util" ) var content = `package main import ( "encoding/json" "log" "net/http" ) func main() { http.HandleFunc("/health", health) log.Println("Start listening http port 9080 ...") if err := http.ListenAndServe(":9080", nil); err != nil { panic(err) } } func health(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { w.WriteHeader(http.StatusMethodNotAllowed) return } resp, err := json.Marshal(map[string]string{ "status": "Authors is healthy in pod", }) if err != nil { w.WriteHeader(http.StatusInternalServerError) log.Println(err) return } w.Write(resp) }` func (u *ut) kubevpnSyncWithFullProxy(t *testing.T) { ctx, cancelFunc := context.WithCancel(context.Background()) defer cancelFunc() endpoint := u.kubevpnSync(t, ctx, false) u.healthChecker(t, endpoint, nil, remoteSyncPod) u.healthChecker(t, endpoint, map[string]string{"env": "test"}, remoteSyncPod) } func (u *ut) kubevpnSync(t *testing.T, ctx context.Context, isServiceMesh bool) string { path := u.writeTempFile(t) name := filepath.Base(path) dir := filepath.Dir(path) remoteDir := "/app/test" args := []string{"sync", "deploy/authors", "--debug", "--sync", fmt.Sprintf("%s:%s", dir, remoteDir), "-c", "authors", "--target-image", "ghcr.io/kubenetworks/authors:latest", } if isServiceMesh { args = append(args, "--headers", "env=test") } cmd := exec.Command("kubevpn", args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err := cmd.Run() if err != nil { t.Fatal(err) } list, err := util.GetRunningPodList(ctx, u.clientset, u.namespace, fields.OneTermEqualSelector("origin-workload", "authors").String()) if err != nil { t.Fatal(err) } if len(list) == 0 { t.Fatal("expect at least one pod") } remotePath := fmt.Sprintf("%s/%s", remoteDir, name) containerName := "authors" u.checkContent(ctx, t, list[0].Name, containerName, remotePath) go u.execServer(ctx, t, list[0].Name, containerName, remotePath) endpoint := fmt.Sprintf("http://%s:%v/health", list[0].Status.PodIP, 9080) u.healthChecker(t, endpoint, nil, remoteSyncPod) app := "authors" ip, err := u.getServiceIP(app) if err != nil { t.Fatal(err) } endpoint = fmt.Sprintf("http://%s:%v/health", ip, 9080) return endpoint } func (u *ut) execServer(ctx context.Context, t *testing.T, podName string, containerName string, remoteDir string) { for ctx.Err() == nil { output, err := util.Shell( ctx, u.clientset, u.restconfig, podName, containerName, u.namespace, []string{"go", "run", remoteDir}, ) if err != nil { t.Log(err, output) } time.Sleep(time.Second * 1) } } func (u *ut) kubevpnSyncWithServiceMesh(t *testing.T) { ctx, cancelFunc := context.WithCancel(context.Background()) defer cancelFunc() endpoint := u.kubevpnSync(t, ctx, true) u.healthChecker(t, endpoint, nil, remoteSyncOrigin) u.healthChecker(t, endpoint, map[string]string{"env": "test"}, remoteSyncPod) } func (u *ut) checkContent(ctx context.Context, t *testing.T, podName string, containerName string, remotePath string) { err := retry.OnError( wait.Backoff{Duration: time.Second, Factor: 1, Jitter: 0, Steps: 120}, func(err error) bool { return err != nil }, func() error { shell, err := util.Shell( ctx, u.clientset, u.restconfig, podName, containerName, u.namespace, []string{"cat", remotePath}, ) if err != nil { return err } if strings.TrimSpace(shell) != strings.TrimSpace(content) { t.Logf("expect: %s, but was: %s", content, shell) return fmt.Errorf("expect: %s, but was: %s", content, shell) } return nil }) if err != nil { t.Fatal(err) } } func TestCompile(t *testing.T) { u := &ut{} u.writeTempFile(t) } func (u *ut) writeTempFile(t *testing.T) string { dir := t.TempDir() file := filepath.Join(dir, "main.go") temp, err := os.Create(file) if err != nil { t.Fatal(err) } _, err = temp.WriteString(content) if err != nil { t.Fatal(err) } err = temp.Chmod(0755) if err != nil { t.Fatal(err) } err = temp.Close() if err != nil { t.Fatal(err) } absPath, err := filepath.Abs(temp.Name()) if err != nil { t.Fatal(err) } return absPath } func (u *ut) checkSyncWithFullProxyStatus(t *testing.T) { cmd := exec.Command("kubevpn", "status", "-o", "json") output, err := cmd.Output() if err != nil { t.Fatal(err, string(output)) } expect := status{List: []*connection{{ Namespace: u.namespace, Status: "connected", ProxyList: []*proxyItem{{ Namespace: u.namespace, Workload: "deployments.apps/authors", RuleList: []*proxyRule{{ Headers: nil, CurrentDevice: false, PortMap: map[int32]int32{9080: 9080, 80: 80}, }}, }}, SyncList: []*syncItem{{ Namespace: u.namespace, Workload: "deploy/authors", RuleList: []*syncRule{{}}, }}, }}} var statuses status if err = json.Unmarshal(output, &statuses); err != nil { t.Fatal(err) } if len(expect.List) == 0 || len(expect.List[0].SyncList) == 0 || len(expect.List[0].SyncList[0].RuleList) == 0 { t.Fatal("expect List[0].SyncList[0].RuleList[0] not found", string(output)) } expect.List[0].SyncList[0].RuleList[0].DstWorkload = statuses.List[0].SyncList[0].RuleList[0].DstWorkload if !reflect.DeepEqual(statuses, expect) { marshal, _ := json.Marshal(expect) marshalB, _ := json.Marshal(statuses) t.Fatalf("expect: %s, but was: %s", string(marshal), string(marshalB)) } } func (u *ut) kubevpnUnSync(t *testing.T) { cmd := exec.Command("kubevpn", "status", "-o", "json") output, err := cmd.Output() if err != nil { t.Fatal(err, string(output)) } var statuses status if err = json.Unmarshal(output, &statuses); err != nil { t.Fatal(err) } if len(statuses.List) == 0 || len(statuses.List[0].SyncList) == 0 || len(statuses.List[0].SyncList[0].RuleList) == 0 { t.Fatal("expect List[0].SyncList[0].RuleList[0] not found", string(output)) } cmd = exec.Command("kubevpn", "unsync", statuses.List[0].SyncList[0].RuleList[0].DstWorkload) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err = cmd.Run() if err != nil { t.Fatal(err) } } func (u *ut) checkSyncWithServiceMeshStatus(t *testing.T) { cmd := exec.Command("kubevpn", "status", "-o", "json") output, err := cmd.Output() if err != nil { t.Fatal(err, string(output)) } expect := status{List: []*connection{{ Namespace: u.namespace, Status: "connected", ProxyList: []*proxyItem{{ Namespace: u.namespace, Workload: "deployments.apps/authors", RuleList: []*proxyRule{{ Headers: map[string]string{"env": "test"}, CurrentDevice: false, PortMap: map[int32]int32{9080: 9080, 80: 80}, }}, }}, SyncList: []*syncItem{{ Namespace: u.namespace, Workload: "deploy/authors", RuleList: []*syncRule{{}}, }}, }}} var statuses status if err = json.Unmarshal(output, &statuses); err != nil { t.Fatal(err) } if len(expect.List) == 0 || len(expect.List[0].SyncList) == 0 || len(expect.List[0].SyncList[0].RuleList) == 0 { t.Fatal("expect List[0].SyncList[0].RuleList[0] not found", string(output)) } expect.List[0].SyncList[0].RuleList[0].DstWorkload = statuses.List[0].SyncList[0].RuleList[0].DstWorkload if !reflect.DeepEqual(statuses, expect) { marshal, _ := json.Marshal(expect) marshalB, _ := json.Marshal(statuses) t.Fatalf("expect: %s, but was: %s", string(marshal), string(marshalB)) } }