diff --git a/acceptanceTests/tap_test.go b/acceptanceTests/tap_test.go index 7bd7805d7..02bca771f 100644 --- a/acceptanceTests/tap_test.go +++ b/acceptanceTests/tap_test.go @@ -77,7 +77,7 @@ func TestTap(t *testing.T) { return fmt.Errorf("unexpected entries result - Expected more than 0 entries") } - entry := entries[0].(map[string]interface{}) + entry := entries[0].(map[string]interface{}) entryUrl := fmt.Sprintf("%v/entries/%v", apiServerUrl, entry["id"]) requestResult, requestErr = executeHttpGetRequest(entryUrl) @@ -150,10 +150,7 @@ func TestTapAllNamespaces(t *testing.T) { t.Skip("ignored acceptance test") } - expectedPods := []struct{ - Name string - Namespace string - }{ + expectedPods := []PodDescriptor{ {Name: "httpbin", Namespace: "mizu-tests"}, {Name: "httpbin", Namespace: "mizu-tests2"}, } @@ -202,19 +199,7 @@ func TestTapAllNamespaces(t *testing.T) { } for _, expectedPod := range expectedPods { - podFound := false - - for _, pod := range pods { - podNamespace := pod["namespace"].(string) - podName := pod["name"].(string) - - if expectedPod.Namespace == podNamespace && strings.Contains(podName, expectedPod.Name) { - podFound = true - break - } - } - - if !podFound { + if !isPodDescriptorInPodArray(pods, expectedPod) { t.Errorf("unexpected result - expected pod not found, pod namespace: %v, pod name: %v", expectedPod.Namespace, expectedPod.Name) return } @@ -226,10 +211,7 @@ func TestTapMultipleNamespaces(t *testing.T) { t.Skip("ignored acceptance test") } - expectedPods := []struct{ - Name string - Namespace string - }{ + expectedPods := []PodDescriptor{ {Name: "httpbin", Namespace: "mizu-tests"}, {Name: "httpbin2", Namespace: "mizu-tests"}, {Name: "httpbin", Namespace: "mizu-tests2"}, @@ -288,19 +270,7 @@ func TestTapMultipleNamespaces(t *testing.T) { } for _, expectedPod := range expectedPods { - podFound := false - - for _, pod := range pods { - podNamespace := pod["namespace"].(string) - podName := pod["name"].(string) - - if expectedPod.Namespace == podNamespace && strings.Contains(podName, expectedPod.Name) { - podFound = true - break - } - } - - if !podFound { + if !isPodDescriptorInPodArray(pods, expectedPod) { t.Errorf("unexpected result - expected pod not found, pod namespace: %v, pod name: %v", expectedPod.Namespace, expectedPod.Name) return } @@ -313,10 +283,7 @@ func TestTapRegex(t *testing.T) { } regexPodName := "httpbin2" - expectedPods := []struct{ - Name string - Namespace string - }{ + expectedPods := []PodDescriptor{ {Name: regexPodName, Namespace: "mizu-tests"}, } @@ -371,19 +338,7 @@ func TestTapRegex(t *testing.T) { } for _, expectedPod := range expectedPods { - podFound := false - - for _, pod := range pods { - podNamespace := pod["namespace"].(string) - podName := pod["name"].(string) - - if expectedPod.Namespace == podNamespace && strings.Contains(podName, expectedPod.Name) { - podFound = true - break - } - } - - if !podFound { + if !isPodDescriptorInPodArray(pods, expectedPod) { t.Errorf("unexpected result - expected pod not found, pod namespace: %v, pod name: %v", expectedPod.Namespace, expectedPod.Name) return } @@ -431,7 +386,7 @@ func TestTapDryRun(t *testing.T) { resultChannel <- "fail" }() - testResult := <- resultChannel + testResult := <-resultChannel if testResult != "success" { t.Errorf("unexpected result - dry run cmd not done") } @@ -497,7 +452,7 @@ func TestTapRedact(t *testing.T) { return fmt.Errorf("unexpected entries result - Expected more than 0 entries") } - firstEntry := entries[0].(map[string]interface{}) + firstEntry := entries[0].(map[string]interface{}) entryUrl := fmt.Sprintf("%v/entries/%v", apiServerUrl, firstEntry["id"]) requestResult, requestErr = executeHttpGetRequest(entryUrl) @@ -517,7 +472,7 @@ func TestTapRedact(t *testing.T) { entryPayload := entryRequest["payload"].(map[string]interface{}) entryDetails := entryPayload["details"].(map[string]interface{}) - headers := entryDetails["headers"].([]interface{}) + headers := entryDetails["headers"].([]interface{}) for _, headerInterface := range headers { header := headerInterface.(map[string]interface{}) if header["name"].(string) != "User-Agent" { @@ -612,7 +567,7 @@ func TestTapNoRedact(t *testing.T) { return fmt.Errorf("unexpected entries result - Expected more than 0 entries") } - firstEntry := entries[0].(map[string]interface{}) + firstEntry := entries[0].(map[string]interface{}) entryUrl := fmt.Sprintf("%v/entries/%v", apiServerUrl, firstEntry["id"]) requestResult, requestErr = executeHttpGetRequest(entryUrl) @@ -632,7 +587,7 @@ func TestTapNoRedact(t *testing.T) { entryPayload := entryRequest["payload"].(map[string]interface{}) entryDetails := entryPayload["details"].(map[string]interface{}) - headers := entryDetails["headers"].([]interface{}) + headers := entryDetails["headers"].([]interface{}) for _, headerInterface := range headers { header := headerInterface.(map[string]interface{}) if header["name"].(string) != "User-Agent" { @@ -727,7 +682,7 @@ func TestTapRegexMasking(t *testing.T) { return fmt.Errorf("unexpected entries result - Expected more than 0 entries") } - firstEntry := entries[0].(map[string]interface{}) + firstEntry := entries[0].(map[string]interface{}) entryUrl := fmt.Sprintf("%v/entries/%v", apiServerUrl, firstEntry["id"]) requestResult, requestErr = executeHttpGetRequest(entryUrl) @@ -805,7 +760,7 @@ func TestTapIgnoredUserAgents(t *testing.T) { proxyUrl := getProxyUrl(defaultNamespaceName, defaultServiceName) ignoredUserAgentCustomHeader := "Ignored-User-Agent" - headers := map[string]string {"User-Agent": ignoredUserAgentValue, ignoredUserAgentCustomHeader: ""} + headers := map[string]string{"User-Agent": ignoredUserAgentValue, ignoredUserAgentCustomHeader: ""} for i := 0; i < defaultEntriesCount; i++ { if _, requestErr := executeHttpGetRequestWithHeaders(fmt.Sprintf("%v/get", proxyUrl), headers); requestErr != nil { t.Errorf("failed to send proxy request, err: %v", requestErr) @@ -823,7 +778,7 @@ func TestTapIgnoredUserAgents(t *testing.T) { ignoredUserAgentsCheckFunc := func() error { timestamp := time.Now().UnixNano() / int64(time.Millisecond) - entriesUrl := fmt.Sprintf("%v/entries?limit=%v&operator=lt×tamp=%v", apiServerUrl, defaultEntriesCount * 2, timestamp) + entriesUrl := fmt.Sprintf("%v/entries?limit=%v&operator=lt×tamp=%v", apiServerUrl, defaultEntriesCount*2, timestamp) requestResult, requestErr := executeHttpGetRequest(entriesUrl) if requestErr != nil { return fmt.Errorf("failed to get entries, err: %v", requestErr) @@ -853,7 +808,7 @@ func TestTapIgnoredUserAgents(t *testing.T) { entryPayload := entryRequest["payload"].(map[string]interface{}) entryDetails := entryPayload["details"].(map[string]interface{}) - entryHeaders := entryDetails["headers"].([]interface{}) + entryHeaders := entryDetails["headers"].([]interface{}) for _, headerInterface := range entryHeaders { header := headerInterface.(map[string]interface{}) if header["name"].(string) != ignoredUserAgentCustomHeader { @@ -973,3 +928,254 @@ func TestTapDumpLogs(t *testing.T) { return } } + +func TestDaemonSeeTraffic(t *testing.T) { + if testing.Short() { + t.Skip("ignored acceptance test") + } + + tests := []int{50} + + for _, entriesCount := range tests { + t.Run(fmt.Sprintf("%d", entriesCount), func(t *testing.T) { + cliPath, cliPathErr := getCliPath() + if cliPathErr != nil { + t.Errorf("failed to get cli path, err: %v", cliPathErr) + return + } + + tapDaemonCmdArgs := getDefaultTapCommandArgsWithDaemonMode() + + tapNamespace := getDefaultTapNamespace() + tapDaemonCmdArgs = append(tapDaemonCmdArgs, tapNamespace...) + + tapCmd := exec.Command(cliPath, tapDaemonCmdArgs...) + + viewCmd := exec.Command(cliPath, getDefaultViewCommandArgs()...) + + t.Cleanup(func() { + daemonCleanup(t, viewCmd) + }) + + t.Logf("running command: %v", tapCmd.String()) + if err := tapCmd.Run(); err != nil { + t.Errorf("error occured while running the tap command, err: %v", err) + return + } + + t.Logf("running command: %v", viewCmd.String()) + if err := viewCmd.Start(); err != nil { + t.Errorf("error occured while running the view command, err: %v", err) + return + } + + apiServerUrl := getApiServerUrl(defaultApiServerPort) + if err := waitTapPodsReady(apiServerUrl); err != nil { + t.Errorf("failed to start tap pods on time, err: %v", err) + return + } + + proxyUrl := getProxyUrl(defaultNamespaceName, defaultServiceName) + for i := 0; i < entriesCount; i++ { + if _, requestErr := executeHttpGetRequest(fmt.Sprintf("%v/get", proxyUrl)); requestErr != nil { + t.Errorf("failed to send proxy request, err: %v", requestErr) + return + } + } + + entriesCheckFunc := func() error { + timestamp := time.Now().UnixNano() / int64(time.Millisecond) + + entriesUrl := fmt.Sprintf("%v/entries?limit=%v&operator=lt×tamp=%v", apiServerUrl, entriesCount, timestamp) + requestResult, requestErr := executeHttpGetRequest(entriesUrl) + if requestErr != nil { + return fmt.Errorf("failed to get entries, err: %v", requestErr) + } + + entries := requestResult.([]interface{}) + if len(entries) == 0 { + return fmt.Errorf("unexpected entries result - Expected more than 0 entries") + } + + entry := entries[0].(map[string]interface{}) + + entryUrl := fmt.Sprintf("%v/entries/%v", apiServerUrl, entry["id"]) + requestResult, requestErr = executeHttpGetRequest(entryUrl) + if requestErr != nil { + return fmt.Errorf("failed to get entry, err: %v", requestErr) + } + + if requestResult == nil { + return fmt.Errorf("unexpected nil entry result") + } + + return nil + } + if err := retriesExecute(shortRetriesCount, entriesCheckFunc); err != nil { + t.Errorf("%v", err) + return + } + }) + } +} + +func TestDaemonMultipleNamespacesSeePods(t *testing.T) { + if testing.Short() { + t.Skip("ignored acceptance test") + } + + expectedPods := []PodDescriptor{ + {Name: "httpbin", Namespace: "mizu-tests"}, + {Name: "httpbin2", Namespace: "mizu-tests"}, + {Name: "httpbin", Namespace: "mizu-tests2"}, + } + + cliPath, cliPathErr := getCliPath() + if cliPathErr != nil { + t.Errorf("failed to get cli path, err: %v", cliPathErr) + return + } + + tapCmdArgs := getDefaultTapCommandArgsWithDaemonMode() + var namespacesCmd []string + for _, expectedPod := range expectedPods { + namespacesCmd = append(namespacesCmd, "-n", expectedPod.Namespace) + } + tapCmdArgs = append(tapCmdArgs, namespacesCmd...) + + tapCmd := exec.Command(cliPath, tapCmdArgs...) + + viewCmd := exec.Command(cliPath, getDefaultViewCommandArgs()...) + + t.Cleanup(func() { + daemonCleanup(t, viewCmd) + }) + + t.Logf("running command: %v", tapCmd.String()) + if err := tapCmd.Run(); err != nil { + t.Errorf("failed to start tap command, err: %v", err) + return + } + + t.Logf("running command: %v", viewCmd.String()) + if err := viewCmd.Start(); err != nil { + t.Errorf("error occured while running the view command, err: %v", err) + return + } + + apiServerUrl := getApiServerUrl(defaultApiServerPort) + if err := waitTapPodsReady(apiServerUrl); err != nil { + t.Errorf("failed to start tap pods on time, err: %v", err) + return + } + + podsUrl := fmt.Sprintf("%v/status/tap", apiServerUrl) + requestResult, requestErr := executeHttpGetRequest(podsUrl) + if requestErr != nil { + t.Errorf("failed to get tap status, err: %v", requestErr) + return + } + + pods, err := getPods(requestResult) + if err != nil { + t.Errorf("failed to get pods, err: %v", err) + return + } + + if len(expectedPods) != len(pods) { + t.Errorf("unexpected result - expected pods length: %v, actual pods length: %v", len(expectedPods), len(pods)) + return + } + + for _, expectedPod := range expectedPods { + if !isPodDescriptorInPodArray(pods, expectedPod) { + t.Errorf("unexpected result - expected pod not found, pod namespace: %v, pod name: %v", expectedPod.Namespace, expectedPod.Name) + return + } + } +} + +func TestDaemonSingleNamespaceSeePods(t *testing.T) { + if testing.Short() { + t.Skip("ignored acceptance test") + } + + expectedPods := []PodDescriptor{ + {Name: "httpbin", Namespace: "mizu-tests"}, + {Name: "httpbin2", Namespace: "mizu-tests"}, + } + unexpectedPods := []PodDescriptor{ + {Name: "httpbin", Namespace: "mizu-tests2"}, + } + + cliPath, cliPathErr := getCliPath() + if cliPathErr != nil { + t.Errorf("failed to get cli path, err: %v", cliPathErr) + return + } + + tapCmdArgs := getDefaultTapCommandArgsWithDaemonMode() + var namespacesCmd []string + for _, expectedPod := range expectedPods { + namespacesCmd = append(namespacesCmd, "-n", expectedPod.Namespace) + } + tapCmdArgs = append(tapCmdArgs, namespacesCmd...) + + tapCmd := exec.Command(cliPath, tapCmdArgs...) + + viewCmd := exec.Command(cliPath, getDefaultViewCommandArgs()...) + + t.Cleanup(func() { + daemonCleanup(t, viewCmd) + }) + + t.Logf("running command: %v", tapCmd.String()) + if err := tapCmd.Run(); err != nil { + t.Errorf("failed to start tap command, err: %v", err) + return + } + + t.Logf("running command: %v", viewCmd.String()) + if err := viewCmd.Start(); err != nil { + t.Errorf("error occured while running the view command, err: %v", err) + return + } + + apiServerUrl := getApiServerUrl(defaultApiServerPort) + if err := waitTapPodsReady(apiServerUrl); err != nil { + t.Errorf("failed to start tap pods on time, err: %v", err) + return + } + + podsUrl := fmt.Sprintf("%v/status/tap", apiServerUrl) + requestResult, requestErr := executeHttpGetRequest(podsUrl) + if requestErr != nil { + t.Errorf("failed to get tap status, err: %v", requestErr) + return + } + + pods, err := getPods(requestResult) + if err != nil { + t.Errorf("failed to get pods, err: %v", err) + return + } + + for _, unexpectedPod := range unexpectedPods { + if isPodDescriptorInPodArray(pods, unexpectedPod) { + t.Errorf("unexpected result - unexpected pod found, pod namespace: %v, pod name: %v", unexpectedPod.Namespace, unexpectedPod.Name) + return + } + } + + if len(expectedPods) != len(pods) { + t.Errorf("unexpected result - expected pods length: %v, actual pods length: %v", len(expectedPods), len(pods)) + return + } + + for _, expectedPod := range expectedPods { + if !isPodDescriptorInPodArray(pods, expectedPod) { + t.Errorf("unexpected result - expected pod not found, pod namespace: %v, pod name: %v", expectedPod.Namespace, expectedPod.Name) + return + } + } +} diff --git a/acceptanceTests/testsUtils.go b/acceptanceTests/testsUtils.go index 9c7ad955a..3569eb375 100644 --- a/acceptanceTests/testsUtils.go +++ b/acceptanceTests/testsUtils.go @@ -3,6 +3,7 @@ package acceptanceTests import ( "bytes" "encoding/json" + "errors" "fmt" "io/ioutil" "net/http" @@ -11,21 +12,40 @@ import ( "path" "strings" "syscall" + "testing" "time" "github.com/up9inc/mizu/shared" ) const ( - longRetriesCount = 100 - shortRetriesCount = 10 - defaultApiServerPort = shared.DefaultApiServerPort - defaultNamespaceName = "mizu-tests" - defaultServiceName = "httpbin" - defaultEntriesCount = 50 + longRetriesCount = 100 + shortRetriesCount = 10 + defaultApiServerPort = shared.DefaultApiServerPort + defaultNamespaceName = "mizu-tests" + defaultServiceName = "httpbin" + defaultEntriesCount = 50 waitAfterTapPodsReady = 3 * time.Second + cleanCommandTimeout = 1 * time.Minute ) +type PodDescriptor struct { + Name string + Namespace string +} + +func isPodDescriptorInPodArray(pods []map[string]interface{}, podDescriptor PodDescriptor) bool { + for _, pod := range pods { + podNamespace := pod["namespace"].(string) + podName := pod["name"].(string) + + if podDescriptor.Namespace == podNamespace && strings.Contains(podName, podDescriptor.Name) { + return true + } + } + return false +} + func getCliPath() (string, error) { dir, filePathErr := os.Getwd() if filePathErr != nil { @@ -78,6 +98,10 @@ func getDefaultTapCommandArgs() []string { return append([]string{tapCommand}, defaultCmdArgs...) } +func getDefaultTapCommandArgsWithDaemonMode() []string { + return append(getDefaultTapCommandArgs(), "--daemon") +} + func getDefaultTapCommandArgsWithRegex(regex string) []string { tapCommand := "tap" defaultCmdArgs := getDefaultCommandArgs() @@ -103,6 +127,14 @@ func getDefaultConfigCommandArgs() []string { return append([]string{configCommand}, defaultCmdArgs...) } +func getDefaultCleanCommandArgs() []string { + return []string{"clean"} +} + +func getDefaultViewCommandArgs() []string { + return []string{"view"} +} + func retriesExecute(retriesCount int, executeFunc func() error) error { var lastError interface{} @@ -205,6 +237,36 @@ func executeHttpPostRequest(url string, body interface{}) (interface{}, error) { return executeHttpRequest(response, requestErr) } +func runMizuClean() error { + cliPath, err := getCliPath() + if err != nil { + return err + } + + cleanCmdArgs := getDefaultCleanCommandArgs() + + cleanCmd := exec.Command(cliPath, cleanCmdArgs...) + + commandDone := make(chan error) + go func() { + if err := cleanCmd.Run(); err != nil { + commandDone <- err + } + commandDone <- nil + }() + + select { + case err = <- commandDone: + if err != nil { + return err + } + case <- time.After(cleanCommandTimeout): + return errors.New("clean command timed out") + } + + return nil +} + func cleanupCommand(cmd *exec.Cmd) error { if err := cmd.Process.Signal(syscall.SIGQUIT); err != nil { return err @@ -239,6 +301,16 @@ func getLogsPath() (string, error) { return logsPath, nil } +func daemonCleanup(t *testing.T, viewCmd *exec.Cmd) { + if err := runMizuClean(); err != nil { + t.Logf("error running mizu clean: %v", err) + } + + if err := cleanupCommand(viewCmd); err != nil { + t.Logf("failed to cleanup view command, err: %v", err) + } +} + func Contains(slice []string, containsValue string) bool { for _, sliceValue := range slice { if sliceValue == containsValue {