Files
kubevpn/pkg/handler/mode_sync_test.go
naison 0548c5a8a0 refactor: refactor code and fix ut (#709)
* feat: ut (#707)

* feat: ut (#708)
2025-08-24 16:46:09 +08:00

310 lines
7.5 KiB
Go

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))
}
}