From 0f0040226ebb97270fae721c89978975bef44b02 Mon Sep 17 00:00:00 2001 From: pawarpranav83 Date: Thu, 8 Feb 2024 08:52:20 +0530 Subject: [PATCH] removed code duplication Signed-off-by: pawarpranav83 --- Dockerfiles/ig-tests.Dockerfile | 4 +- Makefile | 3 +- integration/all/Makefile | 52 +++ integration/all/integration_test.go | 421 ++++++++++++++++++ integration/all/profile_tcprtt_test.go | 243 ++++++++++ integration/all/trace_open_test.go | 112 +++++ integration/common/profile_cpu.go | 64 +++ integration/common/top_ebpf.go | 63 +++ integration/helpers.go | 19 + integration/ig/k8s/profile_cpu_test.go | 75 ++-- integration/ig/k8s/top_ebpf_test.go | 43 +- integration/ig/non-k8s/top_ebpf_test.go | 40 +- .../inspektor-gadget/profile_cpu_test.go | 49 +- integration/inspektor-gadget/top_ebpf_test.go | 57 +-- 14 files changed, 1041 insertions(+), 204 deletions(-) create mode 100644 integration/all/Makefile create mode 100644 integration/all/integration_test.go create mode 100644 integration/all/profile_tcprtt_test.go create mode 100644 integration/all/trace_open_test.go create mode 100644 integration/common/profile_cpu.go create mode 100644 integration/common/top_ebpf.go diff --git a/Dockerfiles/ig-tests.Dockerfile b/Dockerfiles/ig-tests.Dockerfile index 950640d61e9..8f8bf2c3ef6 100644 --- a/Dockerfiles/ig-tests.Dockerfile +++ b/Dockerfiles/ig-tests.Dockerfile @@ -10,7 +10,7 @@ COPY go.mod go.sum /cache/ RUN cd /cache && \ go mod download ADD . /go/src/github.com/inspektor-gadget/inspektor-gadget -WORKDIR /go/src/github.com/inspektor-gadget/inspektor-gadget/integration/ig/k8s +WORKDIR /go/src/github.com/inspektor-gadget/inspektor-gadget/integration/all RUN CGO_ENABLED=0 GOARCH=${TARGETARCH} go test -c -o ig-integration-${TARGETARCH}.test ./... FROM ${BASE_IMAGE} @@ -23,5 +23,5 @@ LABEL org.opencontainers.image.licenses=Apache-2.0 ARG TARGETARCH -COPY --from=builder /go/src/github.com/inspektor-gadget/inspektor-gadget/integration/ig/k8s/ig-integration-${TARGETARCH}.test /usr/bin/ig-integration.test +COPY --from=builder /go/src/github.com/inspektor-gadget/inspektor-gadget/integration/all/ig-integration-${TARGETARCH}.test /usr/bin/ig-integration.test ENTRYPOINT ["/usr/bin/ig-integration.test"] diff --git a/Makefile b/Makefile index 1202176b10b..593fccddae3 100644 --- a/Makefile +++ b/Makefile @@ -256,7 +256,7 @@ ig-tests: .PHONY: integration-tests integration-tests: kubectl-gadget KUBECTL_GADGET="$(shell pwd)/kubectl-gadget" \ - go test ./integration/inspektor-gadget/... \ + go test ./integration/all/... \ -v \ -integration \ -timeout 30m \ @@ -266,6 +266,7 @@ integration-tests: kubectl-gadget -dnstester-image $(DNSTESTER_IMAGE) \ -gadget-repository $(GADGET_REPOSITORY) \ -gadget-tag $(GADGET_TAG) \ + -test-component=inspektor-gadget \ $$INTEGRATION_TESTS_PARAMS diff --git a/integration/all/Makefile b/integration/all/Makefile new file mode 100644 index 00000000000..cc333cf9025 --- /dev/null +++ b/integration/all/Makefile @@ -0,0 +1,52 @@ +include $(shell pwd)/../../minikube.mk + +DNSTESTER_IMAGE ?= "ghcr.io/inspektor-gadget/dnstester:latest" + +# make does not allow implicit rules (with '%') to be phony so let's use +# the 'phony_explicit' dependency to make implicit rules inherit the phony +# attribute +.PHONY: phony_explicit +phony_explicit: + + +build: + make -C $(shell pwd)/../.. ig + +.PHONY: build-tests +build-tests: + docker buildx build --load -t ig-tests -f ./../../Dockerfiles/ig-tests.Dockerfile ../../ + docker create --name ig-tests-container ig-tests + docker cp ig-tests-container:/usr/bin/ig-integration.test ig-integration.test + docker rm ig-tests-container + chmod +x ig-integration.test + +# test + +TEST_TARGETS = \ + test-docker \ + test-containerd \ + test-cri-o + +.PHONY: test-all +test-all: $(TEST_TARGETS) test + +test: test-$(CONTAINER_RUNTIME) + + +# INTEGRATION_TESTS_PARAMS can be used to pass additional parameters locally e.g +# INTEGRATION_TESTS_PARAMS="-test.run TestListContainers" CONTAINER_RUNTIME=containerd make -C integration/ig/k8s test +.PHONY: phony_explicit +test-%: build build-tests + export MINIKUBE_PROFILE=minikube-$* && \ + echo "Checking minikube with profile $${MINIKUBE_PROFILE} is running ..." && \ + $(MINIKUBE) status -p $${MINIKUBE_PROFILE} -f {{.APIServer}} >/dev/null || (echo "Error: $${MINIKUBE_PROFILE} not running, exiting ..." && exit 1) && \ + echo "Preparing minikube with profile $${MINIKUBE_PROFILE} for testing ..." && \ + $(MINIKUBE) cp ../../ig-linux-amd64 $${MINIKUBE_PROFILE}:/bin/ig >/dev/null && \ + $(MINIKUBE) ssh sudo chmod +x /bin/ig && \ + $(MINIKUBE) cp ig-integration.test $${MINIKUBE_PROFILE}:/bin/ig-integration.test >/dev/null && \ + $(MINIKUBE) ssh sudo chmod +x /bin/ig-integration.test && \ + rm ig-integration.test && \ + $(MINIKUBE) -p $${MINIKUBE_PROFILE} ssh "sudo ln -sf /var/lib/minikube/binaries/$(KUBERNETES_VERSION)/kubectl /bin/kubectl" && \ + $(MINIKUBE) -p $${MINIKUBE_PROFILE} ssh "sudo ln -sf /etc/kubernetes/admin.conf /root/.kube/config" && \ + echo "Running test in minikube with profile $${MINIKUBE_PROFILE} ..." && \ + $(MINIKUBE) -p $${MINIKUBE_PROFILE} ssh "sudo ig-integration.test -test.v -test.timeout 30m -integration -dnstester-image $(DNSTESTER_IMAGE) -test-component=ig $${INTEGRATION_TESTS_PARAMS}" diff --git a/integration/all/integration_test.go b/integration/all/integration_test.go new file mode 100644 index 00000000000..98a9112a92a --- /dev/null +++ b/integration/all/integration_test.go @@ -0,0 +1,421 @@ +package main + +import ( + "errors" + "flag" + "fmt" + "os" + "os/signal" + "strings" + "sync/atomic" + "syscall" + "testing" + + . "github.com/inspektor-gadget/inspektor-gadget/integration" +) + +const ( + ContainerRuntimeDocker = "docker" + ContainerRuntimeContainerd = "containerd" + ContainerRuntimeCRIO = "cri-o" + timeout = 10 + + K8sDistroAKSAzureLinux = "aks-AzureLinux" + K8sDistroAKSUbuntu = "aks-Ubuntu" + K8sDistroARO = "aro" + K8sDistroMinikubeGH = "minikube-github" + + securityProfileOperatorNamespace = "security-profiles-operator" + topTimeoutInSeconds = 10 +) + +var ( + supportedContainerRuntimes = []string{ContainerRuntimeDocker, ContainerRuntimeContainerd, ContainerRuntimeCRIO} + supportedK8sDistros = []string{K8sDistroAKSAzureLinux, K8sDistroAKSUbuntu, K8sDistroARO, K8sDistroMinikubeGH} + cleaningUp = uint32(0) + + // containerRuntime = flag.String("container-runtime", "", "allows to do validation for expected runtime in the tests") + image = flag.String("image", "", "gadget container image") + integration = flag.Bool("integration", false, "run integration tests") + dnsTesterImage = flag.String("dnstester-image", "ghcr.io/inspektor-gadget/dnstester:latest", "dnstester container image") + testComponent = flag.String("test-component", "", "run tests for specific component") + + doNotDeployIG = flag.Bool("no-deploy-ig", false, "don't deploy Inspektor Gadget") + doNotDeploySPO = flag.Bool("no-deploy-spo", false, "don't deploy the Security Profiles Operator (SPO)") + + k8sDistro = flag.String("k8s-distro", "", "allows to skip tests that are not supported on a given Kubernetes distribution") + k8sArch = flag.String("k8s-arch", "amd64", "allows to skip tests that are not supported on a given CPU architecture") + + gadgetRepository = flag.String("gadget-repository", "ghcr.io/inspektor-gadget/gadget", "repository where gadget images are stored") + gadgetTag = flag.String("gadget-tag", "latest", "tag used for gadgets's OCI images") +) + +var ( + containerRuntime string + isDockerRuntime bool +) + +func cleanupFunc(cleanupCommands []*Command) { + if !atomic.CompareAndSwapUint32(&cleaningUp, 0, 1) { + return + } + + fmt.Println("Cleaning up...") + + // We don't want to wait for each cleanup command to finish before + // running the next one because in the case the workflow run is + // cancelled, we have few seconds (7.5s + 2.5s) before the runner kills + // the entire process tree. Therefore, let's try to, at least, launch + // the cleanup process in the cluster: + // https://docs.github.com/en/actions/managing-workflow-runs/canceling-a-workflow#steps-github-takes-to-cancel-a-workflow-run + for _, cmd := range cleanupCommands { + err := cmd.StartWithoutTest() + if err != nil { + fmt.Fprintf(os.Stderr, "%s\n", err) + } + } + + for _, cmd := range cleanupCommands { + err := cmd.WaitWithoutTest() + if err != nil { + fmt.Fprintf(os.Stderr, "%s\n", err) + } + } +} + +func testMainInspektorGadget2(m *testing.M) error { + if os.Getenv("KUBECTL_GADGET") == "" { + return fmt.Errorf("please set $KUBECTL_GADGET.") + } + + if *k8sDistro != "" { + found := false + for _, val := range supportedK8sDistros { + if *k8sDistro == val { + found = true + break + } + } + + if !found { + return fmt.Errorf("Error: invalid argument '-k8s-distro': %q. Valid values: %s\n", + *k8sDistro, strings.Join(supportedK8sDistros, ", ")) + } + } + + fmt.Printf("using random seed: %d\n", GetSeed()) + + initCommands := []*Command{} + cleanupCommands := []*Command{DeleteRemainingNamespacesCommand()} + + if !*doNotDeployIG { + imagePullPolicy := "Always" + if *k8sDistro == K8sDistroMinikubeGH { + imagePullPolicy = "Never" + } + deployCmd := DeployInspektorGadget(*image, imagePullPolicy) + initCommands = append(initCommands, deployCmd) + + cleanupCommands = append(cleanupCommands, CleanupInspektorGadget) + } + + deploySPO := !CheckNamespace(securityProfileOperatorNamespace) && !*doNotDeploySPO + if deploySPO { + limitReplicas := false + patchWebhookConfig := false + bestEffortResourceMgmt := false + if *k8sDistro == K8sDistroMinikubeGH { + limitReplicas = true + bestEffortResourceMgmt = true + } + if *k8sDistro == K8sDistroAKSUbuntu { + patchWebhookConfig = true + } + initCommands = append(initCommands, DeploySPO(limitReplicas, patchWebhookConfig, bestEffortResourceMgmt)) + cleanupCommands = append(cleanupCommands, CleanupSPO...) + } + + if CheckNamespace(securityProfileOperatorNamespace) { + fmt.Println("Using existing installation of SPO in the cluster:") + } + + notifyInitDone := make(chan bool, 1) + + cancel := make(chan os.Signal, 1) + signal.Notify(cancel, syscall.SIGINT) + + cancelling := false + notifyCancelDone := make(chan bool, 1) + + go func() { + for { + <-cancel + fmt.Printf("\nHandling cancellation...\n") + + if cancelling { + fmt.Println("Warn: Forcing cancellation. Resources couldn't have been cleaned up") + os.Exit(1) + } + cancelling = true + + go func() { + defer func() { + // This will actually never be called due to the os.Exit() + // but the notifyCancelDone channel helps to make the main + // go routing wait for the handler to finish the clean up. + notifyCancelDone <- true + }() + + // Start by stopping the init commands (in the case they are + // still running) to avoid trying to undeploy resources that are + // being deployed. + fmt.Println("Stop init commands (if they are still running)...") + for _, cmd := range initCommands { + err := cmd.KillWithoutTest() + if err != nil { + fmt.Fprintf(os.Stderr, "%s\n", err) + } + } + + // Wait until init commands have exited before starting the + // cleanup. + <-notifyInitDone + + cleanupFunc(cleanupCommands) + os.Exit(1) + }() + } + }() + + fmt.Printf("Running init commands:\n") + + initDone := true + for _, cmd := range initCommands { + if cancelling { + initDone = false + break + } + + err := cmd.RunWithoutTest() + if err != nil { + fmt.Fprintf(os.Stderr, "%s\n", err) + initDone = false + break + } + } + + // Notify the cancelling handler that the init commands finished + notifyInitDone <- initDone + + defer cleanupFunc(cleanupCommands) + + if !initDone { + // If needed, wait for the cancelling handler to finish before exiting + // from the main go routine. Otherwise, the cancelling handler will be + // terminated as well and the cleanup operations will not be completed. + if cancelling { + <-notifyCancelDone + } + + return nil + } + + return nil +} + +func testMainInspektorGadget(m *testing.M) int { + if os.Getenv("KUBECTL_GADGET") == "" { + fmt.Fprintf(os.Stderr, "please set $KUBECTL_GADGET.") + return -1 + } + + if *k8sDistro != "" { + found := false + for _, val := range supportedK8sDistros { + if *k8sDistro == val { + found = true + break + } + } + + if !found { + fmt.Fprintf(os.Stderr, "Error: invalid argument '-k8s-distro': %q. Valid values: %s\n", + *k8sDistro, strings.Join(supportedK8sDistros, ", ")) + return -1 + } + } + + fmt.Printf("using random seed: %d\n", GetSeed()) + + initCommands := []*Command{} + cleanupCommands := []*Command{DeleteRemainingNamespacesCommand()} + + if !*doNotDeployIG { + imagePullPolicy := "Always" + if *k8sDistro == K8sDistroMinikubeGH { + imagePullPolicy = "Never" + } + deployCmd := DeployInspektorGadget(*image, imagePullPolicy) + initCommands = append(initCommands, deployCmd) + + cleanupCommands = append(cleanupCommands, CleanupInspektorGadget) + } + + deploySPO := !CheckNamespace(securityProfileOperatorNamespace) && !*doNotDeploySPO + if deploySPO { + limitReplicas := false + patchWebhookConfig := false + bestEffortResourceMgmt := false + if *k8sDistro == K8sDistroMinikubeGH { + limitReplicas = true + bestEffortResourceMgmt = true + } + if *k8sDistro == K8sDistroAKSUbuntu { + patchWebhookConfig = true + } + initCommands = append(initCommands, DeploySPO(limitReplicas, patchWebhookConfig, bestEffortResourceMgmt)) + cleanupCommands = append(cleanupCommands, CleanupSPO...) + } + + if CheckNamespace(securityProfileOperatorNamespace) { + fmt.Println("Using existing installation of SPO in the cluster:") + } + + notifyInitDone := make(chan bool, 1) + + cancel := make(chan os.Signal, 1) + signal.Notify(cancel, syscall.SIGINT) + + cancelling := false + notifyCancelDone := make(chan bool, 1) + + go func() { + for { + <-cancel + fmt.Printf("\nHandling cancellation...\n") + + if cancelling { + fmt.Println("Warn: Forcing cancellation. Resources couldn't have been cleaned up") + os.Exit(1) + } + cancelling = true + + go func() { + defer func() { + // This will actually never be called due to the os.Exit() + // but the notifyCancelDone channel helps to make the main + // go routing wait for the handler to finish the clean up. + notifyCancelDone <- true + }() + + // Start by stopping the init commands (in the case they are + // still running) to avoid trying to undeploy resources that are + // being deployed. + fmt.Println("Stop init commands (if they are still running)...") + for _, cmd := range initCommands { + err := cmd.KillWithoutTest() + if err != nil { + fmt.Fprintf(os.Stderr, "%s\n", err) + } + } + + // Wait until init commands have exited before starting the + // cleanup. + <-notifyInitDone + + cleanupFunc(cleanupCommands) + os.Exit(1) + }() + } + }() + + fmt.Printf("Running init commands:\n") + + initDone := true + for _, cmd := range initCommands { + if cancelling { + initDone = false + break + } + + err := cmd.RunWithoutTest() + if err != nil { + fmt.Fprintf(os.Stderr, "%s\n", err) + initDone = false + break + } + } + + // Notify the cancelling handler that the init commands finished + notifyInitDone <- initDone + + defer cleanupFunc(cleanupCommands) + + if !initDone { + // If needed, wait for the cancelling handler to finish before exiting + // from the main go routine. Otherwise, the cancelling handler will be + // terminated as well and the cleanup operations will not be completed. + if cancelling { + <-notifyCancelDone + } + + return 1 + } + + fmt.Println("Start running tests:") + return m.Run() +} + +func testMainIG(m *testing.M) int { + fmt.Println("Start running tests:") + return m.Run() +} + +func testMain(m *testing.M) error { + var err error + + containerRuntime, err = GetContainerRuntime() + if err != nil { + return fmt.Errorf("getting container runtime: %w", err) + } + + isDockerRuntime = containerRuntime == ContainerRuntimeDocker + + if testComponent == nil { + return errors.New("-test-component' must be specified") + } + + return nil +} + +func TestMain(m *testing.M) { + flag.Parse() + if !*integration { + fmt.Println("Skipping integration test.") + os.Exit(0) + } + + err := testMain(m) + if err != nil { + fmt.Println("Error: ", err) + os.Exit(1) + } + + switch *testComponent { + case "ig": + DefaultTestComponent = IgTestComponent + os.Exit(testMainIG(m)) + + case "inspektor-gadget": + if os.Getenv("KUBECTL_GADGET") == "" { + fmt.Println("$KUBECTL_GADGET not set") + os.Exit(1) + } + DefaultTestComponent = InspektorGadgetTestComponent + os.Exit(testMainInspektorGadget(m)) + + default: + fmt.Printf("invalid argument '-test-component': %q. Valid values: ig, inspektor-gadget", *testComponent) + os.Exit(1) + } +} diff --git a/integration/all/profile_tcprtt_test.go b/integration/all/profile_tcprtt_test.go new file mode 100644 index 00000000000..a256dc53c96 --- /dev/null +++ b/integration/all/profile_tcprtt_test.go @@ -0,0 +1,243 @@ +// // Copyright 2023 The Inspektor Gadget authors +// // +// // Licensed under the Apache License, Version 2.0 (the "License"); +// // you may not use this file except in compliance with the License. +// // You may obtain a copy of the License at +// // +// // http://www.apache.org/licenses/LICENSE-2.0 +// // +// // Unless required by applicable law or agreed to in writing, software +// // distributed under the License is distributed on an "AS IS" BASIS, +// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// // See the License for the specific language governing permissions and +// // limitations under the License. + +package main + +import ( + "fmt" + "testing" + + . "github.com/inspektor-gadget/inspektor-gadget/integration" + tcprttProfileTypes "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/profile/tcprtt/types" + "github.com/inspektor-gadget/inspektor-gadget/pkg/histogram" +) + +func newProfileTCPRTTCmd(flags string, useTimeout bool, node string, unit histogram.Unit, addressType tcprttProfileTypes.AddressType, addr string, localPort uint16, remotePort uint16) *Command { + cmd := fmt.Sprintf("%s profile tcprtt -o json %s", DefaultTestComponent, flags) + + if useTimeout { + cmd += " --timeout 10" + } + if node != "" { + cmd += fmt.Sprintf(" --node %s", node) + } + + return &Command{ + Name: "ProfileTCPRTT", + Cmd: cmd, + StartAndStop: !useTimeout, + ValidateOutput: func(t *testing.T, output string) { + expectedEntry := &tcprttProfileTypes.Report{ + Histograms: []*tcprttProfileTypes.ExtendedHistogram{ + tcprttProfileTypes.NewHistogram(unit, nil, addressType, addr, 1, localPort, remotePort), + }, + } + + normalize := func(r *tcprttProfileTypes.Report) { + if len(r.Histograms) != 1 { + return + } + + r.Histograms[0].Intervals = nil + + if r.Histograms[0].Average != 0 { + r.Histograms[0].Average = 1 + } + + // TODO: Verify this also when using milliseconds once gadget + // will be able to report the total latencies between 0 and 1. + // Otherwise, the test will fail because the average latency + // will always be 0 milliseconds, so there is no way to verify + // that it was computed correctly. + if r.Histograms[0].Unit == histogram.UnitMilliseconds { + r.Histograms[0].Average = 1 + } + } + + ExpectEntriesToMatch(t, output, normalize, expectedEntry) + }, + } +} + +func TestProfileTCPRTT(t *testing.T) { + t.Parallel() + + serverPodName := "nginx-pod" + clientPodName := "test-pod" + ns := GenerateTestNamespaceName("test-profile-tcprtt") + + startServerCommands := []*Command{ + CreateTestNamespaceCommand(ns), + PodCommand(serverPodName, "nginx", ns, "", ""), + WaitUntilPodReadyCommand(ns, serverPodName), + } + RunTestSteps(startServerCommands, t, WithCbBeforeCleanup(PrintLogsFn(ns))) + + t.Cleanup(func() { + cleanupCommands := []*Command{ + DeleteTestNamespaceCommand(ns), + } + RunTestSteps(cleanupCommands, t, WithCbBeforeCleanup(PrintLogsFn(ns))) + }) + + serverIP := GetTestPodIP(t, ns, serverPodName) + + generateTrafficCommands := []*Command{ + BusyboxPodRepeatCommand(ns, fmt.Sprintf("wget -q -O /dev/null %s:80", serverIP)), + WaitUntilTestPodReadyCommand(ns), + } + RunTestSteps(generateTrafficCommands, t, WithCbBeforeCleanup(PrintLogsFn())) + + clientIP := GetTestPodIP(t, ns, clientPodName) + + // Filtering by node doesn't make sense for ig but just for kubectl-gadget. + var clientNode string + if DefaultTestComponent == InspektorGadgetTestComponent { + clientNode = GetPodNode(t, ns, clientPodName) + } + + t.Run("DefaultParams", func(t *testing.T) { + t.Parallel() + + topTCPCmd := newProfileTCPRTTCmd( + "", + true, + clientNode, + histogram.UnitMicroseconds, + tcprttProfileTypes.AddressTypeAll, + tcprttProfileTypes.WildcardAddress, + 0, + 0, + ) + RunTestSteps([]*Command{topTCPCmd}, t, WithCbBeforeCleanup(PrintLogsFn(ns))) + }) + + // TODO: Why without timeout the topTCPCmd command doesn't generate any output? + // t.Run("DefaultParamsWithoutTimeout", func(t *testing.T) { + // t.Parallel() + + // topTCPCmd := newProfileTCPRTTCmd( + // "", + // false, + // filterByNode, + // serverNode, + // histogram.UnitMicroseconds, + // tcprttProfileTypes.AddressTypeAll, + // tcprttProfileTypes.WildcardAddress, + // ) + // RunTestSteps([]*Command{topTCPCmd}, t, WithCbBeforeCleanup(PrintLogsFn(ns))) + // }) + + t.Run("FilterRemotePort", func(t *testing.T) { + t.Parallel() + + flags := "--rport 80" + topTCPCmd := newProfileTCPRTTCmd( + flags, + true, + clientNode, + histogram.UnitMicroseconds, + tcprttProfileTypes.AddressTypeAll, + tcprttProfileTypes.WildcardAddress, + 0, + 80, + ) + RunTestSteps([]*Command{topTCPCmd}, t, WithCbBeforeCleanup(PrintLogsFn(ns))) + }) + + t.Run("FilterRemoteAddr", func(t *testing.T) { + t.Parallel() + + flags := fmt.Sprintf("--raddr %s", serverIP) + topTCPCmd := newProfileTCPRTTCmd( + flags, + true, + clientNode, + histogram.UnitMicroseconds, + tcprttProfileTypes.AddressTypeAll, + tcprttProfileTypes.WildcardAddress, + 0, + 0, + ) + RunTestSteps([]*Command{topTCPCmd}, t, WithCbBeforeCleanup(PrintLogsFn(ns))) + }) + + t.Run("FilterLocalAddr", func(t *testing.T) { + t.Parallel() + + flags := fmt.Sprintf("--laddr %s", clientIP) + topTCPCmd := newProfileTCPRTTCmd( + flags, + true, + clientNode, + histogram.UnitMicroseconds, + tcprttProfileTypes.AddressTypeAll, + tcprttProfileTypes.WildcardAddress, + 0, + 0, + ) + RunTestSteps([]*Command{topTCPCmd}, t, WithCbBeforeCleanup(PrintLogsFn(ns))) + }) + + t.Run("ByRemoteAndFilterLocalAddr", func(t *testing.T) { + t.Parallel() + + flags := fmt.Sprintf("--byraddr --laddr %s", clientIP) + topTCPCmd := newProfileTCPRTTCmd( + flags, + true, + clientNode, + histogram.UnitMicroseconds, + tcprttProfileTypes.AddressTypeRemote, + serverIP, + 0, + 0, + ) + RunTestSteps([]*Command{topTCPCmd}, t, WithCbBeforeCleanup(PrintLogsFn(ns))) + }) + + t.Run("ByLocalAndFilterRemoteAddr", func(t *testing.T) { + t.Parallel() + + flags := fmt.Sprintf("--byladdr --raddr %s", serverIP) + topTCPCmd := newProfileTCPRTTCmd( + flags, + true, + clientNode, + histogram.UnitMicroseconds, + tcprttProfileTypes.AddressTypeLocal, + clientIP, + 0, + 0, + ) + RunTestSteps([]*Command{topTCPCmd}, t, WithCbBeforeCleanup(PrintLogsFn(ns))) + }) + + t.Run("MillisecondsAndFilterRemoteAndLocalAddr", func(t *testing.T) { + t.Parallel() + + flags := fmt.Sprintf("--milliseconds --raddr %s --laddr %s", serverIP, clientIP) + topTCPCmd := newProfileTCPRTTCmd( + flags, + true, + clientNode, + histogram.UnitMilliseconds, + tcprttProfileTypes.AddressTypeAll, + tcprttProfileTypes.WildcardAddress, + 0, + 0, + ) + RunTestSteps([]*Command{topTCPCmd}, t, WithCbBeforeCleanup(PrintLogsFn(ns))) + }) +} diff --git a/integration/all/trace_open_test.go b/integration/all/trace_open_test.go new file mode 100644 index 00000000000..10dea6eda34 --- /dev/null +++ b/integration/all/trace_open_test.go @@ -0,0 +1,112 @@ +// Copyright 2022 The Inspektor Gadget authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "strings" + "testing" + + . "github.com/inspektor-gadget/inspektor-gadget/integration" + openTypes "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/open/types" +) + +func TestTraceOpen(t *testing.T) { + t.Parallel() + ns := GenerateTestNamespaceName("test-trace-open") + + var extraArgs string + switch DefaultTestComponent { + case IgTestComponent: + extraArgs = "--runtimes=" + containerRuntime + case InspektorGadgetTestComponent: + extraArgs = "-n " + ns + } + + traceOpenCmd := &Command{ + Name: "TraceOpen", + Cmd: fmt.Sprintf("%s trace open -o json %s", DefaultTestComponent, extraArgs), + StartAndStop: true, + ValidateOutput: func(t *testing.T, output string) { + expectedEntry := &openTypes.Event{ + Event: BuildBaseEvent(ns, + WithRuntimeMetadata(containerRuntime), + WithContainerImageName("docker.io/library/busybox:latest", isDockerRuntime), + ), + Comm: "cat", + Fd: 3, + Ret: 3, + Err: 0, + Path: "/dev/null", + FullPath: "", + Uid: 1000, + Gid: 1111, + Flags: []string{"O_RDONLY"}, + Mode: "----------", + } + + // TODO: why the container name is not provided? + if DefaultTestComponent == InspektorGadgetTestComponent { + expectedEntry.Runtime.ContainerName = "" + } + + normalize := func(e *openTypes.Event) { + + e.Timestamp = 0 + e.MountNsID = 0 + e.Pid = 0 + e.K8s.Node = "" + + e.Runtime.ContainerID = "" + e.Runtime.ContainerImageDigest = "" + + if DefaultTestComponent == IgTestComponent { + // Docker and CRI-O use a custom container name composed, among + // other things, by the pod UID. We don't know the pod UID in + // advance, so we can't match the exact expected container name. + prefixContainerName := "k8s_" + "test-pod" + "_" + "test-pod" + "_" + ns + "_" + if (containerRuntime == ContainerRuntimeDocker || containerRuntime == ContainerRuntimeCRIO) && + strings.HasPrefix(e.Runtime.ContainerName, prefixContainerName) { + e.Runtime.ContainerName = "test-pod" + } + // Docker can provide different values for ContainerImageName. See `getContainerImageNamefromImage` + if isDockerRuntime { + e.Runtime.ContainerImageName = "" + } + } else { + e.K8s.Node = "" + // TODO: Verify container runtime and container name + e.Runtime.RuntimeName = "" + e.Runtime.ContainerName = "" + } + } + + ExpectEntriesToMatch(t, output, normalize, expectedEntry) + }, + } + + fmt.Printf("command is: %s\n", traceOpenCmd.Cmd) + + commands := []*Command{ + CreateTestNamespaceCommand(ns), + traceOpenCmd, + SleepForSecondsCommand(2), // wait to ensure ig has started + BusyboxPodRepeatCommand(ns, "setuidgid 1000:1111 cat /dev/null"), + WaitUntilTestPodReadyCommand(ns), + DeleteTestNamespaceCommand(ns), + } + + RunTestSteps(commands, t, WithCbBeforeCleanup(PrintLogsFn(ns))) +} diff --git a/integration/common/profile_cpu.go b/integration/common/profile_cpu.go new file mode 100644 index 00000000000..1e45bcec646 --- /dev/null +++ b/integration/common/profile_cpu.go @@ -0,0 +1,64 @@ +// Copyright 2022 The Inspektor Gadget authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +import ( + "testing" + + "github.com/inspektor-gadget/inspektor-gadget/integration" + cpuprofileTypes "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/profile/cpu/types" +) + +func NewProfileCPUTestCmds(cmd string, ns string, normalize func(e *cpuprofileTypes.Report), commonDataOpts ...integration.CommonDataOption) []*integration.Command { + profileCPUCmd := &integration.Command{ + Name: "ProfileCpu", + Cmd: cmd, + ValidateOutput: func(t *testing.T, output string) { + expectedEntry := &cpuprofileTypes.Report{ + CommonData: integration.BuildCommonData(ns, + commonDataOpts..., + ), + Comm: "sh", + } + + integration.ExpectEntriesToMatch(t, output, normalize, expectedEntry) + }, + } + + commands := []*integration.Command{ + integration.CreateTestNamespaceCommand(ns), + integration.BusyboxPodCommand(ns, "while true; do echo foo > /dev/null; done"), + integration.WaitUntilTestPodReadyCommand(ns), + profileCPUCmd, + integration.DeleteTestNamespaceCommand(ns), + } + + return commands +} + +func ProfileCPUNormalize(opts ...func(e *cpuprofileTypes.Report)) func(e *cpuprofileTypes.Report) { + return func(e *cpuprofileTypes.Report) { + e.Pid = 0 + e.UserStack = nil + e.KernelStack = nil + e.Count = 0 + e.Runtime.ContainerID = "" + e.Runtime.ContainerImageDigest = "" + + for _, option := range opts { + option(e) + } + } +} diff --git a/integration/common/top_ebpf.go b/integration/common/top_ebpf.go new file mode 100644 index 00000000000..c986d81dde5 --- /dev/null +++ b/integration/common/top_ebpf.go @@ -0,0 +1,63 @@ +// Copyright 2022-2023 The Inspektor Gadget authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +import ( + "testing" + + "github.com/cilium/ebpf" + "github.com/inspektor-gadget/inspektor-gadget/integration" + topebpfTypes "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/top/ebpf/types" +) + +func NewTopEbpfCmd(cmd string, startAndStop bool, normalizeOpts ...func(e *topebpfTypes.Stats)) *integration.Command { + validateOutputFn := func(t *testing.T, output string) { + expectedEntry := &topebpfTypes.Stats{ + Type: ebpf.Tracing.String(), + Name: "ig_top_ebpf_it", + } + + normalize := topEbpfNormalize(normalizeOpts...) + integration.ExpectEntriesInMultipleArrayToMatch(t, output, normalize, expectedEntry) + } + + return &integration.Command{ + Name: "TopEbpf", + ValidateOutput: validateOutputFn, + Cmd: cmd, + StartAndStop: startAndStop, + } +} + +func topEbpfNormalize(options ...func(e *topebpfTypes.Stats)) func(e *topebpfTypes.Stats) { + return func(e *topebpfTypes.Stats) { + e.ProgramID = 0 + e.Processes = nil + e.CurrentRuntime = 0 + e.CurrentRunCount = 0 + e.CumulativeRuntime = 0 + e.CumulativeRunCount = 0 + e.TotalRuntime = 0 + e.TotalRunCount = 0 + e.MapMemory = 0 + e.MapCount = 0 + e.TotalCpuUsage = 0 + e.PerCpuUsage = 0 + + for _, option := range options { + option(e) + } + } +} diff --git a/integration/helpers.go b/integration/helpers.go index 4ac078bc512..be8efd4d87b 100644 --- a/integration/helpers.go +++ b/integration/helpers.go @@ -18,6 +18,7 @@ import ( "bufio" "bytes" "encoding/json" + "fmt" "os/exec" "reflect" "strings" @@ -255,6 +256,24 @@ func CheckNamespace(ns string) bool { return cmd.Run() == nil } +// GetContainerRuntime returns the container runtime the cluster is using. +func GetContainerRuntime() (string, error) { + cmd := exec.Command("kubectl", "get", "node", "-o", "jsonpath={.items[0].status.nodeInfo.containerRuntimeVersion}") + var stderr bytes.Buffer + cmd.Stderr = &stderr + r, err := cmd.Output() + if err != nil { + return "", fmt.Errorf("getting container runtime: %w, %s", err, stderr.String()) + } + + ret := string(r) + parts := strings.Split(ret, ":") + if len(parts) < 1 { + return "", fmt.Errorf("unexpected container runtime version: %s", ret) + } + return parts[0], nil +} + // IsDockerRuntime checks whether the container runtime of the first node in the Kubernetes cluster is Docker or not. func IsDockerRuntime(t *testing.T) bool { cmd := exec.Command("kubectl", "get", "node", "-o", "jsonpath={.items[0].status.nodeInfo.containerRuntimeVersion}") diff --git a/integration/ig/k8s/profile_cpu_test.go b/integration/ig/k8s/profile_cpu_test.go index fb7d1f22db9..4b97b86240f 100644 --- a/integration/ig/k8s/profile_cpu_test.go +++ b/integration/ig/k8s/profile_cpu_test.go @@ -20,61 +20,40 @@ import ( "testing" . "github.com/inspektor-gadget/inspektor-gadget/integration" + "github.com/inspektor-gadget/inspektor-gadget/integration/common" cpuprofileTypes "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/profile/cpu/types" ) func TestProfileCpu(t *testing.T) { t.Parallel() - ns := GenerateTestNamespaceName("test-cpu-profile") - - profileCPUCmd := &Command{ - Name: "ProfileCpu", - Cmd: fmt.Sprintf("ig profile cpu -K -o json --runtimes=%s --timeout 10", *containerRuntime), - ValidateOutput: func(t *testing.T, output string) { - isDockerRuntime := *containerRuntime == ContainerRuntimeDocker - expectedEntry := &cpuprofileTypes.Report{ - CommonData: BuildCommonData(ns, - WithRuntimeMetadata(*containerRuntime), - WithContainerImageName("docker.io/library/busybox:latest", isDockerRuntime), - ), - Comm: "sh", - } - - normalize := func(e *cpuprofileTypes.Report) { - // Docker and CRI-O use a custom container name composed, among - // other things, by the pod UID. We don't know the pod UID in - // advance, so we can't match the exact expected container name. - prefixContainerName := "k8s_" + "test-pod" + "_" + "test-pod" + "_" + ns + "_" - if (*containerRuntime == ContainerRuntimeDocker || *containerRuntime == ContainerRuntimeCRIO) && - strings.HasPrefix(e.Runtime.ContainerName, prefixContainerName) { - e.Runtime.ContainerName = "test-pod" - } - - e.Pid = 0 - e.UserStack = nil - e.KernelStack = nil - e.Count = 0 - - e.Runtime.ContainerID = "" - e.Runtime.ContainerImageDigest = "" - - // Docker can provide different values for ContainerImageName. See `getContainerImageNamefromImage` - if isDockerRuntime { - e.Runtime.ContainerImageName = "" - } - } - - ExpectEntriesToMatch(t, output, normalize, expectedEntry) - }, + ns := GenerateTestNamespaceName("test-profile-cpu") + isDockerRuntime := *containerRuntime == ContainerRuntimeDocker + + normalizeOpt := func(e *cpuprofileTypes.Report) { + // Docker and CRI-O use a custom container name composed, among + // other things, by the pod UID. We don't know the pod UID in + // advance, so we can't match the exact expected container name. + prefixContainerName := "k8s_" + "test-pod" + "_" + "test-pod" + "_" + ns + "_" + if (*containerRuntime == ContainerRuntimeDocker || *containerRuntime == ContainerRuntimeCRIO) && + strings.HasPrefix(e.Runtime.ContainerName, prefixContainerName) { + e.Runtime.ContainerName = "test-pod" + } + + // Docker can provide different values for ContainerImageName. See `getContainerImageNamefromImage` + if isDockerRuntime { + e.Runtime.ContainerImageName = "" + } } - commands := []*Command{ - CreateTestNamespaceCommand(ns), - BusyboxPodCommand(ns, "while true; do echo foo > /dev/null; done"), - WaitUntilTestPodReadyCommand(ns), - profileCPUCmd, - DeleteTestNamespaceCommand(ns), - } + cmd := fmt.Sprintf("ig profile cpu -K -o json --runtimes=%s --timeout 10", *containerRuntime) + normalize := common.ProfileCPUNormalize(normalizeOpt) + commands := common.NewProfileCPUTestCmds( + cmd, + ns, + normalize, + WithRuntimeMetadata(*containerRuntime), + WithContainerImageName("docker.io/library/busybox:latest", isDockerRuntime), + ) RunTestSteps(commands, t, WithCbBeforeCleanup(PrintLogsFn(ns))) } diff --git a/integration/ig/k8s/top_ebpf_test.go b/integration/ig/k8s/top_ebpf_test.go index 2bb5893f2be..d4fa6245f96 100644 --- a/integration/ig/k8s/top_ebpf_test.go +++ b/integration/ig/k8s/top_ebpf_test.go @@ -18,45 +18,10 @@ import ( "fmt" "testing" - "github.com/cilium/ebpf" - . "github.com/inspektor-gadget/inspektor-gadget/integration" - "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/top/ebpf/types" + "github.com/inspektor-gadget/inspektor-gadget/integration/common" ) -func newTopEbpfCmd(cmd string, startAndStop bool) *Command { - validateOutputFn := func(t *testing.T, output string) { - expectedEntry := &types.Stats{ - Type: ebpf.Tracing.String(), - Name: "ig_top_ebpf_it", - } - - normalize := func(e *types.Stats) { - e.ProgramID = 0 - e.Processes = nil - e.CurrentRuntime = 0 - e.CurrentRunCount = 0 - e.CumulativeRuntime = 0 - e.CumulativeRunCount = 0 - e.TotalRuntime = 0 - e.TotalRunCount = 0 - e.MapMemory = 0 - e.MapCount = 0 - e.TotalCpuUsage = 0 - e.PerCpuUsage = 0 - } - - ExpectEntriesInMultipleArrayToMatch(t, output, normalize, expectedEntry) - } - - return &Command{ - Name: "TopEbpf", - ValidateOutput: validateOutputFn, - Cmd: cmd, - StartAndStop: startAndStop, - } -} - func TestTopEbpf(t *testing.T) { t.Parallel() @@ -64,7 +29,7 @@ func TestTopEbpf(t *testing.T) { t.Parallel() cmd := fmt.Sprintf("ig top ebpf -o json --runtimes=%s -m 100", *containerRuntime) - topEbpfCmd := newTopEbpfCmd(cmd, true) + topEbpfCmd := common.NewTopEbpfCmd(cmd, true) RunTestSteps([]*Command{topEbpfCmd}, t, WithCbBeforeCleanup(PrintLogsFn())) }) @@ -73,7 +38,7 @@ func TestTopEbpf(t *testing.T) { cmd := fmt.Sprintf("ig top ebpf -o json --runtimes=%s -m 100 --timeout %d", *containerRuntime, timeout) - topEbpfCmd := newTopEbpfCmd(cmd, false) + topEbpfCmd := common.NewTopEbpfCmd(cmd, false) RunTestSteps([]*Command{topEbpfCmd}, t, WithCbBeforeCleanup(PrintLogsFn())) }) @@ -82,7 +47,7 @@ func TestTopEbpf(t *testing.T) { cmd := fmt.Sprintf("ig top ebpf -o json --runtimes=%s -m 100 --timeout %d --interval %d", *containerRuntime, timeout, timeout) - topEbpfCmd := newTopEbpfCmd(cmd, false) + topEbpfCmd := common.NewTopEbpfCmd(cmd, false) RunTestSteps([]*Command{topEbpfCmd}, t, WithCbBeforeCleanup(PrintLogsFn())) }) } diff --git a/integration/ig/non-k8s/top_ebpf_test.go b/integration/ig/non-k8s/top_ebpf_test.go index 7a8c75ed491..b8122794b5f 100644 --- a/integration/ig/non-k8s/top_ebpf_test.go +++ b/integration/ig/non-k8s/top_ebpf_test.go @@ -17,47 +17,13 @@ package main import ( "testing" - "github.com/cilium/ebpf" - . "github.com/inspektor-gadget/inspektor-gadget/integration" - topebpfTypes "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/top/ebpf/types" + "github.com/inspektor-gadget/inspektor-gadget/integration/common" ) func TestTopEbpf(t *testing.T) { t.Parallel() - topebpfCmd := &Command{ - Name: "TopEbpf", - Cmd: "./ig top ebpf -o json -m 100 --runtimes=docker", - StartAndStop: true, - ValidateOutput: func(t *testing.T, output string) { - expectedEntry := &topebpfTypes.Stats{ - Type: ebpf.Tracing.String(), - Name: "ig_top_ebpf_it", - } - - normalize := func(e *topebpfTypes.Stats) { - e.ProgramID = 0 - e.Processes = nil - e.CurrentRuntime = 0 - e.CurrentRunCount = 0 - e.CumulativeRuntime = 0 - e.CumulativeRunCount = 0 - e.TotalRuntime = 0 - e.TotalRunCount = 0 - e.MapMemory = 0 - e.MapCount = 0 - e.TotalCpuUsage = 0 - e.PerCpuUsage = 0 - } - - ExpectEntriesInMultipleArrayToMatch(t, output, normalize, expectedEntry) - }, - } - - commands := []*Command{ - topebpfCmd, - } - - RunTestSteps(commands, t) + topEbpfCmd := common.NewTopEbpfCmd("./ig top ebpf -o json -m 100 --runtimes=docker", true) + RunTestSteps([]*Command{topEbpfCmd}, t) } diff --git a/integration/inspektor-gadget/profile_cpu_test.go b/integration/inspektor-gadget/profile_cpu_test.go index e60f1fe520c..0e431cdff4a 100644 --- a/integration/inspektor-gadget/profile_cpu_test.go +++ b/integration/inspektor-gadget/profile_cpu_test.go @@ -18,51 +18,34 @@ import ( "fmt" "testing" + "github.com/inspektor-gadget/inspektor-gadget/integration/common" profilecpuTypes "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/profile/cpu/types" . "github.com/inspektor-gadget/inspektor-gadget/integration" ) func TestProfileCpu(t *testing.T) { - ns := GenerateTestNamespaceName("test-profile-cpu") - t.Parallel() + ns := GenerateTestNamespaceName("test-profile-cpu") // TODO: Handle it once we support getting container image name from docker isDockerRuntime := IsDockerRuntime(t) - commands := []*Command{ - CreateTestNamespaceCommand(ns), - BusyboxPodCommand(ns, "while true; do echo foo > /dev/null; done"), - WaitUntilTestPodReadyCommand(ns), - { - Name: "RunProfileCpuGadget", - Cmd: fmt.Sprintf("$KUBECTL_GADGET profile cpu -n %s -p test-pod -K --timeout 15 -o json", ns), - ValidateOutput: func(t *testing.T, output string) { - expectedEntry := &profilecpuTypes.Report{ - CommonData: BuildCommonData(ns, WithContainerImageName("docker.io/library/busybox:latest", isDockerRuntime)), - Comm: "sh", - } - - normalize := func(e *profilecpuTypes.Report) { - e.Pid = 0 - e.UserStack = nil - e.KernelStack = nil - e.Count = 0 - - e.K8s.Node = "" - // TODO: Verify container runtime and container name - e.Runtime.RuntimeName = "" - e.Runtime.ContainerName = "" - e.Runtime.ContainerID = "" - e.Runtime.ContainerImageDigest = "" - } - - ExpectEntriesToMatch(t, output, normalize, expectedEntry) - }, - }, - DeleteTestNamespaceCommand(ns), + normalizeOpt := func(e *profilecpuTypes.Report) { + e.K8s.Node = "" + // TODO: Verify container runtime and container name + e.Runtime.RuntimeName = "" + e.Runtime.ContainerName = "" } + cmd := fmt.Sprintf("$KUBECTL_GADGET profile cpu -n %s -p test-pod -K --timeout 15 -o json", ns) + normalize := common.ProfileCPUNormalize(normalizeOpt) + commands := common.NewProfileCPUTestCmds( + cmd, + ns, + normalize, + WithContainerImageName("docker.io/library/busybox:latest", isDockerRuntime), + ) + RunTestSteps(commands, t, WithCbBeforeCleanup(PrintLogsFn(ns))) } diff --git a/integration/inspektor-gadget/top_ebpf_test.go b/integration/inspektor-gadget/top_ebpf_test.go index b21d7c3e413..d5f094d93aa 100644 --- a/integration/inspektor-gadget/top_ebpf_test.go +++ b/integration/inspektor-gadget/top_ebpf_test.go @@ -18,52 +18,12 @@ import ( "fmt" "testing" - "github.com/cilium/ebpf" - . "github.com/inspektor-gadget/inspektor-gadget/integration" + "github.com/inspektor-gadget/inspektor-gadget/integration/common" topebpfTypes "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/top/ebpf/types" "github.com/inspektor-gadget/inspektor-gadget/pkg/types" ) -func newTopEbpfCmd(cmd string, startAndStop bool) *Command { - validateOutputFn := func(t *testing.T, output string) { - expectedEntry := &topebpfTypes.Stats{ - Type: ebpf.Tracing.String(), - Name: "ig_top_ebpf_it", - } - - normalize := func(e *topebpfTypes.Stats) { - e.ProgramID = 0 - e.Processes = nil - e.CurrentRuntime = 0 - e.CurrentRunCount = 0 - e.CumulativeRuntime = 0 - e.CumulativeRunCount = 0 - e.TotalRuntime = 0 - e.TotalRunCount = 0 - e.MapMemory = 0 - e.MapCount = 0 - e.TotalCpuUsage = 0 - e.PerCpuUsage = 0 - - e.K8s = types.K8sMetadata{} - // TODO: Verify container runtime and container name - e.Runtime.RuntimeName = "" - e.Runtime.ContainerName = "" - e.Runtime.ContainerID = "" - e.Runtime.ContainerImageDigest = "" - } - - ExpectEntriesInMultipleArrayToMatch(t, output, normalize, expectedEntry) - } - return &Command{ - Name: "TopEbpf", - Cmd: cmd, - StartAndStop: startAndStop, - ValidateOutput: validateOutputFn, - } -} - func TestTopEbpf(t *testing.T) { if *k8sDistro == K8sDistroAKSUbuntu && *k8sArch == "amd64" { t.Skip("Skip running top ebpf gadget on AKS Ubuntu amd64: see issue #931") @@ -71,11 +31,20 @@ func TestTopEbpf(t *testing.T) { t.Parallel() + normalizeOpts := func(e *topebpfTypes.Stats) { + e.K8s = types.K8sMetadata{} + // TODO: Verify container runtime and container name + e.Runtime.RuntimeName = "" + e.Runtime.ContainerName = "" + e.Runtime.ContainerID = "" + e.Runtime.ContainerImageDigest = "" + } + t.Run("StartAndStop", func(t *testing.T) { t.Parallel() cmd := "$KUBECTL_GADGET top ebpf -o json -m 100" - topEbpfCmd := newTopEbpfCmd(cmd, true) + topEbpfCmd := common.NewTopEbpfCmd(cmd, true, normalizeOpts) RunTestSteps([]*Command{topEbpfCmd}, t) }) @@ -83,7 +52,7 @@ func TestTopEbpf(t *testing.T) { t.Parallel() cmd := fmt.Sprintf("$KUBECTL_GADGET top ebpf -o json -m 999 --timeout %d", topTimeoutInSeconds) - topEbpfCmd := newTopEbpfCmd(cmd, false) + topEbpfCmd := common.NewTopEbpfCmd(cmd, false, normalizeOpts) RunTestSteps([]*Command{topEbpfCmd}, t) }) @@ -91,7 +60,7 @@ func TestTopEbpf(t *testing.T) { t.Parallel() cmd := fmt.Sprintf("$KUBECTL_GADGET top ebpf -o json -m 999 --timeout %d --interval %d", topTimeoutInSeconds, topTimeoutInSeconds) - topEbpfCmd := newTopEbpfCmd(cmd, false) + topEbpfCmd := common.NewTopEbpfCmd(cmd, false, normalizeOpts) RunTestSteps([]*Command{topEbpfCmd}, t) }) }