diff --git a/Dockerfile b/Dockerfile index 4e9e3eb..5a61c62 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,7 @@ FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.26-openshift-5.0 AS builder WORKDIR /go/src/github.com/openshift/network-tools COPY . . +RUN go mod vendor && make build-e2e-tests && gzip -9 test/bin/network-tools-tests-ext # needed for ovnkube-trace FROM registry.ci.openshift.org/ocp/5.0:ovn-kubernetes AS ovnkube-trace @@ -8,6 +9,7 @@ FROM registry.ci.openshift.org/ocp/5.0:ovn-kubernetes AS ovnkube-trace # tools (openshift-tools) is based off cli FROM registry.ci.openshift.org/ocp/5.0:tools COPY --from=builder /go/src/github.com/openshift/network-tools/debug-scripts/ /opt/bin/ +COPY --from=builder /go/src/github.com/openshift/network-tools/test/bin/network-tools-tests-ext.gz /usr/bin/network-tools-tests-ext.gz COPY --from=ovnkube-trace /usr/bin/ovnkube-trace /usr/bin/ # remove internal scripts from the image and create a symlink for network-tools and gather entrypoint for must-gather diff --git a/Makefile b/Makefile index 519bd7f..cd31ec9 100644 --- a/Makefile +++ b/Makefile @@ -22,3 +22,8 @@ build-image-network-tools-test: podman build --no-cache -f ./Dockerfile.fedora -t network-tools-test . .PHONY: build-image-network-tools-test + +build-e2e-tests: + $(MAKE) -C test build + +.PHONY: build-e2e-tests diff --git a/go.mod b/go.mod index 9414cff..b5d664d 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,71 @@ module github.com/openshift/network-tools -go 1.22.0 +go 1.24.0 -require github.com/openshift/build-machinery-go v0.0.0-20200917070002-f171684f77ab +require ( + github.com/onsi/ginkgo/v2 v2.25.1 + github.com/onsi/gomega v1.38.2 + github.com/openshift-eng/openshift-tests-extension v0.0.0-20260521151256-b5a8f7ec8a38 + github.com/openshift/build-machinery-go v0.0.0-20200917070002-f171684f77ab + github.com/spf13/cobra v1.9.1 + k8s.io/api v0.32.3 + k8s.io/apimachinery v0.32.3 + k8s.io/client-go v0.32.3 + k8s.io/utils v0.0.0-20241210054802-24370beab758 +) + +replace github.com/onsi/ginkgo/v2 => github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20260303184444-1cc650aa0565 + +require ( + github.com/Masterminds/semver/v3 v3.4.0 // indirect + github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.20.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/golang/protobuf v1.5.4 // indirect + github.com/google/cel-go v0.17.8 // 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-20250403155104-27863c87afa6 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // 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/pkg/errors v0.9.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect + github.com/stoewer/go-strcase v1.2.0 // indirect + github.com/x448/float16 v0.8.4 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect + golang.org/x/mod v0.27.0 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/oauth2 v0.23.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/term v0.34.0 // indirect + golang.org/x/text v0.28.0 // indirect + golang.org/x/time v0.7.0 // indirect + golang.org/x/tools v0.36.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 // indirect + google.golang.org/protobuf v1.36.7 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect + sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) diff --git a/go.sum b/go.sum index 7c4891e..b7b6688 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,208 @@ +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18= +github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +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.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= +github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= +github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= +github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= +github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE= +github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +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.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +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/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= +github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +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.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/cel-go v0.17.8 h1:j9m730pMZt1Fc4oKhCLUHfjj6527LuhYcYw0Rl8gqto= +github.com/google/cel-go v0.17.8/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY= +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.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-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +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/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +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/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= +github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= +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/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= +github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= +github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= +github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= +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/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= +github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= +github.com/openshift-eng/openshift-tests-extension v0.0.0-20260521151256-b5a8f7ec8a38 h1:UMblx+pkFZ1RA31arHZOWLyWUe6LROdlya4JxTHxGbo= +github.com/openshift-eng/openshift-tests-extension v0.0.0-20260521151256-b5a8f7ec8a38/go.mod h1:pHOS9c6BjZv91OkkHyIHAOWnYhxwcxWQkyYGEvPyUCE= github.com/openshift/build-machinery-go v0.0.0-20200917070002-f171684f77ab h1:lBrojddP6C9C2p67EMs2vcdpC8eF+H0DDom+fgI2IF0= github.com/openshift/build-machinery-go v0.0.0-20200917070002-f171684f77ab/go.mod h1:b1BuldmJlbA/xYtdZvKi+7j5YGB44qJUJDZ9zwiNCfE= +github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20260303184444-1cc650aa0565 h1:3/q8qM4HbFa+Een8wgzpwO8W6mO7Po+MwY6uxiXi/ac= +github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20260303184444-1cc650aa0565/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +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.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +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/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +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/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= +golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +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.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +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.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +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.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= +golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= +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.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.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.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +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/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 h1:N9BgCIAUvn/M+p4NJccWPWb3BWh88+zyL0ll9HgbEeM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= +google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +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/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +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.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +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= +k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls= +k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k= +k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= +k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU= +k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= +k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= +k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= +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/Makefile b/test/Makefile new file mode 100644 index 0000000..10c671e --- /dev/null +++ b/test/Makefile @@ -0,0 +1,17 @@ +BINARY_NAME := network-tools-tests-ext + +BUILD_DIR := bin + +.PHONY: all +all: build + +.PHONY: build +build: + @echo "Building $(BINARY_NAME)..." + @mkdir -p $(BUILD_DIR) + cd cmd && go build -o ../$(BUILD_DIR)/$(BINARY_NAME) . + +.PHONY: clean +clean: + @echo "Cleaning build artifacts..." + rm -rf $(BUILD_DIR) diff --git a/test/cmd/main.go b/test/cmd/main.go new file mode 100644 index 0000000..ed61877 --- /dev/null +++ b/test/cmd/main.go @@ -0,0 +1,53 @@ +package main + +import ( + "fmt" + "os" + + "github.com/openshift-eng/openshift-tests-extension/pkg/cmd" + "github.com/spf13/cobra" + + e "github.com/openshift-eng/openshift-tests-extension/pkg/extension" + et "github.com/openshift-eng/openshift-tests-extension/pkg/extension/extensiontests" + g "github.com/openshift-eng/openshift-tests-extension/pkg/ginkgo" + + _ "github.com/openshift/network-tools/test/ote" +) + +func main() { + registry := e.NewRegistry() + + ext := e.NewExtension("openshift", "payload", "network-tools") + ext.AddSuite(e.Suite{ + Name: "openshift/network-tools", + Parents: []string{ + "openshift/conformance/parallel", + }, + Qualifiers: []string{ + "name.contains('[Suite:openshift/network-tools]')", + }, + }) + + specs, err := g.BuildExtensionTestSpecsFromOpenShiftGinkgoSuite() + if err != nil { + fmt.Fprintf(os.Stderr, "couldn't build extension test specs from ginkgo: %v\n", err) + os.Exit(1) + } + + specs.Walk(func(spec *et.ExtensionTestSpec) { + spec.Lifecycle = et.LifecycleInforming + }) + ext.AddSpecs(specs) + registry.Register(ext) + + root := &cobra.Command{ + Long: "OpenShift Tests Extension for Network Tools", + } + root.AddCommand(cmd.DefaultExtensionCommands(registry)...) + + if err := func() error { + return root.Execute() + }(); err != nil { + os.Exit(1) + } +} diff --git a/test/ote/network_tools.go b/test/ote/network_tools.go new file mode 100644 index 0000000..3175905 --- /dev/null +++ b/test/ote/network_tools.go @@ -0,0 +1,280 @@ +package ote + +import ( + "context" + "os" + "strings" + "time" + + g "github.com/onsi/ginkgo/v2" + o "github.com/onsi/gomega" + + "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" + netutils "k8s.io/utils/net" +) + +var _ = g.Describe("[sig-networking][Suite:openshift/network-tools] SDN network-tools", func() { + var ( + clientset *kubernetes.Clientset + dynClient *dynamic.DynamicClient + ctx context.Context + cancel context.CancelFunc + + expPod2PodResult = []string{ + "ovn-trace source pod to destination pod indicates success", + "ovn-trace destination pod to source pod indicates success", + "ovs-appctl ofproto/trace source pod to destination pod indicates success", + "ovs-appctl ofproto/trace destination pod to source pod indicates success", + "ovn-detrace source pod to destination pod indicates success", + "ovn-detrace destination pod to source pod indicates success", + } + expPod2PodRemoteResult = []string{ + "ovn-trace (remote) source pod to destination pod indicates success", + "ovn-trace (remote) destination pod to source pod indicates success", + } + expPod2SvcResult = []string{ + "ovn-trace source pod to service clusterIP indicates success", + } + ) + + g.BeforeEach(func() { + ctx, cancel = context.WithTimeout(context.Background(), 30*time.Minute) + + var err error + clientset, dynClient, err = getKubeClientAndConfig() + o.Expect(err).NotTo(o.HaveOccurred()) + + networkType := checkNetworkType(ctx, dynClient) + if !strings.Contains(networkType, "ovn") { + g.Skip("Skip testing on non-ovn cluster") + } + }) + + g.AfterEach(func() { + cancel() + }) + + g.It("[JIRA:Networking][OTP] 67625-67648-Check ovnkube-trace pod2pod and pod2hostnetworkpod traffic", func() { + nodeList, err := getReadySchedulableNodes(ctx, clientset) + o.Expect(err).NotTo(o.HaveOccurred()) + if len(nodeList.Items) < 2 { + g.Skip("Not enough nodes available, need at least 2") + } + workerNode1 := nodeList.Items[0].Name + workerNode2 := nodeList.Items[1].Name + tmpPath := "/tmp/ocp-67625-67648" + defer os.RemoveAll(tmpPath) + + ns := "network-tools-67625-" + strings.ToLower(string(time.Now().Format("150405"))) + err = createNamespace(ctx, clientset, ns) + o.Expect(err).NotTo(o.HaveOccurred()) + defer deleteNamespace(ctx, clientset, ns) + + g.By("Set namespace as privileged for hostnetworked pods") + err = setNamespacePrivileged(ctx, clientset, ns) + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("1. Create hello-pod1, pod located on the first node") + err = createPodOnNode(ctx, clientset, ns, "hello-pod1", workerNode1) + o.Expect(err).NotTo(o.HaveOccurred()) + err = waitPodReady(ctx, clientset, ns, "hello-pod1") + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("2. Create hello-pod2 and hostnetwork-hello-pod2, pod located on the first node") + err = createPodOnNode(ctx, clientset, ns, "hello-pod2", workerNode1) + o.Expect(err).NotTo(o.HaveOccurred()) + err = waitPodReady(ctx, clientset, ns, "hello-pod2") + o.Expect(err).NotTo(o.HaveOccurred()) + err = createHostNetworkPodOnNode(ctx, clientset, ns, "hostnetwork-hello-pod2", workerNode1) + o.Expect(err).NotTo(o.HaveOccurred()) + err = waitPodReady(ctx, clientset, ns, "hostnetwork-hello-pod2") + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("3. Create hello-pod3 and hostnetwork-hello-pod3, pod located on the second node") + err = createPodOnNode(ctx, clientset, ns, "hello-pod3", workerNode2) + o.Expect(err).NotTo(o.HaveOccurred()) + err = waitPodReady(ctx, clientset, ns, "hello-pod3") + o.Expect(err).NotTo(o.HaveOccurred()) + err = createHostNetworkPodOnNode(ctx, clientset, ns, "hostnetwork-hello-pod3", workerNode2) + o.Expect(err).NotTo(o.HaveOccurred()) + err = waitPodReady(ctx, clientset, ns, "hostnetwork-hello-pod3") + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("4. Simulate traffic between pod and pod when they land on the same node") + podIP1, err := getPodIP(ctx, clientset, ns, "hello-pod1") + o.Expect(err).NotTo(o.HaveOccurred()) + addrFamily := "ip4" + if netutils.IsIPv6String(podIP1) { + addrFamily = "ip6" + } + cmd := "ovnkube-trace -src-namespace " + ns + " -src hello-pod1 -dst-namespace " + ns + " -dst hello-pod2 -tcp -addr-family " + addrFamily + traceOutput, cmdErr := collectMustGather(tmpPath, networkToolsImageStream, []string{cmd}) + o.Expect(cmdErr).NotTo(o.HaveOccurred()) + for _, expResult := range expPod2PodResult { + o.Expect(traceOutput).To(o.ContainSubstring(expResult)) + } + + g.By("5. Simulate traffic between pod and pod when they land on different nodes") + cmd = "ovnkube-trace -src-namespace " + ns + " -src hello-pod1 -dst-namespace " + ns + " -dst hello-pod3 -tcp -addr-family " + addrFamily + traceOutput, cmdErr = collectMustGather(tmpPath, networkToolsImageStream, []string{cmd}) + o.Expect(cmdErr).NotTo(o.HaveOccurred()) + for _, expResult := range expPod2PodResult { + o.Expect(traceOutput).To(o.ContainSubstring(expResult)) + } + for _, expResult := range expPod2PodRemoteResult { + o.Expect(traceOutput).To(o.ContainSubstring(expResult)) + } + + g.By("6. Simulate traffic between pod and hostnetwork pod when they land on the same node") + cmd = "ovnkube-trace -src-namespace " + ns + " -src hello-pod1 -dst-namespace " + ns + " -dst hostnetwork-hello-pod2 -udp -addr-family " + addrFamily + traceOutput, cmdErr = collectMustGather(tmpPath, networkToolsImageStream, []string{cmd}) + o.Expect(cmdErr).NotTo(o.HaveOccurred()) + for _, expResult := range expPod2PodResult { + o.Expect(traceOutput).To(o.ContainSubstring(expResult)) + } + + g.By("7. Simulate traffic between pod and hostnetwork pod when they land on different nodes") + cmd = "ovnkube-trace -src-namespace " + ns + " -src hello-pod1 -dst-namespace " + ns + " -dst hostnetwork-hello-pod3 -udp -addr-family " + addrFamily + traceOutput, cmdErr = collectMustGather(tmpPath, networkToolsImageStream, []string{cmd}) + o.Expect(cmdErr).NotTo(o.HaveOccurred()) + for _, expResult := range expPod2PodResult { + o.Expect(traceOutput).To(o.ContainSubstring(expResult)) + } + o.Expect(traceOutput).To(o.ContainSubstring(expPod2PodRemoteResult[1])) + }) + + g.It("[JIRA:Networking][OTP] 67649-Check ovnkube-trace pod2service traffic", func() { + nodeList, err := getReadySchedulableNodes(ctx, clientset) + o.Expect(err).NotTo(o.HaveOccurred()) + if len(nodeList.Items) < 1 { + g.Skip("Not enough nodes available for the test") + } + tmpPath := "/tmp/ocp-67649" + defer os.RemoveAll(tmpPath) + + ns := "network-tools-67649-" + strings.ToLower(string(time.Now().Format("150405"))) + err = createNamespace(ctx, clientset, ns) + o.Expect(err).NotTo(o.HaveOccurred()) + defer deleteNamespace(ctx, clientset, ns) + + g.By("1. Create hello-pod") + err = createPod(ctx, clientset, ns, "hello-pod") + o.Expect(err).NotTo(o.HaveOccurred()) + err = waitPodReady(ctx, clientset, ns, "hello-pod") + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("2. Simulate traffic between pod and service") + podIP, err := getPodIP(ctx, clientset, ns, "hello-pod") + o.Expect(err).NotTo(o.HaveOccurred()) + addrFamily := "ip4" + if netutils.IsIPv6String(podIP) { + addrFamily = "ip6" + } + cmd := "ovnkube-trace -src-namespace " + ns + " -src hello-pod -dst-namespace openshift-dns -service dns-default -tcp -addr-family " + addrFamily + traceOutput, cmdErr := collectMustGather(tmpPath, networkToolsImageStream, []string{cmd}) + o.Expect(cmdErr).NotTo(o.HaveOccurred()) + for _, expResult := range expPod2SvcResult { + o.Expect(traceOutput).To(o.ContainSubstring(expResult)) + } + }) +}) + +var _ = g.Describe("[sig-networking][Suite:openshift/network-tools] SDN network-tools", func() { + var ( + clientset *kubernetes.Clientset + dynClient *dynamic.DynamicClient + ctx context.Context + cancel context.CancelFunc + ) + + g.BeforeEach(func() { + ctx, cancel = context.WithTimeout(context.Background(), 30*time.Minute) + + var err error + clientset, dynClient, err = getKubeClientAndConfig() + o.Expect(err).NotTo(o.HaveOccurred()) + }) + + g.AfterEach(func() { + cancel() + }) + + g.It("[JIRA:Networking][OTP] 55889-Verify ovn-db-run-command", func() { + networkType := checkNetworkType(ctx, dynClient) + if !strings.Contains(networkType, "ovn") { + g.Skip("Skip testing on non-ovn cluster") + } + + g.By("1. Run ovn-nbctl command with ovn-db-run-command script") + mustgatherDir := "/tmp/must-gather-55889-1" + defer os.RemoveAll(mustgatherDir) + output, cmdErr := collectMustGather(mustgatherDir, networkToolsImageStream, []string{"network-tools", "ovn-db-run-command", "ovn-nbctl", "lr-list"}) + o.Expect(cmdErr).NotTo(o.HaveOccurred()) + o.Expect(output).To(o.ContainSubstring("ovn_cluster_router")) + + g.By("2. Run ovn-sbctl command with ovn-db-run-command script") + mustgatherDir = "/tmp/must-gather-55889-2" + defer os.RemoveAll(mustgatherDir) + output, cmdErr = collectMustGather(mustgatherDir, networkToolsImageStream, []string{"network-tools", "ovn-db-run-command", "ovn-sbctl", "show"}) + o.Expect(cmdErr).NotTo(o.HaveOccurred()) + o.Expect(output).To(o.ContainSubstring("Port_Binding")) + + g.By("3. Run ovndb command in specified pod with ovn-db-run-command script") + ovnNodePods, err := getPodsWithLabel(ctx, clientset, "openshift-ovn-kubernetes", "app=ovnkube-node") + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(len(ovnNodePods)).NotTo(o.BeZero()) + + nodeName, err := getPodNodeName(ctx, clientset, "openshift-ovn-kubernetes", ovnNodePods[0]) + o.Expect(err).NotTo(o.HaveOccurred()) + + mustgatherDir = "/tmp/must-gather-55889-3" + defer os.RemoveAll(mustgatherDir) + output, cmdErr = collectMustGather(mustgatherDir, networkToolsImageStream, []string{"network-tools", "ovn-db-run-command", "-p", ovnNodePods[0], "ovn-nbctl", "lr-list"}) + o.Expect(cmdErr).NotTo(o.HaveOccurred()) + o.Expect(output).To(o.ContainSubstring("GR_" + nodeName)) + }) + + g.It("[JIRA:Networking][OTP] 55887-Verify pod-run-netns-command", func() { + nodeList, err := getReadySchedulableNodes(ctx, clientset) + o.Expect(err).NotTo(o.HaveOccurred()) + if len(nodeList.Items) < 1 { + g.Skip("Not enough nodes available for the test") + } + + ns := "network-tools-55887-" + strings.ToLower(string(time.Now().Format("150405"))) + err = createNamespace(ctx, clientset, ns) + o.Expect(err).NotTo(o.HaveOccurred()) + defer deleteNamespace(ctx, clientset, ns) + + g.By("0. Create hello-pod") + err = createPod(ctx, clientset, ns, "hello-pod") + o.Expect(err).NotTo(o.HaveOccurred()) + err = waitPodReady(ctx, clientset, ns, "hello-pod") + o.Expect(err).NotTo(o.HaveOccurred()) + podIP, err := getPodIP(ctx, clientset, ns, "hello-pod") + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("1. Run multiple commands with pod-run-netns-command script") + mustgatherDir := "/tmp/must-gather-55887-1" + defer os.RemoveAll(mustgatherDir) + output, cmdErr := collectMustGather(mustgatherDir, networkToolsImageStream, []string{"network-tools", "pod-run-netns-command", "--multiple-commands", ns, "hello-pod", "ip a show eth0; ip a show lo"}) + o.Expect(cmdErr).NotTo(o.HaveOccurred()) + o.Expect(output).To(o.ContainSubstring(podIP)) + o.Expect(output).To(o.ContainSubstring("127.0.0.1")) + + g.By("2. Run command with no-substitution flag with pod-run-netns-command script") + mustgatherDir = "/tmp/must-gather-55887-2" + defer os.RemoveAll(mustgatherDir) + output, cmdErr = collectMustGather(mustgatherDir, networkToolsImageStream, []string{"network-tools", "pod-run-netns-command", "--no-substitution", ns, "hello-pod", "'i=0; i=$(( $i + 1 )); echo result$i'"}) + o.Expect(cmdErr).NotTo(o.HaveOccurred()) + o.Expect(output).To(o.ContainSubstring("result1")) + + g.By("3. Run command with preserve-pod flag with pod-run-netns-command script") + mustgatherDir = "/tmp/must-gather-55887-3" + defer os.RemoveAll(mustgatherDir) + output, cmdErr = collectMustGather(mustgatherDir, networkToolsImageStream, []string{"network-tools", "pod-run-netns-command", "--preserve-pod", ns, "hello-pod", "timeout 5 tcpdump"}) + o.Expect(cmdErr).NotTo(o.HaveOccurred()) + o.Expect(output).To(o.ContainSubstring("DONE")) + }) +}) diff --git a/test/ote/utils.go b/test/ote/utils.go new file mode 100644 index 0000000..f7c7114 --- /dev/null +++ b/test/ote/utils.go @@ -0,0 +1,319 @@ +package ote + +import ( + "context" + "encoding/json" + "fmt" + "os/exec" + "strings" + "time" + + g "github.com/onsi/ginkgo/v2" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" +) + +const ( + helloSDNImage = "quay.io/openshifttest/hello-sdn@sha256:c89445416459e7adea9a5a416b3365ed3d74f2491beb904d61dc8d1eb89a72a4" + networkToolsImageStream = "openshift/network-tools:latest" +) + +func getKubeClientAndConfig() (*kubernetes.Clientset, *dynamic.DynamicClient, error) { + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + configOverrides := &clientcmd.ConfigOverrides{} + kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) + + config, err := kubeConfig.ClientConfig() + if err != nil { + return nil, nil, fmt.Errorf("failed to load kubeconfig: %w", err) + } + + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, nil, fmt.Errorf("failed to create kubernetes clientset: %w", err) + } + + dynClient, err := dynamic.NewForConfig(config) + if err != nil { + return nil, nil, fmt.Errorf("failed to create dynamic client: %w", err) + } + + return clientset, dynClient, nil +} + +func checkNetworkType(ctx context.Context, dynClient *dynamic.DynamicClient) string { + gvr := schema.GroupVersionResource{ + Group: "operator.openshift.io", + Version: "v1", + Resource: "networks", + } + obj, err := dynClient.Resource(gvr).Get(ctx, "cluster", metav1.GetOptions{}) + if err != nil { + fmt.Fprintf(g.GinkgoWriter, "Failed to get network operator: %v\n", err) + return "" + } + networkType, found, err := unstructured.NestedString(obj.Object, "spec", "defaultNetwork", "type") + if err != nil || !found { + return "" + } + return strings.ToLower(networkType) +} + +func createNamespace(ctx context.Context, clientset *kubernetes.Clientset, name string) error { + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + } + _, err := clientset.CoreV1().Namespaces().Create(ctx, ns, metav1.CreateOptions{}) + return err +} + +func deleteNamespace(ctx context.Context, clientset *kubernetes.Clientset, name string) error { + return clientset.CoreV1().Namespaces().Delete(ctx, name, metav1.DeleteOptions{}) +} + +func setNamespacePrivileged(ctx context.Context, clientset *kubernetes.Clientset, ns string) error { + patch := map[string]interface{}{ + "metadata": map[string]interface{}{ + "labels": map[string]string{ + "pod-security.kubernetes.io/enforce": "privileged", + "pod-security.kubernetes.io/audit": "privileged", + "pod-security.kubernetes.io/warn": "privileged", + }, + }, + } + patchBytes, err := json.Marshal(patch) + if err != nil { + return err + } + _, err = clientset.CoreV1().Namespaces().Patch(ctx, ns, types.MergePatchType, patchBytes, metav1.PatchOptions{}) + return err +} + +func createPod(ctx context.Context, clientset *kubernetes.Clientset, ns, name string) error { + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + Labels: map[string]string{ + "name": "hello-pod", + }, + }, + Spec: corev1.PodSpec{ + SecurityContext: &corev1.PodSecurityContext{ + RunAsNonRoot: boolPtr(true), + SeccompProfile: &corev1.SeccompProfile{ + Type: corev1.SeccompProfileTypeRuntimeDefault, + }, + }, + Containers: []corev1.Container{ + { + Name: "hello-pod", + Image: helloSDNImage, + SecurityContext: &corev1.SecurityContext{ + AllowPrivilegeEscalation: boolPtr(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"ALL"}, + }, + }, + }, + }, + }, + } + _, err := clientset.CoreV1().Pods(ns).Create(ctx, pod, metav1.CreateOptions{}) + return err +} + +func createPodOnNode(ctx context.Context, clientset *kubernetes.Clientset, ns, name, nodeName string) error { + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + Labels: map[string]string{ + "name": "hello-pod", + }, + }, + Spec: corev1.PodSpec{ + NodeName: nodeName, + SecurityContext: &corev1.PodSecurityContext{ + RunAsNonRoot: boolPtr(true), + SeccompProfile: &corev1.SeccompProfile{ + Type: corev1.SeccompProfileTypeRuntimeDefault, + }, + }, + Containers: []corev1.Container{ + { + Name: "hello-pod", + Image: helloSDNImage, + SecurityContext: &corev1.SecurityContext{ + AllowPrivilegeEscalation: boolPtr(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"ALL"}, + }, + }, + }, + }, + }, + } + _, err := clientset.CoreV1().Pods(ns).Create(ctx, pod, metav1.CreateOptions{}) + return err +} + +func createHostNetworkPodOnNode(ctx context.Context, clientset *kubernetes.Clientset, ns, name, nodeName string) error { + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + Labels: map[string]string{ + "name": "hello-pod", + }, + }, + Spec: corev1.PodSpec{ + NodeName: nodeName, + HostNetwork: true, + SecurityContext: &corev1.PodSecurityContext{ + RunAsNonRoot: boolPtr(false), + SeccompProfile: &corev1.SeccompProfile{ + Type: corev1.SeccompProfileTypeRuntimeDefault, + }, + }, + Containers: []corev1.Container{ + { + Name: "hello-pod", + Image: helloSDNImage, + SecurityContext: &corev1.SecurityContext{ + AllowPrivilegeEscalation: boolPtr(false), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"ALL"}, + }, + }, + }, + }, + }, + } + _, err := clientset.CoreV1().Pods(ns).Create(ctx, pod, metav1.CreateOptions{}) + return err +} + +func waitPodReady(ctx context.Context, clientset *kubernetes.Clientset, ns, name string) error { + return wait.PollUntilContextTimeout(ctx, 5*time.Second, 60*time.Second, true, func(ctx context.Context) (bool, error) { + pod, err := clientset.CoreV1().Pods(ns).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + fmt.Fprintf(g.GinkgoWriter, "Error getting pod %s: %v, retrying...\n", name, err) + return false, nil + } + switch pod.Status.Phase { + case corev1.PodRunning, corev1.PodSucceeded: + return true, nil + case corev1.PodFailed: + return false, fmt.Errorf("pod %s failed", name) + default: + fmt.Fprintf(g.GinkgoWriter, "Pod %s phase: %s, waiting...\n", name, pod.Status.Phase) + return false, nil + } + }) +} + +func getPodIP(ctx context.Context, clientset *kubernetes.Clientset, ns, name string) (string, error) { + pod, err := clientset.CoreV1().Pods(ns).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return "", err + } + if len(pod.Status.PodIPs) == 0 { + return "", fmt.Errorf("pod %s has no IPs", name) + } + return pod.Status.PodIPs[0].IP, nil +} + +func getPodNodeName(ctx context.Context, clientset *kubernetes.Clientset, ns, name string) (string, error) { + pod, err := clientset.CoreV1().Pods(ns).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return "", err + } + return pod.Spec.NodeName, nil +} + +func getPodsWithLabel(ctx context.Context, clientset *kubernetes.Clientset, ns, label string) ([]string, error) { + pods, err := clientset.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{LabelSelector: label}) + if err != nil { + return nil, err + } + var names []string + for _, p := range pods.Items { + names = append(names, p.Name) + } + return names, nil +} + +func getOVNKMasterPod(ctx context.Context, clientset *kubernetes.Clientset) (string, error) { + lease, err := clientset.CoordinationV1().Leases("openshift-ovn-kubernetes").Get(ctx, "ovn-kubernetes-master", metav1.GetOptions{}) + if err != nil { + return "", err + } + if lease.Spec.HolderIdentity == nil { + return "", fmt.Errorf("ovn-kubernetes-master lease has no holder") + } + return *lease.Spec.HolderIdentity, nil +} + +func collectMustGather(destDir, imageStream string, params []string) (string, error) { + args := []string{"adm", "must-gather"} + if destDir != "" { + args = append(args, "--dest-dir="+destDir) + } + if imageStream != "" { + args = append(args, "--image-stream="+imageStream) + } + if len(params) > 0 { + args = append(args, "--") + args = append(args, params...) + } + fmt.Fprintf(g.GinkgoWriter, "Running: oc %s\n", strings.Join(args, " ")) + output, err := exec.Command("oc", args...).CombinedOutput() + if err != nil && strings.Contains(string(output), "ImagePullBackOff") { + fmt.Fprintf(g.GinkgoWriter, "Image pull failed, retrying...\n") + output, err = exec.Command("oc", args...).CombinedOutput() + } + if err != nil { + fmt.Fprintf(g.GinkgoWriter, "collectMustGather failed: %v, output: %s\n", err, string(output)) + return string(output), err + } + return string(output), nil +} + +func getReadySchedulableNodes(ctx context.Context, clientset *kubernetes.Clientset) (*corev1.NodeList, error) { + nodes, err := clientset.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, err + } + var filtered []corev1.Node + for _, node := range nodes.Items { + if node.Spec.Unschedulable { + continue + } + ready := false + for _, cond := range node.Status.Conditions { + if cond.Type == corev1.NodeReady && cond.Status == corev1.ConditionTrue { + ready = true + break + } + } + if ready { + filtered = append(filtered, node) + } + } + return &corev1.NodeList{Items: filtered}, nil +} + +func boolPtr(b bool) *bool { + return &b +}