mirror of
https://github.com/kubeshark/kubeshark.git
synced 2025-06-23 23:08:35 +00:00
423 lines
9.9 KiB
Go
423 lines
9.9 KiB
Go
package acceptanceTests
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/client-go/kubernetes"
|
|
"k8s.io/client-go/tools/clientcmd"
|
|
"k8s.io/client-go/util/homedir"
|
|
"net/http"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/up9inc/mizu/shared"
|
|
)
|
|
|
|
const (
|
|
LongRetriesCount = 100
|
|
ShortRetriesCount = 10
|
|
DefaultApiServerPort = shared.DefaultApiServerPort
|
|
DefaultNamespaceName = "mizu-tests"
|
|
DefaultServiceName = "httpbin"
|
|
DefaultEntriesCount = 50
|
|
WaitAfterTapPodsReady = 3 * time.Second
|
|
AllNamespaces = ""
|
|
)
|
|
|
|
type PodDescriptor struct {
|
|
Name string
|
|
Namespace string
|
|
}
|
|
|
|
func GetCliPath() (string, error) {
|
|
dir, filePathErr := os.Getwd()
|
|
if filePathErr != nil {
|
|
return "", filePathErr
|
|
}
|
|
|
|
cliPath := path.Join(dir, "../cli/bin/mizu_ci")
|
|
return cliPath, nil
|
|
}
|
|
|
|
func GetMizuFolderPath() (string, error) {
|
|
home, homeDirErr := os.UserHomeDir()
|
|
if homeDirErr != nil {
|
|
return "", homeDirErr
|
|
}
|
|
|
|
return path.Join(home, ".mizu"), nil
|
|
}
|
|
|
|
func GetConfigPath() (string, error) {
|
|
mizuFolderPath, mizuPathError := GetMizuFolderPath()
|
|
if mizuPathError != nil {
|
|
return "", mizuPathError
|
|
}
|
|
|
|
return path.Join(mizuFolderPath, "config.yaml"), nil
|
|
}
|
|
|
|
func GetProxyUrl(namespace string, service string) string {
|
|
return fmt.Sprintf("http://localhost:8080/api/v1/namespaces/%v/services/%v/proxy", namespace, service)
|
|
}
|
|
|
|
func GetApiServerUrl(port uint16) string {
|
|
return fmt.Sprintf("http://localhost:%v", port)
|
|
}
|
|
|
|
func NewKubernetesProvider() (*KubernetesProvider, error) {
|
|
home := homedir.HomeDir()
|
|
configLoadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: filepath.Join(home, ".kube", "config")}
|
|
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
|
|
configLoadingRules,
|
|
&clientcmd.ConfigOverrides{
|
|
CurrentContext: "",
|
|
},
|
|
)
|
|
|
|
restClientConfig, err := clientConfig.ClientConfig()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
clientSet, err := kubernetes.NewForConfig(restClientConfig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &KubernetesProvider{clientSet}, nil
|
|
}
|
|
|
|
type KubernetesProvider struct {
|
|
clientSet *kubernetes.Clientset
|
|
}
|
|
|
|
func (kp *KubernetesProvider) GetServiceExternalIp(ctx context.Context, namespace string, service string) (string, error) {
|
|
serviceObj, err := kp.clientSet.CoreV1().Services(namespace).Get(ctx, service, metav1.GetOptions{})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
externalIp := serviceObj.Status.LoadBalancer.Ingress[0].IP
|
|
return externalIp, nil
|
|
}
|
|
|
|
func SwitchKubeContextForTest(t *testing.T, newContextName string) error {
|
|
prevKubeContextName, err := GetKubeCurrentContextName()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := SetKubeCurrentContext(newContextName); err != nil {
|
|
return err
|
|
}
|
|
|
|
t.Cleanup(func() {
|
|
if err := SetKubeCurrentContext(prevKubeContextName); err != nil {
|
|
t.Errorf("failed to set Kubernetes context to %s, err: %v", prevKubeContextName, err)
|
|
t.Errorf("cleanup failed, subsequent tests may be affected")
|
|
}
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
func GetKubeCurrentContextName() (string, error) {
|
|
cmd := exec.Command("kubectl", "config", "current-context")
|
|
|
|
output, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return "", fmt.Errorf("%v, %s", err, string(output))
|
|
}
|
|
|
|
return string(bytes.TrimSpace(output)), nil
|
|
}
|
|
|
|
func SetKubeCurrentContext(contextName string) error {
|
|
cmd := exec.Command("kubectl", "config", "use-context", contextName)
|
|
|
|
if output, err := cmd.CombinedOutput(); err != nil {
|
|
return fmt.Errorf("%v, %s", err, string(output))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ApplyKubeFilesForTest(t *testing.T, kubeContext string, namespace string, filename ...string) error {
|
|
for i := range filename {
|
|
fname := filename[i]
|
|
if err := ApplyKubeFile(kubeContext, namespace, fname); err != nil {
|
|
return err
|
|
}
|
|
|
|
t.Cleanup(func() {
|
|
if err := DeleteKubeFile(kubeContext, namespace, fname); err != nil {
|
|
t.Errorf(
|
|
"failed to delete Kubernetes resources in namespace %s from filename %s, err: %v",
|
|
namespace,
|
|
fname,
|
|
err,
|
|
)
|
|
}
|
|
})
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ApplyKubeFile(kubeContext string, namespace string, filename string) (error) {
|
|
cmdArgs := []string{
|
|
"apply",
|
|
"--context", kubeContext,
|
|
"-f", filename,
|
|
}
|
|
if namespace != AllNamespaces {
|
|
cmdArgs = append(cmdArgs, "-n", namespace)
|
|
}
|
|
cmd := exec.Command("kubectl", cmdArgs...)
|
|
|
|
if output, err := cmd.CombinedOutput(); err != nil {
|
|
return fmt.Errorf("%v, %s", err, string(output))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func DeleteKubeFile(kubeContext string, namespace string, filename string) error {
|
|
cmdArgs := []string{
|
|
"delete",
|
|
"--context", kubeContext,
|
|
"-f", filename,
|
|
}
|
|
if namespace != AllNamespaces {
|
|
cmdArgs = append(cmdArgs, "-n", namespace)
|
|
}
|
|
cmd := exec.Command("kubectl", cmdArgs...)
|
|
|
|
if output, err := cmd.CombinedOutput(); err != nil {
|
|
return fmt.Errorf("%v, %s", err, string(output))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getDefaultCommandArgs() []string {
|
|
agentImageValue := os.Getenv("MIZU_CI_IMAGE")
|
|
setFlag := "--set"
|
|
telemetry := "telemetry=false"
|
|
agentImage := fmt.Sprintf("agent-image=%s", agentImageValue)
|
|
imagePullPolicy := "image-pull-policy=IfNotPresent"
|
|
headless := "headless=true"
|
|
|
|
return []string{setFlag, telemetry, setFlag, agentImage, setFlag, imagePullPolicy, setFlag, headless}
|
|
}
|
|
|
|
func GetDefaultTapCommandArgs() []string {
|
|
tapCommand := "tap"
|
|
defaultCmdArgs := getDefaultCommandArgs()
|
|
|
|
return append([]string{tapCommand}, defaultCmdArgs...)
|
|
}
|
|
|
|
func GetDefaultTapCommandArgsWithRegex(regex string) []string {
|
|
tapCommand := "tap"
|
|
defaultCmdArgs := getDefaultCommandArgs()
|
|
|
|
return append([]string{tapCommand, regex}, defaultCmdArgs...)
|
|
}
|
|
|
|
func GetDefaultLogsCommandArgs() []string {
|
|
logsCommand := "logs"
|
|
defaultCmdArgs := getDefaultCommandArgs()
|
|
|
|
return append([]string{logsCommand}, defaultCmdArgs...)
|
|
}
|
|
|
|
func GetDefaultTapNamespace() []string {
|
|
return []string{"-n", "mizu-tests"}
|
|
}
|
|
|
|
func GetDefaultConfigCommandArgs() []string {
|
|
configCommand := "config"
|
|
defaultCmdArgs := getDefaultCommandArgs()
|
|
|
|
return append([]string{configCommand}, defaultCmdArgs...)
|
|
}
|
|
|
|
func RunCypressTests(t *testing.T, cypressRunCmd string) {
|
|
cypressCmd := exec.Command("bash", "-c", cypressRunCmd)
|
|
t.Logf("running command: %v", cypressCmd.String())
|
|
out, err := cypressCmd.CombinedOutput()
|
|
if err != nil {
|
|
t.Errorf("error running cypress, error: %v, output: %v", err, string(out))
|
|
return
|
|
}
|
|
|
|
t.Logf("%s", out)
|
|
}
|
|
|
|
func RetriesExecute(retriesCount int, executeFunc func() error) error {
|
|
var lastError interface{}
|
|
|
|
for i := 0; i < retriesCount; i++ {
|
|
if err := TryExecuteFunc(executeFunc); err != nil {
|
|
lastError = err
|
|
|
|
time.Sleep(1 * time.Second)
|
|
continue
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("reached max retries count, retries count: %v, last err: %v", retriesCount, lastError)
|
|
}
|
|
|
|
func TryExecuteFunc(executeFunc func() error) (err interface{}) {
|
|
defer func() {
|
|
if panicErr := recover(); panicErr != nil {
|
|
err = panicErr
|
|
}
|
|
}()
|
|
|
|
return executeFunc()
|
|
}
|
|
|
|
func WaitTapPodsReady(apiServerUrl string) error {
|
|
resolvingUrl := fmt.Sprintf("%v/status/connectedTappersCount", apiServerUrl)
|
|
tapPodsReadyFunc := func() error {
|
|
requestResult, requestErr := ExecuteHttpGetRequest(resolvingUrl)
|
|
if requestErr != nil {
|
|
return requestErr
|
|
}
|
|
|
|
connectedTappersCount := requestResult.(float64)
|
|
if connectedTappersCount == 0 {
|
|
return fmt.Errorf("no connected tappers running")
|
|
}
|
|
time.Sleep(WaitAfterTapPodsReady)
|
|
return nil
|
|
}
|
|
|
|
return RetriesExecute(LongRetriesCount, tapPodsReadyFunc)
|
|
}
|
|
|
|
func JsonBytesToInterface(jsonBytes []byte) (interface{}, error) {
|
|
var result interface{}
|
|
if parseErr := json.Unmarshal(jsonBytes, &result); parseErr != nil {
|
|
return nil, parseErr
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func ExecuteHttpRequest(response *http.Response, requestErr error) (interface{}, error) {
|
|
if requestErr != nil {
|
|
return nil, requestErr
|
|
} else if response.StatusCode != 200 {
|
|
return nil, fmt.Errorf("invalid status code %v", response.StatusCode)
|
|
}
|
|
|
|
defer func() { response.Body.Close() }()
|
|
|
|
data, readErr := ioutil.ReadAll(response.Body)
|
|
if readErr != nil {
|
|
return nil, readErr
|
|
}
|
|
|
|
return JsonBytesToInterface(data)
|
|
}
|
|
|
|
func ExecuteHttpGetRequestWithHeaders(url string, headers map[string]string) (interface{}, error) {
|
|
request, err := http.NewRequest(http.MethodGet, url, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for headerKey, headerValue := range headers {
|
|
request.Header.Add(headerKey, headerValue)
|
|
}
|
|
|
|
client := &http.Client{}
|
|
response, requestErr := client.Do(request)
|
|
return ExecuteHttpRequest(response, requestErr)
|
|
}
|
|
|
|
func ExecuteHttpGetRequest(url string) (interface{}, error) {
|
|
response, requestErr := http.Get(url)
|
|
return ExecuteHttpRequest(response, requestErr)
|
|
}
|
|
|
|
func ExecuteHttpPostRequestWithHeaders(url string, headers map[string]string, body interface{}) (interface{}, error) {
|
|
requestBody, jsonErr := json.Marshal(body)
|
|
if jsonErr != nil {
|
|
return nil, jsonErr
|
|
}
|
|
|
|
request, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(requestBody))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
request.Header.Add("Content-Type", "application/json")
|
|
for headerKey, headerValue := range headers {
|
|
request.Header.Add(headerKey, headerValue)
|
|
}
|
|
|
|
client := &http.Client{}
|
|
response, requestErr := client.Do(request)
|
|
return ExecuteHttpRequest(response, requestErr)
|
|
}
|
|
|
|
func CleanupCommand(cmd *exec.Cmd) error {
|
|
if err := cmd.Process.Signal(syscall.SIGQUIT); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := cmd.Wait(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func GetLogsPath() (string, error) {
|
|
dir, filePathErr := os.Getwd()
|
|
if filePathErr != nil {
|
|
return "", filePathErr
|
|
}
|
|
|
|
logsPath := path.Join(dir, "mizu_logs.zip")
|
|
return logsPath, nil
|
|
}
|
|
|
|
func Contains(slice []string, containsValue string) bool {
|
|
for _, sliceValue := range slice {
|
|
if sliceValue == containsValue {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func ContainsPartOfValue(slice []string, containsValue string) bool {
|
|
for _, sliceValue := range slice {
|
|
if strings.Contains(sliceValue, containsValue) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|