hs-test: move hs-test to vpp/test-c 00/43600/13
authorAdrian Villin <[email protected]>
Mon, 8 Sep 2025 14:29:39 +0000 (16:29 +0200)
committerAdrian Villin <[email protected]>
Tue, 16 Sep 2025 06:22:35 +0000 (02:22 -0400)
- separated cluster tests from hs-test and moved them into
  test-c/kube-test
- cleaned up kube-test and hs-test
- kube-test setup-cluster.sh improvements
- Makefile in extras/hs-test is temporary
- kube-test stability improvements

Type: test

Change-Id: Iee9fc732ccd303d4b4635d329f673c03f95a4dd4
Signed-off-by: Adrian Villin <[email protected]>
139 files changed:
.gitignore
MAINTAINERS
Makefile
docs/developer/extras/hs_test.rst
extras/hs-test/Makefile
extras/hs-test/infra/common/suite_common.go [deleted file]
extras/hs-test/infra/kind/utils.go [deleted file]
extras/hs-test/vars [deleted file]
test-c/hs-test/Makefile [new file with mode: 0644]
test-c/hs-test/README.rst [moved from extras/hs-test/README.rst with 100% similarity]
test-c/hs-test/cpu_pinning_test.go [moved from extras/hs-test/cpu_pinning_test.go with 100% similarity]
test-c/hs-test/docker/Dockerfile.ab [moved from extras/hs-test/docker/Dockerfile.ab with 100% similarity]
test-c/hs-test/docker/Dockerfile.base [moved from extras/hs-test/docker/Dockerfile.base with 100% similarity]
test-c/hs-test/docker/Dockerfile.curl [moved from extras/hs-test/docker/Dockerfile.curl with 100% similarity]
test-c/hs-test/docker/Dockerfile.envoy [moved from extras/hs-test/docker/Dockerfile.envoy with 100% similarity]
test-c/hs-test/docker/Dockerfile.ginkgo [moved from extras/hs-test/docker/Dockerfile.ginkgo with 100% similarity]
test-c/hs-test/docker/Dockerfile.h2load [moved from extras/hs-test/docker/Dockerfile.h2load with 100% similarity]
test-c/hs-test/docker/Dockerfile.nginx [moved from extras/hs-test/docker/Dockerfile.nginx with 100% similarity]
test-c/hs-test/docker/Dockerfile.nginx-http3 [moved from extras/hs-test/docker/Dockerfile.nginx-http3 with 100% similarity]
test-c/hs-test/docker/Dockerfile.nginx-server [moved from extras/hs-test/docker/Dockerfile.nginx-server with 100% similarity]
test-c/hs-test/docker/Dockerfile.vpp [moved from extras/hs-test/docker/Dockerfile.vpp with 100% similarity]
test-c/hs-test/docker/Dockerfile.wrk [moved from extras/hs-test/docker/Dockerfile.wrk with 100% similarity]
test-c/hs-test/docker/setup-local-registry.sh [moved from extras/hs-test/docker/setup-local-registry.sh with 100% similarity]
test-c/hs-test/echo_test.go [moved from extras/hs-test/echo_test.go with 100% similarity]
test-c/hs-test/framework_test.go [moved from extras/hs-test/framework_test.go with 91% similarity]
test-c/hs-test/go.mod [moved from extras/hs-test/go.mod with 65% similarity]
test-c/hs-test/go.sum [moved from extras/hs-test/go.sum with 78% similarity]
test-c/hs-test/h2spec_extras/h2spec_extras.go [moved from extras/hs-test/h2spec_extras/h2spec_extras.go with 100% similarity]
test-c/hs-test/hs_test.sh [moved from extras/hs-test/hs_test.sh with 92% similarity]
test-c/hs-test/hsi_test.go [moved from extras/hs-test/hsi_test.go with 100% similarity]
test-c/hs-test/http1_test.go [moved from extras/hs-test/http1_test.go with 100% similarity]
test-c/hs-test/http2_test.go [moved from extras/hs-test/http2_test.go with 100% similarity]
test-c/hs-test/infra/address_allocator.go [moved from extras/hs-test/infra/address_allocator.go with 100% similarity]
test-c/hs-test/infra/asserts.go [new file with mode: 0644]
test-c/hs-test/infra/connect_udp_client.go [moved from extras/hs-test/infra/connect_udp_client.go with 100% similarity]
test-c/hs-test/infra/container.go [moved from extras/hs-test/infra/container.go with 99% similarity]
test-c/hs-test/infra/cpu.go [moved from extras/hs-test/infra/cpu.go with 99% similarity]
test-c/hs-test/infra/netconfig.go [moved from extras/hs-test/infra/netconfig.go with 100% similarity]
test-c/hs-test/infra/suite_base.go [moved from extras/hs-test/infra/hst_suite.go with 91% similarity]
test-c/hs-test/infra/suite_cpu_pinning.go [moved from extras/hs-test/infra/suite_cpu_pinning.go with 98% similarity]
test-c/hs-test/infra/suite_envoy_proxy.go [moved from extras/hs-test/infra/suite_envoy_proxy.go with 99% similarity]
test-c/hs-test/infra/suite_hsi.go [moved from extras/hs-test/infra/suite_hsi.go with 99% similarity]
test-c/hs-test/infra/suite_http1.go [moved from extras/hs-test/infra/suite_http1.go with 99% similarity]
test-c/hs-test/infra/suite_http2.go [moved from extras/hs-test/infra/suite_http2.go with 99% similarity]
test-c/hs-test/infra/suite_iperf_linux.go [moved from extras/hs-test/infra/suite_iperf_linux.go with 98% similarity]
test-c/hs-test/infra/suite_ldp.go [moved from extras/hs-test/infra/suite_ldp.go with 99% similarity]
test-c/hs-test/infra/suite_nginx_proxy.go [moved from extras/hs-test/infra/suite_nginx_proxy.go with 99% similarity]
test-c/hs-test/infra/suite_no_topo.go [moved from extras/hs-test/infra/suite_no_topo.go with 99% similarity]
test-c/hs-test/infra/suite_no_topo6.go [moved from extras/hs-test/infra/suite_no_topo6.go with 99% similarity]
test-c/hs-test/infra/suite_veth.go [moved from extras/hs-test/infra/suite_veth.go with 99% similarity]
test-c/hs-test/infra/suite_veth6.go [moved from extras/hs-test/infra/suite_veth6.go with 99% similarity]
test-c/hs-test/infra/suite_vpp_proxy.go [moved from extras/hs-test/infra/suite_vpp_proxy.go with 99% similarity]
test-c/hs-test/infra/suite_vpp_udp_proxy.go [moved from extras/hs-test/infra/suite_vpp_udp_proxy.go with 99% similarity]
test-c/hs-test/infra/topo.go [moved from extras/hs-test/infra/topo.go with 100% similarity]
test-c/hs-test/infra/utils.go [moved from extras/hs-test/infra/utils.go with 68% similarity]
test-c/hs-test/infra/vppinstance.go [moved from extras/hs-test/infra/vppinstance.go with 99% similarity]
test-c/hs-test/iperf_linux_test.go [moved from extras/hs-test/iperf_linux_test.go with 100% similarity]
test-c/hs-test/ldp_test.go [moved from extras/hs-test/ldp_test.go with 99% similarity]
test-c/hs-test/mem_leak_test.go [moved from extras/hs-test/mem_leak_test.go with 100% similarity]
test-c/hs-test/nginx_test.go [moved from extras/hs-test/nginx_test.go with 100% similarity]
test-c/hs-test/nsim_test.go [moved from extras/hs-test/nsim_test.go with 100% similarity]
test-c/hs-test/proxy_test.go [moved from extras/hs-test/proxy_test.go with 100% similarity]
test-c/hs-test/raw_session_test.go [moved from extras/hs-test/raw_session_test.go with 100% similarity]
test-c/hs-test/resources/cert/localhost.crt [moved from extras/hs-test/resources/cert/localhost.crt with 100% similarity]
test-c/hs-test/resources/cert/localhost.key [moved from extras/hs-test/resources/cert/localhost.key with 100% similarity]
test-c/hs-test/resources/curl/write_out_download [moved from extras/hs-test/resources/curl/write_out_download with 100% similarity]
test-c/hs-test/resources/curl/write_out_download_connect [moved from extras/hs-test/resources/curl/write_out_download_connect with 100% similarity]
test-c/hs-test/resources/curl/write_out_upload [moved from extras/hs-test/resources/curl/write_out_upload with 100% similarity]
test-c/hs-test/resources/curl/write_out_upload_connect [moved from extras/hs-test/resources/curl/write_out_upload_connect with 100% similarity]
test-c/hs-test/resources/envoy/envoy.log [moved from extras/hs-test/resources/envoy/envoy.log with 100% similarity]
test-c/hs-test/resources/envoy/proxy.yaml [moved from extras/hs-test/resources/envoy/proxy.yaml with 100% similarity]
test-c/hs-test/resources/envoy/vcl.conf [moved from extras/hs-test/resources/envoy/vcl.conf with 100% similarity]
test-c/hs-test/resources/nginx/html/index.html [moved from extras/hs-test/resources/nginx/html/index.html with 100% similarity]
test-c/hs-test/resources/nginx/nginx.conf [moved from extras/hs-test/resources/nginx/nginx.conf with 100% similarity]
test-c/hs-test/resources/nginx/nginx_http3.conf [moved from extras/hs-test/resources/nginx/nginx_http3.conf with 100% similarity]
test-c/hs-test/resources/nginx/nginx_proxy_mirroring.conf [moved from extras/hs-test/resources/nginx/nginx_proxy_mirroring.conf with 100% similarity]
test-c/hs-test/resources/nginx/nginx_server.conf [moved from extras/hs-test/resources/nginx/nginx_server.conf with 100% similarity]
test-c/hs-test/resources/nginx/nginx_server_mirroring.conf [moved from extras/hs-test/resources/nginx/nginx_server_mirroring.conf with 100% similarity]
test-c/hs-test/resources/nginx/vcl.conf [moved from extras/hs-test/resources/nginx/vcl.conf with 100% similarity]
test-c/hs-test/script/build-images.sh [moved from extras/hs-test/script/build-images.sh with 100% similarity]
test-c/hs-test/script/build_curl.sh [moved from extras/hs-test/script/build_curl.sh with 100% similarity]
test-c/hs-test/script/build_hst.sh [moved from extras/hs-test/script/build_hst.sh with 100% similarity]
test-c/hs-test/script/compress.sh [moved from extras/hs-test/script/compress.sh with 100% similarity]
test-c/hs-test/script/nginx_ldp.sh [moved from extras/hs-test/script/nginx_ldp.sh with 100% similarity]
test-c/hs-test/script/nginx_server_entrypoint.sh [moved from extras/hs-test/script/nginx_server_entrypoint.sh with 100% similarity]
test-c/hs-test/tls_test.go [moved from extras/hs-test/tls_test.go with 100% similarity]
test-c/hs-test/tools/http_server/http_server.go [moved from extras/hs-test/tools/http_server/http_server.go with 100% similarity]
test-c/hs-test/topo-containers/2containers.yaml [moved from extras/hs-test/topo-containers/2containers.yaml with 100% similarity]
test-c/hs-test/topo-containers/2peerVeth.yaml [moved from extras/hs-test/topo-containers/2peerVeth.yaml with 100% similarity]
test-c/hs-test/topo-containers/envoyProxy.yaml [moved from extras/hs-test/topo-containers/envoyProxy.yaml with 100% similarity]
test-c/hs-test/topo-containers/nginxProxy.yaml [moved from extras/hs-test/topo-containers/nginxProxy.yaml with 100% similarity]
test-c/hs-test/topo-containers/single.yaml [moved from extras/hs-test/topo-containers/single.yaml with 100% similarity]
test-c/hs-test/topo-containers/singleCpuPinning.yaml [moved from extras/hs-test/topo-containers/singleCpuPinning.yaml with 100% similarity]
test-c/hs-test/topo-containers/vppProxy.yaml [moved from extras/hs-test/topo-containers/vppProxy.yaml with 100% similarity]
test-c/hs-test/topo-network/2peerVeth.yaml [moved from extras/hs-test/topo-network/2peerVeth.yaml with 100% similarity]
test-c/hs-test/topo-network/2peerVeth6.yaml [moved from extras/hs-test/topo-network/2peerVeth6.yaml with 100% similarity]
test-c/hs-test/topo-network/2taps.yaml [moved from extras/hs-test/topo-network/2taps.yaml with 100% similarity]
test-c/hs-test/topo-network/ns.yaml [moved from extras/hs-test/topo-network/ns.yaml with 100% similarity]
test-c/hs-test/topo-network/tap.yaml [moved from extras/hs-test/topo-network/tap.yaml with 100% similarity]
test-c/hs-test/topo-network/tap6.yaml [moved from extras/hs-test/topo-network/tap6.yaml with 100% similarity]
test-c/hs-test/unittests_test.go [moved from extras/hs-test/unittests_test.go with 100% similarity]
test-c/hs-test/vcl_test.go [moved from extras/hs-test/vcl_test.go with 100% similarity]
test-c/kube-test/Makefile [new file with mode: 0644]
test-c/kube-test/docker/Dockerfile.ab [new file with mode: 0644]
test-c/kube-test/docker/Dockerfile.base [new file with mode: 0644]
test-c/kube-test/docker/Dockerfile.nginx [new file with mode: 0644]
test-c/kube-test/docker/Dockerfile.vpp [new file with mode: 0644]
test-c/kube-test/docker/Dockerfile.wrk [new file with mode: 0644]
test-c/kube-test/docker/setup-local-registry.sh [new file with mode: 0755]
test-c/kube-test/framework_test.go [new file with mode: 0644]
test-c/kube-test/go.mod [new file with mode: 0644]
test-c/kube-test/go.sum [new file with mode: 0644]
test-c/kube-test/infra/asserts.go [new file with mode: 0644]
test-c/kube-test/infra/deployment.go [moved from extras/hs-test/infra/kind/deployment.go with 89% similarity]
test-c/kube-test/infra/pod.go [moved from extras/hs-test/infra/kind/pod.go with 86% similarity]
test-c/kube-test/infra/suite_base.go [new file with mode: 0644]
test-c/kube-test/infra/suite_kube.go [moved from extras/hs-test/infra/kind/suite_kind.go with 66% similarity]
test-c/kube-test/infra/suite_large-mtu.go [moved from extras/hs-test/infra/kind/suite_large-mtu.go with 92% similarity]
test-c/kube-test/infra/utils.go [moved from extras/hs-test/infra/common/utils_common.go with 94% similarity]
test-c/kube-test/kube_test.go [moved from extras/hs-test/kind_test.go with 67% similarity]
test-c/kube-test/kube_test.sh [new file with mode: 0755]
test-c/kube-test/kubernetes/calico-config-template.yaml [moved from extras/hs-test/kubernetes/calico-config-template.yaml with 100% similarity]
test-c/kube-test/kubernetes/kind-config.yaml [moved from extras/hs-test/kubernetes/kind-config.yaml with 100% similarity]
test-c/kube-test/kubernetes/pod-definitions.yaml [moved from extras/hs-test/kubernetes/pod-definitions.yaml with 84% similarity]
test-c/kube-test/kubernetes/registry.yaml [moved from extras/hs-test/kubernetes/registry.yaml with 100% similarity]
test-c/kube-test/resources/cert/localhost.crt [new file with mode: 0644]
test-c/kube-test/resources/cert/localhost.key [new file with mode: 0644]
test-c/kube-test/resources/nginx/html/index.html [new file with mode: 0644]
test-c/kube-test/resources/nginx/nginx.conf [new file with mode: 0644]
test-c/kube-test/resources/nginx/nginx_http3.conf [new file with mode: 0644]
test-c/kube-test/resources/nginx/nginx_proxy_mirroring.conf [new file with mode: 0644]
test-c/kube-test/resources/nginx/nginx_server.conf [new file with mode: 0644]
test-c/kube-test/resources/nginx/nginx_server_mirroring.conf [new file with mode: 0644]
test-c/kube-test/resources/nginx/vcl.conf [new file with mode: 0644]
test-c/kube-test/script/build-images.sh [new file with mode: 0755]
test-c/kube-test/script/build_kube.sh [new file with mode: 0755]
test-c/kube-test/script/compress.sh [new file with mode: 0755]
test-c/kube-test/script/nginx_ldp.sh [new file with mode: 0755]
test-c/kube-test/script/setup-cluster.sh [moved from extras/hs-test/kubernetes/setup-cluster.sh with 92% similarity]

index 68d47ba..963a9da 100644 (file)
@@ -137,18 +137,25 @@ compile_commands.json
 **/test-run-*-*-*
 
 # host stack test framework
-/extras/hs-test/vpp-data
-/extras/hs-test/hs-test
-/extras/hs-test/http_server
-/extras/hs-test/.build.ok
-/extras/hs-test/.build.cov.ok
-/extras/hs-test/.last_hst_ppid
-/extras/hs-test/summary/
-/extras/hs-test/.last_state_hash
-/extras/hs-test/.kind_deps.ok
-/extras/hs-test/.go_cache/
-/extras/hs-test/kubernetes/calico-config.yaml
-/extras/hs-test/kubernetes/.vars
+/test-c/hs-test/vpp-data
+/test-c/hs-test/hs-test
+/test-c/hs-test/http_server
+/test-c/hs-test/.build.ok
+/test-c/hs-test/.build.cov.ok
+/test-c/hs-test/.last_hst_ppid
+/test-c/hs-test/summary/
+/test-c/hs-test/.last_state_hash
+/test-c/hs-test/.go_cache/
+
+# kube-test
+/test-c/kube-test/vpp-data
+/test-c/kube-test/.build.ok
+/test-c/kube-test/.last_ppid
+/test-c/kube-test/summary/
+/test-c/kube-test/.last_state_hash
+/test-c/kube-test/.kube_deps.ok
+/test-c/kube-test/kubernetes/calico-config.yaml
+/test-c/kube-test/kubernetes/.vars
 
 # ./configure
 /CMakeFiles
index 01fc7b1..b00762b 100644 (file)
@@ -890,7 +890,13 @@ I: hs-test
 M:     Florin Coras <[email protected]>
 M:     Matus Fabian <[email protected]>
 M:     Adrian Villin <[email protected]>
-F:     extras/hs-test
+F:     test-c/hs-test
+
+Kube test framework
+I:     kube-test
+M:     Florin Coras <[email protected]>
+M:     Adrian Villin <[email protected]>
+F:     test-c/kube-test
 
 THE REST
 I:     misc
index a7ce998..593ea4e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -580,7 +580,7 @@ test-cov:
 
 .PHONY: test-cov-hs
 test-cov-hs: build-gcov
-       @$(MAKE) CC=$(CC) -C extras/hs-test test-cov \
+       @$(MAKE) CC=$(CC) -C test-c/hs-test test-cov \
        VPP_BUILD_DIR=$(BR)/build-vpp_gcov-native/vpp
 
 .PHONY: test-cov-post-standalone
@@ -698,7 +698,7 @@ test-wipe-all:
 .PHONY: test-checkstyle
 test-checkstyle:
        @$(MAKE) -C test checkstyle-python-all
-       @$(MAKE) -C extras/hs-test checkstyle-go
+       @$(MAKE) -C test-c/hs-test checkstyle-go
 
 # Note: All python venv consolidated in test/Makefile, test/requirements*.txt
 .PHONY: test-checkstyle-diff
@@ -839,7 +839,11 @@ go-api-files: json-api-files
 
 .PHONY: cleanup-hst
 cleanup-hst:
-       $(MAKE) -C extras/hs-test cleanup-hst
+       $(MAKE) -C test-c/hs-test cleanup-hst
+
+.PHONY: cleanup-perf
+cleanup-perf:
+       $(MAKE) -C test-c/kube-test cleanup-perf
 
 .PHONY: ctags
 ctags: ctags.files
@@ -885,11 +889,13 @@ checkstyle-python:
 
 .PHONY: checkstyle-go
 checkstyle-go:
-       @$(MAKE) -C extras/hs-test checkstyle-go
+       @$(MAKE) -C test-c/hs-test checkstyle-go
+       @$(MAKE) -C test-c/kube-test checkstyle-go
 
 .PHONY: fixstyle-go
 fixstyle-go:
-       @$(MAKE) -C extras/hs-test fixstyle-go
+       @$(MAKE) -C test-c/hs-test fixstyle-go
+       @$(MAKE) -C test-c/kube-test fixstyle-go
 
 .PHONY: checkstyle-all
 checkstyle-all: checkstyle-commit checkstyle checkstyle-python docs-spell checkstyle-go
index b7ace79..0bbda04 120000 (symlink)
@@ -1 +1 @@
-../../../extras/hs-test/README.rst
\ No newline at end of file
+../../../test-c/hs-test/README.rst
\ No newline at end of file
index 8788d82..99a8f88 100644 (file)
@@ -1,385 +1,3 @@
-export HS_ROOT=$(CURDIR)
-
-# sets WS_ROOT if called from extras/hs-test
-ifeq ($(WS_ROOT),)
-export WS_ROOT=$(HS_ROOT)/../..
-endif
-
-ifeq ($(VERBOSE),)
-VERBOSE=false
-endif
-
-ifeq ($(PERSIST),)
-PERSIST=false
-endif
-
-ifeq ($(UNCONFIGURE),)
-UNCONFIGURE=false
-endif
-
-ifeq ($(TEST),)
-TEST=all
-endif
-
-ifeq ($(TEST-HS),)
-TEST-HS=all
-endif
-
-ifeq ($(DEBUG),)
-DEBUG=false
-endif
-
-ifeq ($(CPUS),)
-CPUS=1
-endif
-
-ifeq ($(VPP_CPUS),)
-VPP_CPUS=1
-endif
-
-ifeq ($(PARALLEL),)
-PARALLEL=1
-endif
-
-ifeq ($(REPEAT),)
-REPEAT=0
-endif
-
-ifeq ($(CPU0),)
-CPU0=false
-endif
-
-ifeq ($(VPPSRC),)
-VPPSRC=$(shell pwd)/../..
-endif
-
-ifeq ($(UBUNTU_CODENAME),)
-UBUNTU_CODENAME=$(shell grep '^UBUNTU_CODENAME=' /etc/os-release | cut -f2- -d=)
-endif
-
-ifeq ($(ARCH),)
-ARCH=$(shell dpkg --print-architecture)
-endif
-
-ifeq ($(NO_COLOR),)
-VERBOSE=false
-endif
-
-ifeq ($(TIMEOUT),)
-TIMEOUT=5
-endif
-
-ifeq ($(GINKGO_TIMEOUT),)
-GINKGO_TIMEOUT=3h
-endif
-
-CORE_PATTERN := $(shell cat /proc/sys/kernel/core_pattern)
-CORE_VOLUME:=
-DOCKER_TTY:=
-
-ifeq ($(shell expr "$(CORE_PATTERN)" : '^/'), 1)
-CORE_VOLUME := -v $(CORE_PATTERN):$(CORE_PATTERN)
-endif
-
-ifeq ($(shell tty -s && echo $$?), 0)
-DOCKER_TTY := -it
-endif
-
-FORCE_BUILD?=true
-
-# privileged is needed for "ip netns" otherwise we are not able to create namespace
-DOCKER_CAPABILITIES:=--privileged
-DOCKER_DEVICES:=--device /dev/vhost-net:/dev/vhost-net --device /dev/net/tun:/dev/net/tun
-DOCKER_VOLUMES:=-v $(WS_ROOT):$(WS_ROOT) -v /var/run/docker.sock:/var/run/docker.sock -v /tmp/hs-test:/tmp/hs-test \
-       -v /etc/localtime:/etc/localtime:ro $(CORE_VOLUME) -v $(HS_ROOT)/.go_cache/mod:/root/go/pkg/mod \
-       -v $(HS_ROOT)/.go_cache/build:/root/.cache/go-build
-DOCKER_PROXY:=-e HTTP_PROXY=$(HTTP_PROXY) -e HTTPS_PROXY=$(HTTPS_PROXY) -e NO_PROXY=$(NO_PROXY)
-
-.PHONY: help
-help:
-       @echo "Make targets:"
-       @echo " test                     - run tests"
-       @echo " test-debug               - run tests (vpp debug image)"
-       @echo " test-leak                - run memory leak tests (vpp debug image)"
-       @echo " test-perf                - run performance tests (requires a running cluster)"
-       @echo " build                    - build test infra"
-       @echo " build-cov                - coverage build of VPP and Docker images"
-       @echo " build-debug              - build test infra (vpp debug image)"
-       @echo " build-go                 - just build golang files"
-       @echo " cluster-help             - print info about KinD cluster setup (perf testing)"
-       @echo " master-cluster           - setup KinD cluster for performance testing (master CalicoVPP + VPP)"
-       @echo " rebuild-master-cluster   - rebuild VPP and update related pods without shutting down the cluster"
-       @echo " release-cluster          - setup KinD cluster for performance testing (latest CalicoVPP release)"
-       @echo " checkstyle-go            - check style of .go source files"
-       @echo " fixstyle-go              - format .go source files"
-       @echo " cleanup-hst              - removes all docker containers and namespaces from last test run"
-       @echo " cleanup-perf             - removes all kubernetes pods and namespaces from last test run"
-       @echo " list-tests               - list all tests"
-       @echo " install-deps             - install software dependencies"
-       @echo " install-kind-deps        - install software dependencies for cluster"
-       @echo
-       @echo "'make build' and 'make test' arguments:"
-       @echo " UBUNTU_VERSION           - ubuntu version for docker image"
-       @echo " FORCE_BUILD=[true|false] - force docker image building"
-       @echo
-       @echo "'make test' specific arguments:"
-       @echo " PERSIST=[true|false]     - whether clean up topology and dockers after test"
-       @echo " VERBOSE=[true|false]     - verbose output"
-       @echo " UNCONFIGURE=[true|false] - unconfigure selected test"
-       @echo " DEBUG=[true|false]       - attach VPP to GDB"
-       @echo " TEST=[name1,name2...]    - specific test(s) to run"
-       @echo " SKIP=[name1,name2...]    - specific test(s) to skip"
-       @echo " CPUS=[n]                 - number of cpus to allocate to each non-VPP container (default = 1)"
-       @echo " VPP_CPUS=[n]             - number of cpus to allocate to each VPP container (default = 1)"
-       @echo " VPPSRC=[path-to-vpp-src] - path to vpp source files (for gdb)"
-       @echo " PARALLEL=[n]             - number of test processes to spawn to run in parallel"
-       @echo " REPEAT=[n]               - repeat tests up to N times or until a failure occurs"
-       @echo " CPU0=[true|false]        - use cpu0"
-       @echo " DRYRUN=[true|false]      - set up containers but don't run tests"
-       @echo " NO_COLOR=[true|false]    - disables colorful Docker and Ginkgo output"
-       @echo " TIMEOUT=[minutes]        - test timeout override (5 minutes by default)"
-       @echo " GINKGO_TIMEOUT=[Ns/m/h]  - Ginkgo timeout override (3h by default)"
-       @echo
-       @echo "'make setup-cluster' arguments:"
-       @echo " BASE=[master|latest]     - build master CalicoVPP with master VPP or use latest release of CalicoVPP (default = latest)"
-
-.PHONY: list-tests
-list-tests:
-       @go run github.com/onsi/ginkgo/v2/ginkgo --dry-run -v --no-color --seed=2 | head -n -1 | grep 'test.go' | \
-               sed 's/^/* /; s/\(Suite\) /\1\//g'
-
-.PHONY: build-vpp-release
-build-vpp-release:
-       @$(MAKE) -C ../.. build-release
-
-.PHONY: build-vpp-debug
-build-vpp-debug:
-       @$(MAKE) -C ../.. build
-
-.PHONY: build-vpp-gcov
-build-vpp-gcov:
-       @$(MAKE) -C ../.. build-gcov
-
-.build.ok: build
-       @touch .build.ok
-
-.build.cov.ok: build-cov
-       @touch .build.ok
-
-.build_debug.ok: build-debug
-       @touch .build.ok
-
-.PHONY: test
-test: FORCE_BUILD=false
-test: .deps.ok .build.ok
-       docker run $(DOCKER_TTY) --rm $(DOCKER_CAPABILITIES) $(DOCKER_DEVICES) $(DOCKER_PROXY)\
-               -e BUILD_NUMBER=$(BUILD_NUMBER) $(DOCKER_VOLUMES) --name ginkgo hs-test/ginkgo \
-               .$(HS_ROOT)/hs_test.sh --persist=$(PERSIST) --verbose=$(VERBOSE) \
-               --unconfigure=$(UNCONFIGURE) --debug=$(DEBUG) --test=$(TEST) --cpus=$(CPUS) \
-               --vppsrc=$(VPPSRC) --parallel=$(PARALLEL) --repeat=$(REPEAT) --cpu0=$(CPU0) \
-               --dryrun=$(DRYRUN) --skip=$(SKIP) --no_color=$(NO_COLOR) --timeout=$(TIMEOUT) \
-               --ginkgo_timeout=$(GINKGO_TIMEOUT) --vpp_cpus=$(VPP_CPUS) --hs_root=$(HS_ROOT); \
-               ./script/compress.sh $$?
-
-.PHONY: test-debug
-test-debug: FORCE_BUILD=false
-test-debug: .deps.ok .build_debug.ok
-       docker run $(DOCKER_TTY) --rm $(DOCKER_CAPABILITIES) $(DOCKER_DEVICES) $(DOCKER_PROXY)\
-               -e BUILD_NUMBER=$(BUILD_NUMBER) $(DOCKER_VOLUMES) --name ginkgo hs-test/ginkgo \
-               .$(HS_ROOT)/hs_test.sh --persist=$(PERSIST) --verbose=$(VERBOSE) \
-               --unconfigure=$(UNCONFIGURE) --debug=$(DEBUG) --test=$(TEST) --cpus=$(CPUS) \
-               --vppsrc=$(VPPSRC) --parallel=$(PARALLEL) --repeat=$(REPEAT) --debug_build=true \
-               --cpu0=$(CPU0) --dryrun=$(DRYRUN) --skip=$(SKIP) --no_color=$(NO_COLOR) --timeout=$(TIMEOUT) \
-               --ginkgo_timeout=$(GINKGO_TIMEOUT) --vpp_cpus=$(VPP_CPUS) --hs_root=$(HS_ROOT); \
-               ./script/compress.sh $$?
-
-.PHONY: wipe-lcov
-wipe-lcov:
-       @lcov --zerocounters --directory $(WS_ROOT)/build-root/build-vpp_gcov-native/vpp
-
-.PHONY: test-cov
-test-cov: FORCE_BUILD=false
-test-cov: .deps.ok .build.cov.ok wipe-lcov
-       -docker run $(DOCKER_TTY) --rm $(DOCKER_CAPABILITIES) $(DOCKER_DEVICES) $(DOCKER_PROXY)\
-               -e BUILD_NUMBER=$(BUILD_NUMBER) $(DOCKER_VOLUMES) --name ginkgo hs-test/ginkgo \
-               .$(HS_ROOT)/hs_test.sh --coverage=true --persist=$(PERSIST) --verbose=$(VERBOSE) \
-               --unconfigure=$(UNCONFIGURE) --debug=$(DEBUG) --test=$(TEST-HS) --cpus=$(CPUS) \
-               --vppsrc=$(VPPSRC) --cpu0=$(CPU0) --dryrun=$(DRYRUN) --skip=$(SKIP) --no_color=$(NO_COLOR) \
-               --timeout=$(TIMEOUT) --ginkgo_timeout=$(GINKGO_TIMEOUT) --vpp_cpus=$(VPP_CPUS) --hs_root=$(HS_ROOT); \
-               ./script/compress.sh $$?
-       $(MAKE) -C ../.. test-cov-post-standalone HS_TEST=1
-
-.PHONY: test-leak
-test-leak: FORCE_BUILD=false
-test-leak: .deps.ok .build_debug.ok
-       docker run $(DOCKER_TTY) --rm $(DOCKER_CAPABILITIES) $(DOCKER_DEVICES) $(DOCKER_PROXY)\
-               -e BUILD_NUMBER=$(BUILD_NUMBER) $(DOCKER_VOLUMES) --name ginkgo hs-test/ginkgo \
-               .$(HS_ROOT)/hs_test.sh --test=$(TEST) --debug_build=true --leak_check=true --vppsrc=$(VPPSRC) --timeout=$(TIMEOUT) \
-               --ginkgo_timeout=$(GINKGO_TIMEOUT) --vpp_cpus=$(VPP_CPUS) --hs_root=$(HS_ROOT);
-
-.PHONY: test-perf
-test-perf: FORCE_BUILD=false
-test-perf: .deps.ok .build.ok
-       @bash ./hs_test.sh --persist=$(PERSIST) --verbose=$(VERBOSE) \
-               --test=$(TEST) --vppsrc=$(VPPSRC) --repeat=$(REPEAT) \
-               --skip=$(SKIP) --no_color=$(NO_COLOR) --perf=true --timeout=$(TIMEOUT) \
-               --ginkgo_timeout=$(GINKGO_TIMEOUT); \
-               ./script/compress.sh $$?
-
-.PHONY: cluster-help
-cluster-help:
-       @bash ./kubernetes/setup-cluster.sh help
-
-.PHONY: release-cluster
-release-cluster: .kind_deps.ok
-       @bash ./kubernetes/setup-cluster.sh release-cluster
-
-.PHONY: master-cluster
-master-cluster: .kind_deps.ok
-       @bash ./kubernetes/setup-cluster.sh master-cluster
-
-.PHONY: rebuild-master-cluster
-rebuild-master-cluster: .kind_deps.ok
-       @bash ./kubernetes/setup-cluster.sh rebuild-master-cluster
-
-# this is executed in a container by hs-test.sh
-.PHONY: build-go
-build-go:
-       go build --buildvcs=false ./tools/http_server
-
-.PHONY: build
-build: .deps.ok build-vpp-release
-       @rm -f .build.ok
-       bash ./script/build_hst.sh release $(FORCE_BUILD)
-       @touch .build.ok
-
-.PHONY: build-cov
-build-cov: .deps.ok build-vpp-gcov
-       @rm -f .build.cov.ok
-       bash ./script/build_hst.sh gcov $(FORCE_BUILD)
-       @touch .build.cov.ok
-
-.PHONY: build-debug
-build-debug: .deps.ok build-vpp-debug
-       @rm -f .build.ok
-       bash ./script/build_hst.sh debug $(FORCE_BUILD)
-       @touch .build.ok
-
-.deps.ok:
-       @$(MAKE) install-deps
-
-.kind_deps.ok:
-       @$(MAKE) install-kind-deps
-
-.PHONY: install-kind-deps
-install-kind-deps: .deps.ok
-       -@if ! command -v kind >/dev/null 2>&1; then \
-               echo "Installing KinD"; \
-               go install sigs.k8s.io/[email protected]; \
-               echo "Creating symlink from '$(HOME)/go/bin/kind' to '/usr/bin/kind'"; \
-               sudo ln -s $(HOME)/go/bin/kind /usr/bin/kind; \
-       else \
-               echo "KinD is already installed. Skipping."; \
-       fi
-       @if ! command -v kubectl >/dev/null 2>&1; then \
-        echo "kubectl not found. Installing kubectl..."; \
-        sudo -E apt-get update && sudo apt-get install -y apt-transport-https ca-certificates curl gnupg; \
-        curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.31/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg; \
-        sudo -E chmod 644 /etc/apt/keyrings/kubernetes-apt-keyring.gpg; \
-        echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.31/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list; \
-        sudo -E chmod 644 /etc/apt/sources.list.d/kubernetes.list; \
-        sudo apt-get update && sudo apt-get install -y kubectl; \
-    else \
-        echo "kubectl is already installed. Skipping."; \
-       fi
-       @touch .kind_deps.ok
-
-.PHONY: install-deps
-install-deps:
-       @rm -f .deps.ok
-       @if [ -d "/usr/local/go" ]; then \
-        echo "Go is already installed. You may have to update it manually if version < 1.23.10"; \
-               go version; \
-    else \
-        echo "Installing Go 1.23"; \
-               wget -t 2 https://go.dev/dl/go1.23.10.linux-$(ARCH).tar.gz -O /tmp/go1.23.10.linux-$(ARCH).tar.gz && sudo tar -C /usr/local -xzf /tmp/go1.23.10.linux-$(ARCH).tar.gz; \
-               sudo ln -s /usr/local/go/bin/go /usr/bin/go ; \
-       fi
-       @sudo -E apt-get update
-       @sudo -E apt-get install -y apt-transport-https ca-certificates curl software-properties-common \
-               bridge-utils gpg
-       @if [ ! -f /usr/share/keyrings/docker-archive-keyring.gpg ] ; then \
-               curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg; \
-               echo "deb [arch=$(ARCH) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(UBUNTU_CODENAME) stable" \
-                       | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null ; \
-               apt-get update; \
-       fi
-       @sudo -E apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
-       @touch .deps.ok
-
-.PHONY: checkstyle-go
-checkstyle-go:
-       @output=$$(find . -type f -name '*.go' -not -path './.go_cache/*' -exec go run golang.org/x/tools/cmd/[email protected] -d {} +); \
-       status=$$?; \
-       if [ $$status -ne 0 ]; then \
-               exit $$status; \
-    elif [ -z "$$output" ]; then \
-        echo "******************************************************************************"; \
-        echo "* HST Golang Checkstyle OK."; \
-        echo "******************************************************************************"; \
-    else \
-        echo "$$output"; \
-        echo "******************************************************************************"; \
-        echo "* HST Golang Checkstyle FAILED. Use 'make fixstyle-go' or fix errors manually."; \
-        echo "******************************************************************************"; \
-        exit 1; \
-    fi
-
-.PHONY: fixstyle-go
-fixstyle-go:
-       @echo "Modified files:"
-       @find . -type f -name '*.go' -not -path './.go_cache/*' -exec go run golang.org/x/tools/cmd/[email protected] -w -l {} +
-       @go mod tidy
-       @echo "*******************************************************************"
-       @echo "Fixstyle done."
-       @echo "*******************************************************************"
-
-.PHONY: cleanup-hst
-cleanup-hst:
-       @if [ ! -f ".last_hst_ppid" ]; then \
-               echo "'.last_hst_ppid' file does not exist."; \
-               exit 1; \
-       fi
-       @echo "****************************"
-       @echo "Removing docker containers:"
-       @# "-" ignores errors
-       @-sudo docker rm $$(sudo docker stop $$(sudo docker ps -a -q --filter "name=$$(cat .last_hst_ppid)") -t 0)
-       @echo "****************************"
-       @echo "Removing IP address files:"
-       @find . -type f -regextype egrep -regex '.*[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' -exec sudo rm -v {} \;
-       @find . -type f -name "fd00:0*" -exec sudo rm -v {} \;
-       @echo "****************************"
-       @echo "Removing network namespaces:"
-       @for ns in $$(ip netns list | grep $$(cat .last_hst_ppid) | awk '{print $$1}'); do \
-               echo $$ns; \
-       sudo ip netns delete $$ns; \
-       done
-       @echo "****************************"
-       @echo "Done."
-       @echo "****************************"
-
-.PHONY: cleanup-perf
-cleanup-perf:
-       @if [ ! -f ".last_hst_ppid" ]; then \
-               echo "'.last_hst_ppid' file does not exist."; \
-               exit 1; \
-       fi
-       @echo "****************************"
-       @echo "Removing kubernetes pods:"
-       @kubectl delete pods --all --grace-period=0 -n hs-test$$(cat .last_hst_ppid)
-       @echo "****************************"
-       @echo "Removing kubernetes namespace:"
-       @kubectl delete namespace hs-test$$(cat .last_hst_ppid)
-       @echo "****************************"
-       @echo "Done."
-       @echo "****************************"
+# Temp redirect
+%:
+       $(MAKE) -C ../../test-c/hs-test/ -f Makefile $@
\ No newline at end of file
diff --git a/extras/hs-test/infra/common/suite_common.go b/extras/hs-test/infra/common/suite_common.go
deleted file mode 100644 (file)
index be9a0a6..0000000
+++ /dev/null
@@ -1,209 +0,0 @@
-package hst_common
-
-import (
-       "flag"
-       "fmt"
-       "io"
-       "log"
-       "net/http"
-       "os"
-       "strconv"
-       "strings"
-       "time"
-
-       . "github.com/onsi/ginkgo/v2"
-       . "github.com/onsi/gomega"
-)
-
-var IsCoverage = flag.Bool("coverage", false, "use coverage run config")
-var IsPersistent = flag.Bool("persist", false, "persists topology config")
-var IsVerbose = flag.Bool("verbose", false, "verbose test output")
-var WhoAmI = flag.String("whoami", "root", "what user ran hs-test")
-var ParallelTotal = flag.Lookup("ginkgo.parallel.total")
-var IsVppDebug = flag.Bool("debug", false, "attach gdb to vpp")
-var DryRun = flag.Bool("dryrun", false, "set up containers but don't run tests")
-var Timeout = flag.Int("timeout", 5, "test timeout override (in minutes)")
-var PerfTesting = flag.Bool("perf", false, "perf test flag")
-var NumaAwareCpuAlloc bool
-var TestTimeout time.Duration
-var RunningInCi bool
-
-const (
-       LogDir    string = "/tmp/hs-test/"
-       VolumeDir string = "/vol"
-)
-
-type HstCommon struct {
-       Ppid         string
-       ProcessIndex string
-       Logger       *log.Logger
-       LogFile      *os.File
-}
-
-func (s *HstCommon) Skip(args string) {
-       Skip(args)
-}
-
-func (s *HstCommon) SetupTest() {
-       TestCounterFunc()
-       s.Log("[* TEST SETUP]")
-}
-
-func (s *HstCommon) SetupSuite() {
-       s.CreateLogger()
-       s.Log("[* SUITE SETUP]")
-       s.Ppid = fmt.Sprint(os.Getppid())
-       // remove last number so we have space to prepend a process index (interfaces have a char limit)
-       s.Ppid = s.Ppid[:len(s.Ppid)-1]
-       s.ProcessIndex = fmt.Sprint(GinkgoParallelProcess())
-}
-
-func (s *HstCommon) TeardownTest() {
-       if *IsPersistent || *DryRun {
-               s.Skip("Skipping test teardown")
-       }
-       s.Log("[* TEST TEARDOWN]")
-}
-
-func (s *HstCommon) TeardownSuite() {
-       if *IsPersistent || *DryRun {
-               s.Skip("Skipping suite teardown")
-       }
-       s.Log("[* SUITE TEARDOWN]")
-}
-
-func (s *HstCommon) GetCurrentSuiteName() string {
-       return CurrentSpecReport().ContainerHierarchyTexts[0]
-}
-
-func (s *HstCommon) CreateLogger() {
-       suiteName := s.GetCurrentSuiteName()
-       var err error
-       s.LogFile, err = os.Create("summary/" + suiteName + ".log")
-       if err != nil {
-               Fail("Unable to create log file.")
-       }
-       s.Logger = log.New(io.Writer(s.LogFile), "", log.LstdFlags)
-}
-
-// Logs to files by default, logs to stdout when VERBOSE=true with GinkgoWriter
-// to keep console tidy
-func (s *HstCommon) Log(log any, arg ...any) {
-       var logStr string
-       if len(arg) == 0 {
-               logStr = fmt.Sprint(log)
-       } else {
-               logStr = fmt.Sprintf(fmt.Sprint(log), arg...)
-       }
-       logs := strings.Split(logStr, "\n")
-
-       for _, line := range logs {
-               s.Logger.Println(line)
-       }
-       if *IsVerbose {
-               GinkgoWriter.Println(logStr)
-       }
-}
-
-func (s *HstCommon) AssertNil(object any, msgAndArgs ...any) {
-       ExpectWithOffset(2, object).To(BeNil(), msgAndArgs...)
-}
-
-func (s *HstCommon) AssertNotNil(object any, msgAndArgs ...any) {
-       ExpectWithOffset(2, object).ToNot(BeNil(), msgAndArgs...)
-}
-
-func (s *HstCommon) AssertEqual(expected, actual any, msgAndArgs ...any) {
-       ExpectWithOffset(2, actual).To(Equal(expected), msgAndArgs...)
-}
-
-func (s *HstCommon) AssertNotEqual(expected, actual any, msgAndArgs ...any) {
-       ExpectWithOffset(2, actual).ToNot(Equal(expected), msgAndArgs...)
-}
-
-func (s *HstCommon) AssertContains(testString, contains any, msgAndArgs ...any) {
-       ExpectWithOffset(2, strings.ToLower(fmt.Sprint(testString))).To(ContainSubstring(strings.ToLower(fmt.Sprint(contains))), msgAndArgs...)
-}
-
-func (s *HstCommon) AssertNotContains(testString, contains any, msgAndArgs ...any) {
-       ExpectWithOffset(2, strings.ToLower(fmt.Sprint(testString))).ToNot(ContainSubstring(strings.ToLower(fmt.Sprint(contains))), msgAndArgs...)
-}
-
-func (s *HstCommon) AssertEmpty(object any, msgAndArgs ...any) {
-       ExpectWithOffset(2, object).To(BeEmpty(), msgAndArgs...)
-}
-
-func (s *HstCommon) AssertNotEmpty(object any, msgAndArgs ...any) {
-       ExpectWithOffset(2, object).ToNot(BeEmpty(), msgAndArgs...)
-}
-
-func (s *HstCommon) AssertMatchError(actual, expected error, msgAndArgs ...any) {
-       ExpectWithOffset(2, actual).To(MatchError(expected), msgAndArgs...)
-}
-
-func (s *HstCommon) AssertGreaterEqual(actual, expected any, msgAndArgs ...any) {
-       ExpectWithOffset(2, actual).Should(BeNumerically(">=", expected), msgAndArgs...)
-}
-
-func (s *HstCommon) AssertGreaterThan(actual, expected any, msgAndArgs ...any) {
-       ExpectWithOffset(2, actual).Should(BeNumerically(">", expected), msgAndArgs...)
-}
-
-func (s *HstCommon) AssertLessEqual(actual, expected any, msgAndArgs ...any) {
-       ExpectWithOffset(2, actual).Should(BeNumerically("<=", expected), msgAndArgs...)
-}
-
-func (s *HstCommon) AssertLessThan(actual, expected any, msgAndArgs ...any) {
-       ExpectWithOffset(2, actual).Should(BeNumerically("<", expected), msgAndArgs...)
-}
-
-func (s *HstCommon) AssertEqualWithinThreshold(actual, expected, threshold any, msgAndArgs ...any) {
-       ExpectWithOffset(2, actual).Should(BeNumerically("~", expected, threshold), msgAndArgs...)
-}
-
-func (s *HstCommon) AssertTimeEqualWithinThreshold(actual, expected time.Time, threshold time.Duration, msgAndArgs ...any) {
-       ExpectWithOffset(2, actual).Should(BeTemporally("~", expected, threshold), msgAndArgs...)
-}
-
-func (s *HstCommon) AssertHttpStatus(resp *http.Response, expectedStatus int, msgAndArgs ...any) {
-       ExpectWithOffset(2, resp).To(HaveHTTPStatus(expectedStatus), msgAndArgs...)
-}
-
-func (s *HstCommon) AssertHttpHeaderWithValue(resp *http.Response, key string, value any, msgAndArgs ...any) {
-       ExpectWithOffset(2, resp).To(HaveHTTPHeaderWithValue(key, value), msgAndArgs...)
-}
-
-func (s *HstCommon) AssertHttpHeaderNotPresent(resp *http.Response, key string, msgAndArgs ...any) {
-       ExpectWithOffset(2, resp.Header.Get(key)).To(BeEmpty(), msgAndArgs...)
-}
-
-func (s *HstCommon) AssertHttpContentLength(resp *http.Response, expectedContentLen int64, msgAndArgs ...any) {
-       ExpectWithOffset(2, resp).To(HaveHTTPHeaderWithValue("Content-Length", strconv.FormatInt(expectedContentLen, 10)), msgAndArgs...)
-}
-
-func (s *HstCommon) AssertHttpBody(resp *http.Response, expectedBody string, msgAndArgs ...any) {
-       ExpectWithOffset(2, resp).To(HaveHTTPBody(expectedBody), msgAndArgs...)
-}
-
-// Coverage builds take longer to finish -> assert timeout is set to 'TestTimeout - 30 seconds' to let the test finish properly
-func (s *HstCommon) AssertChannelClosed(timeout time.Duration, channel chan error) {
-       if *IsCoverage && timeout > time.Second*30 {
-               timeout = TestTimeout - time.Second*30
-               s.Log("Coverage build, assert timeout is set to %s", timeout.String())
-       }
-       EventuallyWithOffset(2, channel).WithTimeout(timeout).Should(BeClosed())
-}
-
-// Pass the parsed result struct and the minimum amount of data transferred in MB.
-// Won't do anything when testing a coverage build.
-func (s *HstCommon) AssertIperfMinTransfer(result IPerfResult, minTransferred int) {
-       if *IsCoverage {
-               s.Log("Coverage build; not asserting")
-               return
-       }
-       if result.Start.Details.Protocol == "TCP" {
-               s.AssertGreaterEqual(result.End.TcpReceived.MBytes, minTransferred)
-       } else {
-               s.AssertGreaterEqual(result.End.Udp.MBytes, minTransferred)
-       }
-}
diff --git a/extras/hs-test/infra/kind/utils.go b/extras/hs-test/infra/kind/utils.go
deleted file mode 100644 (file)
index ea82085..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-package hst_kind
-
-func boolPtr(b bool) *bool {
-       return &b
-}
-
-func int64Ptr(integer int64) *int64 {
-       return &integer
-}
diff --git a/extras/hs-test/vars b/extras/hs-test/vars
deleted file mode 100644 (file)
index d1ca078..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-export VPP_WS=../../
-
-export HST_LDPRELOAD=${VPP_WS}/build-root/build-vpp-native/vpp/lib/x86_64-linux-gnu/libvcl_ldpreload.so
-export PATH=${VPP_WS}/build-root/build-vpp-native/vpp/bin:$PATH
-
-export UBUNTU_VERSION=$(lsb_release -rs)
-export HST_EXTENDED_TESTS=false
diff --git a/test-c/hs-test/Makefile b/test-c/hs-test/Makefile
new file mode 100644 (file)
index 0000000..033d8ab
--- /dev/null
@@ -0,0 +1,308 @@
+export HS_ROOT=$(CURDIR)
+
+# sets WS_ROOT if called from test-c/hs-test
+ifeq ($(WS_ROOT),)
+export WS_ROOT=$(HS_ROOT)/../..
+endif
+
+ifeq ($(VERBOSE),)
+VERBOSE=false
+endif
+
+ifeq ($(PERSIST),)
+PERSIST=false
+endif
+
+ifeq ($(UNCONFIGURE),)
+UNCONFIGURE=false
+endif
+
+ifeq ($(TEST),)
+TEST=all
+endif
+
+ifeq ($(TEST-HS),)
+TEST-HS=all
+endif
+
+ifeq ($(DEBUG),)
+DEBUG=false
+endif
+
+ifeq ($(CPUS),)
+CPUS=1
+endif
+
+ifeq ($(VPP_CPUS),)
+VPP_CPUS=1
+endif
+
+ifeq ($(PARALLEL),)
+PARALLEL=1
+endif
+
+ifeq ($(REPEAT),)
+REPEAT=0
+endif
+
+ifeq ($(CPU0),)
+CPU0=false
+endif
+
+ifeq ($(VPPSRC),)
+VPPSRC=$(shell pwd)/../..
+endif
+
+ifeq ($(UBUNTU_CODENAME),)
+UBUNTU_CODENAME=$(shell grep '^UBUNTU_CODENAME=' /etc/os-release | cut -f2- -d=)
+endif
+
+ifeq ($(ARCH),)
+ARCH=$(shell dpkg --print-architecture)
+endif
+
+ifeq ($(NO_COLOR),)
+VERBOSE=false
+endif
+
+ifeq ($(TIMEOUT),)
+TIMEOUT=5
+endif
+
+ifeq ($(GINKGO_TIMEOUT),)
+GINKGO_TIMEOUT=3h
+endif
+
+CORE_PATTERN := $(shell cat /proc/sys/kernel/core_pattern)
+CORE_VOLUME:=
+DOCKER_TTY:=
+
+ifeq ($(shell expr "$(CORE_PATTERN)" : '^/'), 1)
+CORE_VOLUME := -v $(CORE_PATTERN):$(CORE_PATTERN)
+endif
+
+ifeq ($(shell tty -s && echo $$?), 0)
+DOCKER_TTY := -it
+endif
+
+FORCE_BUILD?=true
+
+# privileged is needed for "ip netns" otherwise we are not able to create namespace
+DOCKER_CAPABILITIES:=--privileged
+DOCKER_DEVICES:=--device /dev/vhost-net:/dev/vhost-net --device /dev/net/tun:/dev/net/tun
+DOCKER_VOLUMES:=-v $(WS_ROOT):$(WS_ROOT) -v /var/run/docker.sock:/var/run/docker.sock -v /tmp/hs-test:/tmp/hs-test \
+       -v /etc/localtime:/etc/localtime:ro $(CORE_VOLUME) -v $(HS_ROOT)/.go_cache/mod:/root/go/pkg/mod \
+       -v $(HS_ROOT)/.go_cache/build:/root/.cache/go-build
+DOCKER_PROXY:=-e HTTP_PROXY=$(HTTP_PROXY) -e HTTPS_PROXY=$(HTTPS_PROXY) -e NO_PROXY=$(NO_PROXY)
+
+.PHONY: help
+help:
+       @echo "Make targets:"
+       @echo " test                     - run tests"
+       @echo " test-debug               - run tests (vpp debug image)"
+       @echo " test-leak                - run memory leak tests (vpp debug image)"
+       @echo " build                    - build test infra"
+       @echo " build-cov                - coverage build of VPP and Docker images"
+       @echo " build-debug              - build test infra (vpp debug image)"
+       @echo " build-go                 - just build golang files"
+       @echo " checkstyle-go            - check style of .go source files"
+       @echo " fixstyle-go              - format .go source files"
+       @echo " cleanup-hst              - removes all docker containers and namespaces from last test run"
+       @echo " list-tests               - list all tests"
+       @echo " install-deps             - install software dependencies"
+       @echo
+       @echo "'make build' and 'make test' arguments:"
+       @echo " UBUNTU_VERSION           - ubuntu version for docker image"
+       @echo " FORCE_BUILD=[true|false] - force docker image building"
+       @echo
+       @echo "'make test' specific arguments:"
+       @echo " PERSIST=[true|false]     - whether clean up topology and dockers after test"
+       @echo " VERBOSE=[true|false]     - verbose output"
+       @echo " UNCONFIGURE=[true|false] - unconfigure selected test"
+       @echo " DEBUG=[true|false]       - attach VPP to GDB"
+       @echo " TEST=[name1,name2...]    - specific test(s) to run"
+       @echo " SKIP=[name1,name2...]    - specific test(s) to skip"
+       @echo " CPUS=[n]                 - number of cpus to allocate to each non-VPP container (default = 1)"
+       @echo " VPP_CPUS=[n]             - number of cpus to allocate to each VPP container (default = 1)"
+       @echo " VPPSRC=[path-to-vpp-src] - path to vpp source files (for gdb)"
+       @echo " PARALLEL=[n]             - number of test processes to spawn to run in parallel"
+       @echo " REPEAT=[n]               - repeat tests up to N times or until a failure occurs"
+       @echo " CPU0=[true|false]        - use cpu0"
+       @echo " DRYRUN=[true|false]      - set up containers but don't run tests"
+       @echo " NO_COLOR=[true|false]    - disables colorful Docker and Ginkgo output"
+       @echo " TIMEOUT=[minutes]        - test timeout override (5 minutes by default)"
+       @echo " GINKGO_TIMEOUT=[Ns/m/h]  - Ginkgo timeout override (3h by default)"
+
+.PHONY: list-tests
+list-tests:
+       @go run github.com/onsi/ginkgo/v2/ginkgo --dry-run -v --no-color --seed=2 | head -n -1 | grep 'test.go' | \
+               sed 's/^/* /; s/\(Suite\) /\1\//g'
+
+.PHONY: build-vpp-release
+build-vpp-release:
+       @$(MAKE) -C ../.. build-release
+
+.PHONY: build-vpp-debug
+build-vpp-debug:
+       @$(MAKE) -C ../.. build
+
+.PHONY: build-vpp-gcov
+build-vpp-gcov:
+       @$(MAKE) -C ../.. build-gcov
+
+.build.ok: build
+       @touch .build.ok
+
+.build.cov.ok: build-cov
+       @touch .build.ok
+
+.build_debug.ok: build-debug
+       @touch .build.ok
+
+.PHONY: test
+test: FORCE_BUILD=false
+test: .deps.ok .build.ok
+       docker run $(DOCKER_TTY) --rm $(DOCKER_CAPABILITIES) $(DOCKER_DEVICES) $(DOCKER_PROXY)\
+               -e BUILD_NUMBER=$(BUILD_NUMBER) $(DOCKER_VOLUMES) --name ginkgo hs-test/ginkgo \
+               .$(HS_ROOT)/hs_test.sh --persist=$(PERSIST) --verbose=$(VERBOSE) \
+               --unconfigure=$(UNCONFIGURE) --debug=$(DEBUG) --test=$(TEST) --cpus=$(CPUS) \
+               --vppsrc=$(VPPSRC) --parallel=$(PARALLEL) --repeat=$(REPEAT) --cpu0=$(CPU0) \
+               --dryrun=$(DRYRUN) --skip=$(SKIP) --no_color=$(NO_COLOR) --timeout=$(TIMEOUT) \
+               --ginkgo_timeout=$(GINKGO_TIMEOUT) --vpp_cpus=$(VPP_CPUS) --hs_root=$(HS_ROOT); \
+               ./script/compress.sh $$?
+
+.PHONY: test-debug
+test-debug: FORCE_BUILD=false
+test-debug: .deps.ok .build_debug.ok
+       docker run $(DOCKER_TTY) --rm $(DOCKER_CAPABILITIES) $(DOCKER_DEVICES) $(DOCKER_PROXY)\
+               -e BUILD_NUMBER=$(BUILD_NUMBER) $(DOCKER_VOLUMES) --name ginkgo hs-test/ginkgo \
+               .$(HS_ROOT)/hs_test.sh --persist=$(PERSIST) --verbose=$(VERBOSE) \
+               --unconfigure=$(UNCONFIGURE) --debug=$(DEBUG) --test=$(TEST) --cpus=$(CPUS) \
+               --vppsrc=$(VPPSRC) --parallel=$(PARALLEL) --repeat=$(REPEAT) --debug_build=true \
+               --cpu0=$(CPU0) --dryrun=$(DRYRUN) --skip=$(SKIP) --no_color=$(NO_COLOR) --timeout=$(TIMEOUT) \
+               --ginkgo_timeout=$(GINKGO_TIMEOUT) --vpp_cpus=$(VPP_CPUS) --hs_root=$(HS_ROOT); \
+               ./script/compress.sh $$?
+
+.PHONY: wipe-lcov
+wipe-lcov:
+       @lcov --zerocounters --directory $(WS_ROOT)/build-root/build-vpp_gcov-native/vpp
+
+.PHONY: test-cov
+test-cov: FORCE_BUILD=false
+test-cov: .deps.ok .build.cov.ok wipe-lcov
+       -docker run $(DOCKER_TTY) --rm $(DOCKER_CAPABILITIES) $(DOCKER_DEVICES) $(DOCKER_PROXY)\
+               -e BUILD_NUMBER=$(BUILD_NUMBER) $(DOCKER_VOLUMES) --name ginkgo hs-test/ginkgo \
+               .$(HS_ROOT)/hs_test.sh --coverage=true --persist=$(PERSIST) --verbose=$(VERBOSE) \
+               --unconfigure=$(UNCONFIGURE) --debug=$(DEBUG) --test=$(TEST-HS) --cpus=$(CPUS) \
+               --vppsrc=$(VPPSRC) --cpu0=$(CPU0) --dryrun=$(DRYRUN) --skip=$(SKIP) --no_color=$(NO_COLOR) \
+               --timeout=$(TIMEOUT) --ginkgo_timeout=$(GINKGO_TIMEOUT) --vpp_cpus=$(VPP_CPUS) --hs_root=$(HS_ROOT); \
+               ./script/compress.sh $$?
+       $(MAKE) -C ../.. test-cov-post-standalone HS_TEST=1
+
+.PHONY: test-leak
+test-leak: FORCE_BUILD=false
+test-leak: .deps.ok .build_debug.ok
+       docker run $(DOCKER_TTY) --rm $(DOCKER_CAPABILITIES) $(DOCKER_DEVICES) $(DOCKER_PROXY)\
+               -e BUILD_NUMBER=$(BUILD_NUMBER) $(DOCKER_VOLUMES) --name ginkgo hs-test/ginkgo \
+               .$(HS_ROOT)/hs_test.sh --test=$(TEST) --debug_build=true --leak_check=true --vppsrc=$(VPPSRC) --timeout=$(TIMEOUT) \
+               --ginkgo_timeout=$(GINKGO_TIMEOUT) --vpp_cpus=$(VPP_CPUS) --hs_root=$(HS_ROOT);
+
+# this is executed in a container by hs-test.sh
+.PHONY: build-go
+build-go:
+       go build --buildvcs=false ./tools/http_server
+
+.PHONY: build
+build: .deps.ok build-vpp-release
+       @rm -f .build.ok
+       bash ./script/build_hst.sh release $(FORCE_BUILD)
+       @touch .build.ok
+
+.PHONY: build-cov
+build-cov: .deps.ok build-vpp-gcov
+       @rm -f .build.cov.ok
+       bash ./script/build_hst.sh gcov $(FORCE_BUILD)
+       @touch .build.cov.ok
+
+.PHONY: build-debug
+build-debug: .deps.ok build-vpp-debug
+       @rm -f .build.ok
+       bash ./script/build_hst.sh debug $(FORCE_BUILD)
+       @touch .build.ok
+
+.deps.ok:
+       @$(MAKE) install-deps
+
+.PHONY: install-deps
+install-deps:
+       @rm -f .deps.ok
+       @if [ -d "/usr/local/go" ]; then \
+        echo "Go is already installed. You may have to update it manually if version < 1.23.10"; \
+               go version; \
+    else \
+        echo "Installing Go 1.23"; \
+               wget -t 2 https://go.dev/dl/go1.23.10.linux-$(ARCH).tar.gz -O /tmp/go1.23.10.linux-$(ARCH).tar.gz && sudo tar -C /usr/local -xzf /tmp/go1.23.10.linux-$(ARCH).tar.gz; \
+               sudo ln -s /usr/local/go/bin/go /usr/bin/go ; \
+       fi
+       @sudo -E apt-get update
+       @sudo -E apt-get install -y apt-transport-https ca-certificates curl software-properties-common \
+               bridge-utils gpg
+       @if [ ! -f /usr/share/keyrings/docker-archive-keyring.gpg ] ; then \
+               curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg; \
+               echo "deb [arch=$(ARCH) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(UBUNTU_CODENAME) stable" \
+                       | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null ; \
+               apt-get update; \
+       fi
+       @sudo -E apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
+       @touch .deps.ok
+
+.PHONY: checkstyle-go
+checkstyle-go:
+       @output=$$(find . -type f -name '*.go' -not -path './.go_cache/*' -exec go run golang.org/x/tools/cmd/[email protected] -d {} +); \
+       status=$$?; \
+       if [ $$status -ne 0 ]; then \
+               exit $$status; \
+    elif [ -z "$$output" ]; then \
+        echo "******************************************************************************"; \
+        echo "* HST Golang Checkstyle OK."; \
+        echo "******************************************************************************"; \
+    else \
+        echo "$$output"; \
+        echo "******************************************************************************"; \
+        echo "* HST Golang Checkstyle FAILED. Use 'make fixstyle-go' or fix errors manually."; \
+        echo "******************************************************************************"; \
+        exit 1; \
+    fi
+
+.PHONY: fixstyle-go
+fixstyle-go:
+       @echo "Modified files:"
+       @find . -type f -name '*.go' -not -path './.go_cache/*' -exec go run golang.org/x/tools/cmd/[email protected] -w -l {} +
+       @go mod tidy
+       @echo "*******************************************************************"
+       @echo "Fixstyle done."
+       @echo "*******************************************************************"
+
+.PHONY: cleanup-hst
+cleanup-hst:
+       @if [ ! -f ".last_hst_ppid" ]; then \
+               echo "'.last_hst_ppid' file does not exist."; \
+               exit 1; \
+       fi
+       @echo "****************************"
+       @echo "Removing docker containers:"
+       @# "-" ignores errors
+       @-sudo docker rm $$(sudo docker stop $$(sudo docker ps -a -q --filter "name=$$(cat .last_hst_ppid)") -t 0)
+       @echo "****************************"
+       @echo "Removing IP address files:"
+       @find . -type f -regextype egrep -regex '.*[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' -exec sudo rm -v {} \;
+       @find . -type f -name "fd00:0*" -exec sudo rm -v {} \;
+       @echo "****************************"
+       @echo "Removing network namespaces:"
+       @for ns in $$(ip netns list | grep $$(cat .last_hst_ppid) | awk '{print $$1}'); do \
+               echo $$ns; \
+       sudo ip netns delete $$ns; \
+       done
+       @echo "****************************"
+       @echo "Done."
+       @echo "****************************"
similarity index 91%
rename from extras/hs-test/framework_test.go
rename to test-c/hs-test/framework_test.go
index 67aab3f..0117d4d 100644 (file)
@@ -7,11 +7,15 @@ import (
        "testing"
        "time"
 
-       . "fd.io/hs-test/infra/common"
+       . "fd.io/hs-test/infra"
        . "github.com/onsi/ginkgo/v2"
        . "github.com/onsi/gomega"
 )
 
+var _ = ReportBeforeSuite(func(report Report) {
+       TestsThatWillRun = report.PreRunStats.SpecsThatWillRun
+})
+
 var _ = ReportAfterSuite("VPP version under test", func(report Report) {
        for i := range report.SpecReports {
                specReport := report.SpecReports[i]
similarity index 65%
rename from extras/hs-test/go.mod
rename to test-c/hs-test/go.mod
index ec840a9..9fc6e49 100644 (file)
@@ -17,62 +17,38 @@ require (
        go.fd.io/govpp v0.12.0
        golang.org/x/net v0.37.0
        gopkg.in/yaml.v3 v3.0.1
-       k8s.io/api v0.30.2
-       k8s.io/apimachinery v0.30.2
-       k8s.io/client-go v0.30.2
 )
 
 require (
        github.com/Microsoft/go-winio v0.6.2 // indirect
-       github.com/a8m/envsubst v1.4.2
        github.com/containerd/log v0.1.0 // indirect
-       github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
        github.com/distribution/reference v0.6.0 // indirect
        github.com/docker/go-connections v0.4.0 // indirect
-       github.com/emicklei/go-restful/v3 v3.11.2 // indirect
        github.com/fatih/color v1.16.0 // indirect
        github.com/felixge/httpsnoop v1.0.4 // indirect
        github.com/fsnotify/fsnotify v1.9.0 // indirect
        github.com/go-logr/logr v1.4.2 // indirect
        github.com/go-logr/stdr v1.2.2 // indirect
        github.com/go-openapi/errors v0.22.0 // indirect
-       github.com/go-openapi/jsonpointer v0.21.0 // indirect
-       github.com/go-openapi/jsonreference v0.21.0 // indirect
        github.com/go-openapi/strfmt v0.23.0 // indirect
        github.com/go-openapi/swag v0.23.0 // indirect
        github.com/go-openapi/validate v0.24.0 // indirect
        github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
        github.com/gogo/protobuf v1.3.2 // indirect
-       github.com/golang/protobuf v1.5.4 // indirect
-       github.com/google/gnostic-models v0.6.8 // indirect
        github.com/google/go-cmp v0.7.0 // indirect
-       github.com/google/gofuzz v1.2.0 // indirect
        github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
        github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
-       github.com/google/uuid v1.6.0 // indirect
-       github.com/gorilla/websocket v1.5.1 // indirect
        github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
-       github.com/imdario/mergo v0.3.16 // indirect
-       github.com/joho/godotenv v1.5.1
-       github.com/josharian/intern v1.0.0 // indirect
-       github.com/json-iterator/go v1.1.12 // indirect
        github.com/lunixbochs/struc v0.0.0-20200521075829-a4cb8d33dbbe // indirect
-       github.com/mailru/easyjson v0.7.7 // indirect
        github.com/mattn/go-colorable v0.1.13 // indirect
        github.com/mattn/go-isatty v0.0.20 // indirect
        github.com/moby/docker-image-spec v1.3.1 // indirect
-       github.com/moby/spdystream v0.2.0 // indirect
-       github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
-       github.com/modern-go/reflect2 v1.0.2 // indirect
-       github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
-       github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
        github.com/opencontainers/go-digest v1.0.0 // indirect
        github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect
        github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
        github.com/pkg/errors v0.9.1 // indirect
        github.com/quic-go/qpack v0.5.1 // indirect
        github.com/sasha-s/go-deadlock v0.3.1 // indirect
-       github.com/spf13/pflag v1.0.6 // indirect
        github.com/spf13/viper v1.19.0 // indirect
        github.com/vishvananda/netns v0.0.4 // indirect
        go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
@@ -85,22 +61,16 @@ require (
        golang.org/x/crypto v0.36.0 // indirect
        golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect
        golang.org/x/mod v0.23.0 // indirect
-       golang.org/x/oauth2 v0.18.0 // indirect
        golang.org/x/sync v0.13.0 // indirect
        golang.org/x/sys v0.31.0 // indirect
-       golang.org/x/term v0.30.0 // indirect
        golang.org/x/text v0.24.0 // indirect
        golang.org/x/time v0.5.0 // indirect
        golang.org/x/tools v0.30.0 // indirect
-       google.golang.org/appengine v1.6.8 // indirect
        google.golang.org/protobuf v1.36.5 // indirect
-       gopkg.in/inf.v0 v0.9.1 // indirect
-       gopkg.in/yaml.v2 v2.4.0 // indirect
+       gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
        gotest.tools/v3 v3.5.1 // indirect
+       k8s.io/apimachinery v0.30.2 // indirect
+       k8s.io/client-go v0.30.2 // indirect
        k8s.io/klog/v2 v2.120.1 // indirect
-       k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
        k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 // indirect
-       sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
-       sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
-       sigs.k8s.io/yaml v1.4.0 // indirect
 )
similarity index 78%
rename from extras/hs-test/go.sum
rename to test-c/hs-test/go.sum
index 2dda539..302454c 100644 (file)
@@ -2,10 +2,6 @@ github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEK
 github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
 github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
 github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
-github.com/a8m/envsubst v1.4.2 h1:4yWIHXOLEJHQEFd4UjrWDrYeYlV7ncFWJOCBRLOZHQg=
-github.com/a8m/envsubst v1.4.2/go.mod h1:MVUTQNGQ3tsjOOtKCNd+fl8RzhsXcDvvAEzkhGtlsbY=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
 github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
 github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
 github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
@@ -32,8 +28,6 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 github.com/edwarnicke/exechelper v1.0.3 h1:OY2ocGAITTqnEDvZk0dRQSeMIQvyH0SyL/4ncz+5GeQ=
 github.com/edwarnicke/exechelper v1.0.3/go.mod h1:R65OUPKns4bgeHkCmfSHbmqLBU8aHZxTgLmEyUBUk4U=
-github.com/emicklei/go-restful/v3 v3.11.2 h1:1onLa9DcsMYO9P+CXaL0dStDqQ2EHHXLiz+BtnqkLAU=
-github.com/emicklei/go-restful/v3 v3.11.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
 github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
 github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
 github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
@@ -69,28 +63,14 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
 github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
-github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
-github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
-github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
-github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
 github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
-github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
-github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
 github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
-github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
 github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
 github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
 github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
@@ -98,20 +78,17 @@ github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs
 github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
 github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM=
 github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=
-github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
-github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
-github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
-github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
-github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
@@ -131,21 +108,10 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
 github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
 github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
-github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
-github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
 github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
 github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
-github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
-github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
-github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
 github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
-github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
-github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
-github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
 github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 github.com/onsi/ginkgo/v2 v2.23.3 h1:edHxnszytJ4lD9D5Jjc4tiDkPBZ3siDeJJkUZJJVkp0=
@@ -196,7 +162,6 @@ github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
 github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
 github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
@@ -216,7 +181,6 @@ github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1Y
 github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
 github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
 go.fd.io/govpp v0.12.0 h1:5HnMzsKHSFdxglsFyEhR0g+CzncWiLYXG2NDYgNUrnE=
@@ -248,53 +212,36 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/W
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
 golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
 golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
 golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
 golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
 golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
-golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
-golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
 golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
 golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
-golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
 golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
 golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
 golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
@@ -303,15 +250,12 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
 golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
-google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
 google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
 google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0=
 google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw=
@@ -319,41 +263,24 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:
 google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
 google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
 google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
-google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
 google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
-gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
 gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
-gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
 gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
-k8s.io/api v0.30.2 h1:+ZhRj+28QT4UOH+BKznu4CBgPWgkXO7XAvMcMl0qKvI=
-k8s.io/api v0.30.2/go.mod h1:ULg5g9JvOev2dG0u2hig4Z7tQ2hHIuS+m8MNZ+X6EmI=
 k8s.io/apimachinery v0.30.2 h1:fEMcnBj6qkzzPGSVsAZtQThU62SmQ4ZymlXRC5yFSCg=
 k8s.io/apimachinery v0.30.2/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
 k8s.io/client-go v0.30.2 h1:sBIVJdojUNPDU/jObC+18tXWcTJVcwyqS9diGdWHk50=
 k8s.io/client-go v0.30.2/go.mod h1:JglKSWULm9xlJLx4KCkfLLQ7XwtlbflV6uFFSHTMgVs=
 k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
 k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
-k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
-k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
 k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak=
 k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
-sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
-sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
-sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
-sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
-sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
-sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
similarity index 92%
rename from extras/hs-test/hs_test.sh
rename to test-c/hs-test/hs_test.sh
index ac6b407..68fb1fd 100755 (executable)
@@ -1,7 +1,5 @@
 #!/usr/bin/env bash
 
-source vars
-
 args=
 focused_test=0
 persist_set=0
@@ -16,7 +14,6 @@ tc_names=()
 skip_names=()
 dryrun=
 no_color=
-perf=
 hs_root=
 
 for i in "$@"
@@ -119,10 +116,6 @@ case "${i}" in
             ginkgo_args="$ginkgo_args --no-color"
         fi
         ;;
-    --perf=*)
-        perf="${i#*=}"
-        args="$args -perf"
-        ;;
     --timeout=*)
         args="$args -timeout ${i#*=}"
         ;;
@@ -132,12 +125,6 @@ case "${i}" in
 esac
 done
 
-if [ "$perf" = "true" ]; then
-    ginkgo_args="$ginkgo_args --label-filter=Perf"
-else
-    ginkgo_args="$ginkgo_args --label-filter=!Perf"
-fi
-
 if [ ${#tc_names[@]} -gt 1 ]
 then
     focused_test=0
@@ -193,16 +180,13 @@ mkdir -p .go_cache
 make build-go
 
 mkdir -p summary
+rm -f summary/*
 # shellcheck disable=SC2086
-CMD="sudo -E go run github.com/onsi/ginkgo/v2/ginkgo --json-report=summary/report.json $ginkgo_args -- $args"
+CMD="go run github.com/onsi/ginkgo/v2/ginkgo --json-report=summary/report.json $ginkgo_args -- $args"
 echo "$CMD"
 $CMD
 exit_status=$?
 
-if [ -e "summary/failed-summary.log" ]; then
-        rm summary/failed-summary.log
-fi
-
 if [ $exit_status != 0 ]; then
     jq -r '.[0] | .SpecReports[] | select((.State == "failed") or (.State == "timedout") or (.State == "panicked")) | select(.Failure != null) |
 "TestName:
diff --git a/test-c/hs-test/infra/asserts.go b/test-c/hs-test/infra/asserts.go
new file mode 100644 (file)
index 0000000..6ca444b
--- /dev/null
@@ -0,0 +1,114 @@
+package hst
+
+import (
+       "fmt"
+       "net/http"
+       "strconv"
+       "strings"
+       "time"
+
+       . "github.com/onsi/gomega"
+)
+
+func (s *HstSuite) AssertNil(object any, msgAndArgs ...any) {
+       ExpectWithOffset(2, object).To(BeNil(), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertNotNil(object any, msgAndArgs ...any) {
+       ExpectWithOffset(2, object).ToNot(BeNil(), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertEqual(expected, actual any, msgAndArgs ...any) {
+       ExpectWithOffset(2, actual).To(Equal(expected), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertNotEqual(expected, actual any, msgAndArgs ...any) {
+       ExpectWithOffset(2, actual).ToNot(Equal(expected), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertContains(testString, contains any, msgAndArgs ...any) {
+       ExpectWithOffset(2, strings.ToLower(fmt.Sprint(testString))).To(ContainSubstring(strings.ToLower(fmt.Sprint(contains))), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertNotContains(testString, contains any, msgAndArgs ...any) {
+       ExpectWithOffset(2, strings.ToLower(fmt.Sprint(testString))).ToNot(ContainSubstring(strings.ToLower(fmt.Sprint(contains))), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertEmpty(object any, msgAndArgs ...any) {
+       ExpectWithOffset(2, object).To(BeEmpty(), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertNotEmpty(object any, msgAndArgs ...any) {
+       ExpectWithOffset(2, object).ToNot(BeEmpty(), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertMatchError(actual, expected error, msgAndArgs ...any) {
+       ExpectWithOffset(2, actual).To(MatchError(expected), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertGreaterEqual(actual, expected any, msgAndArgs ...any) {
+       ExpectWithOffset(2, actual).Should(BeNumerically(">=", expected), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertGreaterThan(actual, expected any, msgAndArgs ...any) {
+       ExpectWithOffset(2, actual).Should(BeNumerically(">", expected), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertLessEqual(actual, expected any, msgAndArgs ...any) {
+       ExpectWithOffset(2, actual).Should(BeNumerically("<=", expected), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertLessThan(actual, expected any, msgAndArgs ...any) {
+       ExpectWithOffset(2, actual).Should(BeNumerically("<", expected), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertEqualWithinThreshold(actual, expected, threshold any, msgAndArgs ...any) {
+       ExpectWithOffset(2, actual).Should(BeNumerically("~", expected, threshold), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertTimeEqualWithinThreshold(actual, expected time.Time, threshold time.Duration, msgAndArgs ...any) {
+       ExpectWithOffset(2, actual).Should(BeTemporally("~", expected, threshold), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertHttpStatus(resp *http.Response, expectedStatus int, msgAndArgs ...any) {
+       ExpectWithOffset(2, resp).To(HaveHTTPStatus(expectedStatus), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertHttpHeaderWithValue(resp *http.Response, key string, value any, msgAndArgs ...any) {
+       ExpectWithOffset(2, resp).To(HaveHTTPHeaderWithValue(key, value), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertHttpHeaderNotPresent(resp *http.Response, key string, msgAndArgs ...any) {
+       ExpectWithOffset(2, resp.Header.Get(key)).To(BeEmpty(), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertHttpContentLength(resp *http.Response, expectedContentLen int64, msgAndArgs ...any) {
+       ExpectWithOffset(2, resp).To(HaveHTTPHeaderWithValue("Content-Length", strconv.FormatInt(expectedContentLen, 10)), msgAndArgs...)
+}
+
+func (s *HstSuite) AssertHttpBody(resp *http.Response, expectedBody string, msgAndArgs ...any) {
+       ExpectWithOffset(2, resp).To(HaveHTTPBody(expectedBody), msgAndArgs...)
+}
+
+// Coverage builds take longer to finish -> assert timeout is set to 'TestTimeout - 30 seconds' to let the test finish properly
+func (s *HstSuite) AssertChannelClosed(timeout time.Duration, channel chan error) {
+       if *IsCoverage && timeout > time.Second*30 {
+               timeout = TestTimeout - time.Second*30
+               s.Log("Coverage build, assert timeout is set to %s", timeout.String())
+       }
+       EventuallyWithOffset(2, channel).WithTimeout(timeout).Should(BeClosed())
+}
+
+// Pass the parsed result struct and the minimum amount of data transferred in MB.
+// Won't do anything when testing a coverage build.
+func (s *HstSuite) AssertIperfMinTransfer(result IPerfResult, minTransferred int) {
+       if *IsCoverage {
+               s.Log("Coverage build; not asserting")
+               return
+       }
+       if result.Start.Details.Protocol == "TCP" {
+               s.AssertGreaterEqual(result.End.TcpReceived.MBytes, minTransferred)
+       } else {
+               s.AssertGreaterEqual(result.End.Udp.MBytes, minTransferred)
+       }
+}
similarity index 99%
rename from extras/hs-test/infra/container.go
rename to test-c/hs-test/infra/container.go
index 8ea694d..e577192 100644 (file)
@@ -13,7 +13,6 @@ import (
        "text/template"
        "time"
 
-       . "fd.io/hs-test/infra/common"
        "github.com/cilium/cilium/pkg/sysctl"
        containerTypes "github.com/docker/docker/api/types/container"
        "github.com/docker/docker/api/types/filters"
similarity index 99%
rename from extras/hs-test/infra/cpu.go
rename to test-c/hs-test/infra/cpu.go
index 589c51d..621011b 100644 (file)
@@ -7,8 +7,6 @@ import (
        "os"
        "os/exec"
        "strings"
-
-       . "fd.io/hs-test/infra/common"
 )
 
 var CgroupPath = "/sys/fs/cgroup/"
similarity index 91%
rename from extras/hs-test/infra/hst_suite.go
rename to test-c/hs-test/infra/suite_base.go
index d7d9eb8..e4bc86c 100644 (file)
@@ -4,6 +4,8 @@ import (
        "bufio"
        "flag"
        "fmt"
+       "io"
+       "log"
        "net/http"
        "net/http/httputil"
        "os"
@@ -14,7 +16,6 @@ import (
        "strings"
        "time"
 
-       . "fd.io/hs-test/infra/common"
        containerTypes "github.com/docker/docker/api/types/container"
        "github.com/docker/docker/client"
        "github.com/edwarnicke/exechelper"
@@ -35,9 +36,30 @@ var VppSourceFileDir = flag.String("vppsrc", "", "vpp source file directory")
 var IsDebugBuild = flag.Bool("debug_build", false, "some paths are different with debug build")
 var UseCpu0 = flag.Bool("cpu0", false, "use cpu0")
 var IsLeakCheck = flag.Bool("leak_check", false, "run leak-check tests")
+var IsCoverage = flag.Bool("coverage", false, "use coverage run config")
+var IsPersistent = flag.Bool("persist", false, "persists topology config")
+var IsVerbose = flag.Bool("verbose", false, "verbose test output")
+var WhoAmI = flag.String("whoami", "root", "what user ran hs-test")
+var ParallelTotal = flag.Lookup("ginkgo.parallel.total")
+var IsVppDebug = flag.Bool("debug", false, "attach gdb to vpp")
+var DryRun = flag.Bool("dryrun", false, "set up containers but don't run tests")
+var Timeout = flag.Int("timeout", 5, "test timeout override (in minutes)")
+var PerfTesting = flag.Bool("perf", false, "perf test flag")
+var NumaAwareCpuAlloc bool
+var TestTimeout time.Duration
+var RunningInCi bool
+var TestsThatWillRun int
+
+const (
+       LogDir    string = "/tmp/hs-test/"
+       VolumeDir string = "/vol"
+)
 
 type HstSuite struct {
-       HstCommon
+       Ppid                string
+       ProcessIndex        string
+       Logger              *log.Logger
+       LogFile             *os.File
        AllContainers       map[string]*Container
        StartedContainers   []*Container
        NetConfigs          []NetConfig
@@ -189,25 +211,6 @@ func (s *HstSuite) newDockerClient() {
        s.Log("docker client created")
 }
 
-func (s *HstSuite) SetupSuite() {
-       RegisterFailHandler(func(message string, callerSkip ...int) {
-               s.HstFail()
-               Fail(message, callerSkip...)
-       })
-       s.HstCommon.SetupSuite()
-       s.newDockerClient()
-
-       var err error
-       s.CpuAllocator, err = CpuAllocator()
-       s.CpuAllocator.suite = s
-       if err != nil {
-               Fail("failed to init cpu allocator: " + fmt.Sprint(err))
-       }
-       s.CpusPerContainer = *NConfiguredCpus
-       s.CpusPerVppContainer = *NConfiguredVppCpus
-       s.CoverageRun = *IsCoverage
-}
-
 func (s *HstSuite) AllocateCpus(containerName string) []int {
        var cpuCtx *CpuContext
        var err error
@@ -233,8 +236,49 @@ func (s *HstSuite) AddCpuContext(cpuCtx *CpuContext) {
        s.CpuContexts = append(s.CpuContexts, cpuCtx)
 }
 
+func (s *HstSuite) Skip(args string) {
+       Skip(args)
+}
+
+func (s *HstSuite) CreateLogger() {
+       suiteName := s.GetCurrentSuiteName()
+       var err error
+       s.LogFile, err = os.Create("summary/" + suiteName + ".log")
+       if err != nil {
+               Fail("Unable to create log file.")
+       }
+       s.Logger = log.New(io.Writer(s.LogFile), "", log.LstdFlags)
+}
+
+func (s *HstSuite) SetupSuite() {
+       RegisterFailHandler(func(message string, callerSkip ...int) {
+               s.HstFail()
+               Fail(message, callerSkip...)
+       })
+       s.CreateLogger()
+       s.Log("[* SUITE SETUP]")
+       s.Ppid = fmt.Sprint(os.Getppid())
+       // remove last number so we have space to prepend a process index (interfaces have a char limit)
+       s.Ppid = s.Ppid[:len(s.Ppid)-1]
+       s.ProcessIndex = fmt.Sprint(GinkgoParallelProcess())
+       s.newDockerClient()
+
+       var err error
+       s.CpuAllocator, err = CpuAllocator()
+       s.CpuAllocator.suite = s
+       if err != nil {
+               Fail("failed to init cpu allocator: " + fmt.Sprint(err))
+       }
+       s.CpusPerContainer = *NConfiguredCpus
+       s.CpusPerVppContainer = *NConfiguredVppCpus
+       s.CoverageRun = *IsCoverage
+}
+
 func (s *HstSuite) TeardownSuite() {
-       s.HstCommon.TeardownSuite()
+       if *IsPersistent || *DryRun {
+               s.Skip("Skipping suite teardown")
+       }
+       s.Log("[* SUITE TEARDOWN]")
        // allow ports to be reused by removing them from reservedPorts slice
        reservedPorts = reservedPorts[:len(reservedPorts)-s.numOfNewPorts]
        if s.Ip4AddrAllocator != nil {
@@ -249,8 +293,21 @@ func (s *HstSuite) TeardownSuite() {
        s.UnconfigureNetworkTopology()
 }
 
+func (s *HstSuite) SetupTest() {
+       TestCounterFunc()
+       s.Log("[* TEST SETUP]")
+       // doesn't impact MW/solo tests
+       s.CpuAllocator.lastCpu = (GinkgoParallelProcess() - 1) * 4
+       s.StartedContainers = s.StartedContainers[:0]
+       s.SkipIfUnconfiguring()
+       s.SetupContainers()
+}
+
 func (s *HstSuite) TeardownTest() {
-       s.HstCommon.TeardownTest()
+       if *IsPersistent || *DryRun {
+               s.Skip("Skipping test teardown")
+       }
+       s.Log("[* TEST TEARDOWN]")
        s.SkipIfNotEnoguhCpus = false
        // reset to defaults
        s.CpusPerContainer = *NConfiguredCpus
@@ -275,15 +332,6 @@ func (s *HstSuite) SkipIfNotCoverage() {
        }
 }
 
-func (s *HstSuite) SetupTest() {
-       s.HstCommon.SetupTest()
-       // doesn't impact MW/solo tests
-       s.CpuAllocator.lastCpu = (GinkgoParallelProcess() - 1) * 4
-       s.StartedContainers = s.StartedContainers[:0]
-       s.SkipIfUnconfiguring()
-       s.SetupContainers()
-}
-
 func (s *HstSuite) SetupContainers() {
        for _, container := range s.AllContainers {
                if !container.IsOptional {
similarity index 98%
rename from extras/hs-test/infra/suite_cpu_pinning.go
rename to test-c/hs-test/infra/suite_cpu_pinning.go
index a9f3d02..bd7677d 100644 (file)
@@ -6,7 +6,6 @@ import (
        "runtime"
        "strings"
 
-       . "fd.io/hs-test/infra/common"
        . "github.com/onsi/ginkgo/v2"
 )
 
similarity index 99%
rename from extras/hs-test/infra/suite_envoy_proxy.go
rename to test-c/hs-test/infra/suite_envoy_proxy.go
index 1932f18..9ad7b76 100644 (file)
@@ -12,7 +12,6 @@ import (
        "strings"
        "time"
 
-       . "fd.io/hs-test/infra/common"
        . "github.com/onsi/ginkgo/v2"
 )
 
similarity index 99%
rename from extras/hs-test/infra/suite_hsi.go
rename to test-c/hs-test/infra/suite_hsi.go
index 0575577..997cdc2 100644 (file)
@@ -7,7 +7,6 @@ import (
        "runtime"
        "strings"
 
-       . "fd.io/hs-test/infra/common"
        . "github.com/onsi/ginkgo/v2"
 )
 
similarity index 99%
rename from extras/hs-test/infra/suite_http1.go
rename to test-c/hs-test/infra/suite_http1.go
index ead9c89..0190d9f 100644 (file)
@@ -5,7 +5,6 @@ import (
        "runtime"
        "strings"
 
-       . "fd.io/hs-test/infra/common"
        . "github.com/onsi/ginkgo/v2"
 )
 
similarity index 99%
rename from extras/hs-test/infra/suite_http2.go
rename to test-c/hs-test/infra/suite_http2.go
index 770f631..67410d6 100644 (file)
@@ -13,7 +13,6 @@ import (
        "time"
 
        "fd.io/hs-test/h2spec_extras"
-       . "fd.io/hs-test/infra/common"
        . "github.com/onsi/ginkgo/v2"
        "github.com/summerwind/h2spec"
        "github.com/summerwind/h2spec/config"
@@ -457,16 +456,12 @@ var _ = Describe("H2SpecSuite", Ordered, ContinueOnFailure, func() {
                                switch sp.tg {
                                case GenericTestGroup:
                                        tg = generic.Spec()
-                                       break
                                case HpackTestGroup:
                                        tg = hpack.Spec()
-                                       break
                                case Http2TestGroup:
                                        tg = http2.Spec()
-                                       break
                                case ExtrasTestGroup:
                                        tg = h2spec_extras.Spec()
-                                       break
                                }
                                tg.Test(conf)
 
@@ -498,11 +493,9 @@ func h2specdVerifyResult(s Http2Suite, nExecuted int) bool {
        defer resp.Body.Close()
        report, err := io.ReadAll(resp.Body)
        s.AssertContains(string(report), "0 failed")
+       s.AssertNil(err)
        expected := fmt.Sprintf("<div>%d tests, %d passed", nExecuted, nExecuted)
-       if !strings.Contains(string(report), expected) {
-               return false
-       }
-       return true
+       return strings.Contains(string(report), expected)
 }
 
 var _ = Describe("H2SpecClientSuite", Ordered, Serial, func() {
similarity index 98%
rename from extras/hs-test/infra/suite_iperf_linux.go
rename to test-c/hs-test/infra/suite_iperf_linux.go
index 0e31c9c..e061eb2 100644 (file)
@@ -5,7 +5,6 @@ import (
        "runtime"
        "strings"
 
-       . "fd.io/hs-test/infra/common"
        . "github.com/onsi/ginkgo/v2"
 )
 
similarity index 99%
rename from extras/hs-test/infra/suite_ldp.go
rename to test-c/hs-test/infra/suite_ldp.go
index 7009631..ea73efe 100644 (file)
@@ -7,7 +7,6 @@ import (
        "strings"
        "time"
 
-       . "fd.io/hs-test/infra/common"
        . "github.com/onsi/ginkgo/v2"
 )
 
similarity index 99%
rename from extras/hs-test/infra/suite_nginx_proxy.go
rename to test-c/hs-test/infra/suite_nginx_proxy.go
index 90306bd..2ef84e3 100644 (file)
@@ -6,7 +6,6 @@ import (
        "runtime"
        "strings"
 
-       . "fd.io/hs-test/infra/common"
        . "github.com/onsi/ginkgo/v2"
 )
 
similarity index 99%
rename from extras/hs-test/infra/suite_no_topo.go
rename to test-c/hs-test/infra/suite_no_topo.go
index 5a411ad..787c857 100644 (file)
@@ -6,7 +6,6 @@ import (
        "runtime"
        "strings"
 
-       . "fd.io/hs-test/infra/common"
        . "github.com/onsi/ginkgo/v2"
 )
 
similarity index 99%
rename from extras/hs-test/infra/suite_no_topo6.go
rename to test-c/hs-test/infra/suite_no_topo6.go
index 0997e8a..499e970 100644 (file)
@@ -6,7 +6,6 @@ import (
        "runtime"
        "strings"
 
-       . "fd.io/hs-test/infra/common"
        . "github.com/onsi/ginkgo/v2"
 )
 
similarity index 99%
rename from extras/hs-test/infra/suite_veth.go
rename to test-c/hs-test/infra/suite_veth.go
index 37907f1..a3db5cd 100644 (file)
@@ -7,7 +7,6 @@ import (
        "strings"
        "time"
 
-       . "fd.io/hs-test/infra/common"
        . "github.com/onsi/ginkgo/v2"
 )
 
similarity index 99%
rename from extras/hs-test/infra/suite_veth6.go
rename to test-c/hs-test/infra/suite_veth6.go
index a57a752..f3e7fda 100644 (file)
@@ -7,7 +7,6 @@ import (
        "strings"
        "time"
 
-       . "fd.io/hs-test/infra/common"
        . "github.com/onsi/ginkgo/v2"
 )
 
similarity index 99%
rename from extras/hs-test/infra/suite_vpp_proxy.go
rename to test-c/hs-test/infra/suite_vpp_proxy.go
index 7b108b9..0c7c151 100644 (file)
@@ -17,7 +17,6 @@ import (
        "time"
 
        "fd.io/hs-test/h2spec_extras"
-       . "fd.io/hs-test/infra/common"
        . "github.com/onsi/ginkgo/v2"
        "github.com/summerwind/h2spec/config"
 )
similarity index 99%
rename from extras/hs-test/infra/suite_vpp_udp_proxy.go
rename to test-c/hs-test/infra/suite_vpp_udp_proxy.go
index 912ab64..43b89f1 100644 (file)
@@ -13,7 +13,6 @@ import (
 
        "fd.io/hs-test/h2spec_extras"
 
-       . "fd.io/hs-test/infra/common"
        . "github.com/onsi/ginkgo/v2"
        "github.com/summerwind/h2spec/config"
 )
similarity index 68%
rename from extras/hs-test/infra/utils.go
rename to test-c/hs-test/infra/utils.go
index 6812ae7..3cf152e 100644 (file)
@@ -2,6 +2,7 @@ package hst
 
 import (
        "crypto/tls"
+       "encoding/json"
        "errors"
        "fmt"
        "io"
@@ -10,12 +11,15 @@ import (
        "net/http/httputil"
        "os"
        "os/exec"
+       "path/filepath"
        "regexp"
+       "runtime"
        "strconv"
        "strings"
        "time"
 
        "github.com/edwarnicke/exechelper"
+       . "github.com/onsi/ginkgo/v2"
 )
 
 const networkTopologyDir string = "topo-network/"
@@ -382,3 +386,159 @@ func (s *HstSuite) ParseEchoClientTransfer(stats string) (uint64, error) {
        }
        return uVal, nil
 }
+
+// Logs to files by default, logs to stdout when VERBOSE=true with GinkgoWriter
+// to keep console tidy
+func (s *HstSuite) Log(log any, arg ...any) {
+       var logStr string
+       if len(arg) == 0 {
+               logStr = fmt.Sprint(log)
+       } else {
+               logStr = fmt.Sprintf(fmt.Sprint(log), arg...)
+       }
+       logs := strings.Split(logStr, "\n")
+
+       for _, line := range logs {
+               s.Logger.Println(line)
+       }
+       if *IsVerbose {
+               GinkgoWriter.Println(logStr)
+       }
+}
+
+func GetTestFilename() string {
+       _, filename, _, _ := runtime.Caller(2)
+       return filepath.Base(filename)
+}
+
+var testCounter uint16
+var startTime time.Time = time.Now()
+
+func TestCounterFunc() {
+       if ParallelTotal.Value.String() != "1" {
+               return
+       }
+       testCounter++
+       fmt.Printf("Test counter: %d/%d (%.2f%%)\n"+
+               "Time elapsed: %.2fs\n",
+               testCounter, TestsThatWillRun, float64(testCounter)/float64(TestsThatWillRun)*100, time.Since(startTime).Seconds())
+}
+
+type IPerfResult struct {
+       Start struct {
+               Timestamp struct {
+                       Time string `json:"time"`
+               } `json:"timestamp"`
+               Connected []struct {
+                       Socket     int    `json:"socket"`
+                       LocalHost  string `json:"local_host"`
+                       LocalPort  int    `json:"local_port"`
+                       RemoteHost string `json:"remote_host"`
+                       RemotePort int    `json:"remote_port"`
+               } `json:"connected"`
+               Version string `json:"version"`
+               Details struct {
+                       Protocol string `json:"protocol"`
+               } `json:"test_start"`
+       } `json:"start"`
+       End struct {
+               TcpSent *struct {
+                       MbitsPerSecond float64 `json:"bits_per_second"`
+                       MBytes         float64 `json:"bytes"`
+               } `json:"sum_sent,omitempty"`
+               TcpReceived *struct {
+                       MbitsPerSecond float64 `json:"bits_per_second"`
+                       MBytes         float64 `json:"bytes"`
+               } `json:"sum_received,omitempty"`
+               Udp *struct {
+                       MbitsPerSecond float64 `json:"bits_per_second"`
+                       JitterMs       float64 `json:"jitter_ms,omitempty"`
+                       LostPackets    int     `json:"lost_packets,omitempty"`
+                       Packets        int     `json:"packets,omitempty"`
+                       LostPercent    float64 `json:"lost_percent,omitempty"`
+                       MBytes         float64 `json:"bytes"`
+               } `json:"sum,omitempty"`
+       } `json:"end"`
+}
+
+func (s *HstSuite) ParseJsonIperfOutput(jsonResult []byte) IPerfResult {
+       var result IPerfResult
+
+       // VCL/LDP debugging can pollute output so find the first occurrence of a curly brace to locate the start of JSON data
+       jsonStart := -1
+       jsonEnd := len(jsonResult)
+       braceCount := 0
+       for i := 0; i < len(jsonResult); i++ {
+               if jsonResult[i] == '{' {
+                       if jsonStart == -1 {
+                               jsonStart = i
+                       }
+                       braceCount++
+               } else if jsonResult[i] == '}' {
+                       braceCount--
+                       if braceCount == 0 {
+                               jsonEnd = i + 1
+                               break
+                       }
+               }
+       }
+       jsonResult = jsonResult[jsonStart:jsonEnd]
+
+       // remove iperf warning line if present
+       if strings.Contains(string(jsonResult), "warning") {
+               index := strings.Index(string(jsonResult), "\n")
+               jsonResult = jsonResult[index+1:]
+       }
+
+       err := json.Unmarshal(jsonResult, &result)
+       s.AssertNil(err)
+
+       if result.Start.Details.Protocol == "TCP" {
+               result.End.TcpSent.MbitsPerSecond = result.End.TcpSent.MbitsPerSecond / 1000000
+               result.End.TcpSent.MBytes = result.End.TcpSent.MBytes / 1000000
+               result.End.TcpReceived.MbitsPerSecond = result.End.TcpReceived.MbitsPerSecond / 1000000
+               result.End.TcpReceived.MBytes = result.End.TcpReceived.MBytes / 1000000
+       } else {
+               result.End.Udp.MBytes = result.End.Udp.MBytes / 1000000
+               result.End.Udp.MbitsPerSecond = result.End.Udp.MbitsPerSecond / 1000000
+       }
+
+       return result
+}
+
+func (s *HstSuite) LogJsonIperfOutput(result IPerfResult) {
+       s.Log("\n*******************************************\n"+
+               "%s\n"+
+               "[%s] %s:%d connected to %s:%d\n"+
+               "Started:  %s\n",
+               result.Start.Version,
+               result.Start.Details.Protocol,
+               result.Start.Connected[0].LocalHost, result.Start.Connected[0].LocalPort,
+               result.Start.Connected[0].RemoteHost, result.Start.Connected[0].RemotePort,
+               result.Start.Timestamp.Time)
+
+       if result.Start.Details.Protocol == "TCP" {
+               s.Log("Transfer (sent):     %.2f MBytes\n"+
+                       "Bitrate  (sent):     %.2f Mbits/sec\n"+
+                       "Transfer (received): %.2f MBytes\n"+
+                       "Bitrate  (received): %.2f Mbits/sec",
+                       result.End.TcpSent.MBytes,
+                       result.End.TcpSent.MbitsPerSecond,
+                       result.End.TcpReceived.MBytes,
+                       result.End.TcpReceived.MbitsPerSecond)
+       } else {
+               s.Log("Transfer:     %.2f MBytes\n"+
+                       "Bitrate:      %.2f Mbits/sec\n"+
+                       "Jitter:       %.3f ms\n"+
+                       "Packets:      %d\n"+
+                       "Packets lost: %d\n"+
+                       "Percent lost: %.2f%%",
+                       result.End.Udp.MBytes,
+                       result.End.Udp.MbitsPerSecond,
+                       result.End.Udp.JitterMs,
+                       result.End.Udp.Packets,
+                       result.End.Udp.LostPackets,
+                       result.End.Udp.LostPercent)
+       }
+       s.Log("*******************************************\n")
+}
similarity index 99%
rename from extras/hs-test/infra/vppinstance.go
rename to test-c/hs-test/infra/vppinstance.go
index cfb3959..43eaaf3 100644 (file)
@@ -16,7 +16,6 @@ import (
        "syscall"
        "time"
 
-       . "fd.io/hs-test/infra/common"
        "go.fd.io/govpp/binapi/ethernet_types"
 
        "github.com/edwarnicke/exechelper"
similarity index 99%
rename from extras/hs-test/ldp_test.go
rename to test-c/hs-test/ldp_test.go
index b0c6276..94abafc 100644 (file)
@@ -6,7 +6,6 @@ import (
        "time"
 
        . "fd.io/hs-test/infra"
-       . "fd.io/hs-test/infra/common"
        . "github.com/onsi/ginkgo/v2"
 )
 
diff --git a/test-c/kube-test/Makefile b/test-c/kube-test/Makefile
new file mode 100644 (file)
index 0000000..4ab065e
--- /dev/null
@@ -0,0 +1,220 @@
+ifeq ($(VERBOSE),)
+VERBOSE=false
+endif
+
+ifeq ($(PERSIST),)
+PERSIST=false
+endif
+
+ifeq ($(TEST),)
+TEST=all
+endif
+
+ifeq ($(REPEAT),)
+REPEAT=0
+endif
+
+ifeq ($(UBUNTU_CODENAME),)
+UBUNTU_CODENAME=$(shell grep '^UBUNTU_CODENAME=' /etc/os-release | cut -f2- -d=)
+endif
+
+ifeq ($(ARCH),)
+ARCH=$(shell dpkg --print-architecture)
+endif
+
+ifeq ($(NO_COLOR),)
+VERBOSE=false
+endif
+
+ifeq ($(TIMEOUT),)
+TIMEOUT=30
+endif
+
+ifeq ($(GINKGO_TIMEOUT),)
+GINKGO_TIMEOUT=3h
+endif
+
+FORCE_BUILD?=true
+
+.PHONY: help
+help:
+       @echo "Make targets:"
+       @echo " test                     - run kube-test (requires a running cluster)"
+       @echo " build                    - build test infra"
+       @echo " build-debug              - build test infra (vpp debug image)"
+       @echo " cluster-help             - print info about KinD cluster setup (perf testing)"
+       @echo " master-cluster           - setup KinD cluster for performance testing (master CalicoVPP + VPP)"
+       @echo " rebuild-master-cluster   - rebuild VPP and update related pods without shutting down the cluster"
+       @echo " release-cluster          - setup KinD cluster for performance testing (latest CalicoVPP release)"
+       @echo " checkstyle-go            - check style of .go source files"
+       @echo " fixstyle-go              - format .go source files"
+       @echo " cleanup-kube             - removes all kubernetes pods and namespaces from last test run"
+       @echo " list-tests               - list all tests"
+       @echo " install-deps             - install software dependencies"
+       @echo " install-kube-deps        - install software dependencies for kind cluster"
+       @echo
+       @echo "'make build' and 'make test' arguments:"
+       @echo " UBUNTU_VERSION           - ubuntu version for docker image"
+       @echo " FORCE_BUILD=[true|false] - force docker image building"
+       @echo
+       @echo "'make test' specific arguments:"
+       @echo " PERSIST=[true|false]     - whether clean up topology and dockers after test"
+       @echo " VERBOSE=[true|false]     - verbose output"
+       @echo " TEST=[name1,name2...]    - specific test(s) to run"
+       @echo " SKIP=[name1,name2...]    - specific test(s) to skip"
+       @echo " REPEAT=[n]               - repeat tests up to N times or until a failure occurs"
+       @echo " DRYRUN=[true|false]      - set up containers but don't run tests"
+       @echo " NO_COLOR=[true|false]    - disables colorful Docker and Ginkgo output"
+       @echo " TIMEOUT=[minutes]        - test timeout override (30 minutes by default)"
+       @echo " GINKGO_TIMEOUT=[Ns/m/h]  - Ginkgo timeout override (3h by default)"
+
+.PHONY: list-tests
+list-tests:
+       @go run github.com/onsi/ginkgo/v2/ginkgo --dry-run -v --no-color --seed=2 | head -n -1 | grep 'test.go' | \
+               sed 's/^/* /; s/\(Suite\) /\1\//g'
+
+.PHONY: build-vpp-release
+build-vpp-release:
+       @$(MAKE) -C ../.. build-release
+
+.PHONY: build-vpp-debug
+build-vpp-debug:
+       @$(MAKE) -C ../.. build
+
+.build.ok: build
+       @touch .build.ok
+
+.build_debug.ok: build-debug
+       @touch .build.ok
+
+.PHONY: test
+test: FORCE_BUILD=false
+test: .deps.ok .build.ok
+       @bash ./kube_test.sh --persist=$(PERSIST) --verbose=$(VERBOSE) \
+               --test=$(TEST) --repeat=$(REPEAT) --skip=$(SKIP) \
+               --no_color=$(NO_COLOR) --timeout=$(TIMEOUT) \
+               --ginkgo_timeout=$(GINKGO_TIMEOUT); \
+               ./script/compress.sh $$?
+
+.PHONY: cluster-help
+cluster-help:
+       @bash ./script/setup-cluster.sh help
+
+.PHONY: release-cluster
+release-cluster: .kube_deps.ok
+       @bash ./script/setup-cluster.sh release-cluster
+
+.PHONY: master-cluster
+master-cluster: .kube_deps.ok
+       @bash ./script/setup-cluster.sh master-cluster
+
+.PHONY: rebuild-master-cluster
+rebuild-master-cluster: .kube_deps.ok
+       @bash ./script/setup-cluster.sh rebuild-master-cluster
+
+.PHONY: build
+build: .deps.ok build-vpp-release
+       @rm -f .build.ok
+       bash ./script/build_kube.sh release $(FORCE_BUILD)
+       @touch .build.ok
+
+.PHONY: build-debug
+build-debug: .deps.ok build-vpp-debug
+       @rm -f .build.ok
+       bash ./script/build_kube.sh debug $(FORCE_BUILD)
+       @touch .build.ok
+
+.deps.ok:
+       @$(MAKE) install-deps
+
+.kube_deps.ok:
+       @$(MAKE) install-kube-deps
+
+.PHONY: install-kube-deps
+install-kube-deps: .deps.ok
+       -@if ! command -v kube >/dev/null 2>&1; then \
+               echo "Installing KinD"; \
+               go install sigs.k8s.io/[email protected]; \
+               echo "Creating symlink from '$(HOME)/go/bin/kind' to '/usr/bin/kind'"; \
+               sudo ln -s $(HOME)/go/bin/kind /usr/bin/kind; \
+       else \
+               echo "KinD is already installed. Skipping."; \
+       fi
+       @if ! command -v kubectl >/dev/null 2>&1; then \
+        echo "kubectl not found. Installing kubectl..."; \
+        sudo -E apt-get update && sudo apt-get install -y apt-transport-https ca-certificates curl gnupg; \
+        curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.31/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg; \
+        sudo -E chmod 644 /etc/apt/keyrings/kubernetes-apt-keyring.gpg; \
+        echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.31/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list; \
+        sudo -E chmod 644 /etc/apt/sources.list.d/kubernetes.list; \
+        sudo apt-get update && sudo apt-get install -y kubectl; \
+    else \
+        echo "kubectl is already installed. Skipping."; \
+       fi
+       @touch .kube_deps.ok
+
+.PHONY: install-deps
+install-deps:
+       @rm -f .deps.ok
+       @if [ -d "/usr/local/go" ]; then \
+        echo "Go is already installed. You may have to update it manually if version < 1.23.10"; \
+               go version; \
+    else \
+        echo "Installing Go 1.23"; \
+               wget -t 2 https://go.dev/dl/go1.23.10.linux-$(ARCH).tar.gz -O /tmp/go1.23.10.linux-$(ARCH).tar.gz && sudo tar -C /usr/local -xzf /tmp/go1.23.10.linux-$(ARCH).tar.gz; \
+               sudo ln -s /usr/local/go/bin/go /usr/bin/go ; \
+       fi
+       @sudo -E apt-get update
+       @sudo -E apt-get install -y apt-transport-https ca-certificates curl software-properties-common \
+               bridge-utils gpg
+       @if [ ! -f /usr/share/keyrings/docker-archive-keyring.gpg ] ; then \
+               curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg; \
+               echo "deb [arch=$(ARCH) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(UBUNTU_CODENAME) stable" \
+                       | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null ; \
+               apt-get update; \
+       fi
+       @sudo -E apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
+       @touch .deps.ok
+
+.PHONY: checkstyle-go
+checkstyle-go:
+       @output=$$(find . -type f -name '*.go' -not -path './.go_cache/*' -exec go run golang.org/x/tools/cmd/[email protected] -d {} +); \
+       status=$$?; \
+       if [ $$status -ne 0 ]; then \
+               exit $$status; \
+    elif [ -z "$$output" ]; then \
+        echo "******************************************************************************"; \
+        echo "* Kube-Test Golang Checkstyle OK."; \
+        echo "******************************************************************************"; \
+    else \
+        echo "$$output"; \
+        echo "******************************************************************************"; \
+        echo "* Kube-Test Golang Checkstyle FAILED. Use 'make fixstyle-go' or fix errors manually."; \
+        echo "******************************************************************************"; \
+        exit 1; \
+    fi
+
+.PHONY: fixstyle-go
+fixstyle-go:
+       @echo "Modified files:"
+       @find . -type f -name '*.go' -not -path './.go_cache/*' -exec go run golang.org/x/tools/cmd/[email protected] -w -l {} +
+       @go mod tidy
+       @echo "*******************************************************************"
+       @echo "Fixstyle done."
+       @echo "*******************************************************************"
+
+.PHONY: cleanup-kube
+cleanup-kube:
+       @if [ ! -f ".last_ppid" ]; then \
+               echo "'.last_ppid' file does not exist."; \
+               exit 1; \
+       fi
+       @echo "****************************"
+       @echo "Removing kubernetes pods:"
+       @kubectl delete pods --all --grace-period=0 -n kube-test$$(cat .last_ppid)
+       @echo "****************************"
+       @echo "Removing kubernetes namespace:"
+       @kubectl delete namespace kube-test$$(cat .last_ppid)
+       @echo "****************************"
+       @echo "Done."
+       @echo "****************************"
diff --git a/test-c/kube-test/docker/Dockerfile.ab b/test-c/kube-test/docker/Dockerfile.ab
new file mode 100644 (file)
index 0000000..d61e125
--- /dev/null
@@ -0,0 +1,7 @@
+# Apache Bench container that uses the base image
+ARG UBUNTU_VERSION=22.04
+FROM localhost:5001/vpp-test-base:latest
+
+# apache2-utils is now installed in the base image
+
+ENTRYPOINT ["ab"]
diff --git a/test-c/kube-test/docker/Dockerfile.base b/test-c/kube-test/docker/Dockerfile.base
new file mode 100644 (file)
index 0000000..9130da0
--- /dev/null
@@ -0,0 +1,60 @@
+ARG UBUNTU_VERSION=22.04
+FROM ubuntu:${UBUNTU_VERSION} AS base
+
+# Set environment variables
+ENV DEBIAN_FRONTEND=noninteractive
+
+# Install common dependencies needed across multiple containers
+RUN apt-get update && apt-get install -y --no-install-recommends \
+    # Basic utilities
+    ca-certificates \
+    wget \
+    curl \
+    file \
+    gnupg \
+    gnupg2 \
+    git \
+    iproute2 \
+    iputils-ping \
+    less \
+    lsb-release \
+    ubuntu-keyring \
+    vim \
+    # Development & debugging
+    gdb \
+    libunwind-dev \
+    # Libraries frequently needed
+    libapr1 \
+    libnl-3-dev \
+    libnl-route-3-dev \
+    libnuma1 \
+    libsubunit0 \
+    openssl \
+    python3 \
+    # Tools used in tests
+    iperf3 \
+    xz-utils \
+    # Tools moved from derived images
+    apache2-utils \
+    nghttp2 \
+    wrk
+
+# Because of http/3 support we can't use stock nginx in ubuntu 24.04
+RUN curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
+| tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
+RUN echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
+ http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
+ | tee /etc/apt/sources.list.d/nginx.list
+RUN bash -c 'echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \
+| tee /etc/apt/preferences.d/99nginx'
+
+RUN apt update && apt install -y nginx=1.28.0* \
+    # Clean up
+    && apt-get clean \
+    && rm -rf /var/lib/apt/lists/*
+
+# Add a non-root user (useful for containers that need it)
+RUN groupadd -r vpp && useradd -r -g vpp -s /bin/bash vpp
+
+# Set default command
+CMD ["/bin/bash"]
diff --git a/test-c/kube-test/docker/Dockerfile.nginx b/test-c/kube-test/docker/Dockerfile.nginx
new file mode 100644 (file)
index 0000000..5a3e7e2
--- /dev/null
@@ -0,0 +1,19 @@
+# nginx container that uses the base image
+ARG UBUNTU_VERSION=22.04
+FROM localhost:5001/vpp-test-base:latest
+
+# nginx is now installed in the base image
+
+COPY resources/nginx/nginx.conf /nginx.conf
+COPY script/nginx_ldp.sh /usr/bin/nginx_ldp.sh
+
+ENV VCL_CONFIG=/tmp/nginx/vcl.conf
+ENV LDP=/usr/lib/libvcl_ldpreload.so
+ENV LDP_DEBUG=0
+ENV VCL_DEBUG=0
+ENV LDP_SID_BIT=8
+
+# copy vpp-data last to take advantage of caching (do not change)
+COPY vpp-data/lib/* /usr/lib/
+
+ENTRYPOINT ["nginx_ldp.sh", "nginx", "-c", "/nginx.conf"]
diff --git a/test-c/kube-test/docker/Dockerfile.vpp b/test-c/kube-test/docker/Dockerfile.vpp
new file mode 100644 (file)
index 0000000..20d2c65
--- /dev/null
@@ -0,0 +1,38 @@
+# VPP container that uses the base image
+ARG UBUNTU_VERSION=22.04
+FROM localhost:5001/vpp-test-base:latest
+
+# We don't need to install these packages as they're in the base image
+# Just install anything specific to VPP that isn't in the base
+
+ARG OS_ARCH
+RUN echo "I'm building for $OS_ARCH"
+
+ENV DIR=vpp-data/lib/vpp_plugins
+COPY \
+   $DIR/af_packet_plugin.so \
+   $DIR/hs_apps_plugin.so \
+   $DIR/http_plugin.so \
+   $DIR/http_unittest_plugin.so \
+   $DIR/unittest_plugin.so \
+   $DIR/quic_plugin.so \
+   $DIR/quic_quicly_plugin.so \
+   $DIR/hsi_plugin.so \
+   $DIR/http_static_plugin.so \
+   $DIR/ping_plugin.so \
+   $DIR/nsim_plugin.so \
+   $DIR/prom_plugin.so \
+   $DIR/tlsopenssl_plugin.so \
+   $DIR/mactime_plugin.so \
+   $DIR/arping_plugin.so \
+   /usr/lib/$OS_ARCH-linux-gnu/vpp_plugins/
+
+COPY vpp-data/bin/vpp /usr/bin/
+COPY vpp-data/bin/vppctl /usr/bin/
+COPY vpp-data/bin/vpp_echo /usr/bin/
+COPY vpp-data/bin/vcl_* /usr/bin/
+COPY vpp-data/lib/*.so /usr/lib/
+
+# Group already created in base image
+
+ENTRYPOINT ["tail", "-f", "/dev/null"]
diff --git a/test-c/kube-test/docker/Dockerfile.wrk b/test-c/kube-test/docker/Dockerfile.wrk
new file mode 100644 (file)
index 0000000..8517a05
--- /dev/null
@@ -0,0 +1,7 @@
+# wrk HTTP benchmarking container that uses the base image
+ARG UBUNTU_VERSION=22.04
+FROM localhost:5001/vpp-test-base:latest
+
+# wrk is installed in the base image
+
+ENTRYPOINT ["wrk"]
diff --git a/test-c/kube-test/docker/setup-local-registry.sh b/test-c/kube-test/docker/setup-local-registry.sh
new file mode 100755 (executable)
index 0000000..ea8cb1c
--- /dev/null
@@ -0,0 +1,68 @@
+#!/bin/bash
+# Script to set up a local Docker registry
+
+set -e
+
+DOCKER_LOGIN_SCRIPT="/scratch/nomad/.docker-ro/dlogin.sh"
+if [ -x "$DOCKER_LOGIN_SCRIPT" ] ; then
+  $DOCKER_LOGIN_SCRIPT
+fi
+
+# Check if Docker is running
+if ! docker info &>/dev/null; then
+    echo "Error: Docker is not running. Please start Docker and try again."
+    exit 1
+fi
+
+# Registry container name
+REGISTRY_NAME="local-registry"
+REGISTRY_PORT=${1:-5001}
+
+# Check if registry container is already running
+if docker container inspect "$REGISTRY_NAME" &>/dev/null; then
+    echo "=== Local registry '$REGISTRY_NAME' is already running ==="
+    REGISTRY_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$REGISTRY_NAME")
+    echo "Registry is available at: localhost:$REGISTRY_PORT or $REGISTRY_IP:$REGISTRY_PORT"
+else
+    echo "=== Setting up local Docker registry ==="
+
+    # Create a new registry container
+    docker run -d \
+        --name "$REGISTRY_NAME" \
+        --restart=always \
+        -p "$REGISTRY_PORT:5000" \
+        -v /var/lib/registry:/var/lib/registry \
+        registry:2
+
+    REGISTRY_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$REGISTRY_NAME")
+    echo "Registry container created successfully!"
+    echo "Registry is available at: localhost:$REGISTRY_PORT or $REGISTRY_IP:$REGISTRY_PORT"
+
+    # Configure Docker to trust this insecure registry
+    echo "=== Configuring Docker to trust insecure registry ==="
+    if [ -f /etc/docker/daemon.json ]; then
+        # Check if the file already has an insecure-registries entry
+        if grep -q "insecure-registries" /etc/docker/daemon.json; then
+            echo "Insecure registries already configured. Please make sure 'localhost:$REGISTRY_PORT' is included."
+        else
+            echo "Adding 'localhost:$REGISTRY_PORT' to insecure-registries in /etc/docker/daemon.json"
+            echo "You may need to restart Docker for changes to take effect"
+            echo "Please add the following to /etc/docker/daemon.json:"
+            echo "{
+  \"insecure-registries\": [\"localhost:$REGISTRY_PORT\"]
+}"
+        fi
+    else
+        echo "Creating /etc/docker/daemon.json with insecure-registries configuration"
+        echo "You may need to restart Docker for changes to take effect"
+        echo "Please create /etc/docker/daemon.json with the following content:"
+        echo "{
+  \"insecure-registries\": [\"localhost:$REGISTRY_PORT\"]
+}"
+    fi
+fi
+
+echo ""
+echo "=== Local Registry Setup Complete ==="
+echo "To use the local registry, prefix your image tags with 'localhost:$REGISTRY_PORT/'"
+echo "For example: localhost:$REGISTRY_PORT/hs-test/vpp:latest"
diff --git a/test-c/kube-test/framework_test.go b/test-c/kube-test/framework_test.go
new file mode 100644 (file)
index 0000000..3aa121d
--- /dev/null
@@ -0,0 +1,43 @@
+package main
+
+import (
+       "fmt"
+       "os"
+       "testing"
+       "time"
+
+       . "fd.io/kube-test/infra"
+       . "github.com/onsi/ginkgo/v2"
+       . "github.com/onsi/gomega"
+)
+
+var _ = ReportAfterSuite("VPP version under test", func(report Report) {
+       for i := range report.SpecReports {
+               specReport := report.SpecReports[i]
+               for j := range specReport.ReportEntries {
+                       reportEntry := specReport.ReportEntries[j]
+                       if reportEntry.Name == "VPP version" {
+                               fmt.Println(reportEntry.Value)
+                               return
+                       }
+               }
+       }
+})
+
+func TestKube(t *testing.T) {
+       TestTimeout = time.Minute * time.Duration(*Timeout)
+
+       // creates a file with PPID, used for 'make cleanup-kube'
+       ppid := fmt.Sprint(os.Getppid())
+       ppid = ppid[:len(ppid)-1]
+       f, _ := os.Create(".last_ppid")
+       f.Write([]byte(ppid))
+       f.Close()
+
+       RegisterFailHandler(Fail)
+       RunSpecs(t, "Kube Test")
+       if *DryRun || *IsPersistent {
+               fmt.Println("\033[36m" + "Use 'make cleanup-kube' to remove pods " +
+                       "and namespaces. \nPPID: " + ppid + "\033[0m")
+       }
+}
diff --git a/test-c/kube-test/go.mod b/test-c/kube-test/go.mod
new file mode 100644 (file)
index 0000000..ea98fbb
--- /dev/null
@@ -0,0 +1,65 @@
+module fd.io/kube-test
+
+go 1.23.8
+
+toolchain go1.23.10
+
+require (
+       github.com/onsi/ginkgo/v2 v2.23.3
+       github.com/onsi/gomega v1.37.0
+       gopkg.in/yaml.v3 v3.0.1
+       k8s.io/api v0.30.2
+)
+
+require (
+       github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
+       github.com/emicklei/go-restful/v3 v3.11.2 // indirect
+       github.com/go-openapi/jsonpointer v0.21.0 // indirect
+       github.com/go-openapi/jsonreference v0.21.0 // indirect
+       github.com/golang/protobuf v1.5.4 // indirect
+       github.com/google/gnostic-models v0.6.8 // indirect
+       github.com/google/gofuzz v1.2.0 // indirect
+       github.com/google/uuid v1.6.0 // indirect
+       github.com/gorilla/websocket v1.5.1 // indirect
+       github.com/mailru/easyjson v0.7.7 // indirect
+       github.com/moby/spdystream v0.2.0 // indirect
+       github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+       github.com/modern-go/reflect2 v1.0.2 // indirect
+       github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+       github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
+       github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+       github.com/spf13/pflag v1.0.6 // indirect
+       golang.org/x/net v0.37.0 // indirect
+       golang.org/x/oauth2 v0.18.0 // indirect
+       golang.org/x/term v0.30.0 // indirect
+       google.golang.org/appengine v1.6.8 // indirect
+       gopkg.in/inf.v0 v0.9.1 // indirect
+       gopkg.in/yaml.v2 v2.4.0 // indirect
+       k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
+       sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
+       sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
+       sigs.k8s.io/yaml v1.4.0 // indirect
+)
+
+require (
+       github.com/a8m/envsubst v1.4.2
+       github.com/go-logr/logr v1.4.2 // indirect
+       github.com/go-openapi/swag v0.23.0 // indirect
+       github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
+       github.com/gogo/protobuf v1.3.2 // indirect
+       github.com/google/go-cmp v0.7.0 // indirect
+       github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
+       github.com/imdario/mergo v0.3.16 // indirect
+       github.com/joho/godotenv v1.5.1
+       github.com/josharian/intern v1.0.0 // indirect
+       github.com/json-iterator/go v1.1.12 // indirect
+       golang.org/x/sys v0.31.0 // indirect
+       golang.org/x/text v0.24.0 // indirect
+       golang.org/x/time v0.5.0 // indirect
+       golang.org/x/tools v0.30.0 // indirect
+       google.golang.org/protobuf v1.36.5 // indirect
+       k8s.io/apimachinery v0.30.2
+       k8s.io/client-go v0.30.2
+       k8s.io/klog/v2 v2.120.1 // indirect
+       k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 // indirect
+)
diff --git a/test-c/kube-test/go.sum b/test-c/kube-test/go.sum
new file mode 100644 (file)
index 0000000..c4ce270
--- /dev/null
@@ -0,0 +1,174 @@
+github.com/a8m/envsubst v1.4.2 h1:4yWIHXOLEJHQEFd4UjrWDrYeYlV7ncFWJOCBRLOZHQg=
+github.com/a8m/envsubst v1.4.2/go.mod h1:MVUTQNGQ3tsjOOtKCNd+fl8RzhsXcDvvAEzkhGtlsbY=
+github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
+github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/emicklei/go-restful/v3 v3.11.2 h1:1onLa9DcsMYO9P+CXaL0dStDqQ2EHHXLiz+BtnqkLAU=
+github.com/emicklei/go-restful/v3 v3.11.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
+github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
+github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
+github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
+github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
+github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
+github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
+github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
+github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
+github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
+github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
+github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
+github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
+github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
+github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
+github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
+github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
+github.com/onsi/ginkgo/v2 v2.23.3 h1:edHxnszytJ4lD9D5Jjc4tiDkPBZ3siDeJJkUZJJVkp0=
+github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
+github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
+github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
+github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
+github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
+github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
+golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
+golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
+golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
+golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
+golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
+golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
+golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
+golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
+golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
+google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
+google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+k8s.io/api v0.30.2 h1:+ZhRj+28QT4UOH+BKznu4CBgPWgkXO7XAvMcMl0qKvI=
+k8s.io/api v0.30.2/go.mod h1:ULg5g9JvOev2dG0u2hig4Z7tQ2hHIuS+m8MNZ+X6EmI=
+k8s.io/apimachinery v0.30.2 h1:fEMcnBj6qkzzPGSVsAZtQThU62SmQ4ZymlXRC5yFSCg=
+k8s.io/apimachinery v0.30.2/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
+k8s.io/client-go v0.30.2 h1:sBIVJdojUNPDU/jObC+18tXWcTJVcwyqS9diGdWHk50=
+k8s.io/client-go v0.30.2/go.mod h1:JglKSWULm9xlJLx4KCkfLLQ7XwtlbflV6uFFSHTMgVs=
+k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
+k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
+k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
+k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
+k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak=
+k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
+sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
+sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
+sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
+sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
+sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
diff --git a/test-c/kube-test/infra/asserts.go b/test-c/kube-test/infra/asserts.go
new file mode 100644 (file)
index 0000000..fcbd65a
--- /dev/null
@@ -0,0 +1,114 @@
+package kube_test
+
+import (
+       "fmt"
+       "net/http"
+       "strconv"
+       "strings"
+       "time"
+
+       . "github.com/onsi/gomega"
+)
+
+func (s *BaseSuite) AssertNil(object any, msgAndArgs ...any) {
+       ExpectWithOffset(2, object).To(BeNil(), msgAndArgs...)
+}
+
+func (s *BaseSuite) AssertNotNil(object any, msgAndArgs ...any) {
+       ExpectWithOffset(2, object).ToNot(BeNil(), msgAndArgs...)
+}
+
+func (s *BaseSuite) AssertEqual(expected, actual any, msgAndArgs ...any) {
+       ExpectWithOffset(2, actual).To(Equal(expected), msgAndArgs...)
+}
+
+func (s *BaseSuite) AssertNotEqual(expected, actual any, msgAndArgs ...any) {
+       ExpectWithOffset(2, actual).ToNot(Equal(expected), msgAndArgs...)
+}
+
+func (s *BaseSuite) AssertContains(testString, contains any, msgAndArgs ...any) {
+       ExpectWithOffset(2, strings.ToLower(fmt.Sprint(testString))).To(ContainSubstring(strings.ToLower(fmt.Sprint(contains))), msgAndArgs...)
+}
+
+func (s *BaseSuite) AssertNotContains(testString, contains any, msgAndArgs ...any) {
+       ExpectWithOffset(2, strings.ToLower(fmt.Sprint(testString))).ToNot(ContainSubstring(strings.ToLower(fmt.Sprint(contains))), msgAndArgs...)
+}
+
+func (s *BaseSuite) AssertEmpty(object any, msgAndArgs ...any) {
+       ExpectWithOffset(2, object).To(BeEmpty(), msgAndArgs...)
+}
+
+func (s *BaseSuite) AssertNotEmpty(object any, msgAndArgs ...any) {
+       ExpectWithOffset(2, object).ToNot(BeEmpty(), msgAndArgs...)
+}
+
+func (s *BaseSuite) AssertMatchError(actual, expected error, msgAndArgs ...any) {
+       ExpectWithOffset(2, actual).To(MatchError(expected), msgAndArgs...)
+}
+
+func (s *BaseSuite) AssertGreaterEqual(actual, expected any, msgAndArgs ...any) {
+       ExpectWithOffset(2, actual).Should(BeNumerically(">=", expected), msgAndArgs...)
+}
+
+func (s *BaseSuite) AssertGreaterThan(actual, expected any, msgAndArgs ...any) {
+       ExpectWithOffset(2, actual).Should(BeNumerically(">", expected), msgAndArgs...)
+}
+
+func (s *BaseSuite) AssertLessEqual(actual, expected any, msgAndArgs ...any) {
+       ExpectWithOffset(2, actual).Should(BeNumerically("<=", expected), msgAndArgs...)
+}
+
+func (s *BaseSuite) AssertLessThan(actual, expected any, msgAndArgs ...any) {
+       ExpectWithOffset(2, actual).Should(BeNumerically("<", expected), msgAndArgs...)
+}
+
+func (s *BaseSuite) AssertEqualWithinThreshold(actual, expected, threshold any, msgAndArgs ...any) {
+       ExpectWithOffset(2, actual).Should(BeNumerically("~", expected, threshold), msgAndArgs...)
+}
+
+func (s *BaseSuite) AssertTimeEqualWithinThreshold(actual, expected time.Time, threshold time.Duration, msgAndArgs ...any) {
+       ExpectWithOffset(2, actual).Should(BeTemporally("~", expected, threshold), msgAndArgs...)
+}
+
+func (s *BaseSuite) AssertHttpStatus(resp *http.Response, expectedStatus int, msgAndArgs ...any) {
+       ExpectWithOffset(2, resp).To(HaveHTTPStatus(expectedStatus), msgAndArgs...)
+}
+
+func (s *BaseSuite) AssertHttpHeaderWithValue(resp *http.Response, key string, value any, msgAndArgs ...any) {
+       ExpectWithOffset(2, resp).To(HaveHTTPHeaderWithValue(key, value), msgAndArgs...)
+}
+
+func (s *BaseSuite) AssertHttpHeaderNotPresent(resp *http.Response, key string, msgAndArgs ...any) {
+       ExpectWithOffset(2, resp.Header.Get(key)).To(BeEmpty(), msgAndArgs...)
+}
+
+func (s *BaseSuite) AssertHttpContentLength(resp *http.Response, expectedContentLen int64, msgAndArgs ...any) {
+       ExpectWithOffset(2, resp).To(HaveHTTPHeaderWithValue("Content-Length", strconv.FormatInt(expectedContentLen, 10)), msgAndArgs...)
+}
+
+func (s *BaseSuite) AssertHttpBody(resp *http.Response, expectedBody string, msgAndArgs ...any) {
+       ExpectWithOffset(2, resp).To(HaveHTTPBody(expectedBody), msgAndArgs...)
+}
+
+// Coverage builds take longer to finish -> assert timeout is set to 'TestTimeout - 30 seconds' to let the test finish properly
+func (s *BaseSuite) AssertChannelClosed(timeout time.Duration, channel chan error) {
+       if *IsCoverage && timeout > time.Second*30 {
+               timeout = TestTimeout - time.Second*30
+               s.Log("Coverage build, assert timeout is set to %s", timeout.String())
+       }
+       EventuallyWithOffset(2, channel).WithTimeout(timeout).Should(BeClosed())
+}
+
+// Pass the parsed result struct and the minimum amount of data transferred in MB.
+// Won't do anything when testing a coverage build.
+func (s *BaseSuite) AssertIperfMinTransfer(result IPerfResult, minTransferred int) {
+       if *IsCoverage {
+               s.Log("Coverage build; not asserting")
+               return
+       }
+       if result.Start.Details.Protocol == "TCP" {
+               s.AssertGreaterEqual(result.End.TcpReceived.MBytes, minTransferred)
+       } else {
+               s.AssertGreaterEqual(result.End.Udp.MBytes, minTransferred)
+       }
+}
similarity index 89%
rename from extras/hs-test/infra/kind/deployment.go
rename to test-c/kube-test/infra/deployment.go
index a215661..a57b11b 100644 (file)
@@ -1,4 +1,4 @@
-package hst_kind
+package kube_test
 
 import (
        "context"
@@ -11,7 +11,7 @@ import (
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
-func (s *KindSuite) loadDockerImages() {
+func (s *KubeSuite) loadDockerImages() {
        s.Log("This may take a while. If you encounter problems, " +
                "try loading docker images manually: 'kind load docker-image [image]'")
 
@@ -27,7 +27,7 @@ func (s *KindSuite) loadDockerImages() {
        }
 }
 
-func (s *KindSuite) createNamespace(name string) {
+func (s *KubeSuite) createNamespace(name string) {
        namespace := &corev1.Namespace{
                ObjectMeta: metav1.ObjectMeta{
                        Name: name,
@@ -40,22 +40,22 @@ func (s *KindSuite) createNamespace(name string) {
        s.Log("Namespace '%s' created", name)
 }
 
-func (s *KindSuite) deletePod(namespace string, podName string) error {
+func (s *KubeSuite) deletePod(namespace string, podName string) error {
        delete(s.CurrentlyRunning, podName)
        return s.ClientSet.CoreV1().Pods(namespace).Delete(context.TODO(), podName, metav1.DeleteOptions{GracePeriodSeconds: int64Ptr(0)})
 }
 
-func (s *KindSuite) deleteNamespace(namespace string) error {
+func (s *KubeSuite) deleteNamespace(namespace string) error {
        return s.ClientSet.CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{})
 }
 
-func (s *KindSuite) DeployPod(pod *Pod) {
+func (s *KubeSuite) DeployPod(pod *Pod) {
        pod.CreatedPod = &corev1.Pod{
                ObjectMeta: metav1.ObjectMeta{
                        Namespace: s.Namespace,
                        Name:      pod.Name,
                        Labels: map[string]string{
-                               "app": "HST",
+                               "app": "Kube-Test",
                        },
                        Annotations: map[string]string{
                                "cni.projectcalico.org/vppVcl": "enable",
similarity index 86%
rename from extras/hs-test/infra/kind/pod.go
rename to test-c/kube-test/infra/pod.go
index 969882d..87bb5bc 100644 (file)
@@ -1,21 +1,22 @@
-package hst_kind
+package kube_test
 
 import (
        "bytes"
        "context"
+       "errors"
+       "fmt"
        "os"
        "os/exec"
        "slices"
        "text/template"
 
-       . "fd.io/hs-test/infra/common"
        "gopkg.in/yaml.v3"
        corev1 "k8s.io/api/core/v1"
        "k8s.io/client-go/tools/remotecommand"
 )
 
 type Pod struct {
-       suite         *KindSuite
+       suite         *KubeSuite
        Name          string
        Image         string
        ContainerName string
@@ -48,7 +49,7 @@ type Config struct {
        Pods []PodYaml `yaml:"pods"`
 }
 
-func (s *KindSuite) LoadPodConfigs() {
+func (s *KubeSuite) LoadPodConfigs() {
        data, err := os.ReadFile("kubernetes/pod-definitions.yaml")
        s.AssertNil(err)
 
@@ -61,7 +62,7 @@ func (s *KindSuite) LoadPodConfigs() {
        }
 }
 
-func newPod(suite *KindSuite, input PodYaml) (*Pod, error) {
+func newPod(suite *KubeSuite, input PodYaml) (*Pod, error) {
        var pod = new(Pod)
        pod.suite = suite
        pod.Name = input.Name + suite.Ppid
@@ -83,7 +84,7 @@ func newPod(suite *KindSuite, input PodYaml) (*Pod, error) {
        return pod, nil
 }
 
-func (s *KindSuite) initPods() {
+func (s *KubeSuite) initPods() {
        s.Pods.Ab = s.getPodsByName("ab")
        s.Pods.ClientGeneric = s.getPodsByName("client-generic")
        s.Pods.ServerGeneric = s.getPodsByName("server-generic")
@@ -91,7 +92,7 @@ func (s *KindSuite) initPods() {
        s.Pods.NginxProxy = s.getPodsByName("nginx-proxy")
 }
 
-func (s *KindSuite) getPodsByName(podName string) *Pod {
+func (s *KubeSuite) getPodsByName(podName string) *Pod {
        return s.AllPods[podName+s.Ppid]
 }
 
@@ -122,7 +123,7 @@ func (pod *Pod) Exec(ctx context.Context, command []string) (string, error) {
 
        executor, err := remotecommand.NewSPDYExecutor(pod.suite.Config, "POST", req.URL())
        if err != nil {
-               pod.suite.Log("Error creating executor: %s", err.Error())
+               return "", err
        }
 
        err = executor.StreamWithContext(ctx, remotecommand.StreamOptions{
@@ -131,20 +132,19 @@ func (pod *Pod) Exec(ctx context.Context, command []string) (string, error) {
                Tty:    true,
        })
 
-       output := stdout.String() + stderr.String()
-
-       if err != nil {
-               return output, err
+       output := stdout.String()
+       if stderr.String() != "" {
+               err = errors.New(stderr.String())
        }
 
-       return output, nil
+       return output, err
 }
 
 func (pod *Pod) CreateConfigFromTemplate(targetConfigName string, templateName string, values any) {
        template := template.Must(template.ParseFiles(templateName))
 
-       f, err := os.CreateTemp(LogDir, "hst-config")
-       pod.suite.AssertNil(err, err)
+       f, err := os.CreateTemp(LogDir, "kube-config")
+       pod.suite.AssertNil(err, fmt.Sprint(err))
        defer os.Remove(f.Name())
 
        err = template.Execute(f, values)
diff --git a/test-c/kube-test/infra/suite_base.go b/test-c/kube-test/infra/suite_base.go
new file mode 100644 (file)
index 0000000..bdd1f9c
--- /dev/null
@@ -0,0 +1,142 @@
+package kube_test
+
+import (
+       "flag"
+       "fmt"
+       "io"
+       "log"
+       "os"
+       "os/exec"
+       "strings"
+       "time"
+
+       "github.com/joho/godotenv"
+       . "github.com/onsi/ginkgo/v2"
+)
+
+var IsCoverage = flag.Bool("coverage", false, "use coverage run config")
+var IsPersistent = flag.Bool("persist", false, "persists topology config")
+var IsVerbose = flag.Bool("verbose", false, "verbose test output")
+var WhoAmI = flag.String("whoami", "root", "what user ran kube-test")
+var IsVppDebug = flag.Bool("debug", false, "attach gdb to vpp")
+var DryRun = flag.Bool("dryrun", false, "set up containers but don't run tests")
+var Timeout = flag.Int("timeout", 30, "test timeout override (in minutes)")
+var TestTimeout time.Duration
+
+const (
+       LogDir string = "/tmp/kube-test/"
+)
+
+type BaseSuite struct {
+       Ppid    string
+       Logger  *log.Logger
+       LogFile *os.File
+}
+
+func init() {
+       cmd := exec.Command("mkdir", "-p", LogDir)
+       if err := cmd.Run(); err != nil {
+               panic(err)
+       }
+}
+
+func (s *BaseSuite) Skip(args string) {
+       Skip(args)
+}
+
+func (s *BaseSuite) SetupTest() {
+       TestCounterFunc()
+       s.Log("[* TEST SETUP]")
+}
+
+func (s *BaseSuite) SetupSuite() {
+       s.CreateLogger()
+       s.Log("[* SUITE SETUP]")
+       s.Ppid = fmt.Sprint(os.Getppid())
+       // remove last number so we have space to prepend a process index (interfaces have a char limit)
+       s.Ppid = s.Ppid[:len(s.Ppid)-1]
+}
+
+func (s *BaseSuite) TeardownTest() {
+       if *IsPersistent || *DryRun {
+               s.Skip("Skipping test teardown")
+       }
+       s.Log("[* TEST TEARDOWN]")
+}
+
+func (s *BaseSuite) TeardownSuite() {
+       if *IsPersistent || *DryRun {
+               s.Skip("Skipping suite teardown")
+       }
+       s.Log("[* SUITE TEARDOWN]")
+}
+
+func (s *BaseSuite) GetCurrentSuiteName() string {
+       return CurrentSpecReport().ContainerHierarchyTexts[0]
+}
+
+func (s *BaseSuite) CreateLogger() {
+       suiteName := s.GetCurrentSuiteName()
+       var err error
+       s.LogFile, err = os.Create("summary/" + suiteName + ".log")
+       if err != nil {
+               Fail("Unable to create log file.")
+       }
+       s.Logger = log.New(io.Writer(s.LogFile), "", log.LstdFlags)
+}
+
+// Logs to files by default, logs to stdout when VERBOSE=true with GinkgoWriter
+// to keep console tidy
+func (s *BaseSuite) Log(log any, arg ...any) {
+       var logStr string
+       if len(arg) == 0 {
+               logStr = fmt.Sprint(log)
+       } else {
+               logStr = fmt.Sprintf(fmt.Sprint(log), arg...)
+       }
+       logs := strings.Split(logStr, "\n")
+
+       for _, line := range logs {
+               s.Logger.Println(line)
+       }
+       if *IsVerbose {
+               GinkgoWriter.Println(logStr)
+       }
+}
+
+// sets CALICO_NETWORK_CONFIG, ADDITIONAL_VPP_CONFIG, env vars, applies configs and rollout restarts cluster
+func (s *KubeSuite) SetMtuAndRestart(CALICO_NETWORK_CONFIG string, ADDITIONAL_VPP_CONFIG string) {
+       os.Setenv("CALICO_NETWORK_CONFIG", CALICO_NETWORK_CONFIG)
+       os.Setenv("ADDITIONAL_VPP_CONFIG", ADDITIONAL_VPP_CONFIG)
+       s.AssertNil(godotenv.Load("kubernetes/.vars"))
+
+       s.Envsubst("kubernetes/calico-config-template.yaml", "kubernetes/calico-config.yaml")
+
+       cmd := exec.Command("kubectl", "apply", "-f", "kubernetes/calico-config.yaml")
+       s.Log(cmd.String())
+       o, err := cmd.CombinedOutput()
+       s.Log(string(o))
+       s.AssertNil(err)
+
+       cmd = exec.Command("kubectl", "-n", "calico-vpp-dataplane", "rollout", "restart", "ds/calico-vpp-node")
+       s.Log(cmd.String())
+       o, err = cmd.CombinedOutput()
+       s.Log(string(o))
+       s.AssertNil(err)
+
+       cmd = exec.Command("kubectl", "-n", "calico-vpp-dataplane", "rollout", "status", "ds/calico-vpp-node")
+       s.Log(cmd.String())
+       o, err = cmd.CombinedOutput()
+       s.Log(string(o))
+       s.AssertNil(err)
+
+       cmd = exec.Command("kubectl", "-n", "calico-system", "rollout", "status", "ds/calico-node")
+       s.Log(cmd.String())
+       o, err = cmd.CombinedOutput()
+       s.Log(string(o))
+       s.AssertNil(err)
+
+       // let vpp-dataplane recover, should help with stability issues
+       s.Log("Waiting for 10 seconds")
+       time.Sleep(time.Second * 10)
+}
similarity index 66%
rename from extras/hs-test/infra/kind/suite_kind.go
rename to test-c/kube-test/infra/suite_kube.go
index eb386f5..3fb4fd4 100644 (file)
@@ -1,28 +1,24 @@
-package hst_kind
+package kube_test
 
 import (
        "context"
        "fmt"
        "os"
-       "os/exec"
        "reflect"
        "regexp"
        "runtime"
        "strings"
-       "time"
 
-       . "fd.io/hs-test/infra/common"
        "github.com/a8m/envsubst"
        . "github.com/onsi/ginkgo/v2"
 
-       "github.com/joho/godotenv"
        "k8s.io/client-go/kubernetes"
        "k8s.io/client-go/rest"
        "k8s.io/client-go/tools/clientcmd"
 )
 
-type KindSuite struct {
-       HstCommon
+type KubeSuite struct {
+       BaseSuite
        ClientSet        *kubernetes.Clientset
        Config           *rest.Config
        Namespace        string
@@ -41,7 +37,7 @@ type KindSuite struct {
 }
 
 var imagesLoaded bool
-var kindTests = map[string][]func(s *KindSuite){}
+var kubeTests = map[string][]func(s *KubeSuite){}
 
 const VclConfIperf = "echo \"vcl {\n" +
        "rx-fifo-size 4000000\n" +
@@ -62,17 +58,17 @@ const VclConfNginx = "echo \"vcl {\n" +
        "app-socket-api abstract:vpp/session\n" +
        "}\" > /vcl.conf"
 
-func RegisterKindTests(tests ...func(s *KindSuite)) {
-       kindTests[GetTestFilename()] = tests
+func RegisterKubeTests(tests ...func(s *KubeSuite)) {
+       kubeTests[GetTestFilename()] = tests
 }
 
-func (s *KindSuite) SetupTest() {
+func (s *KubeSuite) SetupTest() {
        s.MainContext = context.Background()
-       s.HstCommon.SetupTest()
+       s.BaseSuite.SetupTest()
 }
 
-func (s *KindSuite) SetupSuite() {
-       s.HstCommon.SetupSuite()
+func (s *KubeSuite) SetupSuite() {
+       s.BaseSuite.SetupSuite()
 
        s.CurrentlyRunning = make(map[string]*Pod)
        s.LoadPodConfigs()
@@ -102,45 +98,8 @@ func (s *KindSuite) SetupSuite() {
        }
 }
 
-// sets CALICO_NETWORK_CONFIG, ADDITIONAL_VPP_CONFIG, env vars, applies configs and rollout restarts cluster
-func (s *KindSuite) SetMtuAndRestart(CALICO_NETWORK_CONFIG string, ADDITIONAL_VPP_CONFIG string) {
-       os.Setenv("CALICO_NETWORK_CONFIG", CALICO_NETWORK_CONFIG)
-       os.Setenv("ADDITIONAL_VPP_CONFIG", ADDITIONAL_VPP_CONFIG)
-       s.AssertNil(godotenv.Load("kubernetes/.vars"))
-
-       s.Envsubst("kubernetes/calico-config-template.yaml", "kubernetes/calico-config.yaml")
-
-       cmd := exec.Command("kubectl", "apply", "-f", "kubernetes/calico-config.yaml")
-       s.Log(cmd.String())
-       o, err := cmd.CombinedOutput()
-       s.Log(string(o))
-       s.AssertNil(err)
-
-       cmd = exec.Command("kubectl", "-n", "calico-vpp-dataplane", "rollout", "restart", "ds/calico-vpp-node")
-       s.Log(cmd.String())
-       o, err = cmd.CombinedOutput()
-       s.Log(string(o))
-       s.AssertNil(err)
-
-       cmd = exec.Command("kubectl", "-n", "calico-vpp-dataplane", "rollout", "status", "ds/calico-vpp-node")
-       s.Log(cmd.String())
-       o, err = cmd.CombinedOutput()
-       s.Log(string(o))
-       s.AssertNil(err)
-
-       cmd = exec.Command("kubectl", "-n", "calico-system", "rollout", "status", "ds/calico-node")
-       s.Log(cmd.String())
-       o, err = cmd.CombinedOutput()
-       s.Log(string(o))
-       s.AssertNil(err)
-
-       // let vpp-dataplane recover (tests might timeout otherwise)
-       s.Log("Waiting for 10 seconds")
-       time.Sleep(time.Second * 10)
-}
-
-func (s *KindSuite) TeardownTest() {
-       s.HstCommon.TeardownTest()
+func (s *KubeSuite) TeardownTest() {
+       s.BaseSuite.TeardownTest()
        if len(s.CurrentlyRunning) != 0 {
                s.Log("Removing:")
                for _, pod := range s.CurrentlyRunning {
@@ -150,8 +109,8 @@ func (s *KindSuite) TeardownTest() {
        }
 }
 
-func (s *KindSuite) TeardownSuite() {
-       s.HstCommon.TeardownSuite()
+func (s *KubeSuite) TeardownSuite() {
+       s.BaseSuite.TeardownSuite()
        if len(s.CurrentlyRunning) == 0 {
                return
        }
@@ -161,7 +120,7 @@ func (s *KindSuite) TeardownSuite() {
 
 // Quick and dirty fix for now. Runs 'ldd /usr/lib/libvcl_ldpreload.so'
 // and searches for the first version string, then creates symlinks.
-func (s *KindSuite) FixVersionNumber(pods ...*Pod) {
+func (s *KubeSuite) FixVersionNumber(pods ...*Pod) {
        regex := regexp.MustCompile(`lib.*\.so\.([0-9]+\.[0-9]+)`)
        var match []string
        for _, pod := range pods {
@@ -192,7 +151,7 @@ func (s *KindSuite) FixVersionNumber(pods ...*Pod) {
        }
 }
 
-func (s *KindSuite) CreateNginxConfig(pod *Pod) {
+func (s *KubeSuite) CreateNginxConfig(pod *Pod) {
        values := struct {
                Workers uint8
                Port    uint16
@@ -207,13 +166,13 @@ func (s *KindSuite) CreateNginxConfig(pod *Pod) {
        )
 }
 
-func (s *KindSuite) Envsubst(inputPath string, outputPath string) {
+func (s *KubeSuite) Envsubst(inputPath string, outputPath string) {
        o, err := envsubst.ReadFile(inputPath)
        s.AssertNil(err)
        os.WriteFile(outputPath, o, 0644)
 }
 
-func (s *KindSuite) CreateNginxProxyConfig(pod *Pod) {
+func (s *KubeSuite) CreateNginxProxyConfig(pod *Pod) {
        pod.Exec(context.TODO(), []string{"/bin/bash", "-c", "mkdir -p /tmp/nginx"})
        values := struct {
                Workers   uint8
@@ -241,8 +200,8 @@ func (s *KindSuite) CreateNginxProxyConfig(pod *Pod) {
        )
 }
 
-var _ = Describe("KindSuite", Ordered, ContinueOnFailure, Label("Perf"), func() {
-       var s KindSuite
+var _ = Describe("KubeSuite", Ordered, ContinueOnFailure, Label("Perf"), func() {
+       var s KubeSuite
        BeforeAll(func() {
                s.SetupSuite()
                s.SetMtuAndRestart("", "")
@@ -257,7 +216,7 @@ var _ = Describe("KindSuite", Ordered, ContinueOnFailure, Label("Perf"), func()
                s.TeardownSuite()
        })
 
-       for filename, tests := range kindTests {
+       for filename, tests := range kubeTests {
                for _, test := range tests {
                        test := test
                        pc := reflect.ValueOf(test).Pointer()
similarity index 92%
rename from extras/hs-test/infra/kind/suite_large-mtu.go
rename to test-c/kube-test/infra/suite_large-mtu.go
index 0cf44c6..53484de 100644 (file)
@@ -1,16 +1,15 @@
-package hst_kind
+package kube_test
 
 import (
        "reflect"
        "runtime"
        "strings"
 
-       . "fd.io/hs-test/infra/common"
        . "github.com/onsi/ginkgo/v2"
 )
 
 type LargeMtuSuite struct {
-       KindSuite
+       KubeSuite
 }
 
 var largeMtuTests = map[string][]func(s *LargeMtuSuite){}
@@ -20,7 +19,7 @@ func RegisterLargeMtuTests(tests ...func(s *LargeMtuSuite)) {
 }
 
 func (s *LargeMtuSuite) SetupSuite() {
-       s.KindSuite.SetupSuite()
+       s.KubeSuite.SetupSuite()
        s.SetMtuAndRestart("mtu: 9000", "tcp { mtu 8960 }")
 }
 
similarity index 94%
rename from extras/hs-test/infra/common/utils_common.go
rename to test-c/kube-test/infra/utils.go
index 15db60b..8763594 100644 (file)
@@ -1,4 +1,4 @@
-package hst_common
+package kube_test
 
 import (
        "encoding/json"
@@ -9,6 +9,14 @@ import (
        "time"
 )
 
+func boolPtr(b bool) *bool {
+       return &b
+}
+
+func int64Ptr(integer int64) *int64 {
+       return &integer
+}
+
 func GetTestFilename() string {
        _, filename, _, _ := runtime.Caller(2)
        return filepath.Base(filename)
@@ -18,9 +26,6 @@ var testCounter uint16
 var startTime time.Time = time.Now()
 
 func TestCounterFunc() {
-       if ParallelTotal.Value.String() != "1" {
-               return
-       }
        testCounter++
        fmt.Printf("Test counter: %d\n"+
                "Time elapsed: %.2fs\n",
@@ -64,7 +69,7 @@ type IPerfResult struct {
        } `json:"end"`
 }
 
-func (s *HstCommon) ParseJsonIperfOutput(jsonResult []byte) IPerfResult {
+func (s *BaseSuite) ParseJsonIperfOutput(jsonResult []byte) IPerfResult {
        var result IPerfResult
 
        // VCL/LDP debugging can pollute output so find the first occurrence of a curly brace to locate the start of JSON data
@@ -109,7 +114,7 @@ func (s *HstCommon) ParseJsonIperfOutput(jsonResult []byte) IPerfResult {
        return result
 }
 
-func (s *HstCommon) LogJsonIperfOutput(result IPerfResult) {
+func (s *BaseSuite) LogJsonIperfOutput(result IPerfResult) {
        s.Log("\n*******************************************\n"+
                "%s\n"+
                "[%s] %s:%d connected to %s:%d\n"+
similarity index 67%
rename from extras/hs-test/kind_test.go
rename to test-c/kube-test/kube_test.go
index 799df2a..1652dff 100644 (file)
@@ -6,24 +6,28 @@ import (
        "fmt"
        "time"
 
-       . "fd.io/hs-test/infra/common"
-       . "fd.io/hs-test/infra/kind"
+       . "fd.io/kube-test/infra"
        . "github.com/onsi/ginkgo/v2"
 )
 
 func init() {
-       RegisterKindTests(KindTcpIperfVclTest, KindUdpIperfVclTest, NginxRpsTest, NginxProxyMirroringTest)
-       RegisterLargeMtuTests(KindTcpIperfVclLargeMTUTest)
+       RegisterKubeTests(KubeTcpIperfVclTest, KubeUdpIperfVclTest, NginxRpsTest, NginxProxyMirroringTest)
+       RegisterLargeMtuTests(KubeTcpIperfVclLargeMTUTest)
 }
 
 const vcl string = "VCL_CONFIG=/vcl.conf"
 const ldp string = "LD_PRELOAD=/usr/lib/libvcl_ldpreload.so"
 
-func kindIperfVclTest(s *KindSuite, clientArgs string) IPerfResult {
+func kubeIperfVclTest(s *KubeSuite, clientArgs string) IPerfResult {
        s.DeployPod(s.Pods.ClientGeneric)
        s.DeployPod(s.Pods.ServerGeneric)
-       ctx, cancel := context.WithTimeout(s.MainContext, time.Second*40)
+       ctx, cancel := context.WithTimeout(s.MainContext, time.Minute*2)
        defer cancel()
+       defer func() {
+               o, err := s.Pods.ServerGeneric.Exec(ctx, []string{"/bin/bash", "-c", "cat /iperf_server.log"})
+               s.Log(o)
+               s.AssertNil(err)
+       }()
 
        _, err := s.Pods.ClientGeneric.Exec(ctx, []string{"/bin/bash", "-c", VclConfIperf})
        s.AssertNil(err)
@@ -32,14 +36,16 @@ func kindIperfVclTest(s *KindSuite, clientArgs string) IPerfResult {
 
        s.FixVersionNumber(s.Pods.ClientGeneric, s.Pods.ServerGeneric)
 
-       iperfClientCmd := fmt.Sprintf("%s %s iperf3 %s -J -b 40g -c %s",
+       iperfClientCmd := fmt.Sprintf("%s %s iperf3 %s -J -4 -b 40g -c %s",
                vcl, ldp, clientArgs, s.Pods.ServerGeneric.IpAddress)
 
        o, err := s.Pods.ServerGeneric.Exec(ctx, []string{"/bin/bash", "-c",
-               vcl + " " + ldp + " iperf3 -s -D -4 -B " + s.Pods.ServerGeneric.IpAddress})
+               vcl + " " + ldp + " iperf3 -s -D --logfile /iperf_server.log -B " + s.Pods.ServerGeneric.IpAddress})
+       s.Log("Sleeping for 2s")
+       time.Sleep(time.Second * 2)
        s.AssertNil(err, o)
-       o, err = s.Pods.ClientGeneric.Exec(ctx, []string{"/bin/bash", "-c", iperfClientCmd})
 
+       o, err = s.Pods.ClientGeneric.Exec(ctx, []string{"/bin/bash", "-c", iperfClientCmd})
        s.AssertNil(err, o)
        result := s.ParseJsonIperfOutput([]byte(o))
        s.LogJsonIperfOutput(result)
@@ -47,11 +53,16 @@ func kindIperfVclTest(s *KindSuite, clientArgs string) IPerfResult {
 }
 
 // TODO: use interfaces to avoid duplicated code
-func kindIperfVclMtuTest(s *LargeMtuSuite, clientArgs string) IPerfResult {
+func kubeIperfVclMtuTest(s *LargeMtuSuite, clientArgs string) IPerfResult {
        s.DeployPod(s.Pods.ClientGeneric)
        s.DeployPod(s.Pods.ServerGeneric)
-       ctx, cancel := context.WithTimeout(s.MainContext, time.Second*40)
+       ctx, cancel := context.WithTimeout(s.MainContext, time.Minute*2)
        defer cancel()
+       defer func() {
+               o, err := s.Pods.ServerGeneric.Exec(ctx, []string{"/bin/bash", "-c", "cat /iperf_server.log"})
+               s.Log(o)
+               s.AssertNil(err)
+       }()
 
        _, err := s.Pods.ClientGeneric.Exec(ctx, []string{"/bin/bash", "-c", VclConfIperf})
        s.AssertNil(err)
@@ -60,34 +71,36 @@ func kindIperfVclMtuTest(s *LargeMtuSuite, clientArgs string) IPerfResult {
 
        s.FixVersionNumber(s.Pods.ClientGeneric, s.Pods.ServerGeneric)
 
-       iperfClientCmd := fmt.Sprintf("%s %s iperf3 %s -J -b 40g -c %s",
+       iperfClientCmd := fmt.Sprintf("%s %s iperf3 %s -J -4 -b 40g -c %s",
                vcl, ldp, clientArgs, s.Pods.ServerGeneric.IpAddress)
 
        o, err := s.Pods.ServerGeneric.Exec(ctx, []string{"/bin/bash", "-c",
-               vcl + " " + ldp + " iperf3 -s -D -4 -B " + s.Pods.ServerGeneric.IpAddress})
+               vcl + " " + ldp + " iperf3 -s -D --logfile /iperf_server.log -B " + s.Pods.ServerGeneric.IpAddress})
+       s.Log("Sleeping for 2s")
+       time.Sleep(time.Second * 2)
        s.AssertNil(err, o)
-       o, err = s.Pods.ClientGeneric.Exec(ctx, []string{"/bin/bash", "-c", iperfClientCmd})
 
+       o, err = s.Pods.ClientGeneric.Exec(ctx, []string{"/bin/bash", "-c", iperfClientCmd})
        s.AssertNil(err, o)
        result := s.ParseJsonIperfOutput([]byte(o))
        s.LogJsonIperfOutput(result)
        return result
 }
 
-func KindTcpIperfVclTest(s *KindSuite) {
-       s.AssertIperfMinTransfer(kindIperfVclTest(s, "-M 1460"), 2000)
+func KubeTcpIperfVclTest(s *KubeSuite) {
+       s.AssertIperfMinTransfer(kubeIperfVclTest(s, "-M 1460"), 2000)
 }
 
-func KindTcpIperfVclLargeMTUTest(s *LargeMtuSuite) {
-       s.AssertIperfMinTransfer(kindIperfVclMtuTest(s, "-M 8960"), 2000)
+func KubeTcpIperfVclLargeMTUTest(s *LargeMtuSuite) {
+       s.AssertIperfMinTransfer(kubeIperfVclMtuTest(s, "-M 8900"), 2000)
 }
 
-func KindUdpIperfVclTest(s *KindSuite) {
-       s.AssertIperfMinTransfer(kindIperfVclTest(s, "-l 1460 -u"), 2000)
+func KubeUdpIperfVclTest(s *KubeSuite) {
+       s.AssertIperfMinTransfer(kubeIperfVclTest(s, "-l 1460 -u"), 2000)
 }
 
-func NginxRpsTest(s *KindSuite) {
-       ctx, cancel := context.WithCancel(s.MainContext)
+func NginxRpsTest(s *KubeSuite) {
+       ctx, cancel := context.WithTimeout(s.MainContext, time.Minute*3)
        defer cancel()
 
        s.DeployPod(s.Pods.Nginx)
@@ -112,8 +125,8 @@ func NginxRpsTest(s *KindSuite) {
        s.AssertNil(err)
 }
 
-func NginxProxyMirroringTest(s *KindSuite) {
-       ctx, cancel := context.WithCancel(s.MainContext)
+func NginxProxyMirroringTest(s *KubeSuite) {
+       ctx, cancel := context.WithTimeout(s.MainContext, time.Minute*3)
        defer cancel()
 
        s.DeployPod(s.Pods.Nginx)
diff --git a/test-c/kube-test/kube_test.sh b/test-c/kube-test/kube_test.sh
new file mode 100755 (executable)
index 0000000..265b493
--- /dev/null
@@ -0,0 +1,130 @@
+#!/usr/bin/env bash
+
+args=
+focused_test=0
+persist_set=0
+dryrun_set=0
+debug_set=0
+debug_build=
+ginkgo_args=
+tc_names=()
+skip_names=()
+dryrun=
+no_color=
+
+for i in "$@"
+do
+case "${i}" in
+    --persist=*)
+        persist="${i#*=}"
+        if [ "$persist" = "true" ]; then
+            args="$args -persist"
+            persist_set=1
+        fi
+        ;;
+    --verbose=*)
+        verbose="${i#*=}"
+        if [ "$verbose" = "true" ]; then
+            args="$args -verbose"
+        fi
+        ;;
+    --test=*)
+        tc_list="${i#*=}"
+        ginkgo_args="$ginkgo_args -v"
+        if [ "$tc_list" != "all" ]; then
+            focused_test=1
+            IFS=',' read -r -a tc_names <<< "$tc_list"
+            args="$args -verbose"
+        fi
+        ;;
+    --skip=*)
+        skip_list="${i#*=}"
+        IFS=',' read -r -a skip_names <<< "$skip_list"
+        ;;
+    --ginkgo_timeout=*)
+        ginkgo_args="$ginkgo_args --timeout=${i#*=}"
+        ;;
+    --repeat=*)
+        ginkgo_args="$ginkgo_args --repeat=${i#*=}"
+        ;;
+    --dryrun=*)
+        dryrun="${i#*=}"
+        if [ "$dryrun" = "true" ]; then
+            args="$args -dryrun"
+            dryrun_set=1
+        fi
+        ;;
+    --no_color=*)
+        no_color="${i#*=}"
+        if [ "$no_color" = "true" ]; then
+            ginkgo_args="$ginkgo_args --no-color"
+        fi
+        ;;
+    --timeout=*)
+        args="$args -timeout ${i#*=}"
+        ;;
+esac
+done
+
+if [ ${#tc_names[@]} -gt 1 ]
+then
+    focused_test=0
+fi
+
+for name in "${tc_names[@]}"; do
+    ginkgo_args="$ginkgo_args --focus $name"
+done
+
+for skip in "${skip_names[@]}"; do
+    ginkgo_args="$ginkgo_args --skip $skip"
+done
+
+if [ $focused_test -eq 0 ] && { [ $persist_set -eq 1 ] || [ $dryrun_set -eq 1 ]; }; then
+    echo -e "\e[1;31mpersist/dryrun flag is not supported while running all tests!\e[1;0m"
+    exit 2
+fi
+
+args="$args -whoami $(whoami)"
+
+if [ -n "${BUILD_NUMBER}" ]; then
+        ginkgo_args="$ginkgo_args --no-color"
+fi
+
+mkdir -p summary
+rm -f summary/*
+# shellcheck disable=SC2086
+CMD="go run github.com/onsi/ginkgo/v2/ginkgo --json-report=summary/report.json $ginkgo_args -- $args"
+echo "$CMD"
+$CMD
+exit_status=$?
+
+if [ $exit_status -ne 0 ]; then
+    jq -r '.[0] | .SpecReports[] | select((.State == "failed") or (.State == "timedout") or (.State == "panicked")) | select(.Failure != null) |
+"TestName:
+    \(.LeafNodeText)
+Suite:
+    \(.Failure.FailureNodeLocation.FileName)
+Message:\n"
++ (
+    if .ReportEntries? then
+        (.ReportEntries[] | select(.Name | contains("Backtrace")) |
+        "\tFull Back Trace:
+\(.Value.Representation | ltrimstr("{{red}}") | rtrimstr("{{/}}"))"
+        ) // "\(.Failure.Message)"
+    else
+        "parse error"
+    end
+)
++ (
+    if .Failure.Message == "A spec timeout occurred" then
+        "\n"
+    else
+        "\nFull Stack Trace:
+\(.Failure.Location.FullStackTrace)\n"
+    end
+)' summary/report.json > summary/failed-summary.log \
+&& echo "Summary generated -> summary/failed-summary.log"
+    exit $exit_status
+else
+    exit $exit_status
+fi
@@ -1,11 +1,11 @@
 definitions:
   image-names:
-    - image: &hs-test
-        name: "hs-test/vpp:latest"
+    - image: &kube-test
+        name: "kube-test/vpp:latest"
     - image: &nginx-ldp
-        name: "hs-test/nginx-ldp:latest"
+        name: "kube-test/nginx-ldp:latest"
     - image: &ab
-        name: "hs-test/ab:latest"
+        name: "kube-test/ab:latest"
 
   container-names:
     - container: &client
@@ -15,7 +15,7 @@ definitions:
 
   namespace-names:
     - namespace: &defaultNs
-        name: "hs-test"
+        name: "kube-test"
 
   worker-names:
     - worker: &worker1
@@ -26,7 +26,7 @@ definitions:
 pods:
   - name: "client-generic"
     image:
-      - <<: *hs-test
+      - <<: *kube-test
     container:
       - <<: *client
     worker:
@@ -36,7 +36,7 @@ pods:
 
   - name: "server-generic"
     image:
-      - <<: *hs-test
+      - <<: *kube-test
     container:
       - <<: *server
     worker:
diff --git a/test-c/kube-test/resources/cert/localhost.crt b/test-c/kube-test/resources/cert/localhost.crt
new file mode 100644 (file)
index 0000000..b21fb48
--- /dev/null
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDZTCCAk2gAwIBAgIUF116CAipHqQBCyAEvNesV0u4u0swDQYJKoZIhvcNAQEL
+BQAwQjELMAkGA1UEBhMCU0sxEDAOBgNVBAgMB1ZwcExhbmQxITAfBgNVBAoMGElu
+dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMzA1MjkxMDI0MjhaFw0yNDA1Mjgx
+MDI0MjhaMEIxCzAJBgNVBAYTAlNLMRAwDgYDVQQIDAdWcHBMYW5kMSEwHwYDVQQK
+DBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQCy40rDzrrHPGIyhP24hOBQefEgKD5uUGgSUyJTCur4yB/r2PGt
+LlfipKwDmNArmZuFOgKh8evipu2jYaxf4GHQmi7PGLddvPkqo5FWtVW8oAVJMcp+
+fwfs7OgkqtYD6Y7qjmjfXb9+rMpPN8WZ7cKbJwZpF3lf8GGaLqRmPiQg2j8qzcVy
+nz8cIwBZP8BJVclA9GIagijY7Zcmz0HnTPrPoLMeyLJOTqPMfkUYA2H2eHeISkQP
+BeoFoiwCI5eM35UiWiLyiv9Kojn4BHx6MLrfKBjV13WtcRMgYm5VftsWOZ92lmHm
+bpj9mGgtd84JWtWxs33oG4mNRSAeujf9AE5VAgMBAAGjUzBRMB0GA1UdDgQWBBTj
+s+A5M/Cao+0Phgg6xFBKIPxLqjAfBgNVHSMEGDAWgBTjs+A5M/Cao+0Phgg6xFBK
+IPxLqjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQB3EcGDby5u
+cEGjgAFR18kH4ztnYUdZUrPI72sOFfjRLtJpx00n759SBawqNW1Y2a1QRd+GgUBK
+YpYd2gzWYFjf/4c5BN4SrjeZGnQ8N0YomqqGKvOQO0YdYK4i/lWJjLRaLiVBn9EX
+Z+odYhGqQgoAJHnm5Mmqhx9ts8qxZLbdsh+T93mKvj+/yuai2Is+AJfLgZpdKPQN
+bCoZemRm+nghRvEP8aX/469wiz7SOLqUzxrTOtXV48wTU5LWLDCs1lF9ZdGHR9/r
+vj8unnEHIZiH3ZjN7OgaAoNHZE26Ywbmllc/a0vPw8iHdrLe7+Wtp4zXe2rcxhW7
+b+X1/yRCZ+Wg
+-----END CERTIFICATE-----
diff --git a/test-c/kube-test/resources/cert/localhost.key b/test-c/kube-test/resources/cert/localhost.key
new file mode 100644 (file)
index 0000000..2d65db5
--- /dev/null
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCy40rDzrrHPGIy
+hP24hOBQefEgKD5uUGgSUyJTCur4yB/r2PGtLlfipKwDmNArmZuFOgKh8evipu2j
+Yaxf4GHQmi7PGLddvPkqo5FWtVW8oAVJMcp+fwfs7OgkqtYD6Y7qjmjfXb9+rMpP
+N8WZ7cKbJwZpF3lf8GGaLqRmPiQg2j8qzcVynz8cIwBZP8BJVclA9GIagijY7Zcm
+z0HnTPrPoLMeyLJOTqPMfkUYA2H2eHeISkQPBeoFoiwCI5eM35UiWiLyiv9Kojn4
+BHx6MLrfKBjV13WtcRMgYm5VftsWOZ92lmHmbpj9mGgtd84JWtWxs33oG4mNRSAe
+ujf9AE5VAgMBAAECggEANwiZ/bdh2t2G0Ef9zoCCif+Z4OzAmCuAePK+gpG/TB41
+Q9eQMlkpjH5gtRKUKHWvVMNOAAhvK2FzhmoMH8rmDMkCUZAnCV2TwjxkACr1X3xT
+Y/s/cr8d7xPLL0ynXrjB0QNS3DT5Lr111/0ue3acAiN1Y2tnWc6YGFj1FsdTUg+O
+zRysrpNUp3LAK+MXIhAXMCGKOOLxpjeyrcnUokH0I8e06of1AfAHX8jTn65MG5Ex
+n9wBYPl+u2J3SjILHoqBKjcSoNILUfBN9mQGeXhoqCzwcnygDtOxIu9xgu2nCcJr
+C1R/WXoQ8Jr6wa1n0aEVXDJeOEK9kKXLTt2/I4HB2QKBgQDemLy+o2/tbFwlU2Xy
+8/tZa30kfLCAZ+kq+lE3Kkfqt3pPYzH+lfO7u/UWtavKRQRdsKsNKbpe/EdGq7c4
+YN3L1KG5JiIo3TxilUPilYacGHklfMMbEK7cs8Jebsl6rL7BgnKuqlXGY0HEEx8L
+XqIKN1RdzL04WLOiA8qDGwYp7wKBgQDNu3DECCTkTa+mZdNDRntoffkgyd0AnwPA
+PEf43BHORpKcfGwFIrf8QWRXcLdh72Yrc9o3D53GCq+NSYGPL2OiY+/3HoAy1mH1
+EBgS08qfkZBKr6+VGjWuVAlD2m2jW+AhGXMS+Lu4yzK3V+0EzlAu4WZVBUngg1//
+6ZtyvXLf+wKBgQCozmO0nvUutFJc7BYQXP5sHZvVo8mmVyb4NMSKdUH8ug/DTJKJ
+YuZnpG6FPlh9GEHrWyMc5Fw11FOpQGe+FZeeEC5k3ophOwWkLVZB6useTWDyEN9V
+Ex3IuXnZa2LX6VDwJyEZXIuX24XwUB/m22k/Hh6Y079bj8kKQJ2/NytBeQKBgQCZ
+RGMmJ8sUKqwJEyLoo8GcfvzyaHC03cI1nLMhuxGo0vq2ihsPWGYpD65pVhfIZkl/
+ZbfT/VZVC/DtGS3kNjHL8Rf8ykRHm18u6uaEYDQ73H3apjfwpK4JSaH9YuT7Jp87
+CXKpV5TCft8xp9d0FR+3TUSnYmE/WaBTTv335RuHsQKBgCFLyxzs0hM/MhCLHJ6b
+AqyNPz36Xcwsgit1Svhwm1IC6FqkSJl3cRKhp1AP5w6ktUfUGNpF/TYI3x2jCg/m
+c0nwmqi/3Cha64XKJcI4iT2+lyuE8jXovMdNiJEEKCDalpyYJbhzRaLsoSFSbiD1
+mFDl8/aNVaQKDDboSuj9AkKs
+-----END PRIVATE KEY-----
diff --git a/test-c/kube-test/resources/nginx/html/index.html b/test-c/kube-test/resources/nginx/html/index.html
new file mode 100644 (file)
index 0000000..6b7c97d
--- /dev/null
@@ -0,0 +1,6 @@
+<http>
+    <title>nginx docker with quic</title>
+<body>
+    <p>Greetings!</p>
+</body>
+</http>
diff --git a/test-c/kube-test/resources/nginx/nginx.conf b/test-c/kube-test/resources/nginx/nginx.conf
new file mode 100644 (file)
index 0000000..7eac45c
--- /dev/null
@@ -0,0 +1,31 @@
+master_process on;
+worker_rlimit_nofile 10240;
+worker_processes {{.Workers}};
+daemon off;
+
+events {
+  use epoll;
+  worker_connections  10240;
+  accept_mutex       off;
+  multi_accept       off;
+}
+
+http {
+  keepalive_timeout 300s;
+  keepalive_requests 1000000;
+  sendfile on;
+  server {
+    listen {{.Port}};
+    listen [::]:{{.Port}};
+    root /usr/share/nginx;
+    index index.html index.htm;
+    location /return_ok
+    {
+      return 200 '';
+    }
+    location /64B.json
+    {
+      return 200 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
+    }
+  }
+}
diff --git a/test-c/kube-test/resources/nginx/nginx_http3.conf b/test-c/kube-test/resources/nginx/nginx_http3.conf
new file mode 100644 (file)
index 0000000..c098675
--- /dev/null
@@ -0,0 +1,27 @@
+master_process on;
+worker_processes 2;
+daemon off;
+
+error_log /tmp/nginx/{{.LogPrefix}}-error.log info;
+
+events {
+   use epoll;
+   accept_mutex       off;
+   multi_accept       off;
+}
+
+http {
+  quic_gso on;
+  quic_retry off;
+
+  access_log /tmp/nginx/{{.LogPrefix}}-access.log;
+  keepalive_timeout 300s;
+  sendfile on;
+  server {
+    listen {{.Address}}:{{.Port}} quic;
+    root /usr/share/nginx;
+    ssl_certificate     /etc/nginx/ssl/localhost.crt;
+    ssl_certificate_key /etc/nginx/ssl/localhost.key;
+    index index.html index.htm;
+  }
+}
diff --git a/test-c/kube-test/resources/nginx/nginx_proxy_mirroring.conf b/test-c/kube-test/resources/nginx/nginx_proxy_mirroring.conf
new file mode 100644 (file)
index 0000000..9e0f276
--- /dev/null
@@ -0,0 +1,85 @@
+master_process on;
+worker_processes {{.Workers}};
+worker_rlimit_nofile 102400;
+daemon off;
+
+error_log /tmp/nginx/{{.LogPrefix}}-error.log info;
+
+events {
+  use epoll;
+  worker_connections 102400;
+  accept_mutex off;
+}
+
+http {
+  include mime.types;
+  default_type application/octet-stream;
+
+  access_log off;
+
+  keepalive_timeout 300;
+  keepalive_requests 1000000;
+
+  proxy_connect_timeout 300;
+  large_client_header_buffers 4 512k;
+  client_max_body_size 3000m;
+  client_header_buffer_size 2048m;
+  client_body_buffer_size 1024m;
+  proxy_buffers 16 10240k;
+  proxy_buffer_size 10240k;
+
+  gzip on;
+
+  upstream bk {
+    server {{.Server}}:{{.Upstream1}};
+    keepalive 30000;
+  }
+  upstream bk1 {
+    server {{.Server}}:{{.Upstream2}};
+    keepalive 30000;
+  }
+  upstream bk2 {
+    server {{.Server}}:{{.Upstream3}};
+    keepalive 30000;
+  }
+
+  server {
+    access_log /tmp/nginx/{{.LogPrefix}}-access.log;
+    listen {{.Port}};
+    server_name {{.Proxy}};
+
+    server_tokens off;
+
+    proxy_redirect off;
+
+    location / {
+      root html;
+      index index.html index.htm;
+      proxy_pass http://bk;
+      proxy_set_header Connection "";
+      proxy_set_header X-Original-URI $request_uri;
+      proxy_set_header Host $host:$server_port;
+      chunked_transfer_encoding on;
+      proxy_http_version 1.1;
+      mirror /mimic1;
+      mirror /mimic2;
+      mirror_request_body on;
+    }
+    location /mimic1 {
+      proxy_pass http://bk1$request_uri;
+      proxy_set_header X-Original-URI $request_uri;
+      proxy_set_header Connection "";
+      chunked_transfer_encoding on;
+      proxy_http_version 1.1;
+      proxy_set_header Host $host:$server_port;
+    }
+    location /mimic2 {
+      proxy_pass http://bk2$request_uri;
+      proxy_set_header X-Original-URI $request_uri;
+      proxy_set_header Host $host:$server_port;
+      proxy_set_header Connection "";
+      proxy_http_version 1.1;
+      chunked_transfer_encoding on;
+    }
+  }
+}
diff --git a/test-c/kube-test/resources/nginx/nginx_server.conf b/test-c/kube-test/resources/nginx/nginx_server.conf
new file mode 100644 (file)
index 0000000..f5e4f9e
--- /dev/null
@@ -0,0 +1,61 @@
+master_process on;
+worker_rlimit_nofile 10240;
+worker_processes 2;
+daemon off;
+
+error_log /tmp/nginx/{{.LogPrefix}}-error.log info;
+
+events {
+  use epoll;
+  worker_connections  10240;
+  accept_mutex       off;
+  multi_accept       off;
+}
+
+http {
+  log_format access_log_fmt '$remote_addr - $remote_user [$time_local] '
+                            '"$request" $status $body_bytes_sent '
+                            '"$http_referer" "$http_user_agent" '
+                            'scheme=$scheme conn=$connection conn_reqs=$connection_requests';
+  keepalive_timeout 300s;
+  keepalive_requests 1000000;
+  client_body_timeout {{.Timeout}}s;
+  client_header_timeout {{.Timeout}}s;
+  send_timeout {{.Timeout}}s;
+  sendfile on;
+  server {
+    access_log /tmp/nginx/{{.LogPrefix}}-access.log access_log_fmt;
+    listen {{.Port}};
+    listen {{.PortSsl}} ssl;
+    server_name {{.Address}};
+    root /usr/share/nginx;
+    ssl_certificate     /etc/nginx/ssl/localhost.crt;
+    ssl_certificate_key /etc/nginx/ssl/localhost.key;
+    http2 {{.Http2}};
+    index index.html index.htm;
+    # to allow POST on static pages
+    error_page 405 =200 $uri;
+    location ~ "/upload/([0-9a-zA-Z-.]*)$" {
+      alias /usr/share/nginx/upload/$1;
+      client_body_temp_path /tmp;
+      client_max_body_size 200M;
+      dav_methods PUT;
+      create_full_put_path off;
+      dav_access all:rw;
+    }
+    location /64B {
+      return 200 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
+    }
+    location / {
+      sendfile on;
+    }
+    # HTTP2 will not wait for the post body and return 200
+    location = /test_upload {
+      proxy_pass http://127.0.0.1:{{.Port}}/dev-null;
+    }
+    location = /dev-null {
+      return 200;
+    }
+    # HTTP2 upload fix end
+  }
+}
diff --git a/test-c/kube-test/resources/nginx/nginx_server_mirroring.conf b/test-c/kube-test/resources/nginx/nginx_server_mirroring.conf
new file mode 100644 (file)
index 0000000..3c28dbf
--- /dev/null
@@ -0,0 +1,33 @@
+master_process on;
+worker_rlimit_nofile 10240;
+worker_processes 2;
+daemon off;
+
+error_log /tmp/nginx/{{.LogPrefix}}-error.log info;
+
+events {
+  use epoll;
+  worker_connections  10240;
+  accept_mutex       off;
+  multi_accept       off;
+}
+
+http {
+  keepalive_timeout 300s;
+  keepalive_requests 1000000;
+  sendfile on;
+  server {
+    access_log /tmp/nginx/{{.LogPrefix}}-access.log;
+    listen {{.Address}}:{{.Upstream1}};
+    listen {{.Address}}:{{.Upstream2}};
+    listen {{.Address}}:{{.Upstream3}};
+    root /usr/share/nginx;
+    index index.html index.htm;
+    location /64B {
+      return 200 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
+    }
+    location / {
+      sendfile on;
+    }
+  }
+}
diff --git a/test-c/kube-test/resources/nginx/vcl.conf b/test-c/kube-test/resources/nginx/vcl.conf
new file mode 100644 (file)
index 0000000..2769924
--- /dev/null
@@ -0,0 +1,10 @@
+vcl {
+  heapsize 64M
+  segment-size 4000000000
+  add-segment-size 4000000000
+  rx-fifo-size 4000000
+  tx-fifo-size 4000000
+  event-queue-size 100000
+  use-mq-eventfd
+  app-socket-api /tmp/nginx/var/run/app_ns_sockets/default
+}
diff --git a/test-c/kube-test/script/build-images.sh b/test-c/kube-test/script/build-images.sh
new file mode 100755 (executable)
index 0000000..aa76304
--- /dev/null
@@ -0,0 +1,124 @@
+#!/usr/bin/env bash
+# Build script for all Docker images based on the common base image
+
+set -e
+
+# Get default architecture for multi-arch builds
+ARCH=${OS_ARCH:-$(dpkg --print-architecture)}
+CODENAME=$(lsb_release -cs)
+
+# Set up buildx configuration
+DOCKER_BUILD_DIR="/scratch/docker-build"
+DOCKER_CACHE_DIR="${DOCKER_BUILD_DIR}/docker_cache"
+DOCKER_HST_BUILDER="hst_builder"
+DOCKER_LOGIN_SCRIPT="/scratch/nomad/.docker-ro/dlogin.sh"
+
+if [ -d "${DOCKER_BUILD_DIR}" ] ; then
+  mkdir -p "${DOCKER_CACHE_DIR}"
+
+  if [ -x "$DOCKER_LOGIN_SCRIPT" ] ; then
+    $DOCKER_LOGIN_SCRIPT
+  fi
+
+  # Create buildx builder if it doesn't exist
+  if ! docker buildx ls --format "{{.Name}}" | grep -q "${DOCKER_HST_BUILDER}"; then
+    docker buildx create --use \
+      --driver-opt env.http_proxy="$HTTP_PROXY" \
+      --driver-opt env.https_proxy="$HTTP_PROXY" \
+      --driver-opt '"env.no_proxy='"$NO_PROXY"'"' \
+      --name=${DOCKER_HST_BUILDER} \
+      --driver=docker-container \
+      --use --bootstrap || true
+  fi
+
+  DOCKER_CACHE_ARGS="--builder=${DOCKER_HST_BUILDER} --load --cache-to type=local,dest=${DOCKER_CACHE_DIR},mode=max --cache-from type=local,src=${DOCKER_CACHE_DIR}"
+fi
+
+# Set the tag for the base image
+BASE_TAG=${BASE_TAG:-"localhost:5001/vpp-kube-test-base:latest"}
+
+echo "=== Building base image ==="
+# shellcheck disable=2086
+docker buildx build ${DOCKER_CACHE_ARGS} \
+  --build-arg UBUNTU_VERSION="${UBUNTU_VERSION:-22.04}" \
+  --build-arg http_proxy="$HTTP_PROXY" \
+  --build-arg https_proxy="$HTTP_PROXY" \
+  --build-arg HTTP_PROXY="$HTTP_PROXY" \
+  --build-arg HTTPS_PROXY="$HTTP_PROXY" \
+  -t $BASE_TAG -f docker/Dockerfile.base . || {
+    echo "Error: Failed to build base image"
+    exit 1
+}
+
+# Push the base image to the local registry
+docker push $BASE_TAG || {
+    echo "Error: Failed to push base image to local registry"
+    exit 1
+}
+
+# Function to build each image
+build_image() {
+    local dockerfile="docker/$1"
+    local tag=$2
+    local add_args="${3:-}"
+
+    if [ ! -f "$dockerfile" ]; then
+        echo "Warning: Dockerfile $dockerfile doesn't exist, skipping"
+        return 0
+    fi
+
+    echo "=== Building $tag from $dockerfile ==="
+    echo "Building with architecture: $ARCH"
+
+    # Check if the necessary files for VPP-based images are available
+    if [[ "$dockerfile" == *"vpp"* || "$dockerfile" == *"nginx"* || "$dockerfile" == *"vcl"* ]]; then
+        # Check for essential VPP files
+        for file in vpp-data/bin/vpp vpp-data/lib/*.so; do
+            if [ ! -e "$file" ]; then
+                echo "Warning: Required VPP file $file doesn't exist."
+            fi
+        done
+    fi
+
+    # Build the image
+    # shellcheck disable=2086
+    docker build \
+        --build-arg UBUNTU_VERSION="${UBUNTU_VERSION:-22.04}" \
+        --build-arg OS_ARCH="$ARCH" \
+        --build-arg CODENAME="$CODENAME" \
+        --build-arg http_proxy="$HTTP_PROXY" \
+        --build-arg https_proxy="$HTTP_PROXY" \
+        --build-arg HTTP_PROXY="$HTTP_PROXY" \
+        --build-arg HTTPS_PROXY="$HTTP_PROXY" \
+        $add_args \
+        -t "$tag" \
+        -f "$dockerfile" . || {
+            echo "Error: Failed to build $tag"
+            return 1
+        }
+
+    echo "=== Successfully built and pushed $tag ==="
+}
+
+# Build all standard images
+echo "=== Building standard images ==="
+build_image "Dockerfile.vpp" "kube-test/vpp"
+build_image "Dockerfile.nginx" "kube-test/nginx-ldp"
+build_image "Dockerfile.ab" "kube-test/ab"
+build_image "Dockerfile.wrk" "kube-test/wrk"
+
+# make cache directory multi-user friendly if it exists
+if [ -d "${DOCKER_CACHE_DIR}" ] ; then
+  chgrp -R docker "${DOCKER_CACHE_DIR}" 2>/dev/null || true
+  chmod -R g+rwx "${DOCKER_CACHE_DIR}" 2>/dev/null || true
+fi
+
+# cleanup detached images
+images=$(docker images --filter "dangling=true" -q --no-trunc)
+if [ -n "$images" ]; then
+    echo "=== Cleaning up dangling images ==="
+    # shellcheck disable=SC2086
+    docker rmi $images || true
+fi
+
+echo "=== All container images built successfully ==="
diff --git a/test-c/kube-test/script/build_kube.sh b/test-c/kube-test/script/build_kube.sh
new file mode 100755 (executable)
index 0000000..11c278d
--- /dev/null
@@ -0,0 +1,102 @@
+#!/usr/bin/env bash
+
+if [ "$(lsb_release -is)" != Ubuntu ]; then
+       echo "Host stack test framework is supported only on Ubuntu"
+       exit 1
+fi
+
+export VPP_WS=../..
+export UBUNTU_VERSION=${UBUNTU_VERSION:-"$(lsb_release -rs)"}
+echo "Ubuntu version is set to ${UBUNTU_VERSION}"
+
+if [ "$1" == "debug" ]; then
+       VPP_BUILD_ROOT=${VPP_WS}/build-root/build-vpp_debug-native/vpp
+else
+       VPP_BUILD_ROOT=${VPP_WS}/build-root/build-vpp-native/vpp
+fi
+
+LAST_STATE_FILE=".last_state_hash"
+
+# get current state hash and ubuntu version
+ctime_hash1=$(stat -c %Z "$VPP_BUILD_ROOT"/.mu_build_install_timestamp | sha1sum | awk '{print $1}')
+ctime_hash2=$(stat -c %Z docker/* | sha1sum | awk '{print $1}')
+current_state_hash=$ctime_hash1-$ctime_hash2-$UBUNTU_VERSION$1
+
+if [ -f "$LAST_STATE_FILE" ]; then
+    last_state_hash=$(cat "$LAST_STATE_FILE")
+else
+    last_state_hash=""
+fi
+
+# compare current state with last state and check FORCE_BUILD
+if [ "$current_state_hash" = "$last_state_hash" ] && [ "$2" = "false" ]; then
+    echo "*** Skipping docker build - no new changes ***"
+    exit 0
+fi
+
+OS_ARCH="$(uname -m)"
+DOCKER_BUILD_DIR="/scratch/docker-build"
+DOCKER_CACHE_DIR="${DOCKER_BUILD_DIR}/docker_cache"
+DOCKER_LOGIN_SCRIPT="/scratch/nomad/.docker-ro/dlogin.sh"
+REGISTRY_PORT=5001
+if [ -x "$DOCKER_LOGIN_SCRIPT" ] ; then
+  $DOCKER_LOGIN_SCRIPT
+fi
+
+# Set up the local registry before creating containers
+echo "=== Setting up local registry ==="
+if [ -x "$(dirname "$0")/../docker/setup-local-registry.sh" ]; then
+  "$(dirname "$0")/../docker/setup-local-registry.sh" "$REGISTRY_PORT"
+else
+  echo "Warning: setup-local-registry.sh not found or not executable"
+  echo "Attempting to create and use local registry at localhost:5000"
+  if ! docker ps | grep -q "local-registry"; then
+    docker run -d --restart=always -p $REGISTRY_PORT:5000 --name local-registry registry:2
+  fi
+fi
+
+echo "Taking build objects from ${VPP_BUILD_ROOT}"
+
+export PATH=${VPP_BUILD_ROOT}/bin:$PATH
+
+bin=vpp-data/bin
+lib=vpp-data/lib
+
+mkdir -p ${bin} ${lib} || true
+rm -rf vpp-data/bin/* || true
+rm -rf vpp-data/lib/* || true
+
+declare -i res=0
+cp ${VPP_BUILD_ROOT}/bin/* ${bin}
+res+=$?
+cp -r ${VPP_BUILD_ROOT}/lib/"${OS_ARCH}"-linux-gnu/* ${lib}
+res+=$?
+if [ "$res" -ne 0 ]; then
+       echo "Failed to copy VPP files. Is VPP built? Try running 'make build' in VPP directory."
+       exit 1
+fi
+
+# Use the build-images.sh script to build all containers
+echo "=== Building all containers using build-images.sh ==="
+(
+    # Export necessary environment variables for build-images.sh
+    export BASE_TAG="localhost:$REGISTRY_PORT/vpp-test-base:latest"
+    export OS_ARCH
+    export UBUNTU_VERSION
+    export HTTP_PROXY
+    export HTTPS_PROXY
+    export NO_PROXY
+    export DOCKER_CACHE_DIR="${DOCKER_CACHE_DIR}"
+    export DOCKER_HST_BUILDER="${DOCKER_HST_BUILDER}"
+
+    # Run the build script
+    ./script/build-images.sh
+)
+
+# Check if the build was successful
+if [ $? -ne 0 ]; then
+    echo "Failed to build Docker images. Check the output above for errors."
+    exit 1
+fi
+
+echo "$current_state_hash" > "$LAST_STATE_FILE"
diff --git a/test-c/kube-test/script/compress.sh b/test-c/kube-test/script/compress.sh
new file mode 100755 (executable)
index 0000000..fed2164
--- /dev/null
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+
+if [ $1 == 2 ]
+then
+    exit 1
+fi
+
+# if failed-summary.log is not empty, exit status = 1
+if [ -s "${HS_ROOT}/summary/failed-summary.log" ]
+then
+    if [ -n "${WORKSPACE}" ]
+    then
+        echo -n "Copying docker logs..."
+        dirs=$(jq -r '.[0] | .SpecReports[] | select((.State == "failed") or (.State == "timedout") or (.State == "panicked")) | .LeafNodeText | split("/")[1]' ${HS_ROOT}/summary/report.json)
+        for dirName in $dirs; do
+            logDir=/tmp/kube-test/$dirName
+            if [ -d "$logDir" ]; then
+                mkdir -p ${WORKSPACE}/archives/summary
+                rsync -a --exclude 'vol' $logDir ${WORKSPACE}/archives/summary/
+            fi
+        done
+        echo "Done."
+
+        echo -n "Copying failed test logs into build log archive directory (${WORKSPACE}/archives)... "
+        mkdir -p ${WORKSPACE}/archives/summary
+        cp -a ${HS_ROOT}/summary/* ${WORKSPACE}/archives/summary
+        echo "Done."
+
+        echo -n "Compressing files in ${WORKSPACE}/archives from test runs... "
+        cd ${WORKSPACE}/archives
+        find . -type f \( -name "*.json" -o -name "*.log" \) -exec gzip {} \;
+        echo "Done."
+
+    else
+        echo "Not compressing files in temporary directories from test runs."
+    fi
+    echo "*************************** SUMMARY ***************************"
+    cat "${HS_ROOT}/summary/failed-summary.log"
+    exit 1
+else
+    exit $1
+fi
diff --git a/test-c/kube-test/script/nginx_ldp.sh b/test-c/kube-test/script/nginx_ldp.sh
new file mode 100755 (executable)
index 0000000..416aa54
--- /dev/null
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+
+# shellcheck disable=SC2068
+$1 -v && LD_PRELOAD=$LDP $@ > /proc/1/fd/1 2>&1
similarity index 92%
rename from extras/hs-test/kubernetes/setup-cluster.sh
rename to test-c/kube-test/script/setup-cluster.sh
index c7dbdff..296a7b4 100755 (executable)
@@ -4,10 +4,11 @@ set -e
 COMMAND=$1
 CALICOVPP_DIR="$HOME/vpp-dataplane"
 VPP_DIR=$(pwd)
-VPP_DIR=${VPP_DIR%extras*}
+VPP_DIR=${VPP_DIR%test-c*}
 COMMIT_HASH=$(git rev-parse HEAD)
 reg_name='kind-registry'
 reg_port='5000'
+BASE=${BASE:-"$COMMIT_HASH"}
 
 export CALICO_NETWORK_CONFIG=${CALICO_NETWORK_CONFIG:-"mtu: 9000"}
 export CALICOVPP_VERSION="${CALICOVPP_VERSION:-latest}"
@@ -58,7 +59,8 @@ help() {
   echo "Usage:"
   echo -e "  make master-cluster | rebuild-master-cluster | release-cluster\n"
 
-  echo "'master-cluster' pulls CalicoVPP and builds VPP from this directory, then brings up a KinD cluster."
+  echo "'master-cluster' pulls CalicoVPP and builds VPP from this directory, then brings up a KinD cluster. You can
+    override the version with: BASE=[(remote or local branch) | (commit hash)], e.g. BASE=origin/master"
   echo "'rebuild-master-cluster' stops CalicoVPP pods, rebuilds VPP and restarts CalicoVPP pods. Cluster keeps running."
   echo "'release-cluster' starts up a KinD cluster and uses latest CalicoVPP release (e.g. v3.29),
     or you can override versions by using env variables 'CALICOVPP_VERSION' and 'TIGERA_VERSION':
@@ -93,12 +95,12 @@ cherry_pick() {
   STASHED_CHANGES=0
   echo "checkpoint: $COMMIT_HASH"
   # chery-vpp hard resets the repo to a commit - we want to keep our changes
-  if [[ -n $(git status --porcelain) ]]; then
+  if ! git diff --quiet; then
            echo "Saving stash"
-      git stash save "HST: temp stash"
+      git stash push -u
       STASHED_CHANGES=1
        fi
-  make -C $CALICOVPP_DIR cherry-vpp FORCE=y BASE=origin/master VPP_DIR=$VPP_DIR
+  make -C $CALICOVPP_DIR cherry-vpp FORCE=y BASE=$BASE VPP_DIR=$VPP_DIR
 
   # pop the stash to build VPP with CalicoVPP's patches + our changes
   if [ $STASHED_CHANGES -eq 1 ]; then
@@ -107,7 +109,7 @@ cherry_pick() {
 }
 
 build_load_start_cni() {
-  make -C $VPP_DIR/extras/hs-test build-vpp-release
+  make -C $VPP_DIR/test-c/kube-test build-vpp-release
   make -C $CALICOVPP_DIR dev-kind
   make -C $CALICOVPP_DIR load-kind
   kubectl create --save-config -f kubernetes/calico-config.yaml
@@ -115,9 +117,9 @@ build_load_start_cni() {
 
 restore_repo() {
   # stash changes, reset local repo to the original state and unstash changes (removes CalicoVPP's patches)
-  if [[ -n $(git status --porcelain) ]]; then
+  if ! git diff --quiet; then
            echo "Saving stash"
-      git stash save "HST: temp stash"
+      git stash push -u
       git reset --hard $COMMIT_HASH
       git stash pop
        else
@@ -131,7 +133,7 @@ setup_master() {
   else
       cd $CALICOVPP_DIR
       git pull
-      cd $VPP_DIR/extras/hs-test
+      cd $VPP_DIR/test-c/kube-test
   fi
 
   echo -e "$kind_config" | kind create cluster --config=-