From 3800d035aa0463d089ba8bf555f4de1c2eff2117 Mon Sep 17 00:00:00 2001 From: marbarta Date: Fri, 21 Jul 2023 15:45:34 +0200 Subject: [PATCH 01/36] feat(e2e): parallel testing with different kubeconfigs --- Makefile | 3 + tests/e2e/go.mod | 25 +- tests/e2e/go.sum | 101 ++++- tests/e2e/k8s.go | 41 +- tests/e2e/koperator_suite_test.go | 176 ++++++-- tests/e2e/pkg/common/common.go | 88 ++++ tests/e2e/pkg/common/config/config.go | 64 +++ tests/e2e/pkg/tests/tests.go | 430 ++++++++++++++++++ tests/e2e/pkg/tests/tests_test.go | 627 ++++++++++++++++++++++++++ tests/e2e/test_alltestcase.go | 40 ++ tests/e2e/test_mocktest.go | 57 +++ 11 files changed, 1586 insertions(+), 66 deletions(-) create mode 100644 tests/e2e/pkg/common/common.go create mode 100644 tests/e2e/pkg/common/config/config.go create mode 100644 tests/e2e/pkg/tests/tests.go create mode 100644 tests/e2e/pkg/tests/tests_test.go create mode 100644 tests/e2e/test_alltestcase.go create mode 100644 tests/e2e/test_mocktest.go diff --git a/Makefile b/Makefile index 142b5e3e6..595adb440 100644 --- a/Makefile +++ b/Makefile @@ -108,6 +108,9 @@ test-e2e: --ginkgo.trace \ --ginkgo.v +test-e2e-parallel: + ginkgo -p -v --tags e2e tests/e2e + # Build manager binary manager: generate fmt vet go build -o bin/manager main.go diff --git a/tests/e2e/go.mod b/tests/e2e/go.mod index 8b99c5037..4b946d2e8 100644 --- a/tests/e2e/go.mod +++ b/tests/e2e/go.mod @@ -9,6 +9,7 @@ require ( github.com/gruntwork-io/terratest v0.41.24 github.com/onsi/ginkgo/v2 v2.9.5 github.com/onsi/gomega v1.27.6 + github.com/spf13/viper v1.16.0 github.com/twmb/franz-go v1.13.5 k8s.io/apiextensions-apiserver v0.27.2 k8s.io/apimachinery v0.27.2 @@ -29,9 +30,11 @@ require ( github.com/eapache/queue v1.1.0 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/fatih/color v1.13.0 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-logr/zapr v1.2.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect github.com/huandu/xstrings v1.3.3 // indirect github.com/iancoleman/orderedmap v0.2.0 // indirect github.com/jcmturner/aescts/v2 v2.0.0 // indirect @@ -40,20 +43,27 @@ require ( github.com/jcmturner/gokrb5/v8 v8.4.3 // indirect github.com/jcmturner/rpc/v2 v2.0.3 // indirect github.com/klauspost/compress v1.16.3 // indirect + github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.16 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/pavlo-v-chernykh/keystore-go/v4 v4.4.1 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pierrec/lz4/v4 v4.1.17 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect - github.com/spf13/cast v1.4.1 // indirect + github.com/spf13/afero v1.9.5 // indirect + github.com/spf13/cast v1.5.1 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/subosito/gotenv v1.4.2 // indirect github.com/tidwall/gjson v1.9.3 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/twmb/franz-go/pkg/kmsg v1.4.0 // indirect github.com/wayneashleyberry/terminal-dimensions v1.0.0 // indirect go.uber.org/zap v1.24.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect sigs.k8s.io/controller-runtime v0.14.6 // indirect ) @@ -103,22 +113,23 @@ require ( github.com/pquerna/otp v1.2.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/testify v1.8.1 // indirect + github.com/stretchr/testify v1.8.3 // indirect github.com/urfave/cli v1.22.2 // indirect go.uber.org/atomic v1.9.0 // indirect - go.uber.org/multierr v1.6.0 // indirect - golang.org/x/crypto v0.7.0 // indirect + go.uber.org/multierr v1.8.0 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 golang.org/x/net v0.10.0 // indirect - golang.org/x/oauth2 v0.4.0 // indirect + golang.org/x/oauth2 v0.7.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/term v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.9.1 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.28.1 // indirect + google.golang.org/protobuf v1.30.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.27.2 // indirect k8s.io/client-go v0.27.2 // indirect diff --git a/tests/e2e/go.sum b/tests/e2e/go.sum index a0fab5d55..ebc5470df 100644 --- a/tests/e2e/go.sum +++ b/tests/e2e/go.sum @@ -3,6 +3,7 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= @@ -13,6 +14,9 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -30,6 +34,7 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= emperror.dev/errors v0.8.1 h1:UavXZ5cSX/4u9iyvH6aDcuGkVjeexUGJ7Ij7G4VfQT0= emperror.dev/errors v0.8.1/go.mod h1:YcRvLPh626Ubn2xqtoprejnA5nFha+TJ+2vew48kWuE= @@ -94,6 +99,7 @@ github.com/cisco-open/k8s-objectmatcher v1.9.0 h1:/sfuO0BD09fpynZjXsqeZrh28Juc4V github.com/cisco-open/k8s-objectmatcher v1.9.0/go.mod h1:CH4E6qAK+q+JwKFJn0DaTNqxrbmWCaDQzGthKLK4nZ0= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cppforlife/go-patch v0.2.0 h1:Y14MnCQjDlbw7WXT4k+u6DPAA9XnygN4BfrSpI/19RU= @@ -117,6 +123,7 @@ github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= @@ -131,9 +138,11 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew= github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= @@ -215,6 +224,7 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= @@ -224,6 +234,7 @@ 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/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -231,6 +242,9 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= @@ -239,6 +253,7 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= @@ -258,6 +273,8 @@ github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/C github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= @@ -304,6 +321,7 @@ github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0P github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -314,6 +332,8 @@ 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/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -336,6 +356,8 @@ github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa1 github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= @@ -366,6 +388,8 @@ github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/pavlo-v-chernykh/keystore-go/v4 v4.4.1 h1:FyBdsRqqHH4LctMLL+BL2oGO+ONcIPwn96ctofCVtNE= github.com/pavlo-v-chernykh/keystore-go/v4 v4.4.1/go.mod h1:lAVhWwbNaveeJmxrxuSTxMgKpF6DjnuVpn6T8WiBwYQ= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc= github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= @@ -373,6 +397,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/otp v1.2.0 h1:/A3+Jn+cagqayeR3iHs/L62m5ue7710D35zl1zJ1kok= @@ -414,11 +439,17 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= -github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= +github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= +github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= 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.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -432,8 +463,11 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= +github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/tidwall/gjson v1.9.3 h1:hqzS9wAHMO+KVBBkLxYdkEeeFHuqr95GfClRLKlgK0E= github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -464,14 +498,16 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= @@ -481,9 +517,10 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -494,6 +531,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -506,6 +545,7 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -514,6 +554,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 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.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -547,6 +589,10 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -561,9 +607,13 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M= -golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= +golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= +golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -614,12 +664,18 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/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-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -629,6 +685,7 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -640,6 +697,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -693,8 +751,15 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -718,6 +783,9 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -756,6 +824,13 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -769,7 +844,11 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -785,8 +864,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba 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.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -797,6 +876,8 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 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/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/tests/e2e/k8s.go b/tests/e2e/k8s.go index e1c42cefb..8c4109016 100644 --- a/tests/e2e/k8s.go +++ b/tests/e2e/k8s.go @@ -101,28 +101,16 @@ func createOrReplaceK8sResourcesFromManifest( //nolint:unused // Note: this migh } } -// currentKubernetesContext returns the currently set Kubernetes context based -// on the the environment variables and the KUBECONFIG file. -func currentEnvK8sContext() (kubeconfigPath string, kubecontextName string, err error) { - kubeconfigPath, isExisting := os.LookupEnv("KUBECONFIG") - if !isExisting { - homePath, err := os.UserHomeDir() - if err != nil { - return "", "", errors.WrapIf(err, "retrieving user home directory failed") - } - - kubeconfigPath = path.Join(homePath, ".kube", "config") - } - +func getDefaultKubeContext(kubeconfigPath string) (string, error) { kubeconfigBytes, err := os.ReadFile(kubeconfigPath) if err != nil { - return "", "", errors.WrapIfWithDetails(err, "reading KUBECONFIG file failed", "path", kubeconfigPath) + return "", errors.WrapIfWithDetails(err, "reading KUBECONFIG file failed", "path", kubeconfigPath) } structuredKubeconfig := make(map[string]interface{}) err = yaml.Unmarshal(kubeconfigBytes, &structuredKubeconfig) if err != nil { - return "", "", errors.WrapIfWithDetails( + return "", errors.WrapIfWithDetails( err, "parsing kubeconfig failed", "kubeconfig", string(kubeconfigBytes), @@ -131,13 +119,34 @@ func currentEnvK8sContext() (kubeconfigPath string, kubecontextName string, err kubecontext, isOk := structuredKubeconfig["current-context"].(string) if !isOk { - return "", "", errors.WrapIfWithDetails( + return "", errors.WrapIfWithDetails( err, "kubeconfig current-context is not string", "current-context", structuredKubeconfig["current-context"], ) } + return kubecontext, nil +} + +// currentKubernetesContext returns the currently set Kubernetes context based +// on the the environment variables and the KUBECONFIG file. +func currentEnvK8sContext() (kubeconfigPath string, kubecontextName string, err error) { + kubeconfigPath, isExisting := os.LookupEnv("KUBECONFIG") + if !isExisting { + homePath, err := os.UserHomeDir() + if err != nil { + return "", "", errors.WrapIf(err, "retrieving user home directory failed") + } + + kubeconfigPath = path.Join(homePath, ".kube", "config") + } + + kubecontext, err := getDefaultKubeContext(kubeconfigPath) + if err != nil { + return "", "", err + } + return kubeconfigPath, kubecontext, nil } diff --git a/tests/e2e/koperator_suite_test.go b/tests/e2e/koperator_suite_test.go index ba7a8190a..8571ef1ee 100644 --- a/tests/e2e/koperator_suite_test.go +++ b/tests/e2e/koperator_suite_test.go @@ -17,52 +17,162 @@ package e2e import ( + "fmt" + "log" + "os" + "path/filepath" "testing" + "time" - "github.com/gruntwork-io/terratest/modules/k8s" + "github.com/banzaicloud/koperator/tests/e2e/pkg/common/config" + "github.com/banzaicloud/koperator/tests/e2e/pkg/tests" "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2" + "github.com/onsi/ginkgo/v2/reporters" . "github.com/onsi/gomega" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/spf13/viper" ) +var testPool tests.TestPool + +func beforeSuite() ([]tests.TestType, error) { + k8sClusterPool := tests.K8sClusterPool{} + k8sClusterPool.AddK8sClusters( + tests.NewMockK8sCluster( + "testContextPath1", + "testContextName1", + true, + "1.24", + "provider1", + "clusterID1", + ), + tests.NewMockK8sCluster( + "testContextPath2", + "testContextName2", + true, + "1.24", + "provider2", + "clusterID2", + ), + tests.NewMockK8sCluster( + "testContextPath3", + "testContextName3", + true, + "1.24", + "provider3", + "clusterID3", + ), + tests.NewMockK8sCluster( + "testContextPath4", + "testContextName4", + true, + "1.26", + "provider4", + "clusterID4", + ), + ) + + classifier := tests.NewClassifier(k8sClusterPool, mockTest1, mockTest2) + + var tests []tests.TestType + testStrategy := viper.GetString(config.Tests.TestStrategy) + + switch testStrategy { + case config.TestStrategyMinimal: + tests = classifier.Minimal() + case config.TestStrategyVersionComplete: + tests = classifier.VersionComplete() + case config.TestStrategyProviderComplete: + tests = classifier.ProviderComplete() + case config.TestStrategyComplete: + tests = classifier.Complete() + } + + return tests, nil +} + func TestKoperator(t *testing.T) { - RegisterFailHandler(Fail) // Note: Ginkgo - Gomega connector. - RunSpecs(t, "Koperator end to end test suite") + var err error + + err = runGinkgoTests(t) + if err != nil { + log.Printf("Koperator e2e start failed: %v", err) + } } -var _ = BeforeSuite(func() { - By("Acquiring K8s cluster") - var kubeconfigPath string - var kubecontextName string +func runGinkgoTests(t *testing.T) error { + RegisterFailHandler(Fail) + suiteConfig, reporterConfig := GinkgoConfiguration() - By("Acquiring K8s config and context", func() { - var err error - kubeconfigPath, kubecontextName, err = currentEnvK8sContext() - Expect(err).NotTo(HaveOccurred()) - }) + // Run only selected tests by testID label e.g: "testID:4e980f5b5c" + if labelFilter := viper.GetString(config.Tests.LabelFilter); labelFilter != "" { + suiteConfig.LabelFilter = labelFilter + } - By("Listing kube-system pods", func() { - pods := k8s.ListPods( - ginkgo.GinkgoT(), - k8s.NewKubectlOptions(kubecontextName, kubeconfigPath, "kube-system"), - v1.ListOptions{}, - ) + var err error + // Generated and load tests into the pool + testPool, err = beforeSuite() + if err != nil { + return err + } - Expect(len(pods)).To(Not(BeZero())) + testSuiteDuration := testPool.GetTestSuiteDuration() + maxTimeout, err := time.ParseDuration(viper.GetString(config.Tests.MaxTimeout)) + if err != nil { + return fmt.Errorf("could not parse MaxTimeout into time.Duration: %w", err) + } + // Protection against too long test suites + if testSuiteDuration > maxTimeout { + return fmt.Errorf("tests estimated duration: '%fmin' bigger then maxTimeout: '%fmin'", testSuiteDuration.Seconds(), maxTimeout.Seconds()) + } + + // Calculated timeout can be overran with the specified time length + allowedOverrun, err := time.ParseDuration(viper.GetString(config.Tests.AllowedOverrunDuration)) + if err != nil { + return fmt.Errorf("could not parse AllowedOverrunDuration into time.Duration: %w", err) + } + // Set TestSuite timeout based on the generated tests + suiteConfig.Timeout = testSuiteDuration + allowedOverrun + + if viper.GetBool(config.Tests.CreateTestReportFile) { + if err := createTestReportFile(); err != nil { + return err + } + } + + func() { + defer ginkgo.GinkgoRecover() + RunSpecs(t, testPool.PoolInfo(), suiteConfig, reporterConfig) + }() + + return nil + +} + +func createTestReportFile() error { + reportDir := viper.GetString(config.Tests.ReportDir) + if _, err := os.Stat(reportDir); os.IsNotExist(err) { + if err := os.Mkdir(reportDir, os.FileMode(0o777)); err != nil { + return fmt.Errorf("error while creating report directory %s err: %s", reportDir, err.Error()) + } + } + // Generate JUnit report once all tests have finished with customized settings + _ = ginkgo.ReportAfterSuite("Koperator e2e", func(report ginkgo.Report) { + err := reporters.GenerateJUnitReportWithConfig( + report, + filepath.Join(reportDir, fmt.Sprintf("e2e_%s_%v.xml", viper.GetString(config.Tests.TestStrategy), time.Now().Format(time.RFC3339))), + reporters.JunitReportConfig{OmitSpecLabels: false, OmitLeafNodeType: false}, + ) + if err != nil { + log.Printf("error creating junit report file %s", err.Error()) + } }) -}) + return nil +} -var _ = When("Testing e2e test altogether", Ordered, func() { - var snapshottedInfo = &clusterSnapshot{} - snapshotCluster(snapshottedInfo) - testInstall() - testInstallZookeeperCluster() - testInstallKafkaCluster("../../config/samples/simplekafkacluster.yaml") - testUninstallKafkaCluster() - testInstallKafkaCluster("../../config/samples/simplekafkacluster_ssl.yaml") - testUninstallKafkaCluster() - testUninstallZookeeperCluster() - testUninstall() - snapshotClusterAndCompare(snapshottedInfo) +// Root Describe container +var _ = Describe(time.Now().Format(time.RFC3339), func() { + // In the root container there is no Ordered decorator + // ginkgo execute parallel the generated tests by K8sClusters + testPool.BuildParallelByK8sCluster() }) diff --git a/tests/e2e/pkg/common/common.go b/tests/e2e/pkg/common/common.go new file mode 100644 index 000000000..33e27adfb --- /dev/null +++ b/tests/e2e/pkg/common/common.go @@ -0,0 +1,88 @@ +// Copyright © 2023 Cisco Systems, Inc. and/or its affiliates +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +import ( + "os" + "path" + + "emperror.dev/errors" + "github.com/gruntwork-io/terratest/modules/k8s" + "gopkg.in/yaml.v2" +) + +// currentKubernetesContext returns the currently set Kubernetes context based +// on the the environment variables and the KUBECONFIG file. +func CurrentEnvK8sContext() (kubeconfigPath string, kubecontextName string, err error) { + kubeconfigPath, isExisting := os.LookupEnv("KUBECONFIG") + if !isExisting { + homePath, err := os.UserHomeDir() + if err != nil { + return "", "", errors.WrapIf(err, "retrieving user home directory failed") + } + + kubeconfigPath = path.Join(homePath, ".kube", "config") + } + + kubecontext, err := GetDefaultKubeContext(kubeconfigPath) + if err != nil { + return "", "", err + } + + return kubeconfigPath, kubecontext, nil +} + +// kubectlOptionsForCurrentContext returns a kubectlOptions object for the +// current Kubernetes context or alternatively an error. +func KubectlOptionsForCurrentContext() (k8s.KubectlOptions, error) { + kubeconfigPath, kubecontextName, err := CurrentEnvK8sContext() + if err != nil { + return k8s.KubectlOptions{}, errors.WrapIf(err, "retrieving current environment Kubernetes context failed") + } + + return k8s.KubectlOptions{ + ConfigPath: kubeconfigPath, + ContextName: kubecontextName, + Namespace: "", + }, nil +} + +func GetDefaultKubeContext(kubeconfigPath string) (string, error) { + kubeconfigBytes, err := os.ReadFile(kubeconfigPath) + if err != nil { + return "", errors.WrapIfWithDetails(err, "reading KUBECONFIG file failed", "path", kubeconfigPath) + } + + structuredKubeconfig := make(map[string]interface{}) + err = yaml.Unmarshal(kubeconfigBytes, &structuredKubeconfig) + if err != nil { + return "", errors.WrapIfWithDetails( + err, + "parsing kubeconfig failed", + "kubeconfig", string(kubeconfigBytes), + ) + } + + kubecontext, isOk := structuredKubeconfig["current-context"].(string) + if !isOk { + return "", errors.WrapIfWithDetails( + err, + "kubeconfig current-context is not string", + "current-context", structuredKubeconfig["current-context"], + ) + } + + return kubecontext, nil +} diff --git a/tests/e2e/pkg/common/config/config.go b/tests/e2e/pkg/common/config/config.go new file mode 100644 index 000000000..942c76863 --- /dev/null +++ b/tests/e2e/pkg/common/config/config.go @@ -0,0 +1,64 @@ +// Copyright © 2023 Cisco Systems, Inc. and/or its affiliates +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package config + +import "github.com/spf13/viper" + +type TestStrategy struct{} + +const ( + TestStrategyMinimal = "minimal" + TestStrategyVersionComplete = "version" + TestStrategyProviderComplete = "provider" + TestStrategyComplete = "complete" +) + +var Tests = struct { + ReportDir string + CreateTestReportFile string + MaxTimeout string + AllowedOverrunDuration string + TestStrategy string + LabelFilter string +}{ + CreateTestReportFile: "tests.CreateTestReportFile", + MaxTimeout: "tests.MaxTimeout", + AllowedOverrunDuration: "tests.AllowedOverrunDuration", + TestStrategy: "tests.TestStrategy", + ReportDir: "tests.ReportDir", + LabelFilter: "tests.LabelFilter", +} + +func init() { + viper.AutomaticEnv() + + viper.BindEnv(Tests.CreateTestReportFile, "CREATE_TEST_REPORT_FILE") + viper.SetDefault(Tests.CreateTestReportFile, true) + + viper.BindEnv(Tests.ReportDir, "REPORT_DIR") + viper.SetDefault(Tests.ReportDir, "reports") + + viper.BindEnv(Tests.MaxTimeout, "MAX_TIMEOUT") + viper.SetDefault(Tests.MaxTimeout, "10s") + + viper.BindEnv(Tests.AllowedOverrunDuration, "ALLOWED_OVERRUN_DURATION") + viper.SetDefault(Tests.AllowedOverrunDuration, "5s") + + viper.BindEnv(Tests.TestStrategy, "TEST_STRATEGY") + viper.SetDefault(Tests.TestStrategy, TestStrategyMinimal) + + viper.BindEnv(Tests.LabelFilter, "TEST_MODE") + +} diff --git a/tests/e2e/pkg/tests/tests.go b/tests/e2e/pkg/tests/tests.go new file mode 100644 index 000000000..c662e9b62 --- /dev/null +++ b/tests/e2e/pkg/tests/tests.go @@ -0,0 +1,430 @@ +// Copyright © 2023 Cisco Systems, Inc. and/or its affiliates +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tests + +import ( + "crypto/md5" + "encoding/hex" + "errors" + "fmt" + "log" + "math/rand" + "os" + "path/filepath" + "time" + + "github.com/banzaicloud/koperator/tests/e2e/pkg/common" + "github.com/banzaicloud/koperator/tests/e2e/pkg/common/config" + "github.com/gruntwork-io/terratest/modules/k8s" + . "github.com/onsi/ginkgo/v2" + "github.com/onsi/ginkgo/v2/dsl/decorators" + "github.com/spf13/viper" + "golang.org/x/exp/maps" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type TestPool []TestType + +func (tests TestPool) PoolInfo() string { + testsByContextName := tests.getTestsByContextName() + + return fmt.Sprintf(` + +NumberOfTests: %d + +NumberOfK8sClusters: %d + +NumberOfK8sVersions: + +NumberOfK8sProviders: + +TestK8sMapping: %v + +TestStrategy: %s + +ExpectedDuration: %f + +`, len(tests), len(maps.Keys(testsByContextName)), tests, viper.GetString(config.Tests.TestStrategy), tests.GetTestSuiteDuration().Seconds()) +} + +func (tests TestPool) BuildParallelByK8sCluster() { + testsByClusterID := tests.getTestsByClusterID() + // Because of the "Ordered" decorator testCases inside that container will run each after another + // so this K8s cluster is dedicated for tests for serial test execution + for clusterID, tests := range testsByClusterID { + Describe(clusterID, decorators.Label(fmt.Sprintf("clusterID:%s", clusterID)), Ordered, func() { + for _, t := range tests { + t.Run() + } + }) + } +} + +func (tests TestPool) getTestsByClusterID() map[string][]TestType { + testsByClusterID := make(map[string][]TestType) + for _, test := range tests { + testsByClusterID[test.k8sCluster.clusterInfo.clusterID] = append(testsByClusterID[test.k8sCluster.clusterInfo.clusterID], test) + } + + return testsByClusterID +} + +func (tests TestPool) getTestsByContextName() map[string][]TestType { + testsByContextName := make(map[string][]TestType) + for _, test := range tests { + testsByContextName[test.k8sCluster.kubectlOptions.ContextName] = append(testsByContextName[test.k8sCluster.kubectlOptions.ContextName], test) + } + + return testsByContextName +} + +func (tests TestPool) GetTestSuiteDuration() time.Duration { + testsByClusterID := tests.getTestsByClusterID() + + var max time.Duration + for _, tests := range testsByClusterID { + var localDuration time.Duration + for _, test := range tests { + localDuration += test.testCase.TestDuration + } + + if localDuration > max { + max = localDuration + } + } + return max +} + +func NewClassifier(k8sClusterPool K8sClusterPool, testCases ...TestCase) Classifier { + return Classifier{ + k8sClusterPool: k8sClusterPool, + testCases: testCases, + } +} + +type Classifier struct { + k8sClusterPool K8sClusterPool + testCases []TestCase +} + +func (t Classifier) Minimal() []TestType { + t.k8sClusterPool.mixPool() + tests := make([]TestType, 0, len(t.testCases)) + + for i, testCase := range t.testCases { + tests = append(tests, TestType{ + testCase: testCase, + k8sCluster: t.k8sClusterPool.k8sClusters[i%len(t.k8sClusterPool.k8sClusters)], + }) + } + return tests +} + +func (t Classifier) VersionComplete() []TestType { + t.k8sClusterPool.mixPool() + k8sClustersByVersion := t.k8sClusterPool.getByVersions() + tests := make([]TestType, 0, len(t.testCases)*len(maps.Keys(k8sClustersByVersion))) + + for _, k8sClusters := range k8sClustersByVersion { + for i, testCase := range t.testCases { + tests = append(tests, TestType{ + testCase: testCase, + k8sCluster: k8sClusters[i%len(k8sClusters)], + }) + } + } + return tests +} + +func (t Classifier) ProviderComplete() []TestType { + t.k8sClusterPool.mixPool() + k8sClustersByProviders := t.k8sClusterPool.getByProviders() + tests := make([]TestType, 0, len(t.testCases)*len(maps.Keys(k8sClustersByProviders))) + + for _, k8sClusters := range k8sClustersByProviders { + for i, testCase := range t.testCases { + tests = append(tests, TestType{ + testCase: testCase, + k8sCluster: k8sClusters[i%len(k8sClusters)], + }) + } + } + return tests +} + +func (t Classifier) Complete() []TestType { + t.k8sClusterPool.mixPool() + k8sClustersByProvidersVersions := t.k8sClusterPool.getByProvidersVersions() + var tests []TestType + + for _, byVersions := range k8sClustersByProvidersVersions { + for _, k8sClusters := range byVersions { + for i, testCase := range t.testCases { + tests = append(tests, TestType{ + testCase: testCase, + k8sCluster: k8sClusters[i%len(k8sClusters)], + }) + } + } + } + return tests +} + +type K8sClusterPool struct { + k8sClusters []K8sCluster +} + +func (t *K8sClusterPool) mixPool() { + rand.Shuffle(len(t.k8sClusters), func(i, j int) { t.k8sClusters[i], t.k8sClusters[j] = t.k8sClusters[j], t.k8sClusters[i] }) +} + +func (t *K8sClusterPool) AddK8sClusters(cluster ...K8sCluster) { + t.k8sClusters = append(t.k8sClusters, cluster...) +} + +func (t *K8sClusterPool) FeedFomDirectory(kubeConfigDirectoryPath string) error { + files, err := os.ReadDir(kubeConfigDirectoryPath) + if err != nil { + return fmt.Errorf("unable to read kubeConfig directory '%s' error: %w", kubeConfigDirectoryPath, err) + } + + if len(files) == 0 { + return fmt.Errorf("kubeConfig directory '%s' is empty", kubeConfigDirectoryPath) + } + + for _, file := range files { + if file != nil { + kubeContext, err := common.GetDefaultKubeContext(file.Name()) + if err != nil { + err := fmt.Errorf("could not fetch default kubeContext from file: %s, err: %w", file.Name(), err) + log.Print(err) + continue + } + kubectlPath := filepath.Join(kubeConfigDirectoryPath, file.Name()) + k8sCluster, err := NewK8sClusterFromParams(kubectlPath, kubeContext, true) + if err != nil { + return fmt.Errorf("could not create K8sCluster structure from file '%s' err: %w", kubectlPath, err) + } + t.AddK8sClusters(k8sCluster) + } + } + return nil +} + +func (t K8sClusterPool) getByVersions() map[string][]K8sCluster { + byVersions := make(map[string][]K8sCluster) + + for _, k8sCluster := range t.k8sClusters { + byVersions[k8sCluster.clusterInfo.version] = append(byVersions[k8sCluster.clusterInfo.version], k8sCluster) + } + + return byVersions +} + +func (t K8sClusterPool) getByProviders() map[string][]K8sCluster { + byProviders := make(map[string][]K8sCluster) + + for _, k8sCluster := range t.k8sClusters { + byProviders[k8sCluster.clusterInfo.provider] = append(byProviders[k8sCluster.clusterInfo.provider], k8sCluster) + } + + return byProviders +} + +func (t K8sClusterPool) getByProvidersVersions() map[string]map[string][]K8sCluster { + byProvidersVersions := make(map[string]map[string][]K8sCluster) + byProviders := t.getByProviders() + + for provider, k8sClusters := range byProviders { + for _, k8sCluster := range k8sClusters { + if byProvidersVersions[provider] == nil { + byProvidersVersions[provider] = make(map[string][]K8sCluster) + } + byProvidersVersions[provider][k8sCluster.clusterInfo.version] = append(byProvidersVersions[provider][k8sCluster.clusterInfo.version], k8sCluster) + } + } + return byProvidersVersions +} + +type TestCase struct { + TestDuration time.Duration + TestName string + TestFn func(kubectlOptions k8s.KubectlOptions) +} + +func NewTest(testCase TestCase, k8sCluster K8sCluster) TestType { + testIDseed := fmt.Sprintf("%v%v", testCase.TestName, k8sCluster.clusterInfo) + hash := md5.Sum([]byte(testIDseed)) + testID := hex.EncodeToString(hash[:5]) + return TestType{ + testID: testID, + testCase: testCase, + k8sCluster: k8sCluster, + } +} + +type TestType struct { + testID string + testCase TestCase + k8sCluster K8sCluster +} + +func (t TestType) Equal(test TestType) bool { + return t.TestID() == test.TestID() +} + +func (t TestType) Less(test TestType) bool { + return t.TestID() < test.TestID() +} + +func (t *TestType) TestID() string { + if t.testID == "" { + text := fmt.Sprintf("%v%v", t.testCase.TestName, t.k8sCluster) + hash := md5.Sum([]byte(text)) + t.testID = hex.EncodeToString(hash[:5]) + } + return t.testID +} + +func (t TestType) String() string { + return fmt.Sprintf("%s(%s)", + t.testCase.TestName, t.k8sCluster) +} + +// Run encapsulates the testCase with K8s health check and details into a describe container +// When K8s cluster is not available the further tests are skipped +func (t TestType) Run() { + Describe(fmt.Sprint(t), Ordered, decorators.Label(fmt.Sprintf("testID:%s", t.TestID())), func() { + var err error + kubectlOptions, err := t.k8sCluster.KubectlOptions() + //TODO (marbarta): this should be removed after testing + if err != nil { + kubectlOptions, err = common.KubectlOptionsForCurrentContext() + } + + if err == nil { + _, err = k8s.ListPodsE( + GinkgoT(), + k8s.NewKubectlOptions(kubectlOptions.ContextName, kubectlOptions.ConfigPath, "kube-system"), + metav1.ListOptions{}, + ) + } + + // It("Checking K8s cluster health", func() { + // if err != nil { + // Fail(fmt.Sprintf("skipping testCase... K8s cluster connection problem: %v", err)) + // } + // }) + + t.testCase.TestFn(kubectlOptions) + }) +} + +func newK8sClusterInfo(kubectlOptions k8s.KubectlOptions) (K8sClusterInfo, error) { + // TODO (marbarta): placeholder to get retrieve cluster information from kubeconfig + return K8sClusterInfo{ + workerCount: 3, + multiAZ: false, + clusterID: "testCLusterID", + provider: "testProvider", + version: "1.25", + }, nil +} + +type K8sClusterInfo struct { + workerCount int + multiAZ bool + clusterID string + provider string + version string +} + +// NewK8sCluster retrieve the K8s cluster information and creates the K8sCluster resource +// When K8s cluster information is not available it return error +func NewK8sCluster(kubectlOptions k8s.KubectlOptions, reusable bool) (K8sCluster, error) { + clusterInfo, err := newK8sClusterInfo(kubectlOptions) + if err != nil { + return K8sCluster{}, fmt.Errorf("could not get clusterInfo: %w", err) + } + return K8sCluster{ + clusterInfo: clusterInfo, + reusable: reusable, + kubectlOptions: kubectlOptions, + }, nil +} + +func NewMockK8sCluster(kubeConfigPath, kubeContext string, reusable bool, version, provider, clusterID string) K8sCluster { + return K8sCluster{ + clusterInfo: K8sClusterInfo{ + version: version, + provider: provider, + clusterID: clusterID, + }, + reusable: reusable, + kubectlOptions: k8s.KubectlOptions{ + ConfigPath: kubeConfigPath, + ContextName: kubeContext, + }, + } +} + +func NewK8sClusterFromParams(kubectlConfigPath, kubectlContext string, reusable bool) (K8sCluster, error) { + return NewK8sCluster(*k8s.NewKubectlOptions(kubectlContext, kubectlConfigPath, ""), reusable) +} + +func NewK8sClusterFromCurrentConfig(reusable bool) (K8sCluster, error) { + kubectlOptions, err := common.KubectlOptionsForCurrentContext() + if err != nil { + return K8sCluster{}, err + } + + clusterInfo, err := newK8sClusterInfo(kubectlOptions) + if err != nil { + return K8sCluster{}, fmt.Errorf("could not get clusterInfo: %w", err) + } + + return K8sCluster{ + clusterInfo: clusterInfo, + reusable: reusable, + kubectlOptions: kubectlOptions, + }, nil +} + +type K8sCluster struct { + reusable bool + clusterInfo K8sClusterInfo + kubectlOptions k8s.KubectlOptions +} + +func (c K8sCluster) isKubectlOptionsFilled() bool { + return c.kubectlOptions.ConfigPath != "" && c.kubectlOptions.ContextName != "" +} + +func (c K8sCluster) KubectlOptions() (k8s.KubectlOptions, error) { + if !c.isKubectlOptionsFilled() { + return k8s.KubectlOptions{}, errors.New("kubectlOptions is unfilled") + } + return c.kubectlOptions, nil +} + +func (c K8sCluster) String() string { + if !c.isKubectlOptionsFilled() { + return "Unknown" + } + // return fmt.Sprintf("Provider: %s Version: %s KubeContextPath: %s KubeContext: %s", + // c.clusterInfo.provider, c.clusterInfo.version, c.kubectlOptions.ConfigPath, c.kubectlOptions.ContextName) + return fmt.Sprintf(c.clusterInfo.clusterID) +} diff --git a/tests/e2e/pkg/tests/tests_test.go b/tests/e2e/pkg/tests/tests_test.go new file mode 100644 index 000000000..06934b781 --- /dev/null +++ b/tests/e2e/pkg/tests/tests_test.go @@ -0,0 +1,627 @@ +// Copyright © 2023 Cisco Systems, Inc. and/or its affiliates +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tests + +import ( + "sort" + "testing" +) + +func Test_Classifier_minimal(t *testing.T) { + type fields struct { + k8sClusterPool K8sClusterPool + testCases []TestCase + } + tests := []struct { + name string + fields fields + want []TestType + }{ + { + name: "simpleCase", + fields: fields{ + k8sClusterPool: K8sClusterPool{ + k8sClusters: []K8sCluster{ + { + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local1", + version: "1.24", + provider: "provider1", + }, + }, + { + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local2", + version: "1.25", + provider: "provider1", + }, + }, + }, + }, + testCases: []TestCase{ + { + TestName: "testCase1", + }, + { + TestName: "testCase2", + }, + }, + }, + want: []TestType{ + { + testCase: TestCase{ + TestName: "testCase1", + }, + k8sCluster: K8sCluster{ + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local1", + version: "1.24", + provider: "provider1", + }, + }, + }, + { + testCase: TestCase{ + TestName: "testCase2", + }, + k8sCluster: K8sCluster{ + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local2", + version: "1.25", + provider: "provider1", + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tr := Classifier{ + k8sClusterPool: tt.fields.k8sClusterPool, + testCases: tt.fields.testCases, + } + + got := tr.Minimal() + if len(tt.want) != len(got) { + t.Fatalf("not equal want: %v got: %v", tt.want, got) + } + + sort.SliceStable(got, func(i, j int) bool { + return got[i].Less(got[j]) + }) + sort.SliceStable(tt.want, func(i, j int) bool { + return tt.want[i].Less(tt.want[j]) + }) + + for i := range got { + if !got[i].Equal(tt.want[i]) { + t.Errorf("not equal want: %v got: %v", tt.want[i], got[i]) + } + } + }) + } +} + +func Test_Classifier_providerComplete(t *testing.T) { + type fields struct { + k8sClusterPool K8sClusterPool + testCases []TestCase + } + tests := []struct { + name string + fields fields + want []TestType + }{ + { + name: "simpleCase", + fields: fields{ + k8sClusterPool: K8sClusterPool{ + k8sClusters: []K8sCluster{ + { + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local1", + version: "1.24", + provider: "provider1", + }, + }, + { + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local2", + version: "1.25", + provider: "provider2", + }, + }, + }, + }, + testCases: []TestCase{ + { + TestName: "testCase1", + }, + { + TestName: "testCase2", + }, + }, + }, + want: []TestType{ + { + testCase: TestCase{ + TestName: "testCase1", + }, + k8sCluster: K8sCluster{ + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local1", + version: "1.24", + provider: "provider1", + }, + }, + }, + { + testCase: TestCase{ + TestName: "testCase2", + }, + k8sCluster: K8sCluster{ + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local1", + version: "1.24", + provider: "provider1", + }, + }, + }, + { + testCase: TestCase{ + TestName: "testCase1", + }, + k8sCluster: K8sCluster{ + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local2", + version: "1.25", + provider: "provider2", + }, + }, + }, + { + testCase: TestCase{ + TestName: "testCase2", + }, + k8sCluster: K8sCluster{ + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local2", + version: "1.25", + provider: "provider2", + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tr := Classifier{ + k8sClusterPool: tt.fields.k8sClusterPool, + testCases: tt.fields.testCases, + } + + got := tr.ProviderComplete() + if len(tt.want) != len(got) { + t.Fatalf("not equal want: %v got: %v", tt.want, got) + } + + sort.SliceStable(got, func(i, j int) bool { + return got[i].Less(got[j]) + }) + sort.SliceStable(tt.want, func(i, j int) bool { + return tt.want[i].Less(tt.want[j]) + }) + + for i := range got { + if !got[i].Equal(tt.want[i]) { + t.Errorf("not equal want: %v got: %v", tt.want[i], got[i]) + } + } + }) + } +} + +func Test_Classifier_versionComplete(t *testing.T) { + type fields struct { + k8sClusterPool K8sClusterPool + testCases []TestCase + } + tests := []struct { + name string + fields fields + want []TestType + }{ + { + name: "simpleCase", + fields: fields{ + k8sClusterPool: K8sClusterPool{ + k8sClusters: []K8sCluster{ + { + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local1", + version: "1.24", + provider: "provider1", + }, + }, + { + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local1", + version: "1.25", + provider: "provider1", + }, + }, + }, + }, + testCases: []TestCase{ + { + TestName: "testCase1", + }, + { + TestName: "testCase2", + }, + }, + }, + want: []TestType{ + { + testCase: TestCase{ + TestName: "testCase1", + }, + k8sCluster: K8sCluster{ + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local1", + version: "1.24", + provider: "provider1", + }, + }, + }, + { + testCase: TestCase{ + TestName: "testCase2", + }, + k8sCluster: K8sCluster{ + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local1", + version: "1.24", + provider: "provider1", + }, + }, + }, + { + testCase: TestCase{ + TestName: "testCase1", + }, + k8sCluster: K8sCluster{ + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local1", + version: "1.24", + provider: "provider1", + }, + }, + }, + { + testCase: TestCase{ + TestName: "testCase2", + }, + k8sCluster: K8sCluster{ + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local1", + version: "1.24", + provider: "provider1", + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tr := Classifier{ + k8sClusterPool: tt.fields.k8sClusterPool, + testCases: tt.fields.testCases, + } + + got := tr.VersionComplete() + if len(tt.want) != len(got) { + t.Fatalf("not equal want: %v got: %v", tt.want, got) + } + + sort.SliceStable(got, func(i, j int) bool { + return got[i].Less(got[j]) + }) + sort.SliceStable(tt.want, func(i, j int) bool { + return tt.want[i].Less(tt.want[j]) + }) + + for i := range got { + if !got[i].Equal(tt.want[i]) { + t.Errorf("not equal want: %v got: %v", tt.want[i], got[i]) + } + } + }) + } +} + +func Test_Classifier_complete(t *testing.T) { + type fields struct { + k8sClusterPool K8sClusterPool + testCases []TestCase + } + tests := []struct { + name string + fields fields + want []TestType + }{ + { + name: "simpleCase", + fields: fields{ + k8sClusterPool: K8sClusterPool{ + k8sClusters: []K8sCluster{ + { + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local1", + version: "1.24", + provider: "provider1", + }, + }, + { + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local2", + version: "1.25", + provider: "provider2", + }, + }, + }, + }, + testCases: []TestCase{ + { + TestName: "testCase1", + }, + { + TestName: "testCase2", + }, + }, + }, + want: []TestType{ + { + testCase: TestCase{ + TestName: "testCase1", + }, + k8sCluster: K8sCluster{ + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local1", + version: "1.24", + provider: "provider1", + }, + }, + }, + { + testCase: TestCase{ + TestName: "testCase2", + }, + k8sCluster: K8sCluster{ + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local1", + version: "1.24", + provider: "provider1", + }, + }, + }, + { + testCase: TestCase{ + TestName: "testCase1", + }, + k8sCluster: K8sCluster{ + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local2", + version: "1.25", + provider: "provider2", + }, + }, + }, + { + testCase: TestCase{ + TestName: "testCase2", + }, + k8sCluster: K8sCluster{ + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local2", + version: "1.25", + provider: "provider2", + }, + }, + }, + }, + }, + { + name: "complexCase", + fields: fields{ + k8sClusterPool: K8sClusterPool{ + k8sClusters: []K8sCluster{ + { + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local1", + version: "1.24", + provider: "provider1", + }, + }, + { + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local2", + version: "1.25", + provider: "provider2", + }, + }, + { + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local3", + version: "1.25", + provider: "provider3", + }, + }, + { + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local4", + version: "1.25", + provider: "provider3", + }, + }, + }, + }, + testCases: []TestCase{ + { + TestName: "testCase1", + }, + { + TestName: "testCase2", + }, + }, + }, + want: []TestType{ + { + testCase: TestCase{ + TestName: "testCase1", + }, + k8sCluster: K8sCluster{ + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local1", + version: "1.24", + provider: "provider1", + }, + }, + }, + { + testCase: TestCase{ + TestName: "testCase2", + }, + k8sCluster: K8sCluster{ + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local1", + version: "1.24", + provider: "provider1", + }, + }, + }, + { + testCase: TestCase{ + TestName: "testCase1", + }, + k8sCluster: K8sCluster{ + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local2", + version: "1.25", + provider: "provider2", + }, + }, + }, + { + testCase: TestCase{ + TestName: "testCase2", + }, + k8sCluster: K8sCluster{ + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local2", + version: "1.25", + provider: "provider2", + }, + }, + }, + { + testCase: TestCase{ + TestName: "testCase1", + }, + k8sCluster: K8sCluster{ + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local3", + version: "1.25", + provider: "provider3", + }, + }, + }, + { + testCase: TestCase{ + TestName: "testCase2", + }, + k8sCluster: K8sCluster{ + reusable: true, + clusterInfo: K8sClusterInfo{ + clusterID: "local3", + version: "1.25", + provider: "provider3", + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tr := Classifier{ + k8sClusterPool: tt.fields.k8sClusterPool, + testCases: tt.fields.testCases, + } + + got := tr.Complete() + if len(tt.want) != len(got) { + t.Fatalf("not equal want: %v got: %v", tt.want, got) + } + + sort.SliceStable(got, func(i, j int) bool { + return got[i].Less(got[j]) + }) + sort.SliceStable(tt.want, func(i, j int) bool { + return tt.want[i].Less(tt.want[j]) + }) + + for i := range got { + if !got[i].Equal(tt.want[i]) { + t.Errorf("not equal want: %v got: %v", tt.want[i].TestID(), got[i].TestID()) + } + } + }) + } +} diff --git a/tests/e2e/test_alltestcase.go b/tests/e2e/test_alltestcase.go new file mode 100644 index 000000000..8c2ebb30a --- /dev/null +++ b/tests/e2e/test_alltestcase.go @@ -0,0 +1,40 @@ +// Copyright © 2023 Cisco Systems, Inc. and/or its affiliates +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package e2e + +import ( + "github.com/banzaicloud/koperator/tests/e2e/pkg/tests" + "github.com/gruntwork-io/terratest/modules/k8s" +) + +var alltestCase = tests.TestCase{ + TestName: "Testing e2e test altogether", + TestFn: allTestCase, +} + +// TODO (marbarta): kubectlOptions should be passed for the subtests +func allTestCase(kubectlOptions k8s.KubectlOptions) { + var snapshottedInfo = &clusterSnapshot{} + snapshotCluster(snapshottedInfo) + testInstall() + testInstallZookeeperCluster() + testInstallKafkaCluster("../../config/samples/simplekafkacluster.yaml") + testUninstallKafkaCluster() + testInstallKafkaCluster("../../config/samples/simplekafkacluster_ssl.yaml") + testUninstallKafkaCluster() + testUninstallZookeeperCluster() + testUninstall() + snapshotClusterAndCompare(snapshottedInfo) +} diff --git a/tests/e2e/test_mocktest.go b/tests/e2e/test_mocktest.go new file mode 100644 index 000000000..b2048d067 --- /dev/null +++ b/tests/e2e/test_mocktest.go @@ -0,0 +1,57 @@ +// Copyright © 2023 Cisco Systems, Inc. and/or its affiliates +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package e2e + +import ( + "time" + + "github.com/banzaicloud/koperator/tests/e2e/pkg/tests" + "github.com/gruntwork-io/terratest/modules/k8s" + + . "github.com/onsi/ginkgo/v2" +) + +var mockTest1 = tests.TestCase{ + TestDuration: 4 * time.Second, + TestName: "MockTest1", + TestFn: testMockTest1, +} + +func testMockTest1(kubectlOptions k8s.KubectlOptions) { + It("MockTest1-1", func() { + time.Sleep(time.Second * 2) + }) + It("MockTest1-2", func() { + time.Sleep(time.Second * 2) + }) +} + +var mockTest2 = tests.TestCase{ + TestDuration: 5 * time.Second, + TestName: "MockTest2", + TestFn: testMockTest2, +} + +func testMockTest2(kubectlOptions k8s.KubectlOptions) { + It("MockTest2-1", func() { + time.Sleep(time.Second * 1) + }) + It("MockTest2-2", func() { + time.Sleep(time.Second * 2) + }) + It("MockTest2-3", func() { + time.Sleep(time.Second * 2) + }) +} From 1561b6a65ecfcf9e21d5075c3c113ed8e10de515 Mon Sep 17 00:00:00 2001 From: marbarta Date: Mon, 24 Jul 2023 06:47:37 +0200 Subject: [PATCH 02/36] downgrade e2e k8s.io/apimachinery to the same ver that is used by main module --- Makefile | 19 ++++++----- tests/e2e/go.mod | 8 ++--- tests/e2e/go.sum | 19 +++++------ tests/e2e/koperator_suite_test.go | 8 +++-- tests/e2e/pkg/common/config/config.go | 45 +++++++++++++++++++++------ tests/e2e/pkg/tests/tests.go | 23 +++++++------- 6 files changed, 78 insertions(+), 44 deletions(-) diff --git a/Makefile b/Makefile index 595adb440..eab914cf9 100644 --- a/Makefile +++ b/Makefile @@ -98,19 +98,22 @@ test: generate fmt vet manifests bin/setup-envtest -timeout 1h cd properties && go test -coverprofile cover.out -cover -failfast -v -covermode=count ./pkg/... ./internal/... -# Run e2e tests +# Run e2e tests serial test-e2e: - go test github.com/banzaicloud/koperator/tests/e2e \ - -v \ - -timeout 20m \ - -tags e2e \ - --ginkgo.show-node-events \ - --ginkgo.trace \ - --ginkgo.v + ginkgo -v --tags e2e tests/e2e +# Run e2e tests parallel test-e2e-parallel: ginkgo -p -v --tags e2e tests/e2e +# Compile test binary for debugging +build-e2e-debug-binary: + go test github.com/banzaicloud/koperator/tests/e2e \ + --tags e2e \ + -c \ + -o /Users/marbarta/go/src/github.com/banzaicloud/koperator/tests/e2e/__debug_bin \ + -gcflags all=-N + # Build manager binary manager: generate fmt vet go build -o bin/manager main.go diff --git a/tests/e2e/go.mod b/tests/e2e/go.mod index 4b946d2e8..a3318c523 100644 --- a/tests/e2e/go.mod +++ b/tests/e2e/go.mod @@ -11,8 +11,8 @@ require ( github.com/onsi/gomega v1.27.6 github.com/spf13/viper v1.16.0 github.com/twmb/franz-go v1.13.5 - k8s.io/apiextensions-apiserver v0.27.2 - k8s.io/apimachinery v0.27.2 + k8s.io/apiextensions-apiserver v0.26.4 + k8s.io/apimachinery v0.26.4 sigs.k8s.io/yaml v1.3.0 ) @@ -131,8 +131,8 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.27.2 // indirect - k8s.io/client-go v0.27.2 // indirect + k8s.io/api v0.26.4 // indirect + k8s.io/client-go v0.26.4 // indirect k8s.io/klog/v2 v2.90.1 // indirect k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect diff --git a/tests/e2e/go.sum b/tests/e2e/go.sum index ebc5470df..07e6def69 100644 --- a/tests/e2e/go.sum +++ b/tests/e2e/go.sum @@ -118,6 +118,7 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8 github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1 h1:yY9rWGoXv1U5pl4gxqlULARMQD7x0QG85lqEXTWysik= github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -429,7 +430,7 @@ github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqn github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -903,15 +904,15 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.27.2 h1:+H17AJpUMvl+clT+BPnKf0E3ksMAzoBBg7CntpSuADo= -k8s.io/api v0.27.2/go.mod h1:ENmbocXfBT2ADujUXcBhHV55RIT31IIEvkntP6vZKS4= -k8s.io/apiextensions-apiserver v0.27.2 h1:iwhyoeS4xj9Y7v8YExhUwbVuBhMr3Q4bd/laClBV6Bo= -k8s.io/apiextensions-apiserver v0.27.2/go.mod h1:Oz9UdvGguL3ULgRdY9QMUzL2RZImotgxvGjdWRq6ZXQ= +k8s.io/api v0.26.4 h1:qSG2PmtcD23BkYiWfoYAcak870eF/hE7NNYBYavTT94= +k8s.io/api v0.26.4/go.mod h1:WwKEXU3R1rgCZ77AYa7DFksd9/BAIKyOmRlbVxgvjCk= +k8s.io/apiextensions-apiserver v0.26.4 h1:9D2RTxYGxrG5uYg6D7QZRcykXvavBvcA59j5kTaedQI= +k8s.io/apiextensions-apiserver v0.26.4/go.mod h1:cd4uGFGIgzEqUghWpRsr9KE8j2KNTjY8Ji8pnMMazyw= k8s.io/apimachinery v0.0.0-20190704094733-8f6ac2502e51/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= -k8s.io/apimachinery v0.27.2 h1:vBjGaKKieaIreI+oQwELalVG4d8f3YAMNpWLzDXkxeg= -k8s.io/apimachinery v0.27.2/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= -k8s.io/client-go v0.27.2 h1:vDLSeuYvCHKeoQRhCXjxXO45nHVv2Ip4Fe0MfioMrhE= -k8s.io/client-go v0.27.2/go.mod h1:tY0gVmUsHrAmjzHX9zs7eCjxcBsf8IiNe7KQ52biTcQ= +k8s.io/apimachinery v0.26.4 h1:rZccKdBLg9vP6J09JD+z8Yr99Ce8gk3Lbi9TCx05Jzs= +k8s.io/apimachinery v0.26.4/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= +k8s.io/client-go v0.26.4 h1:/7P/IbGBuT73A+G97trf44NTPSNqvuBREpOfdLbHvD4= +k8s.io/client-go v0.26.4/go.mod h1:6qOItWm3EwxJdl/8p5t7FWtWUOwyMdA8N9ekbW4idpI= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= diff --git a/tests/e2e/koperator_suite_test.go b/tests/e2e/koperator_suite_test.go index 8571ef1ee..dfffb8603 100644 --- a/tests/e2e/koperator_suite_test.go +++ b/tests/e2e/koperator_suite_test.go @@ -116,7 +116,11 @@ func runGinkgoTests(t *testing.T) error { return err } - testSuiteDuration := testPool.GetTestSuiteDuration() + testSuiteDuration := testPool.GetTestSuiteDurationSerial() + if suiteConfig.ParallelProcess > 1 { + testSuiteDuration = testPool.GetTestSuiteDurationParallel() + } + maxTimeout, err := time.ParseDuration(viper.GetString(config.Tests.MaxTimeout)) if err != nil { return fmt.Errorf("could not parse MaxTimeout into time.Duration: %w", err) @@ -142,7 +146,7 @@ func runGinkgoTests(t *testing.T) error { func() { defer ginkgo.GinkgoRecover() - RunSpecs(t, testPool.PoolInfo(), suiteConfig, reporterConfig) + RunSpecs(t, fmt.Sprintf("PoolInfo: \n%s\nConfigurations: \n%s\n", testPool.PoolInfo(), config.Tests), suiteConfig, reporterConfig) }() return nil diff --git a/tests/e2e/pkg/common/config/config.go b/tests/e2e/pkg/common/config/config.go index 942c76863..221fb3ad3 100644 --- a/tests/e2e/pkg/common/config/config.go +++ b/tests/e2e/pkg/common/config/config.go @@ -14,7 +14,11 @@ package config -import "github.com/spf13/viper" +import ( + "fmt" + + "github.com/spf13/viper" +) type TestStrategy struct{} @@ -25,14 +29,37 @@ const ( TestStrategyComplete = "complete" ) -var Tests = struct { +const ( + defaultReportDir = "reports" + defaultCreateTestReportFile = "false" + defaultMaxTimeout = "1m" + defaultAllowedOverrunDuration = "1m" + defaultTestStrategy = TestStrategyMinimal +) + +type TestsType struct { ReportDir string CreateTestReportFile string MaxTimeout string AllowedOverrunDuration string TestStrategy string LabelFilter string -}{ +} + +func (t TestsType) String() string { + return fmt.Sprintf(` +ReportDir: %s +CreateTestReportFile: %s +MaxTimeout: %s +AllowedOverrunDuration: %s +TestStrategy: %s +LabelFilter: %s +`, viper.GetString(t.ReportDir), viper.GetString(t.CreateTestReportFile), + viper.GetString(t.MaxTimeout), viper.GetString(t.AllowedOverrunDuration), + viper.GetString(t.TestStrategy), viper.GetString(t.LabelFilter)) +} + +var Tests = TestsType{ CreateTestReportFile: "tests.CreateTestReportFile", MaxTimeout: "tests.MaxTimeout", AllowedOverrunDuration: "tests.AllowedOverrunDuration", @@ -45,20 +72,18 @@ func init() { viper.AutomaticEnv() viper.BindEnv(Tests.CreateTestReportFile, "CREATE_TEST_REPORT_FILE") - viper.SetDefault(Tests.CreateTestReportFile, true) + viper.SetDefault(Tests.CreateTestReportFile, defaultCreateTestReportFile) viper.BindEnv(Tests.ReportDir, "REPORT_DIR") - viper.SetDefault(Tests.ReportDir, "reports") + viper.SetDefault(Tests.ReportDir, defaultReportDir) viper.BindEnv(Tests.MaxTimeout, "MAX_TIMEOUT") - viper.SetDefault(Tests.MaxTimeout, "10s") + viper.SetDefault(Tests.MaxTimeout, defaultMaxTimeout) viper.BindEnv(Tests.AllowedOverrunDuration, "ALLOWED_OVERRUN_DURATION") - viper.SetDefault(Tests.AllowedOverrunDuration, "5s") + viper.SetDefault(Tests.AllowedOverrunDuration, defaultAllowedOverrunDuration) viper.BindEnv(Tests.TestStrategy, "TEST_STRATEGY") - viper.SetDefault(Tests.TestStrategy, TestStrategyMinimal) - - viper.BindEnv(Tests.LabelFilter, "TEST_MODE") + viper.SetDefault(Tests.TestStrategy, defaultTestStrategy) } diff --git a/tests/e2e/pkg/tests/tests.go b/tests/e2e/pkg/tests/tests.go index c662e9b62..37a293e39 100644 --- a/tests/e2e/pkg/tests/tests.go +++ b/tests/e2e/pkg/tests/tests.go @@ -42,22 +42,15 @@ func (tests TestPool) PoolInfo() string { testsByContextName := tests.getTestsByContextName() return fmt.Sprintf(` - NumberOfTests: %d - NumberOfK8sClusters: %d - NumberOfK8sVersions: - NumberOfK8sProviders: - TestK8sMapping: %v - TestStrategy: %s - -ExpectedDuration: %f - -`, len(tests), len(maps.Keys(testsByContextName)), tests, viper.GetString(config.Tests.TestStrategy), tests.GetTestSuiteDuration().Seconds()) +ExpectedDurationSerial: %f +ExpectedDurationParallel: %f +`, len(tests), len(maps.Keys(testsByContextName)), tests, viper.GetString(config.Tests.TestStrategy), tests.GetTestSuiteDurationSerial().Seconds(), tests.GetTestSuiteDurationParallel().Seconds()) } func (tests TestPool) BuildParallelByK8sCluster() { @@ -91,7 +84,7 @@ func (tests TestPool) getTestsByContextName() map[string][]TestType { return testsByContextName } -func (tests TestPool) GetTestSuiteDuration() time.Duration { +func (tests TestPool) GetTestSuiteDurationParallel() time.Duration { testsByClusterID := tests.getTestsByClusterID() var max time.Duration @@ -108,6 +101,14 @@ func (tests TestPool) GetTestSuiteDuration() time.Duration { return max } +func (tests TestPool) GetTestSuiteDurationSerial() time.Duration { + var allDuration time.Duration + for _, test := range tests { + allDuration += test.testCase.TestDuration + } + return allDuration +} + func NewClassifier(k8sClusterPool K8sClusterPool, testCases ...TestCase) Classifier { return Classifier{ k8sClusterPool: k8sClusterPool, From d7947cc45e8ff9dfcf11f5d121a90e154b58782f Mon Sep 17 00:00:00 2001 From: marbarta Date: Mon, 24 Jul 2023 08:21:35 +0200 Subject: [PATCH 03/36] add bin/ginkgo Makefile --- .github/actions/kind-create/action.yaml | 4 +- Makefile | 19 +++-- tests/e2e/koperator_suite_test.go | 107 ++++++++++++++---------- tests/e2e/pkg/common/common.go | 41 +++++++++ tests/e2e/pkg/common/config/config.go | 43 ++++++---- tests/e2e/pkg/tests/tests.go | 78 +++++++++++++---- tests/e2e/pkg/tests/tests_test.go | 71 ++-------------- tests/e2e/test_alltestcase.go | 9 +- tests/e2e/test_install.go | 20 +++-- 9 files changed, 231 insertions(+), 161 deletions(-) diff --git a/.github/actions/kind-create/action.yaml b/.github/actions/kind-create/action.yaml index 3e063854f..c28292f83 100644 --- a/.github/actions/kind-create/action.yaml +++ b/.github/actions/kind-create/action.yaml @@ -39,8 +39,8 @@ runs: kubectl get pods -n kube-system echo "current-context:" $(kubectl config current-context) echo "environment-kubeconfig:" ${KUBECONFIG} - mkdir tests/e2e/platforms/kind/${{ inputs.kind_k8s_version }} - kubeconfig_path=$(pwd)/tests/e2e/platforms/kind/${{ inputs.kind_k8s_version }}/kind.kconf + mkdir -p tests/e2e/platforms/kind/kubeconfigs + kubeconfig_path=$(pwd)/tests/e2e/platforms/kind/kubeconfigs/kind.kconf kind get kubeconfig --name ${{ inputs.kind_cluster_name }} > $kubeconfig_path chmod 600 $kubeconfig_path echo "kubeconfig=$(echo $kubeconfig_path)" >> $GITHUB_OUTPUT diff --git a/Makefile b/Makefile index eab914cf9..f3eeb8ec6 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,9 @@ CONTROLLER_GEN_VERSION = v0.9.2 CONTROLLER_GEN = $(PWD)/bin/controller-gen ENVTEST_K8S_VERSION = 1.24.2 +GINKGO_VERSION := 2.9.7 +MOCKGEN_VERSION := 1.6.0 + KUSTOMIZE_BASE = config/overlays/specific-manager-version @@ -98,12 +101,21 @@ test: generate fmt vet manifests bin/setup-envtest -timeout 1h cd properties && go test -coverprofile cover.out -cover -failfast -v -covermode=count ./pkg/... ./internal/... +bin/ginkgo: $(BIN_DIR)/ginkgo-$(GINKGO_VERSION) + @ln -sf ginkgo-$(GINKGO_VERSION) $(BIN_DIR)/ginkgo + +$(BIN_DIR)/ginkgo-$(GINKGO_VERSION): + @mkdir -p $(BIN_DIR) + @GOBIN=$(BIN_DIR) go install github.com/onsi/ginkgo/v2/ginkgo@v$(GINKGO_VERSION) + @mv $(BIN_DIR)/ginkgo $(BIN_DIR)/ginkgo-$(GINKGO_VERSION) + + # Run e2e tests serial -test-e2e: +test-e2e: bin/ginkgo ginkgo -v --tags e2e tests/e2e # Run e2e tests parallel -test-e2e-parallel: +test-e2e-parallel: bin/ginkgo ginkgo -p -v --tags e2e tests/e2e # Compile test binary for debugging @@ -258,9 +270,6 @@ gen-license-header: bin/gotemplate ## Generate license header used in source cod --import="$(BOILERPLATE_DIR)/vars.yml" \ --source="$(BOILERPLATE_DIR)" - -MOCKGEN_VERSION := 1.6.0 - bin/mockgen: $(BIN_DIR)/mockgen-$(MOCKGEN_VERSION) @ln -sf mockgen-$(MOCKGEN_VERSION) $(BIN_DIR)/mockgen diff --git a/tests/e2e/koperator_suite_test.go b/tests/e2e/koperator_suite_test.go index dfffb8603..fbe8a0597 100644 --- a/tests/e2e/koperator_suite_test.go +++ b/tests/e2e/koperator_suite_test.go @@ -35,60 +35,29 @@ import ( var testPool tests.TestPool -func beforeSuite() ([]tests.TestType, error) { +func beforeSuite() (tests.TestPool, error) { k8sClusterPool := tests.K8sClusterPool{} - k8sClusterPool.AddK8sClusters( - tests.NewMockK8sCluster( - "testContextPath1", - "testContextName1", - true, - "1.24", - "provider1", - "clusterID1", - ), - tests.NewMockK8sCluster( - "testContextPath2", - "testContextName2", - true, - "1.24", - "provider2", - "clusterID2", - ), - tests.NewMockK8sCluster( - "testContextPath3", - "testContextName3", - true, - "1.24", - "provider3", - "clusterID3", - ), - tests.NewMockK8sCluster( - "testContextPath4", - "testContextName4", - true, - "1.26", - "provider4", - "clusterID4", - ), - ) + if err := k8sClusterPool.FeedFomDirectory(viper.GetString(config.Tests.KubeConfigDirectoryPath)); err != nil { + return nil, err + } - classifier := tests.NewClassifier(k8sClusterPool, mockTest1, mockTest2) + classifier := tests.NewClassifier(k8sClusterPool, testCaseInstall) - var tests []tests.TestType + var testPool tests.TestPool testStrategy := viper.GetString(config.Tests.TestStrategy) switch testStrategy { case config.TestStrategyMinimal: - tests = classifier.Minimal() + testPool = classifier.Minimal() case config.TestStrategyVersionComplete: - tests = classifier.VersionComplete() + testPool = classifier.VersionComplete() case config.TestStrategyProviderComplete: - tests = classifier.ProviderComplete() + testPool = classifier.ProviderComplete() case config.TestStrategyComplete: - tests = classifier.Complete() + testPool = classifier.Complete() } - return tests, nil + return testPool, nil } func TestKoperator(t *testing.T) { @@ -113,7 +82,7 @@ func runGinkgoTests(t *testing.T) error { // Generated and load tests into the pool testPool, err = beforeSuite() if err != nil { - return err + return fmt.Errorf("beforeSuite ran into err: %w", err) } testSuiteDuration := testPool.GetTestSuiteDurationSerial() @@ -144,15 +113,26 @@ func runGinkgoTests(t *testing.T) error { } } + testDescription := fmt.Sprintf("\n%s\nConfigurations: \n%s%s\nPoolInfo: \n%s%s\n", + sectionStringDelimiter(), config.Tests, sectionStringDelimiter(), testPool.PoolInfo(), sectionStringDelimiter()) + func() { defer ginkgo.GinkgoRecover() - RunSpecs(t, fmt.Sprintf("PoolInfo: \n%s\nConfigurations: \n%s\n", testPool.PoolInfo(), config.Tests), suiteConfig, reporterConfig) + RunSpecs(t, testDescription, suiteConfig, reporterConfig) }() return nil } +func sectionStringDelimiter() string { + delimiter := "" + for i := 0; i < 100; i++ { + delimiter += "-" + } + return delimiter +} + func createTestReportFile() error { reportDir := viper.GetString(config.Tests.ReportDir) if _, err := os.Stat(reportDir); os.IsNotExist(err) { @@ -180,3 +160,42 @@ var _ = Describe(time.Now().Format(time.RFC3339), func() { // ginkgo execute parallel the generated tests by K8sClusters testPool.BuildParallelByK8sCluster() }) + +func MockK8sClusterPool() tests.K8sClusterPool { + k8sClusterPool := tests.K8sClusterPool{} + k8sClusterPool.AddK8sClusters( + tests.NewMockK8sCluster( + "testContextPath1", + "testContextName1", + true, + "1.24", + "provider1", + "clusterID1", + ), + tests.NewMockK8sCluster( + "testContextPath2", + "testContextName2", + true, + "1.24", + "provider2", + "clusterID2", + ), + tests.NewMockK8sCluster( + "testContextPath3", + "testContextName3", + true, + "1.24", + "provider3", + "clusterID3", + ), + tests.NewMockK8sCluster( + "testContextPath4", + "testContextName4", + true, + "1.26", + "provider4", + "clusterID4", + ), + ) + return k8sClusterPool +} diff --git a/tests/e2e/pkg/common/common.go b/tests/e2e/pkg/common/common.go index 33e27adfb..914e35699 100644 --- a/tests/e2e/pkg/common/common.go +++ b/tests/e2e/pkg/common/common.go @@ -20,7 +20,11 @@ import ( "emperror.dev/errors" "github.com/gruntwork-io/terratest/modules/k8s" + "golang.org/x/exp/maps" "gopkg.in/yaml.v2" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/tools/clientcmd/api" ) // currentKubernetesContext returns the currently set Kubernetes context based @@ -86,3 +90,40 @@ func GetDefaultKubeContext(kubeconfigPath string) (string, error) { return kubecontext, nil } + +// GetRawConfig creates a raw clientcmd api config +func GetRawConfig(kubeconfigPath string) (api.Config, error) { + rules := clientcmd.NewDefaultClientConfigLoadingRules() + if kubeconfigPath == "" { + return api.Config{}, errors.New("missing kubeconfigPath") + } + rules.ExplicitPath = kubeconfigPath + + clientConfig := clientcmd. + NewNonInteractiveDeferredLoadingClientConfig(rules, nil) + + return clientConfig.RawConfig() +} + +func GetKubeContexts(kubeconfigPath string) ([]string, error) { + configs, err := GetRawConfig(kubeconfigPath) + if err != nil { + return nil, err + } + return maps.Keys(configs.Contexts), nil +} + +// GetConfig returns kubernetes config based on the current environment. +// If fpath is provided, loads configuration from that file. Otherwise, +// GetConfig uses default strategy to load configuration from $KUBECONFIG, +// .kube/config, or just returns in-cluster config. +func GetConfigWithContext(kubeconfigPath, kubeContext string) (*rest.Config, error) { + rules := clientcmd.NewDefaultClientConfigLoadingRules() + if kubeconfigPath != "" { + rules.ExplicitPath = kubeconfigPath + } + overrides := &clientcmd.ConfigOverrides{CurrentContext: kubeContext} + return clientcmd. + NewNonInteractiveDeferredLoadingClientConfig(rules, overrides). + ClientConfig() +} diff --git a/tests/e2e/pkg/common/config/config.go b/tests/e2e/pkg/common/config/config.go index 221fb3ad3..112f00f2b 100644 --- a/tests/e2e/pkg/common/config/config.go +++ b/tests/e2e/pkg/common/config/config.go @@ -30,20 +30,22 @@ const ( ) const ( - defaultReportDir = "reports" - defaultCreateTestReportFile = "false" - defaultMaxTimeout = "1m" - defaultAllowedOverrunDuration = "1m" - defaultTestStrategy = TestStrategyMinimal + defaultReportDir = "reports" + defaultCreateTestReportFile = "false" + defaultMaxTimeout = "30m" + defaultAllowedOverrunDuration = "5m" + defaultTestStrategy = TestStrategyMinimal + defaultKubeConfigDirectoryPath = "platforms/kind/kubeconfigs" ) type TestsType struct { - ReportDir string - CreateTestReportFile string - MaxTimeout string - AllowedOverrunDuration string - TestStrategy string - LabelFilter string + ReportDir string + CreateTestReportFile string + MaxTimeout string + AllowedOverrunDuration string + TestStrategy string + LabelFilter string + KubeConfigDirectoryPath string } func (t TestsType) String() string { @@ -54,18 +56,21 @@ MaxTimeout: %s AllowedOverrunDuration: %s TestStrategy: %s LabelFilter: %s +KubeConfigDirectoryPath: %s `, viper.GetString(t.ReportDir), viper.GetString(t.CreateTestReportFile), viper.GetString(t.MaxTimeout), viper.GetString(t.AllowedOverrunDuration), - viper.GetString(t.TestStrategy), viper.GetString(t.LabelFilter)) + viper.GetString(t.TestStrategy), viper.GetString(t.LabelFilter), + viper.GetString(t.KubeConfigDirectoryPath)) } var Tests = TestsType{ - CreateTestReportFile: "tests.CreateTestReportFile", - MaxTimeout: "tests.MaxTimeout", - AllowedOverrunDuration: "tests.AllowedOverrunDuration", - TestStrategy: "tests.TestStrategy", - ReportDir: "tests.ReportDir", - LabelFilter: "tests.LabelFilter", + CreateTestReportFile: "tests.CreateTestReportFile", + MaxTimeout: "tests.MaxTimeout", + AllowedOverrunDuration: "tests.AllowedOverrunDuration", + TestStrategy: "tests.TestStrategy", + ReportDir: "tests.ReportDir", + LabelFilter: "tests.LabelFilter", + KubeConfigDirectoryPath: "tests.KubeConfigDirectoryPath", } func init() { @@ -86,4 +91,6 @@ func init() { viper.BindEnv(Tests.TestStrategy, "TEST_STRATEGY") viper.SetDefault(Tests.TestStrategy, defaultTestStrategy) + viper.BindEnv(Tests.KubeConfigDirectoryPath, "KUBECONFIG_DIR") + viper.SetDefault(Tests.KubeConfigDirectoryPath, defaultKubeConfigDirectoryPath) } diff --git a/tests/e2e/pkg/tests/tests.go b/tests/e2e/pkg/tests/tests.go index 37a293e39..10b419bd3 100644 --- a/tests/e2e/pkg/tests/tests.go +++ b/tests/e2e/pkg/tests/tests.go @@ -23,6 +23,7 @@ import ( "math/rand" "os" "path/filepath" + "sort" "time" "github.com/banzaicloud/koperator/tests/e2e/pkg/common" @@ -38,6 +39,26 @@ import ( type TestPool []TestType +func (tests TestPool) Equal(other TestPool) bool { + if len(tests) != len(other) { + return false + } + + sort.SliceStable(tests, func(i, j int) bool { + return tests[i].less(tests[j]) + }) + sort.SliceStable(other, func(i, j int) bool { + return other[i].less(other[j]) + }) + + for i := range tests { + if !tests[i].equal(other[i]) { + return false + } + } + return true +} + func (tests TestPool) PoolInfo() string { testsByContextName := tests.getTestsByContextName() @@ -121,7 +142,7 @@ type Classifier struct { testCases []TestCase } -func (t Classifier) Minimal() []TestType { +func (t Classifier) Minimal() TestPool { t.k8sClusterPool.mixPool() tests := make([]TestType, 0, len(t.testCases)) @@ -134,10 +155,10 @@ func (t Classifier) Minimal() []TestType { return tests } -func (t Classifier) VersionComplete() []TestType { +func (t Classifier) VersionComplete() TestPool { t.k8sClusterPool.mixPool() k8sClustersByVersion := t.k8sClusterPool.getByVersions() - tests := make([]TestType, 0, len(t.testCases)*len(maps.Keys(k8sClustersByVersion))) + tests := make(TestPool, 0, len(t.testCases)*len(maps.Keys(k8sClustersByVersion))) for _, k8sClusters := range k8sClustersByVersion { for i, testCase := range t.testCases { @@ -150,10 +171,10 @@ func (t Classifier) VersionComplete() []TestType { return tests } -func (t Classifier) ProviderComplete() []TestType { +func (t Classifier) ProviderComplete() TestPool { t.k8sClusterPool.mixPool() k8sClustersByProviders := t.k8sClusterPool.getByProviders() - tests := make([]TestType, 0, len(t.testCases)*len(maps.Keys(k8sClustersByProviders))) + tests := make(TestPool, 0, len(t.testCases)*len(maps.Keys(k8sClustersByProviders))) for _, k8sClusters := range k8sClustersByProviders { for i, testCase := range t.testCases { @@ -166,10 +187,10 @@ func (t Classifier) ProviderComplete() []TestType { return tests } -func (t Classifier) Complete() []TestType { +func (t Classifier) Complete() TestPool { t.k8sClusterPool.mixPool() k8sClustersByProvidersVersions := t.k8sClusterPool.getByProvidersVersions() - var tests []TestType + var tests TestPool for _, byVersions := range k8sClustersByProvidersVersions { for _, k8sClusters := range byVersions { @@ -208,18 +229,19 @@ func (t *K8sClusterPool) FeedFomDirectory(kubeConfigDirectoryPath string) error for _, file := range files { if file != nil { - kubeContext, err := common.GetDefaultKubeContext(file.Name()) - if err != nil { - err := fmt.Errorf("could not fetch default kubeContext from file: %s, err: %w", file.Name(), err) - log.Print(err) - continue - } + //kubeContext, err := common.GetDefaultKubeContext(file.Name()) + // if err != nil { + // err := fmt.Errorf("could not fetch default kubeContext from file: %s, err: %w", file.Name(), err) + // log.Print(err) + // continue + // } kubectlPath := filepath.Join(kubeConfigDirectoryPath, file.Name()) - k8sCluster, err := NewK8sClusterFromParams(kubectlPath, kubeContext, true) + + k8sClusters, err := NewK8sClustersFromParams(kubectlPath, true) if err != nil { - return fmt.Errorf("could not create K8sCluster structure from file '%s' err: %w", kubectlPath, err) + return fmt.Errorf("could not get K8sClusters from file '%s' err: %w", kubectlPath, err) } - t.AddK8sClusters(k8sCluster) + t.AddK8sClusters(k8sClusters...) } } return nil @@ -283,11 +305,11 @@ type TestType struct { k8sCluster K8sCluster } -func (t TestType) Equal(test TestType) bool { +func (t TestType) equal(test TestType) bool { return t.TestID() == test.TestID() } -func (t TestType) Less(test TestType) bool { +func (t TestType) less(test TestType) bool { return t.TestID() < test.TestID() } @@ -386,6 +408,26 @@ func NewK8sClusterFromParams(kubectlConfigPath, kubectlContext string, reusable return NewK8sCluster(*k8s.NewKubectlOptions(kubectlContext, kubectlConfigPath, ""), reusable) } +func NewK8sClustersFromParams(kubectlConfigPath string, reusable bool) ([]K8sCluster, error) { + kubeContexts, err := common.GetKubeContexts(kubectlConfigPath) + if err != nil { + return nil, fmt.Errorf("could not get kubecontexts: %w", err) + } + + var k8sClusters []K8sCluster + + for _, kubeContext := range kubeContexts { + k8sCluster, err := NewK8sCluster(*k8s.NewKubectlOptions(kubeContext, kubectlConfigPath, ""), reusable) + if err != nil { + err := fmt.Errorf("could not create K8sCluster: %w", err) + log.Print(err) + continue + } + k8sClusters = append(k8sClusters, k8sCluster) + } + return k8sClusters, nil +} + func NewK8sClusterFromCurrentConfig(reusable bool) (K8sCluster, error) { kubectlOptions, err := common.KubectlOptionsForCurrentContext() if err != nil { diff --git a/tests/e2e/pkg/tests/tests_test.go b/tests/e2e/pkg/tests/tests_test.go index 06934b781..3f98b8e5e 100644 --- a/tests/e2e/pkg/tests/tests_test.go +++ b/tests/e2e/pkg/tests/tests_test.go @@ -15,7 +15,6 @@ package tests import ( - "sort" "testing" ) @@ -27,7 +26,7 @@ func Test_Classifier_minimal(t *testing.T) { tests := []struct { name string fields fields - want []TestType + want TestPool }{ { name: "simpleCase", @@ -99,21 +98,8 @@ func Test_Classifier_minimal(t *testing.T) { } got := tr.Minimal() - if len(tt.want) != len(got) { - t.Fatalf("not equal want: %v got: %v", tt.want, got) - } - - sort.SliceStable(got, func(i, j int) bool { - return got[i].Less(got[j]) - }) - sort.SliceStable(tt.want, func(i, j int) bool { - return tt.want[i].Less(tt.want[j]) - }) - - for i := range got { - if !got[i].Equal(tt.want[i]) { - t.Errorf("not equal want: %v got: %v", tt.want[i], got[i]) - } + if !got.Equal(tt.want) { + t.Errorf("want: %v\ngot: %v", tt.want, got) } }) } @@ -225,21 +211,8 @@ func Test_Classifier_providerComplete(t *testing.T) { } got := tr.ProviderComplete() - if len(tt.want) != len(got) { - t.Fatalf("not equal want: %v got: %v", tt.want, got) - } - - sort.SliceStable(got, func(i, j int) bool { - return got[i].Less(got[j]) - }) - sort.SliceStable(tt.want, func(i, j int) bool { - return tt.want[i].Less(tt.want[j]) - }) - - for i := range got { - if !got[i].Equal(tt.want[i]) { - t.Errorf("not equal want: %v got: %v", tt.want[i], got[i]) - } + if !got.Equal(tt.want) { + t.Errorf("want: %v\ngot: %v", tt.want, got) } }) } @@ -351,21 +324,8 @@ func Test_Classifier_versionComplete(t *testing.T) { } got := tr.VersionComplete() - if len(tt.want) != len(got) { - t.Fatalf("not equal want: %v got: %v", tt.want, got) - } - - sort.SliceStable(got, func(i, j int) bool { - return got[i].Less(got[j]) - }) - sort.SliceStable(tt.want, func(i, j int) bool { - return tt.want[i].Less(tt.want[j]) - }) - - for i := range got { - if !got[i].Equal(tt.want[i]) { - t.Errorf("not equal want: %v got: %v", tt.want[i], got[i]) - } + if !got.Equal(tt.want) { + t.Errorf("want: %v\ngot: %v", tt.want, got) } }) } @@ -606,21 +566,8 @@ func Test_Classifier_complete(t *testing.T) { } got := tr.Complete() - if len(tt.want) != len(got) { - t.Fatalf("not equal want: %v got: %v", tt.want, got) - } - - sort.SliceStable(got, func(i, j int) bool { - return got[i].Less(got[j]) - }) - sort.SliceStable(tt.want, func(i, j int) bool { - return tt.want[i].Less(tt.want[j]) - }) - - for i := range got { - if !got[i].Equal(tt.want[i]) { - t.Errorf("not equal want: %v got: %v", tt.want[i].TestID(), got[i].TestID()) - } + if !got.Equal(tt.want) { + t.Errorf("want: %v\ngot: %v", tt.want, got) } }) } diff --git a/tests/e2e/test_alltestcase.go b/tests/e2e/test_alltestcase.go index 8c2ebb30a..4673553db 100644 --- a/tests/e2e/test_alltestcase.go +++ b/tests/e2e/test_alltestcase.go @@ -15,20 +15,23 @@ package e2e import ( + "time" + "github.com/banzaicloud/koperator/tests/e2e/pkg/tests" "github.com/gruntwork-io/terratest/modules/k8s" ) var alltestCase = tests.TestCase{ - TestName: "Testing e2e test altogether", - TestFn: allTestCase, + TestDuration: 10 * time.Minute, + TestName: "Testing e2e test altogether", + TestFn: allTestCase, } // TODO (marbarta): kubectlOptions should be passed for the subtests func allTestCase(kubectlOptions k8s.KubectlOptions) { var snapshottedInfo = &clusterSnapshot{} snapshotCluster(snapshottedInfo) - testInstall() + testInstall(kubectlOptions) testInstallZookeeperCluster() testInstallKafkaCluster("../../config/samples/simplekafkacluster.yaml") testUninstallKafkaCluster() diff --git a/tests/e2e/test_install.go b/tests/e2e/test_install.go index 185065644..41aeae80d 100644 --- a/tests/e2e/test_install.go +++ b/tests/e2e/test_install.go @@ -15,21 +15,23 @@ package e2e import ( + "time" + + "github.com/banzaicloud/koperator/tests/e2e/pkg/tests" "github.com/gruntwork-io/terratest/modules/k8s" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) -func testInstall() bool { - return When("Installing Koperator and dependencies", Ordered, func() { - var kubectlOptions k8s.KubectlOptions - var err error - - It("Acquiring K8s config and context", func() { - kubectlOptions, err = kubectlOptionsForCurrentContext() - Expect(err).NotTo(HaveOccurred()) - }) +var testCaseInstall = tests.TestCase{ + TestDuration: 10 * time.Minute, + TestName: "Testing e2e test altogether", + TestFn: testInstall, +} +func testInstall(kubectlOptions k8s.KubectlOptions) { + When("Installing Koperator and dependencies", Ordered, func() { + var err error When("Installing cert-manager", func() { It("Installing cert-manager Helm chart", func() { err = certManagerHelmDescriptor.installHelmChart(kubectlOptions) From d9efd7fd0c6e32ea4e32aca06d884cff444569f7 Mon Sep 17 00:00:00 2001 From: marbarta Date: Mon, 24 Jul 2023 18:03:21 +0200 Subject: [PATCH 04/36] parent d7947cc45e8ff9dfcf11f5d121a90e154b58782f author marbarta 1690214601 +0200 committer marbarta 1690470420 +0200 create test report --- .github/actions/kind-create/action.yaml | 17 +- .github/workflows/e2e-test.yaml | 39 ++- Makefile | 2 +- tests/e2e/const.go | 6 + tests/e2e/helm.go | 2 +- tests/e2e/kafka.go | 36 +++ tests/e2e/kcat.go | 20 +- tests/e2e/koperator_suite_test.go | 75 ++--- tests/e2e/pkg/common/config/config.go | 4 +- tests/e2e/pkg/tests/mocks.go | 264 +++++++++++++++++ tests/e2e/pkg/tests/tests.go | 273 ++++++++++++------ tests/e2e/pkg/tests/tests_test.go | 142 +++++---- tests/e2e/pkg/tests/utils.go | 69 +++++ tests/e2e/produce_consume.go | 31 +- tests/e2e/templates/kcat.yaml.tmpl | 9 + tests/e2e/templates/user.yaml.tmpl | 11 + ...st_alltestcase.go => test_alltestcases.go} | 22 +- tests/e2e/test_install.go | 4 +- tests/e2e/test_install_cluster.go | 21 +- tests/e2e/test_mocktest.go | 57 ---- tests/e2e/test_produce_consume.go | 25 +- tests/e2e/test_uninstall.go | 13 +- tests/e2e/test_uninstall_cluster.go | 21 +- 23 files changed, 788 insertions(+), 375 deletions(-) create mode 100644 tests/e2e/pkg/tests/mocks.go create mode 100644 tests/e2e/pkg/tests/utils.go create mode 100644 tests/e2e/templates/user.yaml.tmpl rename tests/e2e/{test_alltestcase.go => test_alltestcases.go} (63%) delete mode 100644 tests/e2e/test_mocktest.go diff --git a/.github/actions/kind-create/action.yaml b/.github/actions/kind-create/action.yaml index c28292f83..be18115f6 100644 --- a/.github/actions/kind-create/action.yaml +++ b/.github/actions/kind-create/action.yaml @@ -13,10 +13,17 @@ inputs: # adding these parameters to make this reusable later on description: 'Path to the kind config to use' required: true default: 'tests/e2e/platforms/kind/kind_config.yaml' + kubeconfig_dir_path: + description: 'Relative dir path of the created kind cluster kubeconfig' + required: true + default: 'tests/e2e/kubeconfigs' outputs: kubeconfig: description: 'Path of the resulting kubeconfig' value: ${{ steps.check-kind-cluster.outputs.kubeconfig }} + kubeconfig_dir_path: + description: 'Dir path of the resulting kubeconfig' + value: ${{ steps.check-kind-cluster.outputs.kubeconfig_dir_path }} runs: using: "composite" @@ -38,10 +45,14 @@ runs: kubectl cluster-info kubectl get pods -n kube-system echo "current-context:" $(kubectl config current-context) - echo "environment-kubeconfig:" ${KUBECONFIG} - mkdir -p tests/e2e/platforms/kind/kubeconfigs - kubeconfig_path=$(pwd)/tests/e2e/platforms/kind/kubeconfigs/kind.kconf + kubeconfig_dir_path=$(pwd)/${{ inputs.kubeconfig_dir_path }} + mkdir -p $kubeconfig_dir_path + kubeconfig_filename=${{ inputs.kind_cluster_name }}-${{ inputs.kind_k8s_version }}.yaml + kubeconfig_path=$kubeconfig_dir_path/$kubeconfig_filename kind get kubeconfig --name ${{ inputs.kind_cluster_name }} > $kubeconfig_path chmod 600 $kubeconfig_path + echo "kubeconfig path: $kubeconfig_path" echo "kubeconfig=$(echo $kubeconfig_path)" >> $GITHUB_OUTPUT + echo "kubeconfig_dir_path=$(echo $kubeconfig_dir_path)" >> $GITHUB_OUTPUT + shell: bash diff --git a/.github/workflows/e2e-test.yaml b/.github/workflows/e2e-test.yaml index 30b7a6b3c..b1f5c3c48 100644 --- a/.github/workflows/e2e-test.yaml +++ b/.github/workflows/e2e-test.yaml @@ -9,6 +9,9 @@ on: jobs: build: runs-on: ubuntu-latest + permissions: + checks: write + pull-requests: write steps: - name: Set up Go @@ -19,14 +22,40 @@ jobs: - name: Checkout code uses: actions/checkout@v3 - - name: Setup Kind cluster - id: setup-kind + - name: Setup Kind cluster1 + id: setup-kind1 uses: ./.github/actions/kind-create + with: + kind_cluster_name: kind1 + kind_k8s_version: v1.24.13 + kubeconfig_dir_path: tests/e2e/kubeconfigs + + - name: Setup Kind cluster2 + id: setup-kind2 + uses: ./.github/actions/kind-create + with: + kind_cluster_name: kind2 + kind_k8s_version: v1.25.9 + kubeconfig_dir_path: tests/e2e/kubeconfigs - - name: run tests + - name: Run E2E tests env: - KUBECONFIG: ${{ steps.setup-kind.outputs.kubeconfig }} + TEST_STRATEGY: version + KUBECONFIG_DIR: tests/e2e/kubeconfigs + MAX_TIMEOUT: "2h" run: | go work init go work use -r . - make test-e2e + make test-e2e-parallel + + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action/composite@master + if: always() + with: + commit: ${{ github.event.workflow_run.head_sha }} + report_individual_runs: "true" + check_name: "E2E test report" + large_files: true + comment_mode: off + files: | + tests/e2e/reports/e2e_*.xml diff --git a/Makefile b/Makefile index f3eeb8ec6..c62145ed6 100644 --- a/Makefile +++ b/Makefile @@ -116,7 +116,7 @@ test-e2e: bin/ginkgo # Run e2e tests parallel test-e2e-parallel: bin/ginkgo - ginkgo -p -v --tags e2e tests/e2e + ginkgo -v -p --tags e2e tests/e2e # Compile test binary for debugging build-e2e-debug-binary: diff --git a/tests/e2e/const.go b/tests/e2e/const.go index 3858f5647..cbb97c399 100644 --- a/tests/e2e/const.go +++ b/tests/e2e/const.go @@ -34,10 +34,13 @@ const ( crdKind = "customresourcedefinitions.apiextensions.k8s.io" kafkaKind = "kafkaclusters.kafka.banzaicloud.io" kafkaTopicKind = "kafkatopics.kafka.banzaicloud.io" + kafkaUserKind = "kafkausers.kafka.banzaicloud.io" kafkaClusterName = "kafka" + kafkaUserName = "test-user" testExternalTopicName = "topic-test-external" testInternalTopicName = "topic-test-internal" + defaultTLSSecretName = "test-secret" kcatPodName = "kcat" zookeeperKind = "zookeeperclusters.zookeeper.pravega.io" zookeeperClusterName = "zookeeper-server" @@ -48,8 +51,10 @@ const ( defaultDeletionTimeout = 20 * time.Second defaultPodReadinessWaitTime = 10 * time.Second defaultTopicCreationWaitTime = 10 * time.Second + defaultUserCreationWaitTime = 10 * time.Second kafkaClusterCreateTimeout = 500 * time.Second kafkaClusterResourceCleanupTimeout = 120 * time.Second + kcatDeleetionTimeout = 40 * time.Second zookeeperClusterCreateTimeout = 4 * time.Minute zookeeperClusterResourceCleanupTimeout = 60 * time.Second externalConsumerTimeout = 5 * time.Second @@ -59,6 +64,7 @@ const ( kcatPodTemplate = "templates/kcat.yaml.tmpl" kafkaTopicTemplate = "templates/topic.yaml.tmpl" + kafkaUserTemplate = "templates/user.yaml.tmpl" zookeeperClusterTemplate = "templates/zookeeper_cluster.yaml.tmpl" kubectlNotFoundErrorMsg = "NotFound" diff --git a/tests/e2e/helm.go b/tests/e2e/helm.go index 652339eba..595428c31 100644 --- a/tests/e2e/helm.go +++ b/tests/e2e/helm.go @@ -164,7 +164,7 @@ func (helmDescriptor *helmDescriptor) installHelmChart(kubectlOptions k8s.Kubect fixedArguments := []string{ "--create-namespace", "--atomic", - "--debug", + //"--debug", } helmChartNameOrLocalPath := helmDescriptor.ChartName diff --git a/tests/e2e/kafka.go b/tests/e2e/kafka.go index 7553913fe..835fc8cd3 100644 --- a/tests/e2e/kafka.go +++ b/tests/e2e/kafka.go @@ -15,6 +15,9 @@ package e2e import ( + "context" + "time" + "github.com/gruntwork-io/terratest/modules/k8s" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -48,3 +51,36 @@ func requireDeployingKafkaTopic(kubectlOptions k8s.KubectlOptions, topicName str }) } + +// requireDeleteKafkaUser deletes a kafkaUser resource by name +func requireDeleteKafkaUser(kubectlOptions k8s.KubectlOptions, userName string) { + It("Deleting KafkaUser CR", func() { + err := deleteK8sResource(kubectlOptions, defaultDeletionTimeout, kafkaUserKind, "", userName) + Expect(err).NotTo(HaveOccurred()) + }) +} + +// requireDeployingKafkaUser creates a KafkaUser resource from a template +func requireDeployingKafkaUser(kubectlOptions k8s.KubectlOptions, userName string, tlsSecretName string) { + It("Deploying KafkaUser CR", func() { + templateParameters := map[string]interface{}{ + "Name": userName, + "Namespace": kubectlOptions.Namespace, + "ClusterName": kafkaClusterName, + } + if tlsSecretName != "" { + templateParameters["TLSSecretName"] = tlsSecretName + } + + err := applyK8sResourceFromTemplate(kubectlOptions, + kafkaUserTemplate, + templateParameters, + ) + Expect(err).ShouldNot(HaveOccurred()) + + Eventually(context.Background(), func() bool { + return isExistingK8SResource(kubectlOptions, "Secret", tlsSecretName) + }, defaultUserCreationWaitTime, 3*time.Second).Should(Equal(true)) + }) + +} diff --git a/tests/e2e/kcat.go b/tests/e2e/kcat.go index 784a723b2..8d39f9e5c 100644 --- a/tests/e2e/kcat.go +++ b/tests/e2e/kcat.go @@ -23,16 +23,21 @@ import ( // consumingMessagesInternally consuming messages based on parameters from Kafka cluster. // It returns messages in string slice. -func consumingMessagesInternally(kubectlOptions k8s.KubectlOptions, kcatPodName string, internalKafkaAddress string, topicName string) (string, error) { +func consumingMessagesInternally(kubectlOptions k8s.KubectlOptions, kcatPodName string, internalKafkaAddress string, topicName string, tlsMode bool) (string, error) { By(fmt.Sprintf("Consuming messages from internalKafkaAddress: '%s' topicName: '%s'", internalKafkaAddress, topicName)) + kcatTLSParameters := "" + if tlsMode { + kcatTLSParameters += "-X security.protocol=SSL -X ssl.key.location=/ssl/certs/tls.key -X ssl.certificate.location=/ssl/certs/tls.crt -X ssl.ca.location=/ssl/certs/ca.crt" + } + consumedMessages, err := k8s.RunKubectlAndGetOutputE(GinkgoT(), k8s.NewKubectlOptions(kubectlOptions.ContextName, kubectlOptions.ConfigPath, ""), "exec", kcatPodName, "-n", kubectlOptions.Namespace, "--", - "/bin/sh", "-c", fmt.Sprintf("kcat -L -b %s -t %s -e -C ", internalKafkaAddress, topicName), + "/bin/sh", "-c", fmt.Sprintf("kcat -L -b %s %s -t %s -e -C ", internalKafkaAddress, kcatTLSParameters, topicName), ) if err != nil { @@ -43,16 +48,21 @@ func consumingMessagesInternally(kubectlOptions k8s.KubectlOptions, kcatPodName } // producingMessagesInternally produces messages based on the parameters into kafka cluster. -func producingMessagesInternally(kubectlOptions k8s.KubectlOptions, kcatPodName string, internalKafkaAddress string, topicName string, message string) error { +func producingMessagesInternally(kubectlOptions k8s.KubectlOptions, kcatPodName string, internalKafkaAddress string, topicName string, message string, tlsMode bool) error { By(fmt.Sprintf("Producing messages: '%s' to internalKafkaAddress: '%s' topicName: '%s'", message, internalKafkaAddress, topicName)) + kcatTLSParameters := "" + if tlsMode { + kcatTLSParameters += "-X security.protocol=SSL -X ssl.key.location=/ssl/certs/tls.key -X ssl.certificate.location=/ssl/certs/tls.crt -X ssl.ca.location=/ssl/certs/ca.crt" + } + _, err := k8s.RunKubectlAndGetOutputE(GinkgoT(), k8s.NewKubectlOptions(kubectlOptions.ContextName, kubectlOptions.ConfigPath, ""), "exec", kcatPodName, "-n", kubectlOptions.Namespace, "--", - "/bin/sh", "-c", fmt.Sprintf("echo %s | kcat -L -b %s -t %s -P", - message, internalKafkaAddress, topicName), + "/bin/sh", "-c", fmt.Sprintf("echo %s | kcat -L -b %s %s -t %s -P", + message, internalKafkaAddress, kcatTLSParameters, topicName), ) return err diff --git a/tests/e2e/koperator_suite_test.go b/tests/e2e/koperator_suite_test.go index fbe8a0597..5adaa7b01 100644 --- a/tests/e2e/koperator_suite_test.go +++ b/tests/e2e/koperator_suite_test.go @@ -41,8 +41,21 @@ func beforeSuite() (tests.TestPool, error) { return nil, err } + // k8sCluster, err := tests.New + // if err != nil { + // return nil, err + // } + //k8sClusterPool.AddK8sClusters(k8sCluster) + classifier := tests.NewClassifier(k8sClusterPool, testCaseInstall) + //classifier := tests.MockTestsMinimal() + //classifier := tests.MockTestsProvider() + //classifier := tests.MockTestsProviderMoreTestsThenProvider() + //classifier := tests.MockTestsVersionOne() + // classifier := tests.MockTestsVersion() + //classifier := tests.MockTestsComplete() + var testPool tests.TestPool testStrategy := viper.GetString(config.Tests.TestStrategy) @@ -65,7 +78,7 @@ func TestKoperator(t *testing.T) { err = runGinkgoTests(t) if err != nil { - log.Printf("Koperator e2e start failed: %v", err) + t.Errorf("Koperator E2E start failed: %v", err) } } @@ -96,7 +109,7 @@ func runGinkgoTests(t *testing.T) error { } // Protection against too long test suites if testSuiteDuration > maxTimeout { - return fmt.Errorf("tests estimated duration: '%fmin' bigger then maxTimeout: '%fmin'", testSuiteDuration.Seconds(), maxTimeout.Seconds()) + return fmt.Errorf("tests estimated duration: '%dsec' bigger then maxTimeout: '%dsec'", int(testSuiteDuration.Seconds()), int(maxTimeout.Seconds())) } // Calculated timeout can be overran with the specified time length @@ -125,6 +138,18 @@ func runGinkgoTests(t *testing.T) error { } +// Maybe this can be used to get more debug information into the test report. +// var _ = ReportAfterEach(func(report SpecReport) { +// fmt.Fprintf(os.Stderr, "SPEC REPORT: %s | %s\nFAILURE MESSAGE: %s\n OUTPUT: %s\n", report.State, report.FullText(), report.FailureMessage(), report.CapturedStdOutErr) +// }) + +// Root Describe container +var _ = Describe("", func() { + // In the root container there is no Ordered decorator + // ginkgo execute parallel the generated tests by K8sClusters + testPool.BuildParallelByK8sCluster() +}) + func sectionStringDelimiter() string { delimiter := "" for i := 0; i < 100; i++ { @@ -153,49 +178,3 @@ func createTestReportFile() error { }) return nil } - -// Root Describe container -var _ = Describe(time.Now().Format(time.RFC3339), func() { - // In the root container there is no Ordered decorator - // ginkgo execute parallel the generated tests by K8sClusters - testPool.BuildParallelByK8sCluster() -}) - -func MockK8sClusterPool() tests.K8sClusterPool { - k8sClusterPool := tests.K8sClusterPool{} - k8sClusterPool.AddK8sClusters( - tests.NewMockK8sCluster( - "testContextPath1", - "testContextName1", - true, - "1.24", - "provider1", - "clusterID1", - ), - tests.NewMockK8sCluster( - "testContextPath2", - "testContextName2", - true, - "1.24", - "provider2", - "clusterID2", - ), - tests.NewMockK8sCluster( - "testContextPath3", - "testContextName3", - true, - "1.24", - "provider3", - "clusterID3", - ), - tests.NewMockK8sCluster( - "testContextPath4", - "testContextName4", - true, - "1.26", - "provider4", - "clusterID4", - ), - ) - return k8sClusterPool -} diff --git a/tests/e2e/pkg/common/config/config.go b/tests/e2e/pkg/common/config/config.go index 112f00f2b..ee167d03e 100644 --- a/tests/e2e/pkg/common/config/config.go +++ b/tests/e2e/pkg/common/config/config.go @@ -31,11 +31,11 @@ const ( const ( defaultReportDir = "reports" - defaultCreateTestReportFile = "false" + defaultCreateTestReportFile = "true" defaultMaxTimeout = "30m" defaultAllowedOverrunDuration = "5m" defaultTestStrategy = TestStrategyMinimal - defaultKubeConfigDirectoryPath = "platforms/kind/kubeconfigs" + defaultKubeConfigDirectoryPath = "kubeconfigs" ) type TestsType struct { diff --git a/tests/e2e/pkg/tests/mocks.go b/tests/e2e/pkg/tests/mocks.go new file mode 100644 index 000000000..c83c3a6a6 --- /dev/null +++ b/tests/e2e/pkg/tests/mocks.go @@ -0,0 +1,264 @@ +// Copyright © 2023 Cisco Systems, Inc. and/or its affiliates +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tests + +import ( + "github.com/gruntwork-io/terratest/modules/k8s" + . "github.com/onsi/ginkgo/v2" + + //. "github.com/onsi/gomega" + "time" +) + +// MockTestsMinimal: +// 3 different provider 2 different version 3 different K8s cluster with 2 tests +// Expected: 2 testCase 1 testCase on any available K8sCluster +// 1x2 + 1x3 = 5 test all-together +// Runtime parallel: 1x(5) = 5sec (time of the longest testCase) +func MockTestsMinimal() Classifier { + k8sClusterPool := K8sClusterPool{} + k8sClusterPool.AddK8sClusters( + NewMockK8sCluster( + "testContextPath1", + "testContextName1", + "1.24", + "provider1", + "clusterID1", + ), + NewMockK8sCluster( + "testContextPath2", + "testContextName2", + "1.25", + "provider2", + "clusterID2", + ), + NewMockK8sCluster( + "testContextPath3", + "testContextName3", + "1.24", + "provider3", + "clusterID3", + ), + ) + return NewClassifier(k8sClusterPool, mockTest1, mockTest2) +} + +// MockTestsProvider: +// 3 different provider 3 different K8s cluster with 2 tests +// Expected: 6 testCase 2 testCase on every provider +// 3x2 + 3x3 = 15 test all-together +// Runtime parallel: 3x(3) = 9sec (time of the longest testCase) +func MockTestsProvider() Classifier { + k8sClusterPool := K8sClusterPool{} + k8sClusterPool.AddK8sClusters( + NewMockK8sCluster( + "testContextPath1", + "testContextName1", + "1.24", + "provider1", + "clusterID1", + ), + NewMockK8sCluster( + "testContextPath2", + "testContextName2", + "1.24", + "provider2", + "clusterID2", + ), + NewMockK8sCluster( + "testContextPath3", + "testContextName3", + "1.24", + "provider3", + "clusterID3", + ), + ) + return NewClassifier(k8sClusterPool, mockTest1, mockTest2) +} + +// MockTestsProviderMoreTestsThenProvider: +// 2 different provider 2 different K8s cluster with 3 tests +// Expected: 6 testCase 3 testCase on every provider +// 2x2 + 2x2 + 2x3 = 14 test all-together +// Runtime parallel: 4 + 4 + 5 = 13 +func MockTestsProviderMoreTestsThenProvider() Classifier { + k8sClusterPool := K8sClusterPool{} + k8sClusterPool.AddK8sClusters( + NewMockK8sCluster( + "testContextPath1", + "testContextName1", + "1.24", + "provider1", + "clusterID1", + ), + NewMockK8sCluster( + "testContextPath2", + "testContextName2", + "1.24", + "provider2", + "clusterID2", + ), + ) + return NewClassifier(k8sClusterPool, mockTest1, mockTest2, mockTest3) +} + +// MockTestsVersionOne: +// no different version 2 different K8s cluster with 2 tests +// Expected: 2 testCase -> 1 testCase on each K8sCluster +// 2x2 2x3 = 10 test all-together +// Runtime parallel: 1x5 = 5 +func MockTestsVersionOne() Classifier { + k8sClusterPool := K8sClusterPool{} + k8sClusterPool.AddK8sClusters( + NewMockK8sCluster( + "testContextPath1", + "testContextName1", + "1.24", + "provider1", + "clusterID1", + ), + NewMockK8sCluster( + "testContextPath2", + "testContextName2", + "1.24", + "provider2", + "clusterID2", + ), + ) + return NewClassifier(k8sClusterPool, mockTest1, mockTest2) +} + +// MockTestsVersion: +// 2 different version 3 different K8s cluster with 2 tests +// Expected: 4 testCase -> 2 testCase on each version +// 2x2 2x3 = 10 test all-together +// Runtime parallel: 4 + 5 = 9 +func MockTestsVersion() Classifier { + k8sClusterPool := K8sClusterPool{} + k8sClusterPool.AddK8sClusters( + NewMockK8sCluster( + "testContextPath1", + "testContextName1", + "1.24", + "provider1", + "clusterID1", + ), + NewMockK8sCluster( + "testContextPath2", + "testContextName2", + "1.24", + "provider2", + "clusterID2", + ), + NewMockK8sCluster( + "testContextPath4", + "testContextName4", + "1.26", + "provider3", + "clusterID4", + ), + ) + return NewClassifier(k8sClusterPool, mockTest1, mockTest2) +} + +// MockTestsVersion: +// 2 different version 2 different version 4 K8s cluster with 2 tests +// Expected: 4 testCase -> 2 testCase on each version +// 2x2 2x3 = 10 test all-together +// Runtime parallel: 4 + 5 = 9 +func MockTestsComplete() Classifier { + k8sClusterPool := K8sClusterPool{} + k8sClusterPool.AddK8sClusters( + NewMockK8sCluster( + "testContextPath1", + "testContextName1", + "1.24", + "provider1", + "clusterID1", + ), + NewMockK8sCluster( + "testContextPath2", + "testContextName2", + "1.24", + "provider2", + "clusterID2", + ), + NewMockK8sCluster( + "testContextPath3", + "testContextName3", + "1.25", + "provider3", + "clusterID3", + ), + NewMockK8sCluster( + "testContextPath4", + "testContextName4", + "1.25", + "provider3", + "clusterID4", + ), + ) + return NewClassifier(k8sClusterPool, mockTest1, mockTest2) +} + +var mockTest1 = TestCase{ + TestDuration: 4 * time.Second, + TestName: "MockTest1", + TestFn: testMockTest1, +} + +func testMockTest1(kubectlOptions k8s.KubectlOptions) { + It("MockTest1-1", func() { + time.Sleep(time.Second * 1) + }) + It("MockTest1-2", func() { + time.Sleep(time.Second * 1) + }) +} + +var mockTest2 = TestCase{ + TestDuration: 5 * time.Second, + TestName: "MockTest2", + TestFn: testMockTest2, +} + +func testMockTest2(kubectlOptions k8s.KubectlOptions) { + It("MockTest2-1", func() { + time.Sleep(time.Second * 1) + }) + It("MockTest2-2", func() { + time.Sleep(time.Second * 1) + //Expect(0).Should(Equal(1)) + AddReportEntry("Output:", CurrentSpecReport().CapturedGinkgoWriterOutput) + }) + It("MockTest2-3", func() { + time.Sleep(time.Second * 1) + }) +} + +var mockTest3 = TestCase{ + TestDuration: 4 * time.Second, + TestName: "MockTest3", + TestFn: testMockTest3, +} + +func testMockTest3(kubectlOptions k8s.KubectlOptions) { + It("MockTest3-1", func() { + time.Sleep(time.Second * 2) + }) + It("MockTest3-2", func() { + time.Sleep(time.Second * 2) + }) +} diff --git a/tests/e2e/pkg/tests/tests.go b/tests/e2e/pkg/tests/tests.go index 10b419bd3..9b10f7b5b 100644 --- a/tests/e2e/pkg/tests/tests.go +++ b/tests/e2e/pkg/tests/tests.go @@ -19,8 +19,6 @@ import ( "encoding/hex" "errors" "fmt" - "log" - "math/rand" "os" "path/filepath" "sort" @@ -33,23 +31,25 @@ import ( "github.com/onsi/ginkgo/v2/dsl/decorators" "github.com/spf13/viper" "golang.org/x/exp/maps" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -type TestPool []TestType +// []testCase +// | +// Classifier ==> TestPool <-- []Test <-- K8sCluster <-- ClusterInfo +// ^ ^ +// | | +// K8sClusterPool testCase + +type TestPool []Test func (tests TestPool) Equal(other TestPool) bool { if len(tests) != len(other) { return false } - sort.SliceStable(tests, func(i, j int) bool { - return tests[i].less(tests[j]) - }) - sort.SliceStable(other, func(i, j int) bool { - return other[i].less(other[j]) - }) + tests.Sort() + other.Sort() for i := range tests { if !tests[i].equal(other[i]) { @@ -61,34 +61,53 @@ func (tests TestPool) Equal(other TestPool) bool { func (tests TestPool) PoolInfo() string { testsByContextName := tests.getTestsByContextName() + testsByProviders := tests.getTestsByProviders() + testsByVersions := tests.getTestsByVersions() return fmt.Sprintf(` NumberOfTests: %d NumberOfK8sClusters: %d -NumberOfK8sVersions: -NumberOfK8sProviders: +K8sVersions: %v +K8sProviders: %v TestK8sMapping: %v TestStrategy: %s ExpectedDurationSerial: %f ExpectedDurationParallel: %f -`, len(tests), len(maps.Keys(testsByContextName)), tests, viper.GetString(config.Tests.TestStrategy), tests.GetTestSuiteDurationSerial().Seconds(), tests.GetTestSuiteDurationParallel().Seconds()) +`, len(tests), len(maps.Keys(testsByContextName)), maps.Keys(testsByVersions), maps.Keys(testsByProviders), + tests, viper.GetString(config.Tests.TestStrategy), tests.GetTestSuiteDurationSerial().Seconds(), + tests.GetTestSuiteDurationParallel().Seconds()) } func (tests TestPool) BuildParallelByK8sCluster() { - testsByClusterID := tests.getTestsByClusterID() + testsByClusterID := tests.getSortedTestsByClusterID() + testsByClusterIDKeys := sortedKeys(testsByClusterID) + describeID := 0 // Because of the "Ordered" decorator testCases inside that container will run each after another // so this K8s cluster is dedicated for tests for serial test execution - for clusterID, tests := range testsByClusterID { - Describe(clusterID, decorators.Label(fmt.Sprintf("clusterID:%s", clusterID)), Ordered, func() { - for _, t := range tests { - t.Run() + for _, clusterID := range testsByClusterIDKeys { + describeID += 1 + tests := testsByClusterID[clusterID] + // Describe name has to be unique and consistent between processes for proper parallelization + Describe(fmt.Sprintf("%d:", describeID), decorators.Label(fmt.Sprintf("K8sID:%s", clusterID)), Ordered, func() { + for _, test := range tests { + test.Run() } }) } } -func (tests TestPool) getTestsByClusterID() map[string][]TestType { - testsByClusterID := make(map[string][]TestType) +func (tests TestPool) Sort() { + sort.SliceStable(tests, func(i, j int) bool { + return tests[i].less(tests[j]) + }) +} + +func (tests TestPool) getSortedTestsByClusterID() map[string][]Test { + testsByClusterID := make(map[string][]Test) + // Need to be sorted to achieve test specs tree consistency between processes + // otherwise it can happen that specs order will be different for each process + tests.Sort() + for _, test := range tests { testsByClusterID[test.k8sCluster.clusterInfo.clusterID] = append(testsByClusterID[test.k8sCluster.clusterInfo.clusterID], test) } @@ -96,8 +115,8 @@ func (tests TestPool) getTestsByClusterID() map[string][]TestType { return testsByClusterID } -func (tests TestPool) getTestsByContextName() map[string][]TestType { - testsByContextName := make(map[string][]TestType) +func (tests TestPool) getTestsByContextName() map[string][]Test { + testsByContextName := make(map[string][]Test) for _, test := range tests { testsByContextName[test.k8sCluster.kubectlOptions.ContextName] = append(testsByContextName[test.k8sCluster.kubectlOptions.ContextName], test) } @@ -105,8 +124,28 @@ func (tests TestPool) getTestsByContextName() map[string][]TestType { return testsByContextName } +func (tests TestPool) getTestsByVersions() map[string][]Test { + testsByVersions := make(map[string][]Test) + for _, test := range tests { + testsByVersions[test.k8sCluster.clusterInfo.version] = append(testsByVersions[test.k8sCluster.clusterInfo.version], test) + } + + return testsByVersions +} + +func (tests TestPool) getTestsByProviders() map[string][]Test { + testsByProviders := make(map[string][]Test) + for _, test := range tests { + testsByProviders[test.k8sCluster.clusterInfo.provider] = append(testsByProviders[test.k8sCluster.clusterInfo.provider], test) + } + + return testsByProviders +} + +// GetTestSuiteDurationParallel calculate the expected duration of the tests when +// suite is executed in parallel func (tests TestPool) GetTestSuiteDurationParallel() time.Duration { - testsByClusterID := tests.getTestsByClusterID() + testsByClusterID := tests.getSortedTestsByClusterID() var max time.Duration for _, tests := range testsByClusterID { @@ -122,6 +161,8 @@ func (tests TestPool) GetTestSuiteDurationParallel() time.Duration { return max } +// GetTestSuiteDurationParallel calculate the expected duration of the tests when +// suite is executed in serial func (tests TestPool) GetTestSuiteDurationSerial() time.Duration { var allDuration time.Duration for _, test := range tests { @@ -130,6 +171,7 @@ func (tests TestPool) GetTestSuiteDurationSerial() time.Duration { return allDuration } +// NewClassifier creates a test classifier from K8sClusterPool and TestCases. func NewClassifier(k8sClusterPool K8sClusterPool, testCases ...TestCase) Classifier { return Classifier{ k8sClusterPool: k8sClusterPool, @@ -137,17 +179,20 @@ func NewClassifier(k8sClusterPool K8sClusterPool, testCases ...TestCase) Classif } } +// Classifier makes pairs from testCases to K8sClusters based on the specified test strategy type Classifier struct { k8sClusterPool K8sClusterPool testCases []TestCase } +// Minimal creates a TestPool based on minimal test strategy. +// It means running every test case exactly once each on any provider and +// any version of the K8sCluster pool (the ones first available to use). func (t Classifier) Minimal() TestPool { - t.k8sClusterPool.mixPool() - tests := make([]TestType, 0, len(t.testCases)) + tests := make([]Test, 0, len(t.testCases)) for i, testCase := range t.testCases { - tests = append(tests, TestType{ + tests = append(tests, Test{ testCase: testCase, k8sCluster: t.k8sClusterPool.k8sClusters[i%len(t.k8sClusterPool.k8sClusters)], }) @@ -155,14 +200,16 @@ func (t Classifier) Minimal() TestPool { return tests } +// VersionComplete creates a TestPool based on version complete test strategy. +// It means running every test case exactly len(versions) times each, +// using any provider and every version once per case of the K8sCluster pool. func (t Classifier) VersionComplete() TestPool { - t.k8sClusterPool.mixPool() k8sClustersByVersion := t.k8sClusterPool.getByVersions() tests := make(TestPool, 0, len(t.testCases)*len(maps.Keys(k8sClustersByVersion))) for _, k8sClusters := range k8sClustersByVersion { for i, testCase := range t.testCases { - tests = append(tests, TestType{ + tests = append(tests, Test{ testCase: testCase, k8sCluster: k8sClusters[i%len(k8sClusters)], }) @@ -171,14 +218,16 @@ func (t Classifier) VersionComplete() TestPool { return tests } +// ProviderComplete creates a TestPool based on provider complete test strategy. +// It means running every test case exactly len(providers) times each, +// using any version and every provider once per case of the K8sCluster pool. func (t Classifier) ProviderComplete() TestPool { - t.k8sClusterPool.mixPool() k8sClustersByProviders := t.k8sClusterPool.getByProviders() tests := make(TestPool, 0, len(t.testCases)*len(maps.Keys(k8sClustersByProviders))) for _, k8sClusters := range k8sClustersByProviders { for i, testCase := range t.testCases { - tests = append(tests, TestType{ + tests = append(tests, Test{ testCase: testCase, k8sCluster: k8sClusters[i%len(k8sClusters)], }) @@ -187,15 +236,17 @@ func (t Classifier) ProviderComplete() TestPool { return tests } +// Complete creates a TestPool based on complete test strategy. +// It means running every test case exactly len(providers)*len(versions) times each, +// using every provider and every version combination once of the pool. func (t Classifier) Complete() TestPool { - t.k8sClusterPool.mixPool() k8sClustersByProvidersVersions := t.k8sClusterPool.getByProvidersVersions() var tests TestPool for _, byVersions := range k8sClustersByProvidersVersions { for _, k8sClusters := range byVersions { for i, testCase := range t.testCases { - tests = append(tests, TestType{ + tests = append(tests, Test{ testCase: testCase, k8sCluster: k8sClusters[i%len(k8sClusters)], }) @@ -205,18 +256,17 @@ func (t Classifier) Complete() TestPool { return tests } +// K8sClusterPool contains the K8sClusters type K8sClusterPool struct { k8sClusters []K8sCluster } -func (t *K8sClusterPool) mixPool() { - rand.Shuffle(len(t.k8sClusters), func(i, j int) { t.k8sClusters[i], t.k8sClusters[j] = t.k8sClusters[j], t.k8sClusters[i] }) -} - +// AddK8sClusters add a K8sCluster(s) into the pool func (t *K8sClusterPool) AddK8sClusters(cluster ...K8sCluster) { t.k8sClusters = append(t.k8sClusters, cluster...) } +// FeedFomDirectory creates K8sClusters from kubeconfigs in the specified directory and add them into the pool func (t *K8sClusterPool) FeedFomDirectory(kubeConfigDirectoryPath string) error { files, err := os.ReadDir(kubeConfigDirectoryPath) if err != nil { @@ -229,15 +279,9 @@ func (t *K8sClusterPool) FeedFomDirectory(kubeConfigDirectoryPath string) error for _, file := range files { if file != nil { - //kubeContext, err := common.GetDefaultKubeContext(file.Name()) - // if err != nil { - // err := fmt.Errorf("could not fetch default kubeContext from file: %s, err: %w", file.Name(), err) - // log.Print(err) - // continue - // } kubectlPath := filepath.Join(kubeConfigDirectoryPath, file.Name()) + k8sClusters, err := NewK8sClustersFromParams(kubectlPath) - k8sClusters, err := NewK8sClustersFromParams(kubectlPath, true) if err != nil { return fmt.Errorf("could not get K8sClusters from file '%s' err: %w", kubectlPath, err) } @@ -282,38 +326,47 @@ func (t K8sClusterPool) getByProvidersVersions() map[string]map[string][]K8sClus return byProvidersVersions } +// TestCase is a representation of an E2E test case +// TestDuration specifies the expected length of the test +// TestName is the name of the test +// TestFn specifies the test function type TestCase struct { TestDuration time.Duration TestName string TestFn func(kubectlOptions k8s.KubectlOptions) } -func NewTest(testCase TestCase, k8sCluster K8sCluster) TestType { +// NewTest creates a Test from a TestCase and from a K8sCluster +// testID for Test is generated automatically based on testName and K8s clusterInfo +func NewTest(testCase TestCase, k8sCluster K8sCluster) Test { testIDseed := fmt.Sprintf("%v%v", testCase.TestName, k8sCluster.clusterInfo) hash := md5.Sum([]byte(testIDseed)) testID := hex.EncodeToString(hash[:5]) - return TestType{ + return Test{ testID: testID, testCase: testCase, k8sCluster: k8sCluster, } } -type TestType struct { +// Test represents an E2E test. +// It is a combination of a testCase and a k8sCluster +type Test struct { testID string testCase TestCase k8sCluster K8sCluster } -func (t TestType) equal(test TestType) bool { +func (t Test) equal(test Test) bool { return t.TestID() == test.TestID() } -func (t TestType) less(test TestType) bool { +func (t Test) less(test Test) bool { return t.TestID() < test.TestID() } -func (t *TestType) TestID() string { +// TestID return the test UID based on testName and K8sCluster +func (t *Test) TestID() string { if t.testID == "" { text := fmt.Sprintf("%v%v", t.testCase.TestName, t.k8sCluster) hash := md5.Sum([]byte(text)) @@ -322,21 +375,17 @@ func (t *TestType) TestID() string { return t.testID } -func (t TestType) String() string { +func (t Test) String() string { return fmt.Sprintf("%s(%s)", t.testCase.TestName, t.k8sCluster) } -// Run encapsulates the testCase with K8s health check and details into a describe container +// Run encapsulates the testCase in a Describe container with +// K8s cluster health check and testID label. // When K8s cluster is not available the further tests are skipped -func (t TestType) Run() { +func (t Test) Run() { Describe(fmt.Sprint(t), Ordered, decorators.Label(fmt.Sprintf("testID:%s", t.TestID())), func() { - var err error kubectlOptions, err := t.k8sCluster.KubectlOptions() - //TODO (marbarta): this should be removed after testing - if err != nil { - kubectlOptions, err = common.KubectlOptionsForCurrentContext() - } if err == nil { _, err = k8s.ListPodsE( @@ -351,25 +400,47 @@ func (t TestType) Run() { // Fail(fmt.Sprintf("skipping testCase... K8s cluster connection problem: %v", err)) // } // }) - t.testCase.TestFn(kubectlOptions) }) } -func newK8sClusterInfo(kubectlOptions k8s.KubectlOptions) (K8sClusterInfo, error) { - // TODO (marbarta): placeholder to get retrieve cluster information from kubeconfig - return K8sClusterInfo{ - workerCount: 3, - multiAZ: false, - clusterID: "testCLusterID", - provider: "testProvider", - version: "1.25", +func newK8sClusterInfo(kubectlOptions k8s.KubectlOptions) (k8sClusterInfo, error) { + version, err := k8s.GetKubernetesClusterVersionWithOptionsE(GinkgoT(), &kubectlOptions) + if err != nil { + return k8sClusterInfo{}, fmt.Errorf("could not get version: %w", err) + } + version, err = versionIdentifier(version) + if err != nil { + return k8sClusterInfo{}, fmt.Errorf("could not get version: %w", err) + } + kubeSystemNS, err := k8s.GetNamespaceE(GinkgoT(), &kubectlOptions, "kube-system") + if err != nil { + return k8sClusterInfo{}, fmt.Errorf("could not get UUID: %w", err) + } + nodes, err := k8s.GetNodesE(GinkgoT(), &kubectlOptions) + if err != nil { + return k8sClusterInfo{}, fmt.Errorf("could not get node count: %w", err) + } + if len(nodes) == 0 { + return k8sClusterInfo{}, errors.New("node count is: 0") + } + + provider, err := providerIdentifier(nodes[0]) + if err != nil { + return k8sClusterInfo{}, fmt.Errorf("could not provider: %w", err) + } + + return k8sClusterInfo{ + workerCount: len(nodes), + clusterID: string(kubeSystemNS.UID), + provider: string(provider), + version: version, }, nil } -type K8sClusterInfo struct { +// k8sClusterInfo contains K8s cluster informations about K8sCluster +type k8sClusterInfo struct { workerCount int - multiAZ bool clusterID string provider string version string @@ -377,26 +448,45 @@ type K8sClusterInfo struct { // NewK8sCluster retrieve the K8s cluster information and creates the K8sCluster resource // When K8s cluster information is not available it return error -func NewK8sCluster(kubectlOptions k8s.KubectlOptions, reusable bool) (K8sCluster, error) { +func NewK8sCluster(kubectlOptions k8s.KubectlOptions) (K8sCluster, error) { clusterInfo, err := newK8sClusterInfo(kubectlOptions) if err != nil { - return K8sCluster{}, fmt.Errorf("could not get clusterInfo: %w", err) + return K8sCluster{}, fmt.Errorf("could not get clusterInfo for K8s cluster (path: '%s' context: '%s') err: %w", + kubectlOptions.ConfigPath, kubectlOptions.ContextName, err) } return K8sCluster{ clusterInfo: clusterInfo, - reusable: reusable, kubectlOptions: kubectlOptions, }, nil } -func NewMockK8sCluster(kubeConfigPath, kubeContext string, reusable bool, version, provider, clusterID string) K8sCluster { +// NewK8sClusterWithProvider creates K8sCluster from kubeconfig path and kubecontext with the +// specified provider information +func NewK8sClusterWithProvider(kubectlOptions k8s.KubectlOptions, provider string) (K8sCluster, error) { + clusterInfo, err := newK8sClusterInfo(kubectlOptions) + if err != nil { + return K8sCluster{}, fmt.Errorf("could not get clusterInfo for K8s cluster (path: '%s' context: '%s') err: %w", + kubectlOptions.ConfigPath, kubectlOptions.ContextName, err) + } + + if provider != "" { + clusterInfo.provider = provider + } + + return K8sCluster{ + clusterInfo: clusterInfo, + kubectlOptions: kubectlOptions, + }, nil +} + +// NewMockK8sCluster creates K8sCluster objects from the provided argument for testing purpose +func NewMockK8sCluster(kubeConfigPath, kubeContext string, version, provider, clusterID string) K8sCluster { return K8sCluster{ - clusterInfo: K8sClusterInfo{ + clusterInfo: k8sClusterInfo{ version: version, provider: provider, clusterID: clusterID, }, - reusable: reusable, kubectlOptions: k8s.KubectlOptions{ ConfigPath: kubeConfigPath, ContextName: kubeContext, @@ -404,11 +494,16 @@ func NewMockK8sCluster(kubeConfigPath, kubeContext string, reusable bool, versio } } -func NewK8sClusterFromParams(kubectlConfigPath, kubectlContext string, reusable bool) (K8sCluster, error) { - return NewK8sCluster(*k8s.NewKubectlOptions(kubectlContext, kubectlConfigPath, ""), reusable) +// NewK8sClusterFromParams creates K8sCluster from kubectlConfigPath path and kubectlContext. +// Cluster information is fetched from the K8s cluster automatically +func NewK8sClusterFromParams(kubectlConfigPath, kubectlContext string) (K8sCluster, error) { + return NewK8sCluster(*k8s.NewKubectlOptions(kubectlContext, kubectlConfigPath, "")) } -func NewK8sClustersFromParams(kubectlConfigPath string, reusable bool) ([]K8sCluster, error) { +// NewK8sClustersFromParams creates K8sClusters based on kubeconfig path +// When multiple context is found in the kubeconfig it creates multiple K8sClusters for all of them +// Cluster information is fetched from the K8s cluster automatically +func NewK8sClustersFromParams(kubectlConfigPath string) ([]K8sCluster, error) { kubeContexts, err := common.GetKubeContexts(kubectlConfigPath) if err != nil { return nil, fmt.Errorf("could not get kubecontexts: %w", err) @@ -417,18 +512,18 @@ func NewK8sClustersFromParams(kubectlConfigPath string, reusable bool) ([]K8sClu var k8sClusters []K8sCluster for _, kubeContext := range kubeContexts { - k8sCluster, err := NewK8sCluster(*k8s.NewKubectlOptions(kubeContext, kubectlConfigPath, ""), reusable) + k8sCluster, err := NewK8sCluster(*k8s.NewKubectlOptions(kubeContext, kubectlConfigPath, "")) if err != nil { - err := fmt.Errorf("could not create K8sCluster: %w", err) - log.Print(err) - continue + return nil, err } k8sClusters = append(k8sClusters, k8sCluster) } return k8sClusters, nil } -func NewK8sClusterFromCurrentConfig(reusable bool) (K8sCluster, error) { +// NewK8sClusterFromCurrentConfig creates K8sCluster from the local KUBECONFIG env and it's current context +// Cluster information is fetched from the K8s cluster automatically +func NewK8sClusterFromCurrentConfig() (K8sCluster, error) { kubectlOptions, err := common.KubectlOptionsForCurrentContext() if err != nil { return K8sCluster{}, err @@ -436,19 +531,20 @@ func NewK8sClusterFromCurrentConfig(reusable bool) (K8sCluster, error) { clusterInfo, err := newK8sClusterInfo(kubectlOptions) if err != nil { - return K8sCluster{}, fmt.Errorf("could not get clusterInfo: %w", err) + return K8sCluster{}, fmt.Errorf("could not get clusterInfo for K8s cluster (path: '%s' context: '%s') err: %w", + kubectlOptions.ConfigPath, kubectlOptions.ContextName, err) } return K8sCluster{ clusterInfo: clusterInfo, - reusable: reusable, kubectlOptions: kubectlOptions, }, nil } +// K8sCluster represents a K8s cluster. +// It contains informations and access related configurations about K8s cluster type K8sCluster struct { - reusable bool - clusterInfo K8sClusterInfo + clusterInfo k8sClusterInfo kubectlOptions k8s.KubectlOptions } @@ -456,6 +552,7 @@ func (c K8sCluster) isKubectlOptionsFilled() bool { return c.kubectlOptions.ConfigPath != "" && c.kubectlOptions.ContextName != "" } +// KubectlOptions returns K8s access related configurations for kubectl func (c K8sCluster) KubectlOptions() (k8s.KubectlOptions, error) { if !c.isKubectlOptionsFilled() { return k8s.KubectlOptions{}, errors.New("kubectlOptions is unfilled") @@ -467,7 +564,5 @@ func (c K8sCluster) String() string { if !c.isKubectlOptionsFilled() { return "Unknown" } - // return fmt.Sprintf("Provider: %s Version: %s KubeContextPath: %s KubeContext: %s", - // c.clusterInfo.provider, c.clusterInfo.version, c.kubectlOptions.ConfigPath, c.kubectlOptions.ContextName) - return fmt.Sprintf(c.clusterInfo.clusterID) + return fmt.Sprintf(c.kubectlOptions.ContextName) } diff --git a/tests/e2e/pkg/tests/tests_test.go b/tests/e2e/pkg/tests/tests_test.go index 3f98b8e5e..b9309d4e6 100644 --- a/tests/e2e/pkg/tests/tests_test.go +++ b/tests/e2e/pkg/tests/tests_test.go @@ -34,16 +34,14 @@ func Test_Classifier_minimal(t *testing.T) { k8sClusterPool: K8sClusterPool{ k8sClusters: []K8sCluster{ { - reusable: true, - clusterInfo: K8sClusterInfo{ + clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.24", provider: "provider1", }, }, { - reusable: true, - clusterInfo: K8sClusterInfo{ + clusterInfo: k8sClusterInfo{ clusterID: "local2", version: "1.25", provider: "provider1", @@ -60,14 +58,14 @@ func Test_Classifier_minimal(t *testing.T) { }, }, }, - want: []TestType{ + want: []Test{ { testCase: TestCase{ TestName: "testCase1", }, k8sCluster: K8sCluster{ - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.24", provider: "provider1", @@ -79,8 +77,8 @@ func Test_Classifier_minimal(t *testing.T) { TestName: "testCase2", }, k8sCluster: K8sCluster{ - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local2", version: "1.25", provider: "provider1", @@ -113,7 +111,7 @@ func Test_Classifier_providerComplete(t *testing.T) { tests := []struct { name string fields fields - want []TestType + want []Test }{ { name: "simpleCase", @@ -121,16 +119,16 @@ func Test_Classifier_providerComplete(t *testing.T) { k8sClusterPool: K8sClusterPool{ k8sClusters: []K8sCluster{ { - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.24", provider: "provider1", }, }, { - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local2", version: "1.25", provider: "provider2", @@ -147,14 +145,14 @@ func Test_Classifier_providerComplete(t *testing.T) { }, }, }, - want: []TestType{ + want: []Test{ { testCase: TestCase{ TestName: "testCase1", }, k8sCluster: K8sCluster{ - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.24", provider: "provider1", @@ -166,8 +164,8 @@ func Test_Classifier_providerComplete(t *testing.T) { TestName: "testCase2", }, k8sCluster: K8sCluster{ - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.24", provider: "provider1", @@ -179,8 +177,8 @@ func Test_Classifier_providerComplete(t *testing.T) { TestName: "testCase1", }, k8sCluster: K8sCluster{ - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local2", version: "1.25", provider: "provider2", @@ -192,8 +190,8 @@ func Test_Classifier_providerComplete(t *testing.T) { TestName: "testCase2", }, k8sCluster: K8sCluster{ - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local2", version: "1.25", provider: "provider2", @@ -226,7 +224,7 @@ func Test_Classifier_versionComplete(t *testing.T) { tests := []struct { name string fields fields - want []TestType + want []Test }{ { name: "simpleCase", @@ -234,16 +232,16 @@ func Test_Classifier_versionComplete(t *testing.T) { k8sClusterPool: K8sClusterPool{ k8sClusters: []K8sCluster{ { - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.24", provider: "provider1", }, }, { - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.25", provider: "provider1", @@ -260,14 +258,14 @@ func Test_Classifier_versionComplete(t *testing.T) { }, }, }, - want: []TestType{ + want: []Test{ { testCase: TestCase{ TestName: "testCase1", }, k8sCluster: K8sCluster{ - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.24", provider: "provider1", @@ -279,8 +277,8 @@ func Test_Classifier_versionComplete(t *testing.T) { TestName: "testCase2", }, k8sCluster: K8sCluster{ - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.24", provider: "provider1", @@ -292,8 +290,8 @@ func Test_Classifier_versionComplete(t *testing.T) { TestName: "testCase1", }, k8sCluster: K8sCluster{ - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.24", provider: "provider1", @@ -305,8 +303,8 @@ func Test_Classifier_versionComplete(t *testing.T) { TestName: "testCase2", }, k8sCluster: K8sCluster{ - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.24", provider: "provider1", @@ -339,7 +337,7 @@ func Test_Classifier_complete(t *testing.T) { tests := []struct { name string fields fields - want []TestType + want []Test }{ { name: "simpleCase", @@ -347,16 +345,16 @@ func Test_Classifier_complete(t *testing.T) { k8sClusterPool: K8sClusterPool{ k8sClusters: []K8sCluster{ { - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.24", provider: "provider1", }, }, { - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local2", version: "1.25", provider: "provider2", @@ -373,14 +371,14 @@ func Test_Classifier_complete(t *testing.T) { }, }, }, - want: []TestType{ + want: []Test{ { testCase: TestCase{ TestName: "testCase1", }, k8sCluster: K8sCluster{ - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.24", provider: "provider1", @@ -392,8 +390,8 @@ func Test_Classifier_complete(t *testing.T) { TestName: "testCase2", }, k8sCluster: K8sCluster{ - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.24", provider: "provider1", @@ -405,8 +403,8 @@ func Test_Classifier_complete(t *testing.T) { TestName: "testCase1", }, k8sCluster: K8sCluster{ - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local2", version: "1.25", provider: "provider2", @@ -418,8 +416,8 @@ func Test_Classifier_complete(t *testing.T) { TestName: "testCase2", }, k8sCluster: K8sCluster{ - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local2", version: "1.25", provider: "provider2", @@ -434,32 +432,32 @@ func Test_Classifier_complete(t *testing.T) { k8sClusterPool: K8sClusterPool{ k8sClusters: []K8sCluster{ { - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.24", provider: "provider1", }, }, { - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local2", version: "1.25", provider: "provider2", }, }, { - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local3", version: "1.25", provider: "provider3", }, }, { - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local4", version: "1.25", provider: "provider3", @@ -476,14 +474,14 @@ func Test_Classifier_complete(t *testing.T) { }, }, }, - want: []TestType{ + want: []Test{ { testCase: TestCase{ TestName: "testCase1", }, k8sCluster: K8sCluster{ - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.24", provider: "provider1", @@ -495,8 +493,8 @@ func Test_Classifier_complete(t *testing.T) { TestName: "testCase2", }, k8sCluster: K8sCluster{ - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.24", provider: "provider1", @@ -508,8 +506,8 @@ func Test_Classifier_complete(t *testing.T) { TestName: "testCase1", }, k8sCluster: K8sCluster{ - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local2", version: "1.25", provider: "provider2", @@ -521,8 +519,8 @@ func Test_Classifier_complete(t *testing.T) { TestName: "testCase2", }, k8sCluster: K8sCluster{ - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local2", version: "1.25", provider: "provider2", @@ -534,8 +532,8 @@ func Test_Classifier_complete(t *testing.T) { TestName: "testCase1", }, k8sCluster: K8sCluster{ - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local3", version: "1.25", provider: "provider3", @@ -547,8 +545,8 @@ func Test_Classifier_complete(t *testing.T) { TestName: "testCase2", }, k8sCluster: K8sCluster{ - reusable: true, - clusterInfo: K8sClusterInfo{ + + clusterInfo: k8sClusterInfo{ clusterID: "local3", version: "1.25", provider: "provider3", diff --git a/tests/e2e/pkg/tests/utils.go b/tests/e2e/pkg/tests/utils.go new file mode 100644 index 000000000..c0c7a65cf --- /dev/null +++ b/tests/e2e/pkg/tests/utils.go @@ -0,0 +1,69 @@ +package tests + +import ( + "errors" + "fmt" + "regexp" + "sort" + + "golang.org/x/exp/constraints" + "golang.org/x/exp/maps" + corev1 "k8s.io/api/core/v1" +) + +type k8sProvider string + +const ( + k8sProviderGKE k8sProvider = "GKE" + k8sProviderPKE k8sProvider = "PKE" + k8sProviderAKS k8sProvider = "AKS" + k8sProviderKind k8sProvider = "Kind" + k8sProviderEKS k8sProvider = "EKS" + k8sProviderUnknown k8sProvider = "Unknown" + + k8sVersionRegexp = "^v[0-9].[0-9]+.[0-9]+" +) + +var k8sProviderRegexp = map[k8sProvider]string{ + k8sProviderGKE: "^gke-", + k8sProviderAKS: "^aks-", + k8sProviderPKE: ".compute.internal", //TODO (marbarta): this is not so good (is not unique PKE) + k8sProviderEKS: ".[0-9]-eks-", + k8sProviderKind: "^kind", +} + +func versionIdentifier(version string) (string, error) { + version = regexp.MustCompile(k8sVersionRegexp).FindString(version) + if version == "" { + return "", fmt.Errorf("K8s cluster version could not be recognized: '%s'", version) + } + return version, nil +} + +func providerIdentifier(node corev1.Node) (k8sProvider, error) { + checkString := node.Name + node.Status.NodeInfo.KubeletVersion + var foundProvider k8sProvider + for provider, regexpProvider := range k8sProviderRegexp { + if regexp.MustCompile(regexpProvider).MatchString(checkString) { + if foundProvider != "" { + return "", fmt.Errorf("K8s cluster provider name matched for multiple patterns: '%s' and '%s'", foundProvider, provider) + } + foundProvider = provider + } + } + if foundProvider == "" { + return "", errors.New("provider could not been identified") + } + return foundProvider, nil +} + +func sortedKeys[K constraints.Ordered, V any](m map[K]V) []K { + keys := make([]K, len(maps.Keys(m))) + i := 0 + for k := range m { + keys[i] = k + i++ + } + sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] }) + return keys +} diff --git a/tests/e2e/produce_consume.go b/tests/e2e/produce_consume.go index d54dee9fb..a656070e2 100644 --- a/tests/e2e/produce_consume.go +++ b/tests/e2e/produce_consume.go @@ -26,14 +26,19 @@ import ( ) // requireDeployingKcatPod deploys kcat pod form a template and checks the pod readiness -func requireDeployingKcatPod(kubectlOptions k8s.KubectlOptions, podName string) { +func requireDeployingKcatPod(kubectlOptions k8s.KubectlOptions, podName string, tlsSecretName string) { It("Deploying Kcat Pod", func() { + templateParameters := map[string]interface{}{ + "Name": kcatPodName, + "Namespace": kubectlOptions.Namespace, + } + if tlsSecretName != "" { + templateParameters["TLSSecretName"] = tlsSecretName + } + err := applyK8sResourceFromTemplate(kubectlOptions, kcatPodTemplate, - map[string]interface{}{ - "Name": kcatPodName, - "Namespace": kubectlOptions.Namespace, - }, + templateParameters, ) Expect(err).ShouldNot(HaveOccurred()) @@ -48,15 +53,16 @@ func requireDeployingKcatPod(kubectlOptions k8s.KubectlOptions, podName string) // requireDeleteKcatPod deletes kcat pod. func requireDeleteKcatPod(kubectlOptions k8s.KubectlOptions, podName string) { It("Deleting Kcat pod", func() { - err := deleteK8sResource(kubectlOptions, defaultDeletionTimeout, "pods", "", podName) + err := deleteK8sResource(kubectlOptions, kcatDeleetionTimeout, "pods", "", podName) Expect(err).NotTo(HaveOccurred()) }) } // requireInternalProducingConsumingMessage produces and consumes messages internally through a kcat pod // and makes comparisons between the produced and consumed messages. -// When internalAddress parameter is empty, it gets the internal address from the kafkaCluster CR status -func requireInternalProducingConsumingMessage(kubectlOptions k8s.KubectlOptions, internalAddress, kcatPodName, topicName string) { +// When internalAddress parameter is empty, it gets the internal address from the kafkaCluster CR status. +// When tlsSecretName is set +func requireInternalProducingConsumingMessage(kubectlOptions k8s.KubectlOptions, internalAddress, kcatPodName, topicName string, tlsSecretName string) { It(fmt.Sprintf("Producing and consuming messages to/from topicName: '%s", topicName), func() { if internalAddress == "" { By("Getting Kafka cluster internal addresses") @@ -83,11 +89,16 @@ func requireInternalProducingConsumingMessage(kubectlOptions k8s.KubectlOptions, internalAddress = internalListenerAddresses[0] } + tlsMode := false + if tlsSecretName != "" { + tlsMode = true + } + currentTime := time.Now() - err := producingMessagesInternally(kubectlOptions, kcatPodName, internalAddress, topicName, currentTime.String()) + err := producingMessagesInternally(kubectlOptions, kcatPodName, internalAddress, topicName, currentTime.String(), tlsMode) Expect(err).NotTo(HaveOccurred()) - consumedMessages, err := consumingMessagesInternally(kubectlOptions, kcatPodName, internalAddress, topicName) + consumedMessages, err := consumingMessagesInternally(kubectlOptions, kcatPodName, internalAddress, topicName, tlsMode) Expect(err).NotTo(HaveOccurred()) Expect(consumedMessages).Should(ContainSubstring(currentTime.String())) diff --git a/tests/e2e/templates/kcat.yaml.tmpl b/tests/e2e/templates/kcat.yaml.tmpl index 47e783070..e04978992 100644 --- a/tests/e2e/templates/kcat.yaml.tmpl +++ b/tests/e2e/templates/kcat.yaml.tmpl @@ -11,3 +11,12 @@ spec: # Just spin & wait forever command: [ "/bin/sh", "-c", "--" ] args: [ "while true; do sleep 3000; done;" ] + {{ with .TLSSecretName }} + volumeMounts: + - name: sslcerts + mountPath: "/ssl/certs" + volumes: + - name: sslcerts + secret: + secretName: {{ . }} + {{ end }} diff --git a/tests/e2e/templates/user.yaml.tmpl b/tests/e2e/templates/user.yaml.tmpl new file mode 100644 index 000000000..be1ed92b6 --- /dev/null +++ b/tests/e2e/templates/user.yaml.tmpl @@ -0,0 +1,11 @@ +apiVersion: kafka.banzaicloud.io/v1alpha1 +kind: KafkaUser +metadata: + name: {{or .Name "test-user"}} + namespace: {{or .Namespace "kafka" }} +spec: + clusterRef: + name: {{or .ClusterName "kafka" }} + {{ with .TLSSecretName }} + secretName: {{ . }} + {{ end }} diff --git a/tests/e2e/test_alltestcase.go b/tests/e2e/test_alltestcases.go similarity index 63% rename from tests/e2e/test_alltestcase.go rename to tests/e2e/test_alltestcases.go index 4673553db..d5cf238ce 100644 --- a/tests/e2e/test_alltestcase.go +++ b/tests/e2e/test_alltestcases.go @@ -22,8 +22,8 @@ import ( ) var alltestCase = tests.TestCase{ - TestDuration: 10 * time.Minute, - TestName: "Testing e2e test altogether", + TestDuration: 30 * time.Minute, + TestName: "ALL_TESTCASE", TestFn: allTestCase, } @@ -32,12 +32,16 @@ func allTestCase(kubectlOptions k8s.KubectlOptions) { var snapshottedInfo = &clusterSnapshot{} snapshotCluster(snapshottedInfo) testInstall(kubectlOptions) - testInstallZookeeperCluster() - testInstallKafkaCluster("../../config/samples/simplekafkacluster.yaml") - testUninstallKafkaCluster() - testInstallKafkaCluster("../../config/samples/simplekafkacluster_ssl.yaml") - testUninstallKafkaCluster() - testUninstallZookeeperCluster() - testUninstall() + testInstallZookeeperCluster(kubectlOptions) + testInstallKafkaCluster(kubectlOptions, "../../config/samples/simplekafkacluster.yaml") + testProduceConsumeExternal(kubectlOptions, "") + testProduceConsumeInternal(kubectlOptions) + testUninstallKafkaCluster(kubectlOptions) + testInstallKafkaCluster(kubectlOptions, "../../config/samples/simplekafkacluster_ssl.yaml") + testProduceConsumeExternal(kubectlOptions, "") + testProduceConsumeInternal(kubectlOptions) + testUninstallKafkaCluster(kubectlOptions) + testUninstallZookeeperCluster(kubectlOptions) + testUninstall(kubectlOptions) snapshotClusterAndCompare(snapshottedInfo) } diff --git a/tests/e2e/test_install.go b/tests/e2e/test_install.go index 41aeae80d..8dc51faeb 100644 --- a/tests/e2e/test_install.go +++ b/tests/e2e/test_install.go @@ -24,8 +24,8 @@ import ( ) var testCaseInstall = tests.TestCase{ - TestDuration: 10 * time.Minute, - TestName: "Testing e2e test altogether", + TestDuration: 4 * time.Minute, + TestName: "INSTALL_TESTCASE", TestFn: testInstall, } diff --git a/tests/e2e/test_install_cluster.go b/tests/e2e/test_install_cluster.go index 4f1f82788..6c845b830 100644 --- a/tests/e2e/test_install_cluster.go +++ b/tests/e2e/test_install_cluster.go @@ -17,34 +17,17 @@ package e2e import ( "github.com/gruntwork-io/terratest/modules/k8s" . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" ) -func testInstallZookeeperCluster() bool { +func testInstallZookeeperCluster(kubectlOptions k8s.KubectlOptions) bool { return When("Installing Zookeeper cluster", func() { - var kubectlOptions k8s.KubectlOptions - var err error - - It("Acquiring K8s config and context", func() { - kubectlOptions, err = kubectlOptionsForCurrentContext() - Expect(err).NotTo(HaveOccurred()) - }) - kubectlOptions.Namespace = zookeeperOperatorHelmDescriptor.Namespace requireCreatingZookeeperCluster(kubectlOptions) }) } -func testInstallKafkaCluster(kafkaClusterManifestPath string) bool { +func testInstallKafkaCluster(kubectlOptions k8s.KubectlOptions, kafkaClusterManifestPath string) bool { return When("Installing Kafka cluster", func() { - var kubectlOptions k8s.KubectlOptions - var err error - - It("Acquiring K8s config and context", func() { - kubectlOptions, err = kubectlOptionsForCurrentContext() - Expect(err).NotTo(HaveOccurred()) - }) - kubectlOptions.Namespace = koperatorLocalHelmDescriptor.Namespace requireCreatingKafkaCluster(kubectlOptions, kafkaClusterManifestPath) }) diff --git a/tests/e2e/test_mocktest.go b/tests/e2e/test_mocktest.go deleted file mode 100644 index b2048d067..000000000 --- a/tests/e2e/test_mocktest.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright © 2023 Cisco Systems, Inc. and/or its affiliates -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package e2e - -import ( - "time" - - "github.com/banzaicloud/koperator/tests/e2e/pkg/tests" - "github.com/gruntwork-io/terratest/modules/k8s" - - . "github.com/onsi/ginkgo/v2" -) - -var mockTest1 = tests.TestCase{ - TestDuration: 4 * time.Second, - TestName: "MockTest1", - TestFn: testMockTest1, -} - -func testMockTest1(kubectlOptions k8s.KubectlOptions) { - It("MockTest1-1", func() { - time.Sleep(time.Second * 2) - }) - It("MockTest1-2", func() { - time.Sleep(time.Second * 2) - }) -} - -var mockTest2 = tests.TestCase{ - TestDuration: 5 * time.Second, - TestName: "MockTest2", - TestFn: testMockTest2, -} - -func testMockTest2(kubectlOptions k8s.KubectlOptions) { - It("MockTest2-1", func() { - time.Sleep(time.Second * 1) - }) - It("MockTest2-2", func() { - time.Sleep(time.Second * 2) - }) - It("MockTest2-3", func() { - time.Sleep(time.Second * 2) - }) -} diff --git a/tests/e2e/test_produce_consume.go b/tests/e2e/test_produce_consume.go index a20b0f1be..23deecd3b 100644 --- a/tests/e2e/test_produce_consume.go +++ b/tests/e2e/test_produce_consume.go @@ -3,39 +3,22 @@ package e2e import ( "github.com/gruntwork-io/terratest/modules/k8s" . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" ) -func testProduceConsumeInternal() bool { +func testProduceConsumeInternal(kubectlOptions k8s.KubectlOptions) bool { return When("Internally produce and consume message to/from Kafka cluster", func() { - var kubectlOptions k8s.KubectlOptions - var err error - - It("Acquiring K8s config and context", func() { - kubectlOptions, err = kubectlOptionsForCurrentContext() - Expect(err).NotTo(HaveOccurred()) - }) - kubectlOptions.Namespace = koperatorLocalHelmDescriptor.Namespace - requireDeployingKcatPod(kubectlOptions, kcatPodName) + requireDeployingKcatPod(kubectlOptions, kcatPodName, "") requireDeployingKafkaTopic(kubectlOptions, testInternalTopicName) - requireInternalProducingConsumingMessage(kubectlOptions, "", kcatPodName, testInternalTopicName) + requireInternalProducingConsumingMessage(kubectlOptions, "", kcatPodName, testInternalTopicName, "") requireDeleteKafkaTopic(kubectlOptions, testInternalTopicName) requireDeleteKcatPod(kubectlOptions, kcatPodName) }) } -func testProduceConsumeExternal(tlsSecretName string) bool { +func testProduceConsumeExternal(kubectlOptions k8s.KubectlOptions, tlsSecretName string) bool { return When("Externally produce and consume message to/from Kafka cluster", func() { - var kubectlOptions k8s.KubectlOptions - var err error - - It("Acquiring K8s config and context", func() { - kubectlOptions, err = kubectlOptionsForCurrentContext() - Expect(err).NotTo(HaveOccurred()) - }) - kubectlOptions.Namespace = koperatorLocalHelmDescriptor.Namespace requireDeployingKafkaTopic(kubectlOptions, testExternalTopicName) diff --git a/tests/e2e/test_uninstall.go b/tests/e2e/test_uninstall.go index 6c1bdaf3d..3cb9f6b91 100644 --- a/tests/e2e/test_uninstall.go +++ b/tests/e2e/test_uninstall.go @@ -20,24 +20,14 @@ import ( . "github.com/onsi/gomega" ) -func testUninstall() bool { +func testUninstall(kubectlOptions k8s.KubectlOptions) bool { return When("Uninstalling Koperator and dependencies", Ordered, func() { - var kubectlOptions k8s.KubectlOptions - var err error - When("Initializing", func() { - It("Acquiring K8s config and context", func() { - kubectlOptions, err = kubectlOptionsForCurrentContext() - Expect(err).NotTo(HaveOccurred()) - }) - It("Setting globals", func() { err := dependencyCRDs.Initialize(kubectlOptions) Expect(err).NotTo(HaveOccurred()) }) - }) - requireUninstallingKoperator(k8s.KubectlOptions{ ContextName: kubectlOptions.ContextName, ConfigPath: kubectlOptions.ConfigPath, @@ -58,6 +48,5 @@ func testUninstall() bool { ConfigPath: kubectlOptions.ConfigPath, Namespace: certManagerHelmDescriptor.Namespace, }) - }) } diff --git a/tests/e2e/test_uninstall_cluster.go b/tests/e2e/test_uninstall_cluster.go index 78d71aad1..e39c381ad 100644 --- a/tests/e2e/test_uninstall_cluster.go +++ b/tests/e2e/test_uninstall_cluster.go @@ -17,35 +17,18 @@ package e2e import ( "github.com/gruntwork-io/terratest/modules/k8s" . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" ) -func testUninstallZookeeperCluster() bool { +func testUninstallZookeeperCluster(kubectlOptions k8s.KubectlOptions) bool { return When("Uninstalling Zookeeper cluster", func() { - var kubectlOptions k8s.KubectlOptions - var err error - - It("Acquiring K8s config and context", func() { - kubectlOptions, err = kubectlOptionsForCurrentContext() - Expect(err).NotTo(HaveOccurred()) - }) - kubectlOptions.Namespace = zookeeperOperatorHelmDescriptor.Namespace requireDeleteZookeeperCluster(kubectlOptions, zookeeperClusterName) }) } -func testUninstallKafkaCluster() bool { +func testUninstallKafkaCluster(kubectlOptions k8s.KubectlOptions) bool { return When("Uninstalling Kafka cluster", func() { - var kubectlOptions k8s.KubectlOptions - var err error - - It("Acquiring K8s config and context", func() { - kubectlOptions, err = kubectlOptionsForCurrentContext() - Expect(err).NotTo(HaveOccurred()) - }) - kubectlOptions.Namespace = koperatorLocalHelmDescriptor.Namespace requireDeleteKafkaCluster(kubectlOptions, kafkaClusterName) }) From ccc9a6d5cd8261f397ca090510ecb40c55dc640e Mon Sep 17 00:00:00 2001 From: marbarta Date: Thu, 27 Jul 2023 16:42:53 +0200 Subject: [PATCH 05/36] update ginkgo to 2.11.0 --- .github/actions/kind-create/action.yaml | 5 ---- .github/workflows/e2e-test.yaml | 2 +- Makefile | 11 ++------- go.mod | 14 +++++------ go.sum | 31 +++++++++++++------------ tests/e2e/go.mod | 8 +++---- tests/e2e/go.sum | 7 ++++++ tests/e2e/koperator_suite_test.go | 6 ++--- 8 files changed, 40 insertions(+), 44 deletions(-) diff --git a/.github/actions/kind-create/action.yaml b/.github/actions/kind-create/action.yaml index be18115f6..52a059431 100644 --- a/.github/actions/kind-create/action.yaml +++ b/.github/actions/kind-create/action.yaml @@ -21,9 +21,6 @@ outputs: kubeconfig: description: 'Path of the resulting kubeconfig' value: ${{ steps.check-kind-cluster.outputs.kubeconfig }} - kubeconfig_dir_path: - description: 'Dir path of the resulting kubeconfig' - value: ${{ steps.check-kind-cluster.outputs.kubeconfig_dir_path }} runs: using: "composite" @@ -53,6 +50,4 @@ runs: chmod 600 $kubeconfig_path echo "kubeconfig path: $kubeconfig_path" echo "kubeconfig=$(echo $kubeconfig_path)" >> $GITHUB_OUTPUT - echo "kubeconfig_dir_path=$(echo $kubeconfig_dir_path)" >> $GITHUB_OUTPUT - shell: bash diff --git a/.github/workflows/e2e-test.yaml b/.github/workflows/e2e-test.yaml index b1f5c3c48..b9ead1ace 100644 --- a/.github/workflows/e2e-test.yaml +++ b/.github/workflows/e2e-test.yaml @@ -41,7 +41,7 @@ jobs: - name: Run E2E tests env: TEST_STRATEGY: version - KUBECONFIG_DIR: tests/e2e/kubeconfigs + KUBECONFIG_DIR: kubeconfigs MAX_TIMEOUT: "2h" run: | go work init diff --git a/Makefile b/Makefile index c62145ed6..ccf179a2a 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,8 @@ CONTROLLER_GEN_VERSION = v0.9.2 CONTROLLER_GEN = $(PWD)/bin/controller-gen ENVTEST_K8S_VERSION = 1.24.2 -GINKGO_VERSION := 2.9.7 +# Ginkgo version should be the same that is used in the module imports +GINKGO_VERSION := 2.11.0 MOCKGEN_VERSION := 1.6.0 @@ -118,14 +119,6 @@ test-e2e: bin/ginkgo test-e2e-parallel: bin/ginkgo ginkgo -v -p --tags e2e tests/e2e -# Compile test binary for debugging -build-e2e-debug-binary: - go test github.com/banzaicloud/koperator/tests/e2e \ - --tags e2e \ - -c \ - -o /Users/marbarta/go/src/github.com/banzaicloud/koperator/tests/e2e/__debug_bin \ - -gcflags all=-N - # Build manager binary manager: generate fmt vet go build -o bin/manager main.go diff --git a/go.mod b/go.mod index 00cbd7668..1275086d6 100644 --- a/go.mod +++ b/go.mod @@ -18,8 +18,8 @@ require ( github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 github.com/go-logr/logr v1.2.4 github.com/imdario/mergo v0.3.13 - github.com/onsi/ginkgo/v2 v2.9.2 - github.com/onsi/gomega v1.27.6 + github.com/onsi/ginkgo/v2 v2.11.0 + github.com/onsi/gomega v1.27.8 github.com/pavlo-v-chernykh/keystore-go/v4 v4.4.1 github.com/prometheus/common v0.37.0 github.com/stretchr/testify v1.8.1 @@ -38,7 +38,7 @@ require ( require ( github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect - golang.org/x/tools v0.7.0 // indirect + golang.org/x/tools v0.9.3 // indirect ) require ( @@ -113,11 +113,11 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.6.0 // indirect golang.org/x/crypto v0.7.0 // indirect - golang.org/x/net v0.8.0 // indirect + golang.org/x/net v0.10.0 // indirect golang.org/x/oauth2 v0.4.0 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/term v0.6.0 // indirect - golang.org/x/text v0.8.0 // indirect + golang.org/x/sys v0.9.0 // indirect + golang.org/x/term v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index ca2457a75..3f3c2099f 100644 --- a/go.sum +++ b/go.sum @@ -361,12 +361,12 @@ github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU= -github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= +github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= +github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= -github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= +github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= github.com/pavlo-v-chernykh/keystore-go/v4 v4.4.1 h1:FyBdsRqqHH4LctMLL+BL2oGO+ONcIPwn96ctofCVtNE= github.com/pavlo-v-chernykh/keystore-go/v4 v4.4.1/go.mod h1:lAVhWwbNaveeJmxrxuSTxMgKpF6DjnuVpn6T8WiBwYQ= github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= @@ -532,6 +532,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -575,8 +576,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220725212005-46097bf591d3/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM= golang.org/x/net v0.0.0-20220809184613-07c6da5e1ced/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -599,7 +600,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -655,13 +656,13 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -671,8 +672,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -725,8 +726,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= 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= diff --git a/tests/e2e/go.mod b/tests/e2e/go.mod index a3318c523..e3ac2832f 100644 --- a/tests/e2e/go.mod +++ b/tests/e2e/go.mod @@ -7,8 +7,8 @@ require ( github.com/banzaicloud/koperator v0.25.0 github.com/cisco-open/k8s-objectmatcher v1.9.0 github.com/gruntwork-io/terratest v0.41.24 - github.com/onsi/ginkgo/v2 v2.9.5 - github.com/onsi/gomega v1.27.6 + github.com/onsi/ginkgo/v2 v2.11.0 + github.com/onsi/gomega v1.27.8 github.com/spf13/viper v1.16.0 github.com/twmb/franz-go v1.13.5 k8s.io/apiextensions-apiserver v0.26.4 @@ -121,11 +121,11 @@ require ( golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 golang.org/x/net v0.10.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect - golang.org/x/sys v0.8.0 // indirect + golang.org/x/sys v0.9.0 // indirect golang.org/x/term v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.9.1 // indirect + golang.org/x/tools v0.9.3 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/tests/e2e/go.sum b/tests/e2e/go.sum index 07e6def69..5588c8c6b 100644 --- a/tests/e2e/go.sum +++ b/tests/e2e/go.sum @@ -383,10 +383,13 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= +github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= +github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= github.com/pavlo-v-chernykh/keystore-go/v4 v4.4.1 h1:FyBdsRqqHH4LctMLL+BL2oGO+ONcIPwn96ctofCVtNE= github.com/pavlo-v-chernykh/keystore-go/v4 v4.4.1/go.mod h1:lAVhWwbNaveeJmxrxuSTxMgKpF6DjnuVpn6T8WiBwYQ= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= @@ -689,6 +692,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= @@ -763,6 +768,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= 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= diff --git a/tests/e2e/koperator_suite_test.go b/tests/e2e/koperator_suite_test.go index 5adaa7b01..52b5adbf6 100644 --- a/tests/e2e/koperator_suite_test.go +++ b/tests/e2e/koperator_suite_test.go @@ -51,7 +51,7 @@ func beforeSuite() (tests.TestPool, error) { //classifier := tests.MockTestsMinimal() //classifier := tests.MockTestsProvider() - //classifier := tests.MockTestsProviderMoreTestsThenProvider() + // classifier := tests.MockTestsProviderMoreTestsThenProvider() //classifier := tests.MockTestsVersionOne() // classifier := tests.MockTestsVersion() //classifier := tests.MockTestsComplete() @@ -84,7 +84,7 @@ func TestKoperator(t *testing.T) { func runGinkgoTests(t *testing.T) error { RegisterFailHandler(Fail) - suiteConfig, reporterConfig := GinkgoConfiguration() + suiteConfig, _ := GinkgoConfiguration() // Run only selected tests by testID label e.g: "testID:4e980f5b5c" if labelFilter := viper.GetString(config.Tests.LabelFilter); labelFilter != "" { @@ -131,7 +131,7 @@ func runGinkgoTests(t *testing.T) error { func() { defer ginkgo.GinkgoRecover() - RunSpecs(t, testDescription, suiteConfig, reporterConfig) + RunSpecs(t, testDescription, suiteConfig) }() return nil From 56c06340d0c56ac15fa7ea7682cf76c29c0fa4f6 Mon Sep 17 00:00:00 2001 From: marbarta Date: Thu, 27 Jul 2023 17:41:37 +0200 Subject: [PATCH 06/36] run allTestCase --- tests/e2e/pkg/common/config/config.go | 2 +- tests/e2e/test_alltestcases.go | 9 +++++---- tests/e2e/test_produce_consume.go | 13 +++++++++++++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/tests/e2e/pkg/common/config/config.go b/tests/e2e/pkg/common/config/config.go index ee167d03e..2c0c90161 100644 --- a/tests/e2e/pkg/common/config/config.go +++ b/tests/e2e/pkg/common/config/config.go @@ -33,7 +33,7 @@ const ( defaultReportDir = "reports" defaultCreateTestReportFile = "true" defaultMaxTimeout = "30m" - defaultAllowedOverrunDuration = "5m" + defaultAllowedOverrunDuration = "10m" defaultTestStrategy = TestStrategyMinimal defaultKubeConfigDirectoryPath = "kubeconfigs" ) diff --git a/tests/e2e/test_alltestcases.go b/tests/e2e/test_alltestcases.go index d5cf238ce..bc9cb0350 100644 --- a/tests/e2e/test_alltestcases.go +++ b/tests/e2e/test_alltestcases.go @@ -22,7 +22,7 @@ import ( ) var alltestCase = tests.TestCase{ - TestDuration: 30 * time.Minute, + TestDuration: 20 * time.Minute, TestName: "ALL_TESTCASE", TestFn: allTestCase, } @@ -34,12 +34,13 @@ func allTestCase(kubectlOptions k8s.KubectlOptions) { testInstall(kubectlOptions) testInstallZookeeperCluster(kubectlOptions) testInstallKafkaCluster(kubectlOptions, "../../config/samples/simplekafkacluster.yaml") - testProduceConsumeExternal(kubectlOptions, "") + //testProduceConsumeExternal(kubectlOptions, "") testProduceConsumeInternal(kubectlOptions) testUninstallKafkaCluster(kubectlOptions) testInstallKafkaCluster(kubectlOptions, "../../config/samples/simplekafkacluster_ssl.yaml") - testProduceConsumeExternal(kubectlOptions, "") - testProduceConsumeInternal(kubectlOptions) + //testProduceConsumeExternal(kubectlOptions, "") + //testProduceConsumeInternal(kubectlOptions) + testProduceConsumeInternalSSL(kubectlOptions, defaultTLSSecretName) testUninstallKafkaCluster(kubectlOptions) testUninstallZookeeperCluster(kubectlOptions) testUninstall(kubectlOptions) diff --git a/tests/e2e/test_produce_consume.go b/tests/e2e/test_produce_consume.go index 4a295b136..d4bfc9aba 100644 --- a/tests/e2e/test_produce_consume.go +++ b/tests/e2e/test_produce_consume.go @@ -17,6 +17,19 @@ func testProduceConsumeInternal(kubectlOptions k8s.KubectlOptions) bool { }) } +func testProduceConsumeInternalSSL(kubectlOptions k8s.KubectlOptions, tlsSecretName string) bool { + return When("Internally produce and consume message to/from Kafka cluster using SSL", func() { + kubectlOptions.Namespace = koperatorLocalHelmDescriptor.Namespace + + requireDeployingKafkaUser(kubectlOptions, kafkaUserName, tlsSecretName) + requireDeployingKcatPod(kubectlOptions, kcatPodName, tlsSecretName) + requireDeployingKafkaTopic(kubectlOptions, testInternalTopicName) + requireInternalProducingConsumingMessage(kubectlOptions, "", kcatPodName, testInternalTopicName, tlsSecretName) + requireDeleteKafkaTopic(kubectlOptions, testInternalTopicName) + requireDeleteKcatPod(kubectlOptions, kcatPodName) + requireDeleteKafkaUser(kubectlOptions, kafkaUserName) + }) +} func testProduceConsumeExternal(kubectlOptions k8s.KubectlOptions, tlsSecretName string) bool { return When("Externally produce and consume message to/from Kafka cluster", func() { From 527e501d60f56a531123eb991c01d3fec9883d54 Mon Sep 17 00:00:00 2001 From: marbarta Date: Thu, 27 Jul 2023 17:52:38 +0200 Subject: [PATCH 07/36] format time output --- .github/workflows/e2e-test.yaml | 2 +- tests/e2e/koperator_suite_test.go | 17 ++--------------- tests/e2e/pkg/tests/tests.go | 8 ++++---- 3 files changed, 7 insertions(+), 20 deletions(-) diff --git a/.github/workflows/e2e-test.yaml b/.github/workflows/e2e-test.yaml index b9ead1ace..fa978dbfe 100644 --- a/.github/workflows/e2e-test.yaml +++ b/.github/workflows/e2e-test.yaml @@ -42,7 +42,7 @@ jobs: env: TEST_STRATEGY: version KUBECONFIG_DIR: kubeconfigs - MAX_TIMEOUT: "2h" + MAX_TIMEOUT: "1h" run: | go work init go work use -r . diff --git a/tests/e2e/koperator_suite_test.go b/tests/e2e/koperator_suite_test.go index 52b5adbf6..2b967acc4 100644 --- a/tests/e2e/koperator_suite_test.go +++ b/tests/e2e/koperator_suite_test.go @@ -41,20 +41,7 @@ func beforeSuite() (tests.TestPool, error) { return nil, err } - // k8sCluster, err := tests.New - // if err != nil { - // return nil, err - // } - //k8sClusterPool.AddK8sClusters(k8sCluster) - - classifier := tests.NewClassifier(k8sClusterPool, testCaseInstall) - - //classifier := tests.MockTestsMinimal() - //classifier := tests.MockTestsProvider() - // classifier := tests.MockTestsProviderMoreTestsThenProvider() - //classifier := tests.MockTestsVersionOne() - // classifier := tests.MockTestsVersion() - //classifier := tests.MockTestsComplete() + classifier := tests.NewClassifier(k8sClusterPool, alltestCase) var testPool tests.TestPool testStrategy := viper.GetString(config.Tests.TestStrategy) @@ -109,7 +96,7 @@ func runGinkgoTests(t *testing.T) error { } // Protection against too long test suites if testSuiteDuration > maxTimeout { - return fmt.Errorf("tests estimated duration: '%dsec' bigger then maxTimeout: '%dsec'", int(testSuiteDuration.Seconds()), int(maxTimeout.Seconds())) + return fmt.Errorf("tests estimated duration: '%s' bigger then maxTimeout: '%s'", testSuiteDuration.String(), maxTimeout.String()) } // Calculated timeout can be overran with the specified time length diff --git a/tests/e2e/pkg/tests/tests.go b/tests/e2e/pkg/tests/tests.go index 9b10f7b5b..f4cf6563e 100644 --- a/tests/e2e/pkg/tests/tests.go +++ b/tests/e2e/pkg/tests/tests.go @@ -71,11 +71,11 @@ K8sVersions: %v K8sProviders: %v TestK8sMapping: %v TestStrategy: %s -ExpectedDurationSerial: %f -ExpectedDurationParallel: %f +ExpectedDurationSerial: %s +ExpectedDurationParallel: %s `, len(tests), len(maps.Keys(testsByContextName)), maps.Keys(testsByVersions), maps.Keys(testsByProviders), - tests, viper.GetString(config.Tests.TestStrategy), tests.GetTestSuiteDurationSerial().Seconds(), - tests.GetTestSuiteDurationParallel().Seconds()) + tests, viper.GetString(config.Tests.TestStrategy), tests.GetTestSuiteDurationSerial().String(), + tests.GetTestSuiteDurationParallel().String()) } func (tests TestPool) BuildParallelByK8sCluster() { From 0f0e07ec4c74c0c464b5c3f0135b1e3d69f0d8a5 Mon Sep 17 00:00:00 2001 From: marbarta Date: Thu, 27 Jul 2023 18:23:38 +0200 Subject: [PATCH 08/36] increase zookeepercluster and kafkacluster creation timeout --- tests/e2e/const.go | 18 +++++++++--------- tests/e2e/pkg/tests/tests.go | 10 +++++----- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/e2e/const.go b/tests/e2e/const.go index 93322950a..bd19a7a22 100644 --- a/tests/e2e/const.go +++ b/tests/e2e/const.go @@ -46,17 +46,17 @@ const ( zookeeperClusterName = "zookeeper-server" managedByHelmLabelTemplate = "app.kubernetes.io/managed-by=Helm,app.kubernetes.io/instance=%s" - cruiseControlPodReadinessTimeout = 50 * time.Second - kafkaClusterResourceReadinessTimeout = 60 * time.Second - defaultDeletionTimeout = 20 * time.Second - defaultPodReadinessWaitTime = 10 * time.Second - defaultTopicCreationWaitTime = 10 * time.Second - defaultUserCreationWaitTime = 10 * time.Second - - kafkaClusterCreateTimeout = 600 * time.Second + cruiseControlPodReadinessTimeout = 50 * time.Second + kafkaClusterResourceReadinessTimeout = 60 * time.Second + defaultDeletionTimeout = 20 * time.Second + defaultPodReadinessWaitTime = 10 * time.Second + defaultTopicCreationWaitTime = 10 * time.Second + defaultUserCreationWaitTime = 10 * time.Second + + kafkaClusterCreateTimeout = 600 * time.Second // Increased from 600 to 700 for multiple kind kafkaClusterResourceCleanupTimeout = 120 * time.Second kcatDeleetionTimeout = 40 * time.Second - zookeeperClusterCreateTimeout = 4 * time.Minute + zookeeperClusterCreateTimeout = 6 * time.Minute // Increased from 4 to 6 for multiple kind zookeeperClusterResourceCleanupTimeout = 60 * time.Second externalConsumerTimeout = 5 * time.Second externalProducerTimeout = 5 * time.Second diff --git a/tests/e2e/pkg/tests/tests.go b/tests/e2e/pkg/tests/tests.go index f4cf6563e..3edd275c4 100644 --- a/tests/e2e/pkg/tests/tests.go +++ b/tests/e2e/pkg/tests/tests.go @@ -395,11 +395,11 @@ func (t Test) Run() { ) } - // It("Checking K8s cluster health", func() { - // if err != nil { - // Fail(fmt.Sprintf("skipping testCase... K8s cluster connection problem: %v", err)) - // } - // }) + It("Checking K8s cluster health", func() { + if err != nil { + Fail(fmt.Sprintf("skipping testCase... K8s cluster connection problem: %v", err)) + } + }) t.testCase.TestFn(kubectlOptions) }) } From 4826fa1f3ea8e9b88052b94e122ef7f8f446bbc2 Mon Sep 17 00:00:00 2001 From: marbarta Date: Thu, 27 Jul 2023 18:44:50 +0200 Subject: [PATCH 09/36] run allTests serial --- .github/workflows/e2e-test.yaml | 2 +- tests/e2e/test_alltestcases.go | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/e2e-test.yaml b/.github/workflows/e2e-test.yaml index fa978dbfe..2012b93db 100644 --- a/.github/workflows/e2e-test.yaml +++ b/.github/workflows/e2e-test.yaml @@ -46,7 +46,7 @@ jobs: run: | go work init go work use -r . - make test-e2e-parallel + make test-e2e - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action/composite@master diff --git a/tests/e2e/test_alltestcases.go b/tests/e2e/test_alltestcases.go index bc9cb0350..8cd8640c9 100644 --- a/tests/e2e/test_alltestcases.go +++ b/tests/e2e/test_alltestcases.go @@ -19,6 +19,7 @@ import ( "github.com/banzaicloud/koperator/tests/e2e/pkg/tests" "github.com/gruntwork-io/terratest/modules/k8s" + "github.com/onsi/gomega/format" ) var alltestCase = tests.TestCase{ @@ -29,6 +30,7 @@ var alltestCase = tests.TestCase{ // TODO (marbarta): kubectlOptions should be passed for the subtests func allTestCase(kubectlOptions k8s.KubectlOptions) { + format.MaxLength = 0 var snapshottedInfo = &clusterSnapshot{} snapshotCluster(snapshottedInfo) testInstall(kubectlOptions) From 0764631a09da18574bee63ac21048c31a077eb47 Mon Sep 17 00:00:00 2001 From: marbarta Date: Fri, 28 Jul 2023 04:49:58 +0200 Subject: [PATCH 10/36] zookeeper timeout increased --- tests/e2e/const.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/const.go b/tests/e2e/const.go index bd19a7a22..542e63fde 100644 --- a/tests/e2e/const.go +++ b/tests/e2e/const.go @@ -56,7 +56,7 @@ const ( kafkaClusterCreateTimeout = 600 * time.Second // Increased from 600 to 700 for multiple kind kafkaClusterResourceCleanupTimeout = 120 * time.Second kcatDeleetionTimeout = 40 * time.Second - zookeeperClusterCreateTimeout = 6 * time.Minute // Increased from 4 to 6 for multiple kind + zookeeperClusterCreateTimeout = 7 * time.Minute // Increased from 4 to 7 for multiple kind zookeeperClusterResourceCleanupTimeout = 60 * time.Second externalConsumerTimeout = 5 * time.Second externalProducerTimeout = 5 * time.Second From 86586a40b35b61d9b407a34a43d695d03046e7e6 Mon Sep 17 00:00:00 2001 From: marbarta Date: Fri, 28 Jul 2023 12:11:58 +0200 Subject: [PATCH 11/36] add test progress indicator --- tests/e2e/helm.go | 2 +- tests/e2e/koperator_suite_test.go | 39 +++++++++++++++-- tests/e2e/pkg/common/config/config.go | 4 +- tests/e2e/pkg/tests/mocks.go | 18 ++++---- tests/e2e/pkg/tests/tests.go | 39 +++++++++++------ tests/e2e/pkg/tests/tests_test.go | 60 +++++++++++++-------------- tests/e2e/test_alltestcases.go | 10 ++--- tests/e2e/test_install.go | 6 +-- 8 files changed, 111 insertions(+), 67 deletions(-) diff --git a/tests/e2e/helm.go b/tests/e2e/helm.go index 595428c31..652339eba 100644 --- a/tests/e2e/helm.go +++ b/tests/e2e/helm.go @@ -164,7 +164,7 @@ func (helmDescriptor *helmDescriptor) installHelmChart(kubectlOptions k8s.Kubect fixedArguments := []string{ "--create-namespace", "--atomic", - //"--debug", + "--debug", } helmChartNameOrLocalPath := helmDescriptor.ChartName diff --git a/tests/e2e/koperator_suite_test.go b/tests/e2e/koperator_suite_test.go index 2b967acc4..3768fb5df 100644 --- a/tests/e2e/koperator_suite_test.go +++ b/tests/e2e/koperator_suite_test.go @@ -30,6 +30,7 @@ import ( . "github.com/onsi/ginkgo/v2" "github.com/onsi/ginkgo/v2/reporters" . "github.com/onsi/gomega" + "github.com/onsi/gomega/format" "github.com/spf13/viper" ) @@ -73,6 +74,9 @@ func runGinkgoTests(t *testing.T) error { RegisterFailHandler(Fail) suiteConfig, _ := GinkgoConfiguration() + // Gomega configurations + format.MaxLength = 0 + // Run only selected tests by testID label e.g: "testID:4e980f5b5c" if labelFilter := viper.GetString(config.Tests.LabelFilter); labelFilter != "" { suiteConfig.LabelFilter = labelFilter @@ -85,8 +89,10 @@ func runGinkgoTests(t *testing.T) error { return fmt.Errorf("beforeSuite ran into err: %w", err) } + runningSuiteProgress.allSpecCount = testPool.GetTestSuiteSpecsCount() + testSuiteDuration := testPool.GetTestSuiteDurationSerial() - if suiteConfig.ParallelProcess > 1 { + if suiteConfig.ParallelTotal > 1 { testSuiteDuration = testPool.GetTestSuiteDurationParallel() } @@ -125,10 +131,35 @@ func runGinkgoTests(t *testing.T) error { } +type runningSuiteData struct { + passedTestCount int + skippedTestCount int + failedTestCount int + allSpecCount int +} + +var runningSuiteProgress = runningSuiteData{} + // Maybe this can be used to get more debug information into the test report. -// var _ = ReportAfterEach(func(report SpecReport) { -// fmt.Fprintf(os.Stderr, "SPEC REPORT: %s | %s\nFAILURE MESSAGE: %s\n OUTPUT: %s\n", report.State, report.FullText(), report.FailureMessage(), report.CapturedStdOutErr) -// }) +var _ = ReportAfterEach(func(report SpecReport) { + switch report.State.String() { + case "failed": + runningSuiteProgress.failedTestCount += 1 + case "passed": + runningSuiteProgress.passedTestCount += 1 + case "skipped": + runningSuiteProgress.skippedTestCount += 1 + } + + r := fmt.Sprintf("{{red}}%s(TOTAL:%d PROGRESS:%d/%d/%d){{/}}", + report.State, + runningSuiteProgress.allSpecCount, + runningSuiteProgress.passedTestCount, + runningSuiteProgress.failedTestCount, + runningSuiteProgress.skippedTestCount) + + AddReportEntry("PROGRESS,", r) +}) // Root Describe container var _ = Describe("", func() { diff --git a/tests/e2e/pkg/common/config/config.go b/tests/e2e/pkg/common/config/config.go index 2c0c90161..5760e7664 100644 --- a/tests/e2e/pkg/common/config/config.go +++ b/tests/e2e/pkg/common/config/config.go @@ -32,7 +32,7 @@ const ( const ( defaultReportDir = "reports" defaultCreateTestReportFile = "true" - defaultMaxTimeout = "30m" + defaultMaxTimeout = "1h" defaultAllowedOverrunDuration = "10m" defaultTestStrategy = TestStrategyMinimal defaultKubeConfigDirectoryPath = "kubeconfigs" @@ -93,4 +93,6 @@ func init() { viper.BindEnv(Tests.KubeConfigDirectoryPath, "KUBECONFIG_DIR") viper.SetDefault(Tests.KubeConfigDirectoryPath, defaultKubeConfigDirectoryPath) + + viper.BindEnv(Tests.LabelFilter, "LABEL_FILTER") } diff --git a/tests/e2e/pkg/tests/mocks.go b/tests/e2e/pkg/tests/mocks.go index c83c3a6a6..eb5f5e02d 100644 --- a/tests/e2e/pkg/tests/mocks.go +++ b/tests/e2e/pkg/tests/mocks.go @@ -214,9 +214,9 @@ func MockTestsComplete() Classifier { } var mockTest1 = TestCase{ - TestDuration: 4 * time.Second, - TestName: "MockTest1", - TestFn: testMockTest1, + Duration: 4 * time.Second, + Name: "MockTest1", + TestFn: testMockTest1, } func testMockTest1(kubectlOptions k8s.KubectlOptions) { @@ -229,9 +229,9 @@ func testMockTest1(kubectlOptions k8s.KubectlOptions) { } var mockTest2 = TestCase{ - TestDuration: 5 * time.Second, - TestName: "MockTest2", - TestFn: testMockTest2, + Duration: 5 * time.Second, + Name: "MockTest2", + TestFn: testMockTest2, } func testMockTest2(kubectlOptions k8s.KubectlOptions) { @@ -249,9 +249,9 @@ func testMockTest2(kubectlOptions k8s.KubectlOptions) { } var mockTest3 = TestCase{ - TestDuration: 4 * time.Second, - TestName: "MockTest3", - TestFn: testMockTest3, + Duration: 4 * time.Second, + Name: "MockTest3", + TestFn: testMockTest3, } func testMockTest3(kubectlOptions k8s.KubectlOptions) { diff --git a/tests/e2e/pkg/tests/tests.go b/tests/e2e/pkg/tests/tests.go index 3edd275c4..72189e2c8 100644 --- a/tests/e2e/pkg/tests/tests.go +++ b/tests/e2e/pkg/tests/tests.go @@ -66,6 +66,7 @@ func (tests TestPool) PoolInfo() string { return fmt.Sprintf(` NumberOfTests: %d +NumberOfSpecs: %d NumberOfK8sClusters: %d K8sVersions: %v K8sProviders: %v @@ -73,7 +74,7 @@ TestK8sMapping: %v TestStrategy: %s ExpectedDurationSerial: %s ExpectedDurationParallel: %s -`, len(tests), len(maps.Keys(testsByContextName)), maps.Keys(testsByVersions), maps.Keys(testsByProviders), +`, len(tests), tests.GetTestSuiteSpecsCount(), len(maps.Keys(testsByContextName)), maps.Keys(testsByVersions), maps.Keys(testsByProviders), tests, viper.GetString(config.Tests.TestStrategy), tests.GetTestSuiteDurationSerial().String(), tests.GetTestSuiteDurationParallel().String()) } @@ -151,7 +152,7 @@ func (tests TestPool) GetTestSuiteDurationParallel() time.Duration { for _, tests := range testsByClusterID { var localDuration time.Duration for _, test := range tests { - localDuration += test.testCase.TestDuration + localDuration += test.testCase.Duration } if localDuration > max { @@ -166,11 +167,21 @@ func (tests TestPool) GetTestSuiteDurationParallel() time.Duration { func (tests TestPool) GetTestSuiteDurationSerial() time.Duration { var allDuration time.Duration for _, test := range tests { - allDuration += test.testCase.TestDuration + allDuration += test.testCase.Duration } return allDuration } +// GetTestSuiteDurationParallel calculate the expected duration of the tests when +// suite is executed in serial +func (tests TestPool) GetTestSuiteSpecsCount() int { + var specsCount int + for _, test := range tests { + specsCount += test.testCase.SpecsCount + } + return specsCount +} + // NewClassifier creates a test classifier from K8sClusterPool and TestCases. func NewClassifier(k8sClusterPool K8sClusterPool, testCases ...TestCase) Classifier { return Classifier{ @@ -327,19 +338,21 @@ func (t K8sClusterPool) getByProvidersVersions() map[string]map[string][]K8sClus } // TestCase is a representation of an E2E test case -// TestDuration specifies the expected length of the test -// TestName is the name of the test +// SpecsCount is the number of the gingko specs +// Duration specifies the expected length of the test +// Name is the name of the test // TestFn specifies the test function type TestCase struct { - TestDuration time.Duration - TestName string - TestFn func(kubectlOptions k8s.KubectlOptions) + SpecsCount int + Duration time.Duration + Name string + TestFn func(kubectlOptions k8s.KubectlOptions) } // NewTest creates a Test from a TestCase and from a K8sCluster -// testID for Test is generated automatically based on testName and K8s clusterInfo +// testID for Test is generated automatically based on Name and K8s clusterInfo func NewTest(testCase TestCase, k8sCluster K8sCluster) Test { - testIDseed := fmt.Sprintf("%v%v", testCase.TestName, k8sCluster.clusterInfo) + testIDseed := fmt.Sprintf("%v%v", testCase.Name, k8sCluster.clusterInfo) hash := md5.Sum([]byte(testIDseed)) testID := hex.EncodeToString(hash[:5]) return Test{ @@ -365,10 +378,10 @@ func (t Test) less(test Test) bool { return t.TestID() < test.TestID() } -// TestID return the test UID based on testName and K8sCluster +// TestID return the test UID based on test name and K8sCluster func (t *Test) TestID() string { if t.testID == "" { - text := fmt.Sprintf("%v%v", t.testCase.TestName, t.k8sCluster) + text := fmt.Sprintf("%v%v", t.testCase.Name, t.k8sCluster) hash := md5.Sum([]byte(text)) t.testID = hex.EncodeToString(hash[:5]) } @@ -377,7 +390,7 @@ func (t *Test) TestID() string { func (t Test) String() string { return fmt.Sprintf("%s(%s)", - t.testCase.TestName, t.k8sCluster) + t.testCase.Name, t.k8sCluster) } // Run encapsulates the testCase in a Describe container with diff --git a/tests/e2e/pkg/tests/tests_test.go b/tests/e2e/pkg/tests/tests_test.go index b9309d4e6..8bc7e59a8 100644 --- a/tests/e2e/pkg/tests/tests_test.go +++ b/tests/e2e/pkg/tests/tests_test.go @@ -51,17 +51,17 @@ func Test_Classifier_minimal(t *testing.T) { }, testCases: []TestCase{ { - TestName: "testCase1", + Name: "testCase1", }, { - TestName: "testCase2", + Name: "testCase2", }, }, }, want: []Test{ { testCase: TestCase{ - TestName: "testCase1", + Name: "testCase1", }, k8sCluster: K8sCluster{ @@ -74,7 +74,7 @@ func Test_Classifier_minimal(t *testing.T) { }, { testCase: TestCase{ - TestName: "testCase2", + Name: "testCase2", }, k8sCluster: K8sCluster{ @@ -138,17 +138,17 @@ func Test_Classifier_providerComplete(t *testing.T) { }, testCases: []TestCase{ { - TestName: "testCase1", + Name: "testCase1", }, { - TestName: "testCase2", + Name: "testCase2", }, }, }, want: []Test{ { testCase: TestCase{ - TestName: "testCase1", + Name: "testCase1", }, k8sCluster: K8sCluster{ @@ -161,7 +161,7 @@ func Test_Classifier_providerComplete(t *testing.T) { }, { testCase: TestCase{ - TestName: "testCase2", + Name: "testCase2", }, k8sCluster: K8sCluster{ @@ -174,7 +174,7 @@ func Test_Classifier_providerComplete(t *testing.T) { }, { testCase: TestCase{ - TestName: "testCase1", + Name: "testCase1", }, k8sCluster: K8sCluster{ @@ -187,7 +187,7 @@ func Test_Classifier_providerComplete(t *testing.T) { }, { testCase: TestCase{ - TestName: "testCase2", + Name: "testCase2", }, k8sCluster: K8sCluster{ @@ -251,17 +251,17 @@ func Test_Classifier_versionComplete(t *testing.T) { }, testCases: []TestCase{ { - TestName: "testCase1", + Name: "testCase1", }, { - TestName: "testCase2", + Name: "testCase2", }, }, }, want: []Test{ { testCase: TestCase{ - TestName: "testCase1", + Name: "testCase1", }, k8sCluster: K8sCluster{ @@ -274,7 +274,7 @@ func Test_Classifier_versionComplete(t *testing.T) { }, { testCase: TestCase{ - TestName: "testCase2", + Name: "testCase2", }, k8sCluster: K8sCluster{ @@ -287,7 +287,7 @@ func Test_Classifier_versionComplete(t *testing.T) { }, { testCase: TestCase{ - TestName: "testCase1", + Name: "testCase1", }, k8sCluster: K8sCluster{ @@ -300,7 +300,7 @@ func Test_Classifier_versionComplete(t *testing.T) { }, { testCase: TestCase{ - TestName: "testCase2", + Name: "testCase2", }, k8sCluster: K8sCluster{ @@ -364,17 +364,17 @@ func Test_Classifier_complete(t *testing.T) { }, testCases: []TestCase{ { - TestName: "testCase1", + Name: "testCase1", }, { - TestName: "testCase2", + Name: "testCase2", }, }, }, want: []Test{ { testCase: TestCase{ - TestName: "testCase1", + Name: "testCase1", }, k8sCluster: K8sCluster{ @@ -387,7 +387,7 @@ func Test_Classifier_complete(t *testing.T) { }, { testCase: TestCase{ - TestName: "testCase2", + Name: "testCase2", }, k8sCluster: K8sCluster{ @@ -400,7 +400,7 @@ func Test_Classifier_complete(t *testing.T) { }, { testCase: TestCase{ - TestName: "testCase1", + Name: "testCase1", }, k8sCluster: K8sCluster{ @@ -413,7 +413,7 @@ func Test_Classifier_complete(t *testing.T) { }, { testCase: TestCase{ - TestName: "testCase2", + Name: "testCase2", }, k8sCluster: K8sCluster{ @@ -467,17 +467,17 @@ func Test_Classifier_complete(t *testing.T) { }, testCases: []TestCase{ { - TestName: "testCase1", + Name: "testCase1", }, { - TestName: "testCase2", + Name: "testCase2", }, }, }, want: []Test{ { testCase: TestCase{ - TestName: "testCase1", + Name: "testCase1", }, k8sCluster: K8sCluster{ @@ -490,7 +490,7 @@ func Test_Classifier_complete(t *testing.T) { }, { testCase: TestCase{ - TestName: "testCase2", + Name: "testCase2", }, k8sCluster: K8sCluster{ @@ -503,7 +503,7 @@ func Test_Classifier_complete(t *testing.T) { }, { testCase: TestCase{ - TestName: "testCase1", + Name: "testCase1", }, k8sCluster: K8sCluster{ @@ -516,7 +516,7 @@ func Test_Classifier_complete(t *testing.T) { }, { testCase: TestCase{ - TestName: "testCase2", + Name: "testCase2", }, k8sCluster: K8sCluster{ @@ -529,7 +529,7 @@ func Test_Classifier_complete(t *testing.T) { }, { testCase: TestCase{ - TestName: "testCase1", + Name: "testCase1", }, k8sCluster: K8sCluster{ @@ -542,7 +542,7 @@ func Test_Classifier_complete(t *testing.T) { }, { testCase: TestCase{ - TestName: "testCase2", + Name: "testCase2", }, k8sCluster: K8sCluster{ diff --git a/tests/e2e/test_alltestcases.go b/tests/e2e/test_alltestcases.go index 8cd8640c9..778143552 100644 --- a/tests/e2e/test_alltestcases.go +++ b/tests/e2e/test_alltestcases.go @@ -19,18 +19,16 @@ import ( "github.com/banzaicloud/koperator/tests/e2e/pkg/tests" "github.com/gruntwork-io/terratest/modules/k8s" - "github.com/onsi/gomega/format" ) var alltestCase = tests.TestCase{ - TestDuration: 20 * time.Minute, - TestName: "ALL_TESTCASE", - TestFn: allTestCase, + SpecsCount: 45, + Duration: 20 * time.Minute, + Name: "ALL_TESTCASE", + TestFn: allTestCase, } -// TODO (marbarta): kubectlOptions should be passed for the subtests func allTestCase(kubectlOptions k8s.KubectlOptions) { - format.MaxLength = 0 var snapshottedInfo = &clusterSnapshot{} snapshotCluster(snapshottedInfo) testInstall(kubectlOptions) diff --git a/tests/e2e/test_install.go b/tests/e2e/test_install.go index 8dc51faeb..4ee497265 100644 --- a/tests/e2e/test_install.go +++ b/tests/e2e/test_install.go @@ -24,9 +24,9 @@ import ( ) var testCaseInstall = tests.TestCase{ - TestDuration: 4 * time.Minute, - TestName: "INSTALL_TESTCASE", - TestFn: testInstall, + Duration: 4 * time.Minute, + Name: "INSTALL_TESTCASE", + TestFn: testInstall, } func testInstall(kubectlOptions k8s.KubectlOptions) { From 2df49bb6eb0618bba1e5fb295a80bbaeb53ab8ec Mon Sep 17 00:00:00 2001 From: marbarta Date: Fri, 28 Jul 2023 13:43:16 +0200 Subject: [PATCH 12/36] report entry refactored --- tests/e2e/koperator_suite_test.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/e2e/koperator_suite_test.go b/tests/e2e/koperator_suite_test.go index 3768fb5df..7cc19d23a 100644 --- a/tests/e2e/koperator_suite_test.go +++ b/tests/e2e/koperator_suite_test.go @@ -26,6 +26,7 @@ import ( "github.com/banzaicloud/koperator/tests/e2e/pkg/common/config" "github.com/banzaicloud/koperator/tests/e2e/pkg/tests" + "github.com/onsi/ginkgo/types" "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2" "github.com/onsi/ginkgo/v2/reporters" @@ -140,7 +141,7 @@ type runningSuiteData struct { var runningSuiteProgress = runningSuiteData{} -// Maybe this can be used to get more debug information into the test report. +// Report suit progress only into the std output var _ = ReportAfterEach(func(report SpecReport) { switch report.State.String() { case "failed": @@ -151,14 +152,15 @@ var _ = ReportAfterEach(func(report SpecReport) { runningSuiteProgress.skippedTestCount += 1 } - r := fmt.Sprintf("{{red}}%s(TOTAL:%d PROGRESS:%d/%d/%d){{/}}", + r := fmt.Sprintf("{{red}}%s(TOTAL:%d PROGRESS:%d/%d/%d) PID: %d{{/}}", report.State, runningSuiteProgress.allSpecCount, runningSuiteProgress.passedTestCount, runningSuiteProgress.failedTestCount, - runningSuiteProgress.skippedTestCount) + runningSuiteProgress.skippedTestCount, + report.ParallelProcess) - AddReportEntry("PROGRESS,", r) + AddReportEntry("", r, ReportEntryVisibilityNever, types.CodeLocation{}) }) // Root Describe container From cd250f570bd180e014d02d401939af83301886c2 Mon Sep 17 00:00:00 2001 From: marbarta Date: Sat, 29 Jul 2023 06:15:39 +0200 Subject: [PATCH 13/36] go with one kind cluster --- .github/workflows/e2e-test.yaml | 15 ++++++++------- tests/e2e/koperator_suite_test.go | 10 +++++++--- tests/e2e/pkg/tests/mocks.go | 21 ++++++++++++--------- tests/e2e/pkg/tests/tests.go | 5 +++++ 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/.github/workflows/e2e-test.yaml b/.github/workflows/e2e-test.yaml index 2012b93db..596b667c7 100644 --- a/.github/workflows/e2e-test.yaml +++ b/.github/workflows/e2e-test.yaml @@ -30,13 +30,14 @@ jobs: kind_k8s_version: v1.24.13 kubeconfig_dir_path: tests/e2e/kubeconfigs - - name: Setup Kind cluster2 - id: setup-kind2 - uses: ./.github/actions/kind-create - with: - kind_cluster_name: kind2 - kind_k8s_version: v1.25.9 - kubeconfig_dir_path: tests/e2e/kubeconfigs + # TODO: ZookeeperCluster create time out always on the second kind cluster + # - name: Setup Kind cluster2 + # id: setup-kind2 + # uses: ./.github/actions/kind-create + # with: + # kind_cluster_name: kind2 + # kind_k8s_version: v1.25.9 + # kubeconfig_dir_path: tests/e2e/kubeconfigs - name: Run E2E tests env: diff --git a/tests/e2e/koperator_suite_test.go b/tests/e2e/koperator_suite_test.go index 7cc19d23a..ea6d20790 100644 --- a/tests/e2e/koperator_suite_test.go +++ b/tests/e2e/koperator_suite_test.go @@ -26,7 +26,6 @@ import ( "github.com/banzaicloud/koperator/tests/e2e/pkg/common/config" "github.com/banzaicloud/koperator/tests/e2e/pkg/tests" - "github.com/onsi/ginkgo/types" "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2" "github.com/onsi/ginkgo/v2/reporters" @@ -152,7 +151,7 @@ var _ = ReportAfterEach(func(report SpecReport) { runningSuiteProgress.skippedTestCount += 1 } - r := fmt.Sprintf("{{red}}%s(TOTAL:%d PROGRESS:%d/%d/%d) PID: %d{{/}}", + entry := fmt.Sprintf("{{red}}%s(TOTAL:%d PROGRESS/PROC:%d/%d/%d PID: %d){{/}}", report.State, runningSuiteProgress.allSpecCount, runningSuiteProgress.passedTestCount, @@ -160,7 +159,12 @@ var _ = ReportAfterEach(func(report SpecReport) { runningSuiteProgress.skippedTestCount, report.ParallelProcess) - AddReportEntry("", r, ReportEntryVisibilityNever, types.CodeLocation{}) + // TODO: it would be better to calculate somehow the total specs count per process + // Im not sure about that is possible because specs number can be different on each processes + // This is because the classifier sort the tests the best possible way so when there are + // more available cluster then tests it is possible that one of the test is executed on a cluster and another on another one + // e.g: [MockTest2(testContextName2) MockTest1(testContextName1) MockTest2(testContextName1) MockTest1(testContextName2) MockTest1(testContextName3) MockTest2(testContextName4)] + AddReportEntry(entry) }) // Root Describe container diff --git a/tests/e2e/pkg/tests/mocks.go b/tests/e2e/pkg/tests/mocks.go index eb5f5e02d..c165b9891 100644 --- a/tests/e2e/pkg/tests/mocks.go +++ b/tests/e2e/pkg/tests/mocks.go @@ -214,9 +214,10 @@ func MockTestsComplete() Classifier { } var mockTest1 = TestCase{ - Duration: 4 * time.Second, - Name: "MockTest1", - TestFn: testMockTest1, + SpecsCount: 2, + Duration: 4 * time.Second, + Name: "MockTest1", + TestFn: testMockTest1, } func testMockTest1(kubectlOptions k8s.KubectlOptions) { @@ -229,9 +230,10 @@ func testMockTest1(kubectlOptions k8s.KubectlOptions) { } var mockTest2 = TestCase{ - Duration: 5 * time.Second, - Name: "MockTest2", - TestFn: testMockTest2, + SpecsCount: 3, + Duration: 5 * time.Second, + Name: "MockTest2", + TestFn: testMockTest2, } func testMockTest2(kubectlOptions k8s.KubectlOptions) { @@ -249,9 +251,10 @@ func testMockTest2(kubectlOptions k8s.KubectlOptions) { } var mockTest3 = TestCase{ - Duration: 4 * time.Second, - Name: "MockTest3", - TestFn: testMockTest3, + SpecsCount: 2, + Duration: 4 * time.Second, + Name: "MockTest3", + TestFn: testMockTest3, } func testMockTest3(kubectlOptions k8s.KubectlOptions) { diff --git a/tests/e2e/pkg/tests/tests.go b/tests/e2e/pkg/tests/tests.go index 72189e2c8..8d99c8bfd 100644 --- a/tests/e2e/pkg/tests/tests.go +++ b/tests/e2e/pkg/tests/tests.go @@ -182,6 +182,11 @@ func (tests TestPool) GetTestSuiteSpecsCount() int { return specsCount } +// GetParallelTotal returns the maximum number of the possible parallelization +func (tests TestPool) GetParallelTotal() int { + return len(maps.Keys(tests.getSortedTestsByClusterID())) +} + // NewClassifier creates a test classifier from K8sClusterPool and TestCases. func NewClassifier(k8sClusterPool K8sClusterPool, testCases ...TestCase) Classifier { return Classifier{ From 347d28d72914d7dfe6b9cafe839080f47fe09ef8 Mon Sep 17 00:00:00 2001 From: marbarta Date: Mon, 31 Jul 2023 12:39:38 +0200 Subject: [PATCH 14/36] refactor comments and K8sClusterPool --- tests/e2e/pkg/tests/mocks.go | 30 +++--- tests/e2e/pkg/tests/tests.go | 114 +++++++++++---------- tests/e2e/pkg/tests/tests_test.go | 160 ++++++++++++++---------------- 3 files changed, 143 insertions(+), 161 deletions(-) diff --git a/tests/e2e/pkg/tests/mocks.go b/tests/e2e/pkg/tests/mocks.go index c165b9891..d64af0c5e 100644 --- a/tests/e2e/pkg/tests/mocks.go +++ b/tests/e2e/pkg/tests/mocks.go @@ -28,8 +28,7 @@ import ( // 1x2 + 1x3 = 5 test all-together // Runtime parallel: 1x(5) = 5sec (time of the longest testCase) func MockTestsMinimal() Classifier { - k8sClusterPool := K8sClusterPool{} - k8sClusterPool.AddK8sClusters( + k8sClusterPool := K8sClusterPool{ NewMockK8sCluster( "testContextPath1", "testContextName1", @@ -51,7 +50,7 @@ func MockTestsMinimal() Classifier { "provider3", "clusterID3", ), - ) + } return NewClassifier(k8sClusterPool, mockTest1, mockTest2) } @@ -61,8 +60,7 @@ func MockTestsMinimal() Classifier { // 3x2 + 3x3 = 15 test all-together // Runtime parallel: 3x(3) = 9sec (time of the longest testCase) func MockTestsProvider() Classifier { - k8sClusterPool := K8sClusterPool{} - k8sClusterPool.AddK8sClusters( + k8sClusterPool := K8sClusterPool{ NewMockK8sCluster( "testContextPath1", "testContextName1", @@ -84,7 +82,7 @@ func MockTestsProvider() Classifier { "provider3", "clusterID3", ), - ) + } return NewClassifier(k8sClusterPool, mockTest1, mockTest2) } @@ -94,8 +92,7 @@ func MockTestsProvider() Classifier { // 2x2 + 2x2 + 2x3 = 14 test all-together // Runtime parallel: 4 + 4 + 5 = 13 func MockTestsProviderMoreTestsThenProvider() Classifier { - k8sClusterPool := K8sClusterPool{} - k8sClusterPool.AddK8sClusters( + k8sClusterPool := K8sClusterPool{ NewMockK8sCluster( "testContextPath1", "testContextName1", @@ -110,7 +107,7 @@ func MockTestsProviderMoreTestsThenProvider() Classifier { "provider2", "clusterID2", ), - ) + } return NewClassifier(k8sClusterPool, mockTest1, mockTest2, mockTest3) } @@ -120,8 +117,7 @@ func MockTestsProviderMoreTestsThenProvider() Classifier { // 2x2 2x3 = 10 test all-together // Runtime parallel: 1x5 = 5 func MockTestsVersionOne() Classifier { - k8sClusterPool := K8sClusterPool{} - k8sClusterPool.AddK8sClusters( + k8sClusterPool := K8sClusterPool{ NewMockK8sCluster( "testContextPath1", "testContextName1", @@ -136,7 +132,7 @@ func MockTestsVersionOne() Classifier { "provider2", "clusterID2", ), - ) + } return NewClassifier(k8sClusterPool, mockTest1, mockTest2) } @@ -146,8 +142,7 @@ func MockTestsVersionOne() Classifier { // 2x2 2x3 = 10 test all-together // Runtime parallel: 4 + 5 = 9 func MockTestsVersion() Classifier { - k8sClusterPool := K8sClusterPool{} - k8sClusterPool.AddK8sClusters( + k8sClusterPool := K8sClusterPool{ NewMockK8sCluster( "testContextPath1", "testContextName1", @@ -169,7 +164,7 @@ func MockTestsVersion() Classifier { "provider3", "clusterID4", ), - ) + } return NewClassifier(k8sClusterPool, mockTest1, mockTest2) } @@ -179,8 +174,7 @@ func MockTestsVersion() Classifier { // 2x2 2x3 = 10 test all-together // Runtime parallel: 4 + 5 = 9 func MockTestsComplete() Classifier { - k8sClusterPool := K8sClusterPool{} - k8sClusterPool.AddK8sClusters( + k8sClusterPool := K8sClusterPool{ NewMockK8sCluster( "testContextPath1", "testContextName1", @@ -209,7 +203,7 @@ func MockTestsComplete() Classifier { "provider3", "clusterID4", ), - ) + } return NewClassifier(k8sClusterPool, mockTest1, mockTest2) } diff --git a/tests/e2e/pkg/tests/tests.go b/tests/e2e/pkg/tests/tests.go index 8d99c8bfd..87125be3d 100644 --- a/tests/e2e/pkg/tests/tests.go +++ b/tests/e2e/pkg/tests/tests.go @@ -43,6 +43,7 @@ import ( type TestPool []Test +// Equal returns true when the TestPools are equal func (tests TestPool) Equal(other TestPool) bool { if len(tests) != len(other) { return false @@ -79,16 +80,18 @@ ExpectedDurationParallel: %s tests.GetTestSuiteDurationParallel().String()) } +// BuildParallelByK8sCluster builds Describe container blocks from tests and K8sClusters. +// In this way we can have guarantee to the tests serial execution for a K8sCluster. func (tests TestPool) BuildParallelByK8sCluster() { testsByClusterID := tests.getSortedTestsByClusterID() testsByClusterIDKeys := sortedKeys(testsByClusterID) describeID := 0 // Because of the "Ordered" decorator testCases inside that container will run each after another - // so this K8s cluster is dedicated for tests for serial test execution + // thus this K8s cluster (container) is dedicated for certain test for serial execution. for _, clusterID := range testsByClusterIDKeys { describeID += 1 tests := testsByClusterID[clusterID] - // Describe name has to be unique and consistent between processes for proper parallelization + // Describe name has to be unique and consistent between processes for proper parallelization. Describe(fmt.Sprintf("%d:", describeID), decorators.Label(fmt.Sprintf("K8sID:%s", clusterID)), Ordered, func() { for _, test := range tests { test.Run() @@ -143,8 +146,8 @@ func (tests TestPool) getTestsByProviders() map[string][]Test { return testsByProviders } -// GetTestSuiteDurationParallel calculate the expected duration of the tests when -// suite is executed in parallel +// GetTestSuiteDurationParallel calculates the expected duration of the tests when +// suite is executed in parallel. func (tests TestPool) GetTestSuiteDurationParallel() time.Duration { testsByClusterID := tests.getSortedTestsByClusterID() @@ -162,8 +165,8 @@ func (tests TestPool) GetTestSuiteDurationParallel() time.Duration { return max } -// GetTestSuiteDurationParallel calculate the expected duration of the tests when -// suite is executed in serial +// GetTestSuiteDurationSerial calculates the expected duration of the tests when +// test suite is executed in serial. func (tests TestPool) GetTestSuiteDurationSerial() time.Duration { var allDuration time.Duration for _, test := range tests { @@ -172,8 +175,7 @@ func (tests TestPool) GetTestSuiteDurationSerial() time.Duration { return allDuration } -// GetTestSuiteDurationParallel calculate the expected duration of the tests when -// suite is executed in serial +// GetTestSuiteSpecsCount returns the number of specs in the pool based on the testCase explicit information. func (tests TestPool) GetTestSuiteSpecsCount() int { var specsCount int for _, test := range tests { @@ -195,7 +197,7 @@ func NewClassifier(k8sClusterPool K8sClusterPool, testCases ...TestCase) Classif } } -// Classifier makes pairs from testCases to K8sClusters based on the specified test strategy +// Classifier makes pairs from testCases and K8sClusters based on the specified test strategy type Classifier struct { k8sClusterPool K8sClusterPool testCases []TestCase @@ -210,7 +212,7 @@ func (t Classifier) Minimal() TestPool { for i, testCase := range t.testCases { tests = append(tests, Test{ testCase: testCase, - k8sCluster: t.k8sClusterPool.k8sClusters[i%len(t.k8sClusterPool.k8sClusters)], + k8sCluster: t.k8sClusterPool[i%len(t.k8sClusterPool)], }) } return tests @@ -272,18 +274,11 @@ func (t Classifier) Complete() TestPool { return tests } -// K8sClusterPool contains the K8sClusters -type K8sClusterPool struct { - k8sClusters []K8sCluster -} - -// AddK8sClusters add a K8sCluster(s) into the pool -func (t *K8sClusterPool) AddK8sClusters(cluster ...K8sCluster) { - t.k8sClusters = append(t.k8sClusters, cluster...) -} +// K8sClusterPool is a representation of the K8sClusters. +type K8sClusterPool []K8sCluster -// FeedFomDirectory creates K8sClusters from kubeconfigs in the specified directory and add them into the pool -func (t *K8sClusterPool) FeedFomDirectory(kubeConfigDirectoryPath string) error { +// FeedFomDirectory creates K8sClusters from kubeconfigs in the specified directory and add them into the pool. +func (k8sPool K8sClusterPool) FeedFomDirectory(kubeConfigDirectoryPath string) error { files, err := os.ReadDir(kubeConfigDirectoryPath) if err != nil { return fmt.Errorf("unable to read kubeConfig directory '%s' error: %w", kubeConfigDirectoryPath, err) @@ -301,35 +296,35 @@ func (t *K8sClusterPool) FeedFomDirectory(kubeConfigDirectoryPath string) error if err != nil { return fmt.Errorf("could not get K8sClusters from file '%s' err: %w", kubectlPath, err) } - t.AddK8sClusters(k8sClusters...) + k8sPool = append(k8sPool, k8sClusters...) } } return nil } -func (t K8sClusterPool) getByVersions() map[string][]K8sCluster { +func (k8sPool K8sClusterPool) getByVersions() map[string][]K8sCluster { byVersions := make(map[string][]K8sCluster) - for _, k8sCluster := range t.k8sClusters { + for _, k8sCluster := range k8sPool { byVersions[k8sCluster.clusterInfo.version] = append(byVersions[k8sCluster.clusterInfo.version], k8sCluster) } return byVersions } -func (t K8sClusterPool) getByProviders() map[string][]K8sCluster { +func (k8sPool K8sClusterPool) getByProviders() map[string][]K8sCluster { byProviders := make(map[string][]K8sCluster) - for _, k8sCluster := range t.k8sClusters { + for _, k8sCluster := range k8sPool { byProviders[k8sCluster.clusterInfo.provider] = append(byProviders[k8sCluster.clusterInfo.provider], k8sCluster) } return byProviders } -func (t K8sClusterPool) getByProvidersVersions() map[string]map[string][]K8sCluster { +func (k8sPool K8sClusterPool) getByProvidersVersions() map[string]map[string][]K8sCluster { byProvidersVersions := make(map[string]map[string][]K8sCluster) - byProviders := t.getByProviders() + byProviders := k8sPool.getByProviders() for provider, k8sClusters := range byProviders { for _, k8sCluster := range k8sClusters { @@ -342,11 +337,11 @@ func (t K8sClusterPool) getByProvidersVersions() map[string]map[string][]K8sClus return byProvidersVersions } -// TestCase is a representation of an E2E test case -// SpecsCount is the number of the gingko specs -// Duration specifies the expected length of the test -// Name is the name of the test -// TestFn specifies the test function +// TestCase is a representation of an E2E test case. +// - SpecsCount is the number of the gingko specs in the test. +// - Duration specifies the expected length of the test. +// - Name is the name of the test. +// - TestFn specifies the test function. type TestCase struct { SpecsCount int Duration time.Duration @@ -354,21 +349,20 @@ type TestCase struct { TestFn func(kubectlOptions k8s.KubectlOptions) } -// NewTest creates a Test from a TestCase and from a K8sCluster -// testID for Test is generated automatically based on Name and K8s clusterInfo +// NewTest creates a Test from a TestCase and from a K8sCluster. +// The testID for Test is generated automatically based on Name and K8s clusterInfo. func NewTest(testCase TestCase, k8sCluster K8sCluster) Test { - testIDseed := fmt.Sprintf("%v%v", testCase.Name, k8sCluster.clusterInfo) - hash := md5.Sum([]byte(testIDseed)) - testID := hex.EncodeToString(hash[:5]) - return Test{ - testID: testID, + test := Test{ testCase: testCase, k8sCluster: k8sCluster, } + test.generateTestID() + + return test } // Test represents an E2E test. -// It is a combination of a testCase and a k8sCluster +// It is a combination of a testCase and a k8sCluster. type Test struct { testID string testCase TestCase @@ -383,13 +377,17 @@ func (t Test) less(test Test) bool { return t.TestID() < test.TestID() } -// TestID return the test UID based on test name and K8sCluster +// generateTestID generates the test UID based on the name of the test and the K8sCluster. +func (t *Test) generateTestID() string { + text := fmt.Sprintf("%v%v", t.testCase.Name, t.k8sCluster) + hash := md5.Sum([]byte(text)) + t.testID = hex.EncodeToString(hash[:5]) + + return t.testID +} + +// TestID returns the test UID based on the name of the test and the K8sCluster. func (t *Test) TestID() string { - if t.testID == "" { - text := fmt.Sprintf("%v%v", t.testCase.Name, t.k8sCluster) - hash := md5.Sum([]byte(text)) - t.testID = hex.EncodeToString(hash[:5]) - } return t.testID } @@ -400,7 +398,7 @@ func (t Test) String() string { // Run encapsulates the testCase in a Describe container with // K8s cluster health check and testID label. -// When K8s cluster is not available the further tests are skipped +// When K8s cluster is not available the further tests will be skipped. func (t Test) Run() { Describe(fmt.Sprint(t), Ordered, decorators.Label(fmt.Sprintf("testID:%s", t.TestID())), func() { kubectlOptions, err := t.k8sCluster.KubectlOptions() @@ -464,8 +462,8 @@ type k8sClusterInfo struct { version string } -// NewK8sCluster retrieve the K8s cluster information and creates the K8sCluster resource -// When K8s cluster information is not available it return error +// NewK8sCluster retrieves the K8s cluster information and creates the K8sCluster resource. +// When it cannot get K8s cluster information it returns error. func NewK8sCluster(kubectlOptions k8s.KubectlOptions) (K8sCluster, error) { clusterInfo, err := newK8sClusterInfo(kubectlOptions) if err != nil { @@ -513,14 +511,14 @@ func NewMockK8sCluster(kubeConfigPath, kubeContext string, version, provider, cl } // NewK8sClusterFromParams creates K8sCluster from kubectlConfigPath path and kubectlContext. -// Cluster information is fetched from the K8s cluster automatically +// Cluster information is fetched from the K8s cluster automatically. func NewK8sClusterFromParams(kubectlConfigPath, kubectlContext string) (K8sCluster, error) { return NewK8sCluster(*k8s.NewKubectlOptions(kubectlContext, kubectlConfigPath, "")) } -// NewK8sClustersFromParams creates K8sClusters based on kubeconfig path -// When multiple context is found in the kubeconfig it creates multiple K8sClusters for all of them -// Cluster information is fetched from the K8s cluster automatically +// NewK8sClustersFromParams creates K8sClusters based on kubeconfig path. +// When multiple context is found in the kubeconfig it creates multiple K8sClusters from all of them. +// Cluster information is fetched from the K8s cluster automatically at creation time. func NewK8sClustersFromParams(kubectlConfigPath string) ([]K8sCluster, error) { kubeContexts, err := common.GetKubeContexts(kubectlConfigPath) if err != nil { @@ -539,8 +537,8 @@ func NewK8sClustersFromParams(kubectlConfigPath string) ([]K8sCluster, error) { return k8sClusters, nil } -// NewK8sClusterFromCurrentConfig creates K8sCluster from the local KUBECONFIG env and it's current context -// Cluster information is fetched from the K8s cluster automatically +// NewK8sClusterFromCurrentConfig creates K8sCluster from the local KUBECONFIG env and it's current context. +// Cluster information is fetched from the K8s cluster automatically. func NewK8sClusterFromCurrentConfig() (K8sCluster, error) { kubectlOptions, err := common.KubectlOptionsForCurrentContext() if err != nil { @@ -560,7 +558,7 @@ func NewK8sClusterFromCurrentConfig() (K8sCluster, error) { } // K8sCluster represents a K8s cluster. -// It contains informations and access related configurations about K8s cluster +// It contains informations and access related configurations about K8s cluster. type K8sCluster struct { clusterInfo k8sClusterInfo kubectlOptions k8s.KubectlOptions @@ -570,7 +568,7 @@ func (c K8sCluster) isKubectlOptionsFilled() bool { return c.kubectlOptions.ConfigPath != "" && c.kubectlOptions.ContextName != "" } -// KubectlOptions returns K8s access related configurations for kubectl +// KubectlOptions returns K8s access related configurations for kubectl. func (c K8sCluster) KubectlOptions() (k8s.KubectlOptions, error) { if !c.isKubectlOptionsFilled() { return k8s.KubectlOptions{}, errors.New("kubectlOptions is unfilled") diff --git a/tests/e2e/pkg/tests/tests_test.go b/tests/e2e/pkg/tests/tests_test.go index 8bc7e59a8..cb0a094d6 100644 --- a/tests/e2e/pkg/tests/tests_test.go +++ b/tests/e2e/pkg/tests/tests_test.go @@ -32,20 +32,18 @@ func Test_Classifier_minimal(t *testing.T) { name: "simpleCase", fields: fields{ k8sClusterPool: K8sClusterPool{ - k8sClusters: []K8sCluster{ - { - clusterInfo: k8sClusterInfo{ - clusterID: "local1", - version: "1.24", - provider: "provider1", - }, - }, - { - clusterInfo: k8sClusterInfo{ - clusterID: "local2", - version: "1.25", - provider: "provider1", - }, + { + clusterInfo: k8sClusterInfo{ + clusterID: "local1", + version: "1.24", + provider: "provider1", + }, + }, + { + clusterInfo: k8sClusterInfo{ + clusterID: "local2", + version: "1.25", + provider: "provider1", }, }, }, @@ -117,22 +115,20 @@ func Test_Classifier_providerComplete(t *testing.T) { name: "simpleCase", fields: fields{ k8sClusterPool: K8sClusterPool{ - k8sClusters: []K8sCluster{ - { + { - clusterInfo: k8sClusterInfo{ - clusterID: "local1", - version: "1.24", - provider: "provider1", - }, + clusterInfo: k8sClusterInfo{ + clusterID: "local1", + version: "1.24", + provider: "provider1", }, - { + }, + { - clusterInfo: k8sClusterInfo{ - clusterID: "local2", - version: "1.25", - provider: "provider2", - }, + clusterInfo: k8sClusterInfo{ + clusterID: "local2", + version: "1.25", + provider: "provider2", }, }, }, @@ -230,22 +226,20 @@ func Test_Classifier_versionComplete(t *testing.T) { name: "simpleCase", fields: fields{ k8sClusterPool: K8sClusterPool{ - k8sClusters: []K8sCluster{ - { + { - clusterInfo: k8sClusterInfo{ - clusterID: "local1", - version: "1.24", - provider: "provider1", - }, + clusterInfo: k8sClusterInfo{ + clusterID: "local1", + version: "1.24", + provider: "provider1", }, - { + }, + { - clusterInfo: k8sClusterInfo{ - clusterID: "local1", - version: "1.25", - provider: "provider1", - }, + clusterInfo: k8sClusterInfo{ + clusterID: "local1", + version: "1.25", + provider: "provider1", }, }, }, @@ -343,22 +337,20 @@ func Test_Classifier_complete(t *testing.T) { name: "simpleCase", fields: fields{ k8sClusterPool: K8sClusterPool{ - k8sClusters: []K8sCluster{ - { + { - clusterInfo: k8sClusterInfo{ - clusterID: "local1", - version: "1.24", - provider: "provider1", - }, + clusterInfo: k8sClusterInfo{ + clusterID: "local1", + version: "1.24", + provider: "provider1", }, - { + }, + { - clusterInfo: k8sClusterInfo{ - clusterID: "local2", - version: "1.25", - provider: "provider2", - }, + clusterInfo: k8sClusterInfo{ + clusterID: "local2", + version: "1.25", + provider: "provider2", }, }, }, @@ -430,38 +422,36 @@ func Test_Classifier_complete(t *testing.T) { name: "complexCase", fields: fields{ k8sClusterPool: K8sClusterPool{ - k8sClusters: []K8sCluster{ - { - - clusterInfo: k8sClusterInfo{ - clusterID: "local1", - version: "1.24", - provider: "provider1", - }, - }, - { - - clusterInfo: k8sClusterInfo{ - clusterID: "local2", - version: "1.25", - provider: "provider2", - }, - }, - { - - clusterInfo: k8sClusterInfo{ - clusterID: "local3", - version: "1.25", - provider: "provider3", - }, - }, - { - - clusterInfo: k8sClusterInfo{ - clusterID: "local4", - version: "1.25", - provider: "provider3", - }, + { + + clusterInfo: k8sClusterInfo{ + clusterID: "local1", + version: "1.24", + provider: "provider1", + }, + }, + { + + clusterInfo: k8sClusterInfo{ + clusterID: "local2", + version: "1.25", + provider: "provider2", + }, + }, + { + + clusterInfo: k8sClusterInfo{ + clusterID: "local3", + version: "1.25", + provider: "provider3", + }, + }, + { + + clusterInfo: k8sClusterInfo{ + clusterID: "local4", + version: "1.25", + provider: "provider3", }, }, }, From 2b85e9f4affdc52e1b2980983dcb990fa193bf2f Mon Sep 17 00:00:00 2001 From: marbarta Date: Thu, 3 Aug 2023 09:07:19 +0200 Subject: [PATCH 15/36] fix mockTests description --- tests/e2e/pkg/tests/mocks.go | 74 ++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/tests/e2e/pkg/tests/mocks.go b/tests/e2e/pkg/tests/mocks.go index d64af0c5e..78a793608 100644 --- a/tests/e2e/pkg/tests/mocks.go +++ b/tests/e2e/pkg/tests/mocks.go @@ -22,11 +22,13 @@ import ( "time" ) -// MockTestsMinimal: -// 3 different provider 2 different version 3 different K8s cluster with 2 tests -// Expected: 2 testCase 1 testCase on any available K8sCluster -// 1x2 + 1x3 = 5 test all-together -// Runtime parallel: 1x(5) = 5sec (time of the longest testCase) +// MockTestsMinimal returns a Classifier that has 3 different K8s clusters using 2 different K8s versions and 3 different providers. +// The Classifier contains 2 tests. +// +// Expected (minimal strategy): +// - 2 testCases +// - 1 testCase on any of the available K8sClusters +// - runtime parallel: 1x(5) = 5sec (time of the longest testCase) func MockTestsMinimal() Classifier { k8sClusterPool := K8sClusterPool{ NewMockK8sCluster( @@ -54,11 +56,13 @@ func MockTestsMinimal() Classifier { return NewClassifier(k8sClusterPool, mockTest1, mockTest2) } -// MockTestsProvider: -// 3 different provider 3 different K8s cluster with 2 tests -// Expected: 6 testCase 2 testCase on every provider -// 3x2 + 3x3 = 15 test all-together -// Runtime parallel: 3x(3) = 9sec (time of the longest testCase) +// MockTestsProvider returns a Classifier that has 3 different K8s clusters using 3 different K8s provider. +// The Classifier contains 2 tests. +// +// Expected (provider strategy): +// - 6 testCases +// - 2 testCases on different providers +// - runtime parallel: 3x(3) = 9sec func MockTestsProvider() Classifier { k8sClusterPool := K8sClusterPool{ NewMockK8sCluster( @@ -86,11 +90,13 @@ func MockTestsProvider() Classifier { return NewClassifier(k8sClusterPool, mockTest1, mockTest2) } -// MockTestsProviderMoreTestsThenProvider: -// 2 different provider 2 different K8s cluster with 3 tests -// Expected: 6 testCase 3 testCase on every provider -// 2x2 + 2x2 + 2x3 = 14 test all-together -// Runtime parallel: 4 + 4 + 5 = 13 +// MockTestsProviderMoreTestsThenProvider returns a Classifier that has 2 different K8s clusters using 2 different K8s provider. +// The Classifier contains 3 tests. +// +// Expected (provider strategy): +// - 6 testCases +// - 3 testCases on different providers +// - runtime parallel: 4 + 4 + 5 = 13sec func MockTestsProviderMoreTestsThenProvider() Classifier { k8sClusterPool := K8sClusterPool{ NewMockK8sCluster( @@ -111,11 +117,13 @@ func MockTestsProviderMoreTestsThenProvider() Classifier { return NewClassifier(k8sClusterPool, mockTest1, mockTest2, mockTest3) } -// MockTestsVersionOne: -// no different version 2 different K8s cluster with 2 tests -// Expected: 2 testCase -> 1 testCase on each K8sCluster -// 2x2 2x3 = 10 test all-together -// Runtime parallel: 1x5 = 5 +// MockTestsVersionOne returns a Classifier that has 2 different K8s clusters using same K8s versions and 2 different providers. +// The Classifier contains 2 tests. +// +// Expected (version strategy): +// - 2 testCases +// - 1 testCase on any of the available K8sClusters +// - runtime parallel: 1 x 5 = 5sec func MockTestsVersionOne() Classifier { k8sClusterPool := K8sClusterPool{ NewMockK8sCluster( @@ -136,11 +144,13 @@ func MockTestsVersionOne() Classifier { return NewClassifier(k8sClusterPool, mockTest1, mockTest2) } -// MockTestsVersion: -// 2 different version 3 different K8s cluster with 2 tests -// Expected: 4 testCase -> 2 testCase on each version -// 2x2 2x3 = 10 test all-together -// Runtime parallel: 4 + 5 = 9 +// MockTestsVersion returns a Classifier that has 3 different K8s clusters using 2 different K8s versions and 2 different providers. +// The Classifier contains 2 tests. +// +// Expected (version strategy): +// - 2 testCases +// - 1 testCase on any of the available K8sClusters +// - runtime parallel: 1 x 5 = 5sec func MockTestsVersion() Classifier { k8sClusterPool := K8sClusterPool{ NewMockK8sCluster( @@ -168,11 +178,13 @@ func MockTestsVersion() Classifier { return NewClassifier(k8sClusterPool, mockTest1, mockTest2) } -// MockTestsVersion: -// 2 different version 2 different version 4 K8s cluster with 2 tests -// Expected: 4 testCase -> 2 testCase on each version -// 2x2 2x3 = 10 test all-together -// Runtime parallel: 4 + 5 = 9 +// MockTestsComplete returns a Classifier that has 4 different K8s clusters using 2 different K8s versions and 3 different providers. +// The Classifier contains 2 tests. +// +// Expected (complete strategy): +// - 6 testCases +// - 2 testCase on every different K8sClusters provider and version +// - runtime parallel: 4 + 5 = 9sec func MockTestsComplete() Classifier { k8sClusterPool := K8sClusterPool{ NewMockK8sCluster( @@ -236,8 +248,6 @@ func testMockTest2(kubectlOptions k8s.KubectlOptions) { }) It("MockTest2-2", func() { time.Sleep(time.Second * 1) - //Expect(0).Should(Equal(1)) - AddReportEntry("Output:", CurrentSpecReport().CapturedGinkgoWriterOutput) }) It("MockTest2-3", func() { time.Sleep(time.Second * 1) From b678b1756446e619a22cd9024b8298a78c3ae292 Mon Sep 17 00:00:00 2001 From: marbarta Date: Thu, 3 Aug 2023 09:11:32 +0200 Subject: [PATCH 16/36] fix: missing kubectlOptions param --- tests/e2e/test_alltestcases.go | 4 ++-- tests/e2e/test_snapshot.go | 16 ++++------------ 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/tests/e2e/test_alltestcases.go b/tests/e2e/test_alltestcases.go index 778143552..5011867cd 100644 --- a/tests/e2e/test_alltestcases.go +++ b/tests/e2e/test_alltestcases.go @@ -30,7 +30,7 @@ var alltestCase = tests.TestCase{ func allTestCase(kubectlOptions k8s.KubectlOptions) { var snapshottedInfo = &clusterSnapshot{} - snapshotCluster(snapshottedInfo) + snapshotCluster(kubectlOptions, snapshottedInfo) testInstall(kubectlOptions) testInstallZookeeperCluster(kubectlOptions) testInstallKafkaCluster(kubectlOptions, "../../config/samples/simplekafkacluster.yaml") @@ -44,5 +44,5 @@ func allTestCase(kubectlOptions k8s.KubectlOptions) { testUninstallKafkaCluster(kubectlOptions) testUninstallZookeeperCluster(kubectlOptions) testUninstall(kubectlOptions) - snapshotClusterAndCompare(snapshottedInfo) + snapshotClusterAndCompare(kubectlOptions, snapshottedInfo) } diff --git a/tests/e2e/test_snapshot.go b/tests/e2e/test_snapshot.go index c6a0206c3..c8a7955a6 100644 --- a/tests/e2e/test_snapshot.go +++ b/tests/e2e/test_snapshot.go @@ -59,19 +59,11 @@ type localComparisonPartialObjectMetadataType struct { // snapshotCluster takes a clusterSnapshot of a K8s cluster and // stores it into the snapshotCluster instance referenced as input -func snapshotCluster(snapshottedInfo *clusterSnapshot) bool { +func snapshotCluster(kubectlOptions k8s.KubectlOptions, snapshottedInfo *clusterSnapshot) bool { return When("Get cluster resources state", Ordered, func() { - var kubectlOptions k8s.KubectlOptions - var err error - - BeforeAll(func() { - By("Acquiring K8s config and context") - kubectlOptions, err = kubectlOptionsForCurrentContext() - Expect(err).NotTo(HaveOccurred()) - }) - var clusterResourceNames []string var namespacedResourceNames []string + var err error When("Get api-resources names", func() { It("Get cluster-scoped api-resources names", func() { @@ -135,10 +127,10 @@ func snapshotCluster(snapshottedInfo *clusterSnapshot) bool { // snapshotClusterAndCompare takes a current snapshot of the K8s cluster and // compares it against a snapshot provided as input -func snapshotClusterAndCompare(snapshottedInitialInfo *clusterSnapshot) bool { +func snapshotClusterAndCompare(kubectlOptions k8s.KubectlOptions, snapshottedInitialInfo *clusterSnapshot) bool { return When("Verifying cluster resources state", Ordered, func() { var snapshottedCurrentInfo = &clusterSnapshot{} - snapshotCluster(snapshottedCurrentInfo) + snapshotCluster(kubectlOptions, snapshottedCurrentInfo) It("Checking resources list", func() { // Temporarily increase maximum output length (default 4000) to fit more objects in the printed diff. From 400c0ff9d666b916831c6613d2fc80175563f46a Mon Sep 17 00:00:00 2001 From: marbarta Date: Thu, 3 Aug 2023 09:18:45 +0200 Subject: [PATCH 17/36] fix David suggestions 1 --- tests/e2e/const.go | 2 +- tests/e2e/koperator_suite_test.go | 10 ++++++---- tests/e2e/pkg/tests/tests.go | 8 ++++---- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/tests/e2e/const.go b/tests/e2e/const.go index 542e63fde..b4b74b703 100644 --- a/tests/e2e/const.go +++ b/tests/e2e/const.go @@ -53,7 +53,7 @@ const ( defaultTopicCreationWaitTime = 10 * time.Second defaultUserCreationWaitTime = 10 * time.Second - kafkaClusterCreateTimeout = 600 * time.Second // Increased from 600 to 700 for multiple kind + kafkaClusterCreateTimeout = 600 * time.Second kafkaClusterResourceCleanupTimeout = 120 * time.Second kcatDeleetionTimeout = 40 * time.Second zookeeperClusterCreateTimeout = 7 * time.Minute // Increased from 4 to 7 for multiple kind diff --git a/tests/e2e/koperator_suite_test.go b/tests/e2e/koperator_suite_test.go index ea6d20790..34f82141b 100644 --- a/tests/e2e/koperator_suite_test.go +++ b/tests/e2e/koperator_suite_test.go @@ -100,19 +100,21 @@ func runGinkgoTests(t *testing.T) error { if err != nil { return fmt.Errorf("could not parse MaxTimeout into time.Duration: %w", err) } - // Protection against too long test suites - if testSuiteDuration > maxTimeout { - return fmt.Errorf("tests estimated duration: '%s' bigger then maxTimeout: '%s'", testSuiteDuration.String(), maxTimeout.String()) - } // Calculated timeout can be overran with the specified time length allowedOverrun, err := time.ParseDuration(viper.GetString(config.Tests.AllowedOverrunDuration)) if err != nil { return fmt.Errorf("could not parse AllowedOverrunDuration into time.Duration: %w", err) } + // Set TestSuite timeout based on the generated tests suiteConfig.Timeout = testSuiteDuration + allowedOverrun + // Protection against too long test suites + if suiteConfig.Timeout > maxTimeout { + return fmt.Errorf("tests estimated duration: '%s' longer then maxTimeout: '%s'", suiteConfig.Timeout.String(), maxTimeout.String()) + } + if viper.GetBool(config.Tests.CreateTestReportFile) { if err := createTestReportFile(); err != nil { return err diff --git a/tests/e2e/pkg/tests/tests.go b/tests/e2e/pkg/tests/tests.go index 87125be3d..7bb7b3f3f 100644 --- a/tests/e2e/pkg/tests/tests.go +++ b/tests/e2e/pkg/tests/tests.go @@ -49,8 +49,8 @@ func (tests TestPool) Equal(other TestPool) bool { return false } - tests.Sort() - other.Sort() + tests.sort() + other.sort() for i := range tests { if !tests[i].equal(other[i]) { @@ -100,7 +100,7 @@ func (tests TestPool) BuildParallelByK8sCluster() { } } -func (tests TestPool) Sort() { +func (tests TestPool) sort() { sort.SliceStable(tests, func(i, j int) bool { return tests[i].less(tests[j]) }) @@ -110,7 +110,7 @@ func (tests TestPool) getSortedTestsByClusterID() map[string][]Test { testsByClusterID := make(map[string][]Test) // Need to be sorted to achieve test specs tree consistency between processes // otherwise it can happen that specs order will be different for each process - tests.Sort() + tests.sort() for _, test := range tests { testsByClusterID[test.k8sCluster.clusterInfo.clusterID] = append(testsByClusterID[test.k8sCluster.clusterInfo.clusterID], test) From 4c0b935457d32e7f364886804092ebd9a8d376e6 Mon Sep 17 00:00:00 2001 From: marbarta Date: Thu, 3 Aug 2023 09:32:09 +0200 Subject: [PATCH 18/36] fix David suggestions 2 --- tests/e2e/k8s.go | 53 ++-------------------------------- tests/e2e/pkg/common/common.go | 20 ++----------- tests/e2e/pkg/tests/tests.go | 1 + 3 files changed, 6 insertions(+), 68 deletions(-) diff --git a/tests/e2e/k8s.go b/tests/e2e/k8s.go index 8c4109016..f34d6570b 100644 --- a/tests/e2e/k8s.go +++ b/tests/e2e/k8s.go @@ -20,13 +20,13 @@ import ( "io" "net/http" "os" - "path" "strings" "text/template" "time" "emperror.dev/errors" "github.com/Masterminds/sprig" + "github.com/banzaicloud/koperator/tests/e2e/pkg/common" "github.com/cisco-open/k8s-objectmatcher/patch" "github.com/gruntwork-io/terratest/modules/k8s" . "github.com/onsi/ginkgo/v2" @@ -101,55 +101,6 @@ func createOrReplaceK8sResourcesFromManifest( //nolint:unused // Note: this migh } } -func getDefaultKubeContext(kubeconfigPath string) (string, error) { - kubeconfigBytes, err := os.ReadFile(kubeconfigPath) - if err != nil { - return "", errors.WrapIfWithDetails(err, "reading KUBECONFIG file failed", "path", kubeconfigPath) - } - - structuredKubeconfig := make(map[string]interface{}) - err = yaml.Unmarshal(kubeconfigBytes, &structuredKubeconfig) - if err != nil { - return "", errors.WrapIfWithDetails( - err, - "parsing kubeconfig failed", - "kubeconfig", string(kubeconfigBytes), - ) - } - - kubecontext, isOk := structuredKubeconfig["current-context"].(string) - if !isOk { - return "", errors.WrapIfWithDetails( - err, - "kubeconfig current-context is not string", - "current-context", structuredKubeconfig["current-context"], - ) - } - - return kubecontext, nil -} - -// currentKubernetesContext returns the currently set Kubernetes context based -// on the the environment variables and the KUBECONFIG file. -func currentEnvK8sContext() (kubeconfigPath string, kubecontextName string, err error) { - kubeconfigPath, isExisting := os.LookupEnv("KUBECONFIG") - if !isExisting { - homePath, err := os.UserHomeDir() - if err != nil { - return "", "", errors.WrapIf(err, "retrieving user home directory failed") - } - - kubeconfigPath = path.Join(homePath, ".kube", "config") - } - - kubecontext, err := getDefaultKubeContext(kubeconfigPath) - if err != nil { - return "", "", err - } - - return kubeconfigPath, kubecontext, nil -} - // getK8sCRD queries and returns the CRD of the specified CRD name from the // provided Kubernetes context. func getK8sCRD(kubectlOptions k8s.KubectlOptions, crdName string) ([]byte, error) { //nolint:unused // Note: this might come in handy for manual CRD operations. @@ -390,7 +341,7 @@ func kubectlOptions(kubecontextName, kubeconfigPath, namespace string) k8s.Kubec // kubectlOptionsForCurrentContext returns a kubectlOptions object for the // current Kubernetes context or alternatively an error. func kubectlOptionsForCurrentContext() (k8s.KubectlOptions, error) { - kubeconfigPath, kubecontextName, err := currentEnvK8sContext() + kubeconfigPath, kubecontextName, err := common.CurrentEnvK8sContext() if err != nil { return k8s.KubectlOptions{}, errors.WrapIf(err, "retrieving current environment Kubernetes context failed") } diff --git a/tests/e2e/pkg/common/common.go b/tests/e2e/pkg/common/common.go index 914e35699..19b5844ee 100644 --- a/tests/e2e/pkg/common/common.go +++ b/tests/e2e/pkg/common/common.go @@ -22,12 +22,11 @@ import ( "github.com/gruntwork-io/terratest/modules/k8s" "golang.org/x/exp/maps" "gopkg.in/yaml.v2" - "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd/api" ) -// currentKubernetesContext returns the currently set Kubernetes context based +// CurrentEnvK8sContext returns the currently set Kubernetes context based // on the the environment variables and the KUBECONFIG file. func CurrentEnvK8sContext() (kubeconfigPath string, kubecontextName string, err error) { kubeconfigPath, isExisting := os.LookupEnv("KUBECONFIG") @@ -63,6 +62,7 @@ func KubectlOptionsForCurrentContext() (k8s.KubectlOptions, error) { }, nil } +// GetDefaultKubeContext returns the default kubeContext name from the given kubeconfig file func GetDefaultKubeContext(kubeconfigPath string) (string, error) { kubeconfigBytes, err := os.ReadFile(kubeconfigPath) if err != nil { @@ -105,6 +105,7 @@ func GetRawConfig(kubeconfigPath string) (api.Config, error) { return clientConfig.RawConfig() } +// GetKubeContexts returns the available kubecontext names in the kubeconfig file func GetKubeContexts(kubeconfigPath string) ([]string, error) { configs, err := GetRawConfig(kubeconfigPath) if err != nil { @@ -112,18 +113,3 @@ func GetKubeContexts(kubeconfigPath string) ([]string, error) { } return maps.Keys(configs.Contexts), nil } - -// GetConfig returns kubernetes config based on the current environment. -// If fpath is provided, loads configuration from that file. Otherwise, -// GetConfig uses default strategy to load configuration from $KUBECONFIG, -// .kube/config, or just returns in-cluster config. -func GetConfigWithContext(kubeconfigPath, kubeContext string) (*rest.Config, error) { - rules := clientcmd.NewDefaultClientConfigLoadingRules() - if kubeconfigPath != "" { - rules.ExplicitPath = kubeconfigPath - } - overrides := &clientcmd.ConfigOverrides{CurrentContext: kubeContext} - return clientcmd. - NewNonInteractiveDeferredLoadingClientConfig(rules, overrides). - ClientConfig() -} diff --git a/tests/e2e/pkg/tests/tests.go b/tests/e2e/pkg/tests/tests.go index 7bb7b3f3f..ce6224b61 100644 --- a/tests/e2e/pkg/tests/tests.go +++ b/tests/e2e/pkg/tests/tests.go @@ -60,6 +60,7 @@ func (tests TestPool) Equal(other TestPool) bool { return true } +// PoolInfo returns a formatted string as information about the current testPool func (tests TestPool) PoolInfo() string { testsByContextName := tests.getTestsByContextName() testsByProviders := tests.getTestsByProviders() From 4031097bc70584321d1e3bf013d35a7fa92ad687 Mon Sep 17 00:00:00 2001 From: marbarta Date: Thu, 3 Aug 2023 09:36:48 +0200 Subject: [PATCH 19/36] fix unnecessary imports --- tests/e2e/pkg/tests/mocks.go | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/e2e/pkg/tests/mocks.go b/tests/e2e/pkg/tests/mocks.go index 78a793608..74102105d 100644 --- a/tests/e2e/pkg/tests/mocks.go +++ b/tests/e2e/pkg/tests/mocks.go @@ -18,7 +18,6 @@ import ( "github.com/gruntwork-io/terratest/modules/k8s" . "github.com/onsi/ginkgo/v2" - //. "github.com/onsi/gomega" "time" ) From edab06c2c569bbce3d7e0ada7a4af494143383ce Mon Sep 17 00:00:00 2001 From: marbarta Date: Tue, 8 Aug 2023 13:33:07 +0200 Subject: [PATCH 20/36] fix: feedFromDirectory --- tests/e2e/koperator_suite_test.go | 2 +- tests/e2e/pkg/tests/tests.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/e2e/koperator_suite_test.go b/tests/e2e/koperator_suite_test.go index 34f82141b..3f59c4ecf 100644 --- a/tests/e2e/koperator_suite_test.go +++ b/tests/e2e/koperator_suite_test.go @@ -37,7 +37,7 @@ import ( var testPool tests.TestPool func beforeSuite() (tests.TestPool, error) { - k8sClusterPool := tests.K8sClusterPool{} + var k8sClusterPool tests.K8sClusterPool if err := k8sClusterPool.FeedFomDirectory(viper.GetString(config.Tests.KubeConfigDirectoryPath)); err != nil { return nil, err } diff --git a/tests/e2e/pkg/tests/tests.go b/tests/e2e/pkg/tests/tests.go index ce6224b61..92027cda7 100644 --- a/tests/e2e/pkg/tests/tests.go +++ b/tests/e2e/pkg/tests/tests.go @@ -279,7 +279,7 @@ func (t Classifier) Complete() TestPool { type K8sClusterPool []K8sCluster // FeedFomDirectory creates K8sClusters from kubeconfigs in the specified directory and add them into the pool. -func (k8sPool K8sClusterPool) FeedFomDirectory(kubeConfigDirectoryPath string) error { +func (k8sPool *K8sClusterPool) FeedFomDirectory(kubeConfigDirectoryPath string) error { files, err := os.ReadDir(kubeConfigDirectoryPath) if err != nil { return fmt.Errorf("unable to read kubeConfig directory '%s' error: %w", kubeConfigDirectoryPath, err) @@ -297,7 +297,7 @@ func (k8sPool K8sClusterPool) FeedFomDirectory(kubeConfigDirectoryPath string) e if err != nil { return fmt.Errorf("could not get K8sClusters from file '%s' err: %w", kubectlPath, err) } - k8sPool = append(k8sPool, k8sClusters...) + *k8sPool = append(*k8sPool, k8sClusters...) } } return nil From 98b4a0d94ecdc67a248900a0a67bd0002b2e2dae Mon Sep 17 00:00:00 2001 From: marbarta Date: Wed, 9 Aug 2023 11:54:44 +0200 Subject: [PATCH 21/36] add fix and test for version and provider identifier --- tests/e2e/pkg/tests/utils.go | 10 +- tests/e2e/pkg/tests/utils_test.go | 196 ++++++++++++++++++++++++++++++ 2 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 tests/e2e/pkg/tests/utils_test.go diff --git a/tests/e2e/pkg/tests/utils.go b/tests/e2e/pkg/tests/utils.go index c0c7a65cf..562edbc58 100644 --- a/tests/e2e/pkg/tests/utils.go +++ b/tests/e2e/pkg/tests/utils.go @@ -27,21 +27,21 @@ const ( var k8sProviderRegexp = map[k8sProvider]string{ k8sProviderGKE: "^gke-", k8sProviderAKS: "^aks-", - k8sProviderPKE: ".compute.internal", //TODO (marbarta): this is not so good (is not unique PKE) + k8sProviderPKE: ".compute.internal@v[0-9].[0-9]+.[0-9]+$", //TODO (marbarta): this is not so good (is not unique PKE) k8sProviderEKS: ".[0-9]-eks-", k8sProviderKind: "^kind", } func versionIdentifier(version string) (string, error) { - version = regexp.MustCompile(k8sVersionRegexp).FindString(version) - if version == "" { + versionMatch := regexp.MustCompile(k8sVersionRegexp).FindString(version) + if versionMatch == "" { return "", fmt.Errorf("K8s cluster version could not be recognized: '%s'", version) } - return version, nil + return versionMatch, nil } func providerIdentifier(node corev1.Node) (k8sProvider, error) { - checkString := node.Name + node.Status.NodeInfo.KubeletVersion + checkString := node.Name + "@" + node.Status.NodeInfo.KubeletVersion var foundProvider k8sProvider for provider, regexpProvider := range k8sProviderRegexp { if regexp.MustCompile(regexpProvider).MatchString(checkString) { diff --git a/tests/e2e/pkg/tests/utils_test.go b/tests/e2e/pkg/tests/utils_test.go new file mode 100644 index 000000000..d146868ed --- /dev/null +++ b/tests/e2e/pkg/tests/utils_test.go @@ -0,0 +1,196 @@ +package tests + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func Test_providerIdentifier(t *testing.T) { + type args struct { + node corev1.Node + } + tests := []struct { + name string + args args + want k8sProvider + wantErr error + }{ + { + name: "Unrecognized provider", + args: args{corev1.Node{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "almafa", + }, + Status: corev1.NodeStatus{ + NodeInfo: corev1.NodeSystemInfo{ + KubeletVersion: " v1.24.14", + }, + }, + }}, + wantErr: errors.New("provider could not been identified"), + }, + { + name: "GKE provider", + args: args{corev1.Node{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "gke-name-pool1-a9e92295-f4ml", + }, + Status: corev1.NodeStatus{ + NodeInfo: corev1.NodeSystemInfo{ + KubeletVersion: "v1.24.14-gke.2700", + }, + }, + }}, + want: "GKE", + wantErr: nil, + }, + { + name: "AKS provider", + args: args{corev1.Node{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "aks-name-pool1-a9e92295-f4ml", + }, + Status: corev1.NodeStatus{ + NodeInfo: corev1.NodeSystemInfo{ + KubeletVersion: "v1.24.14", + }, + }, + }}, + want: "AKS", + wantErr: nil, + }, + { + name: "EKS provider", + args: args{corev1.Node{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "ip-192-168-64-60.eu-west-1.compute.internal", + }, + Status: corev1.NodeStatus{ + NodeInfo: corev1.NodeSystemInfo{ + KubeletVersion: "v1.23.9-eks-ba74326", + }, + }, + }}, + want: "EKS", + wantErr: nil, + }, + { + name: "Kind provider", + args: args{corev1.Node{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "kind.node1", + }, + Status: corev1.NodeStatus{ + NodeInfo: corev1.NodeSystemInfo{ + KubeletVersion: "v1.24.14", + }, + }, + }}, + want: "Kind", + wantErr: nil, + }, + { + name: "PKE provider", + args: args{corev1.Node{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "ip-192-168-64-60.eu-west-1.compute.internal", + }, + Status: corev1.NodeStatus{ + NodeInfo: corev1.NodeSystemInfo{ + KubeletVersion: "v1.24.14", + }, + }, + }}, + want: "PKE", + wantErr: nil, + }, + { + name: "AKS and EKS provider", + args: args{corev1.Node{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "aks-name-pool1-a9e92295-f4ml", + }, + Status: corev1.NodeStatus{ + NodeInfo: corev1.NodeSystemInfo{ + KubeletVersion: "v1.23.9-eks-ba74326", + }, + }, + }}, + want: "", + wantErr: errors.New("K8s cluster provider name matched for multiple patterns: 'EKS' and 'AKS'"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := providerIdentifier(tt.args.node) + if err != nil && tt.wantErr == nil { + require.NoError(t, err) + } + if tt.wantErr != nil { + require.Error(t, err) + } + assert.Equal(t, tt.want, got) + }) + } +} + +func Test_versionIdentifier(t *testing.T) { + type args struct { + version string + } + tests := []struct { + name string + args args + want string + wantErr error + }{ + { + name: "Unrecognized version", + args: args{"123"}, + want: "", + wantErr: errors.New("K8s cluster version could not be recognized: '123'"), + }, + { + name: "Standard version format", + args: args{"v1.24.14"}, + want: "v1.24.14", + wantErr: nil, + }, + { + name: "Subversion format", + args: args{"v1.24.14-gke.270"}, + want: "v1.24.14", + wantErr: nil, + }, + { + name: "Edge case version format", + args: args{"v1.3.0"}, + want: "v1.3.0", + wantErr: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := versionIdentifier(tt.args.version) + if err != nil && tt.wantErr == nil { + require.NoError(t, err) + } + if tt.wantErr != nil { + require.EqualError(t, err, tt.wantErr.Error()) + } + assert.Equal(t, tt.want, got) + }) + } +} From 8b72d6ced1a5258d2e7ecbdfe5a9b98db06fe10f Mon Sep 17 00:00:00 2001 From: marbarta Date: Wed, 9 Aug 2023 17:23:24 +0200 Subject: [PATCH 22/36] add test for GetTestSuiteDurationParallel --- tests/e2e/pkg/tests/mocks.go | 16 +++++----- tests/e2e/pkg/tests/tests_test.go | 49 +++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/tests/e2e/pkg/tests/mocks.go b/tests/e2e/pkg/tests/mocks.go index 74102105d..a36589a03 100644 --- a/tests/e2e/pkg/tests/mocks.go +++ b/tests/e2e/pkg/tests/mocks.go @@ -27,7 +27,7 @@ import ( // Expected (minimal strategy): // - 2 testCases // - 1 testCase on any of the available K8sClusters -// - runtime parallel: 1x(5) = 5sec (time of the longest testCase) +// - runtime parallel: 1x(3) = 3sec (time of the longest testCase) func MockTestsMinimal() Classifier { k8sClusterPool := K8sClusterPool{ NewMockK8sCluster( @@ -61,7 +61,7 @@ func MockTestsMinimal() Classifier { // Expected (provider strategy): // - 6 testCases // - 2 testCases on different providers -// - runtime parallel: 3x(3) = 9sec +// - runtime parallel: 2 + 3 = 5sec func MockTestsProvider() Classifier { k8sClusterPool := K8sClusterPool{ NewMockK8sCluster( @@ -95,7 +95,7 @@ func MockTestsProvider() Classifier { // Expected (provider strategy): // - 6 testCases // - 3 testCases on different providers -// - runtime parallel: 4 + 4 + 5 = 13sec +// - runtime parallel: 2 + 3 + 4 = 9sec func MockTestsProviderMoreTestsThenProvider() Classifier { k8sClusterPool := K8sClusterPool{ NewMockK8sCluster( @@ -122,7 +122,7 @@ func MockTestsProviderMoreTestsThenProvider() Classifier { // Expected (version strategy): // - 2 testCases // - 1 testCase on any of the available K8sClusters -// - runtime parallel: 1 x 5 = 5sec +// - runtime parallel: 1 x 3 = 3sec func MockTestsVersionOne() Classifier { k8sClusterPool := K8sClusterPool{ NewMockK8sCluster( @@ -147,8 +147,8 @@ func MockTestsVersionOne() Classifier { // The Classifier contains 2 tests. // // Expected (version strategy): -// - 2 testCases -// - 1 testCase on any of the available K8sClusters +// - 4 testCases +// - 2 testCase on any of the available K8sClusters // - runtime parallel: 1 x 5 = 5sec func MockTestsVersion() Classifier { k8sClusterPool := K8sClusterPool{ @@ -220,7 +220,7 @@ func MockTestsComplete() Classifier { var mockTest1 = TestCase{ SpecsCount: 2, - Duration: 4 * time.Second, + Duration: 2 * time.Second, Name: "MockTest1", TestFn: testMockTest1, } @@ -236,7 +236,7 @@ func testMockTest1(kubectlOptions k8s.KubectlOptions) { var mockTest2 = TestCase{ SpecsCount: 3, - Duration: 5 * time.Second, + Duration: 3 * time.Second, Name: "MockTest2", TestFn: testMockTest2, } diff --git a/tests/e2e/pkg/tests/tests_test.go b/tests/e2e/pkg/tests/tests_test.go index cb0a094d6..14d8b220d 100644 --- a/tests/e2e/pkg/tests/tests_test.go +++ b/tests/e2e/pkg/tests/tests_test.go @@ -16,6 +16,9 @@ package tests import ( "testing" + "time" + + "github.com/stretchr/testify/assert" ) func Test_Classifier_minimal(t *testing.T) { @@ -560,3 +563,49 @@ func Test_Classifier_complete(t *testing.T) { }) } } + +func TestTestPool_GetTestSuiteDurationParallel(t *testing.T) { + tests := []struct { + name string + tests TestPool + want time.Duration + }{ + { + name: "MockTestsProvider", + tests: MockTestsProvider().ProviderComplete(), + want: 5 * time.Second, + }, + { + name: "MockTestsProviderMoreTestsThenProvider", + tests: MockTestsProviderMoreTestsThenProvider().ProviderComplete(), + want: 9 * time.Second, + }, + + { + name: "MockTestsVersionOne", + tests: MockTestsVersionOne().VersionComplete(), + want: 3 * time.Second, + }, + { + name: "MockTestsComplete", + tests: MockTestsComplete().Complete(), + want: 5 * time.Second, + }, + { + name: "MockTestsVersion", + tests: MockTestsVersion().VersionComplete(), + want: 5 * time.Second, + }, + { + name: "MockTestsMinimal", + tests: MockTestsMinimal().Minimal(), + want: 3 * time.Second, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.tests.GetTestSuiteDurationParallel() + assert.Equal(t, tt.want, got) + }) + } +} From 5154223adda35e567c383182eedebd6d861dfab3 Mon Sep 17 00:00:00 2001 From: marbarta Date: Thu, 10 Aug 2023 14:36:06 +0200 Subject: [PATCH 23/36] add test for testpool --- tests/e2e/pkg/tests/tests.go | 5 +- tests/e2e/pkg/tests/tests_test.go | 160 ++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+), 1 deletion(-) diff --git a/tests/e2e/pkg/tests/tests.go b/tests/e2e/pkg/tests/tests.go index 92027cda7..bb57ae040 100644 --- a/tests/e2e/pkg/tests/tests.go +++ b/tests/e2e/pkg/tests/tests.go @@ -380,7 +380,7 @@ func (t Test) less(test Test) bool { // generateTestID generates the test UID based on the name of the test and the K8sCluster. func (t *Test) generateTestID() string { - text := fmt.Sprintf("%v%v", t.testCase.Name, t.k8sCluster) + text := fmt.Sprintf("%v%#v", t.testCase.Name, t.k8sCluster) hash := md5.Sum([]byte(text)) t.testID = hex.EncodeToString(hash[:5]) @@ -389,6 +389,9 @@ func (t *Test) generateTestID() string { // TestID returns the test UID based on the name of the test and the K8sCluster. func (t *Test) TestID() string { + if t.testID == "" { + t.generateTestID() + } return t.testID } diff --git a/tests/e2e/pkg/tests/tests_test.go b/tests/e2e/pkg/tests/tests_test.go index 14d8b220d..8814e08a2 100644 --- a/tests/e2e/pkg/tests/tests_test.go +++ b/tests/e2e/pkg/tests/tests_test.go @@ -609,3 +609,163 @@ func TestTestPool_GetTestSuiteDurationParallel(t *testing.T) { }) } } + +func TestTestPool_Equal(t *testing.T) { + type args struct { + other TestPool + } + tests := []struct { + name string + tests TestPool + args args + want bool + }{ + { + name: "Simple case true", + tests: []Test{ + { + testCase: mockTest1, + k8sCluster: NewMockK8sCluster( + "testContextPath2", + "testContextName2", + "1.25", + "provider2", + "clusterID1", + ), + }, + { + testCase: mockTest1, + k8sCluster: NewMockK8sCluster( + "testContextPath3", + "testContextName2", + "1.25", + "provider2", + "clusterID2", + ), + }, + { + testCase: mockTest1, + k8sCluster: NewMockK8sCluster( + "testContextPath3", + "testContextName3", + "1.25", + "provider2", + "clusterID3", + ), + }, + }, + args: args{ + other: []Test{ + { + testCase: mockTest1, + k8sCluster: NewMockK8sCluster( + "testContextPath3", + "testContextName2", + "1.25", + "provider2", + "clusterID2", + ), + }, + { + testCase: mockTest1, + k8sCluster: NewMockK8sCluster( + "testContextPath2", + "testContextName2", + "1.25", + "provider2", + "clusterID1", + ), + }, + { + testCase: mockTest1, + k8sCluster: NewMockK8sCluster( + "testContextPath3", + "testContextName3", + "1.25", + "provider2", + "clusterID3", + ), + }, + }, + }, + want: true, + }, + { + name: "Simple case false", + tests: []Test{ + { + testCase: mockTest1, + k8sCluster: NewMockK8sCluster( + "testContextPath2", + "testContextName2", + "1.25", + "provider2", + "clusterID1", + ), + }, + { + testCase: mockTest1, + k8sCluster: NewMockK8sCluster( + "testContextPath3", + "testContextName2", + "1.25", + "provider2", + "clusterID2", + ), + }, + { + testCase: mockTest1, + k8sCluster: NewMockK8sCluster( + "testContextPath3", + "testContextName3", + "1.25", + "provider2", + "clusterID3", + ), + }, + }, + args: args{ + other: []Test{ + { + testCase: mockTest1, + k8sCluster: NewMockK8sCluster( + "testContextPath3", + "testContextName2", + "1.25", + "provider2", + "clusterID2", + ), + }, + { + testCase: mockTest1, + k8sCluster: NewMockK8sCluster( + "testContextPath2", + "testContextName2", + "1.25", + "provider2", + "clusterID1", + ), + }, + { + testCase: mockTest1, + k8sCluster: NewMockK8sCluster( + "testContextPath3", + "testContextName3", + "1.25", + "provider2", + "clusterID4", + ), + }, + }, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + got := tt.tests.Equal(tt.args.other) + assert.Equal(t, tt.want, got) + }) + } +} From bc10e92b53a70cb5d2c64623b65be461e1932920 Mon Sep 17 00:00:00 2001 From: marbarta Date: Sat, 12 Aug 2023 17:32:58 +0200 Subject: [PATCH 24/36] refactor classifier using NewTest constructor --- tests/e2e/pkg/tests/tests.go | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/tests/e2e/pkg/tests/tests.go b/tests/e2e/pkg/tests/tests.go index bb57ae040..9132b197c 100644 --- a/tests/e2e/pkg/tests/tests.go +++ b/tests/e2e/pkg/tests/tests.go @@ -211,10 +211,7 @@ func (t Classifier) Minimal() TestPool { tests := make([]Test, 0, len(t.testCases)) for i, testCase := range t.testCases { - tests = append(tests, Test{ - testCase: testCase, - k8sCluster: t.k8sClusterPool[i%len(t.k8sClusterPool)], - }) + tests = append(tests, NewTest(testCase, t.k8sClusterPool[i%len(t.k8sClusterPool)])) } return tests } @@ -228,10 +225,7 @@ func (t Classifier) VersionComplete() TestPool { for _, k8sClusters := range k8sClustersByVersion { for i, testCase := range t.testCases { - tests = append(tests, Test{ - testCase: testCase, - k8sCluster: k8sClusters[i%len(k8sClusters)], - }) + tests = append(tests, NewTest(testCase, k8sClusters[i%len(k8sClusters)])) } } return tests @@ -246,10 +240,7 @@ func (t Classifier) ProviderComplete() TestPool { for _, k8sClusters := range k8sClustersByProviders { for i, testCase := range t.testCases { - tests = append(tests, Test{ - testCase: testCase, - k8sCluster: k8sClusters[i%len(k8sClusters)], - }) + tests = append(tests, NewTest(testCase, k8sClusters[i%len(k8sClusters)])) } } return tests @@ -265,10 +256,7 @@ func (t Classifier) Complete() TestPool { for _, byVersions := range k8sClustersByProvidersVersions { for _, k8sClusters := range byVersions { for i, testCase := range t.testCases { - tests = append(tests, Test{ - testCase: testCase, - k8sCluster: k8sClusters[i%len(k8sClusters)], - }) + tests = append(tests, NewTest(testCase, k8sClusters[i%len(k8sClusters)])) } } } From c1fd10062736223b19430915b8ed22baffd9606d Mon Sep 17 00:00:00 2001 From: marbarta Date: Sat, 12 Aug 2023 17:34:29 +0200 Subject: [PATCH 25/36] fix classifier unit test --- tests/e2e/pkg/tests/tests_test.go | 45 ++++++++++++++++--------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/tests/e2e/pkg/tests/tests_test.go b/tests/e2e/pkg/tests/tests_test.go index 8814e08a2..9d93ec195 100644 --- a/tests/e2e/pkg/tests/tests_test.go +++ b/tests/e2e/pkg/tests/tests_test.go @@ -18,6 +18,7 @@ import ( "testing" "time" + "github.com/gruntwork-io/terratest/modules/k8s" "github.com/stretchr/testify/assert" ) @@ -230,7 +231,7 @@ func Test_Classifier_versionComplete(t *testing.T) { fields: fields{ k8sClusterPool: K8sClusterPool{ { - + kubectlOptions: k8s.KubectlOptions{ContextName: "local1", ConfigPath: "local1"}, clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.24", @@ -238,9 +239,9 @@ func Test_Classifier_versionComplete(t *testing.T) { }, }, { - + kubectlOptions: k8s.KubectlOptions{ContextName: "local2", ConfigPath: "local2"}, clusterInfo: k8sClusterInfo{ - clusterID: "local1", + clusterID: "local2", version: "1.25", provider: "provider1", }, @@ -261,7 +262,7 @@ func Test_Classifier_versionComplete(t *testing.T) { Name: "testCase1", }, k8sCluster: K8sCluster{ - + kubectlOptions: k8s.KubectlOptions{ContextName: "local1", ConfigPath: "local1"}, clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.24", @@ -274,7 +275,7 @@ func Test_Classifier_versionComplete(t *testing.T) { Name: "testCase2", }, k8sCluster: K8sCluster{ - + kubectlOptions: k8s.KubectlOptions{ContextName: "local1", ConfigPath: "local1"}, clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.24", @@ -287,10 +288,10 @@ func Test_Classifier_versionComplete(t *testing.T) { Name: "testCase1", }, k8sCluster: K8sCluster{ - + kubectlOptions: k8s.KubectlOptions{ContextName: "local2", ConfigPath: "local2"}, clusterInfo: k8sClusterInfo{ - clusterID: "local1", - version: "1.24", + clusterID: "local2", + version: "1.25", provider: "provider1", }, }, @@ -300,10 +301,10 @@ func Test_Classifier_versionComplete(t *testing.T) { Name: "testCase2", }, k8sCluster: K8sCluster{ - + kubectlOptions: k8s.KubectlOptions{ContextName: "local2", ConfigPath: "local2"}, clusterInfo: k8sClusterInfo{ - clusterID: "local1", - version: "1.24", + clusterID: "local2", + version: "1.25", provider: "provider1", }, }, @@ -426,7 +427,7 @@ func Test_Classifier_complete(t *testing.T) { fields: fields{ k8sClusterPool: K8sClusterPool{ { - + kubectlOptions: k8s.KubectlOptions{ContextName: "local1", ConfigPath: "local1"}, clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.24", @@ -434,7 +435,7 @@ func Test_Classifier_complete(t *testing.T) { }, }, { - + kubectlOptions: k8s.KubectlOptions{ContextName: "local2", ConfigPath: "local2"}, clusterInfo: k8sClusterInfo{ clusterID: "local2", version: "1.25", @@ -442,7 +443,7 @@ func Test_Classifier_complete(t *testing.T) { }, }, { - + kubectlOptions: k8s.KubectlOptions{ContextName: "local3", ConfigPath: "local3"}, clusterInfo: k8sClusterInfo{ clusterID: "local3", version: "1.25", @@ -450,7 +451,7 @@ func Test_Classifier_complete(t *testing.T) { }, }, { - + kubectlOptions: k8s.KubectlOptions{ContextName: "local4", ConfigPath: "local4"}, clusterInfo: k8sClusterInfo{ clusterID: "local4", version: "1.25", @@ -473,7 +474,7 @@ func Test_Classifier_complete(t *testing.T) { Name: "testCase1", }, k8sCluster: K8sCluster{ - + kubectlOptions: k8s.KubectlOptions{ContextName: "local1", ConfigPath: "local1"}, clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.24", @@ -486,7 +487,7 @@ func Test_Classifier_complete(t *testing.T) { Name: "testCase2", }, k8sCluster: K8sCluster{ - + kubectlOptions: k8s.KubectlOptions{ContextName: "local1", ConfigPath: "local1"}, clusterInfo: k8sClusterInfo{ clusterID: "local1", version: "1.24", @@ -499,7 +500,7 @@ func Test_Classifier_complete(t *testing.T) { Name: "testCase1", }, k8sCluster: K8sCluster{ - + kubectlOptions: k8s.KubectlOptions{ContextName: "local2", ConfigPath: "local2"}, clusterInfo: k8sClusterInfo{ clusterID: "local2", version: "1.25", @@ -512,7 +513,7 @@ func Test_Classifier_complete(t *testing.T) { Name: "testCase2", }, k8sCluster: K8sCluster{ - + kubectlOptions: k8s.KubectlOptions{ContextName: "local2", ConfigPath: "local2"}, clusterInfo: k8sClusterInfo{ clusterID: "local2", version: "1.25", @@ -525,7 +526,7 @@ func Test_Classifier_complete(t *testing.T) { Name: "testCase1", }, k8sCluster: K8sCluster{ - + kubectlOptions: k8s.KubectlOptions{ContextName: "local3", ConfigPath: "local3"}, clusterInfo: k8sClusterInfo{ clusterID: "local3", version: "1.25", @@ -538,9 +539,9 @@ func Test_Classifier_complete(t *testing.T) { Name: "testCase2", }, k8sCluster: K8sCluster{ - + kubectlOptions: k8s.KubectlOptions{ContextName: "local4", ConfigPath: "local4"}, clusterInfo: k8sClusterInfo{ - clusterID: "local3", + clusterID: "local4", version: "1.25", provider: "provider3", }, From eb7c5a1570daba1f2322c17f4eaa4267bdbfbf0f Mon Sep 17 00:00:00 2001 From: marbarta Date: Sat, 12 Aug 2023 17:35:10 +0200 Subject: [PATCH 26/36] add e2e unit tests execution into Makefile --- Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 4cabfa5ae..61eef91ef 100644 --- a/Makefile +++ b/Makefile @@ -91,7 +91,7 @@ install-kustomize: # Run tests test: generate fmt vet manifests bin/setup-envtest - cd api && go test ./... + go test -failfast ./api/... ./tests/e2e/... ./properties/... KUBEBUILDER_ASSETS=$$($(BIN_DIR)/setup-envtest --print path --bin-dir $(BIN_DIR) use $(ENVTEST_K8S_VERSION)) \ go test ./... \ -coverprofile cover.out \ @@ -100,7 +100,6 @@ test: generate fmt vet manifests bin/setup-envtest -test.v \ -test.paniconexit0 \ -timeout 1h - cd properties && go test -coverprofile cover.out -cover -failfast -v -covermode=count ./pkg/... ./internal/... bin/ginkgo: $(BIN_DIR)/ginkgo-$(GINKGO_VERSION) @ln -sf ginkgo-$(GINKGO_VERSION) $(BIN_DIR)/ginkgo From a2952f638fcfa5b1f091a4b155a3fc6518cb6e3d Mon Sep 17 00:00:00 2001 From: marbarta Date: Sun, 13 Aug 2023 10:01:46 +0200 Subject: [PATCH 27/36] add e2e go fmt go vet --- Makefile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 61eef91ef..4a719d085 100644 --- a/Makefile +++ b/Makefile @@ -90,8 +90,10 @@ install-kustomize: fi # Run tests -test: generate fmt vet manifests bin/setup-envtest - go test -failfast ./api/... ./tests/e2e/... ./properties/... +test: generate fmt vet manifests bin/setup-envtest + cd api && go test -failfast ./... + cd tests/e2e && go test -failfast ./... + cd properties && go test -failfast ./... KUBEBUILDER_ASSETS=$$($(BIN_DIR)/setup-envtest --print path --bin-dir $(BIN_DIR) use $(ENVTEST_K8S_VERSION)) \ go test ./... \ -coverprofile cover.out \ @@ -155,12 +157,14 @@ fmt: go fmt ./... cd api && go fmt ./... cd properties && go fmt ./... + cd tests/e2e && go fmt ./... # Run go vet against code vet: go vet ./... cd api && go fmt ./... cd properties && go vet ./... + cd tests/e2e && go vet ./... # Generate code generate: bin/controller-gen gen-license-header ## Generate source code for APIs, Mocks, etc From 95d63711dc10967ff2cd7b80601fe18fab5c484c Mon Sep 17 00:00:00 2001 From: marbarta Date: Sun, 13 Aug 2023 15:55:42 +0200 Subject: [PATCH 28/36] fix go.mod --- tests/e2e/go.mod | 6 +++--- tests/e2e/go.sum | 9 +-------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/tests/e2e/go.mod b/tests/e2e/go.mod index e3ac2832f..e4b8eee37 100644 --- a/tests/e2e/go.mod +++ b/tests/e2e/go.mod @@ -113,7 +113,7 @@ require ( github.com/pquerna/otp v1.2.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/testify v1.8.3 // indirect + github.com/stretchr/testify v1.8.3 github.com/urfave/cli v1.22.2 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.8.0 // indirect @@ -131,8 +131,8 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.26.4 // indirect - k8s.io/client-go v0.26.4 // indirect + k8s.io/api v0.26.4 + k8s.io/client-go v0.26.4 k8s.io/klog/v2 v2.90.1 // indirect k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect diff --git a/tests/e2e/go.sum b/tests/e2e/go.sum index 5588c8c6b..259dfa936 100644 --- a/tests/e2e/go.sum +++ b/tests/e2e/go.sum @@ -381,14 +381,11 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= -github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= -github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= github.com/pavlo-v-chernykh/keystore-go/v4 v4.4.1 h1:FyBdsRqqHH4LctMLL+BL2oGO+ONcIPwn96ctofCVtNE= github.com/pavlo-v-chernykh/keystore-go/v4 v4.4.1/go.mod h1:lAVhWwbNaveeJmxrxuSTxMgKpF6DjnuVpn6T8WiBwYQ= @@ -690,8 +687,6 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -766,8 +761,6 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 50d3a81fb3d8f01feb1f9d55f6ccf9a40098142e Mon Sep 17 00:00:00 2001 From: marbarta Date: Mon, 14 Aug 2023 11:47:47 +0200 Subject: [PATCH 29/36] rename GetRawConfig to CreateRawConfig --- tests/e2e/pkg/common/common.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/e2e/pkg/common/common.go b/tests/e2e/pkg/common/common.go index 19b5844ee..7e272b4ee 100644 --- a/tests/e2e/pkg/common/common.go +++ b/tests/e2e/pkg/common/common.go @@ -30,6 +30,7 @@ import ( // on the the environment variables and the KUBECONFIG file. func CurrentEnvK8sContext() (kubeconfigPath string, kubecontextName string, err error) { kubeconfigPath, isExisting := os.LookupEnv("KUBECONFIG") + if !isExisting { homePath, err := os.UserHomeDir() if err != nil { @@ -91,23 +92,22 @@ func GetDefaultKubeContext(kubeconfigPath string) (string, error) { return kubecontext, nil } -// GetRawConfig creates a raw clientcmd api config -func GetRawConfig(kubeconfigPath string) (api.Config, error) { +// CreateRawConfig creates a raw clientcmd api config +func CreateRawConfig(kubeconfigPath string) (api.Config, error) { rules := clientcmd.NewDefaultClientConfigLoadingRules() if kubeconfigPath == "" { return api.Config{}, errors.New("missing kubeconfigPath") } rules.ExplicitPath = kubeconfigPath - clientConfig := clientcmd. - NewNonInteractiveDeferredLoadingClientConfig(rules, nil) + clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, nil) return clientConfig.RawConfig() } // GetKubeContexts returns the available kubecontext names in the kubeconfig file func GetKubeContexts(kubeconfigPath string) ([]string, error) { - configs, err := GetRawConfig(kubeconfigPath) + configs, err := CreateRawConfig(kubeconfigPath) if err != nil { return nil, err } From 129e7fe35b3f9a4d0c70361a85ce70caec6a99a2 Mon Sep 17 00:00:00 2001 From: marbarta Date: Mon, 14 Aug 2023 12:07:28 +0200 Subject: [PATCH 30/36] zookeepecluster create timeout defaults 4min --- tests/e2e/const.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/const.go b/tests/e2e/const.go index b4b74b703..9b8487d9d 100644 --- a/tests/e2e/const.go +++ b/tests/e2e/const.go @@ -56,7 +56,7 @@ const ( kafkaClusterCreateTimeout = 600 * time.Second kafkaClusterResourceCleanupTimeout = 120 * time.Second kcatDeleetionTimeout = 40 * time.Second - zookeeperClusterCreateTimeout = 7 * time.Minute // Increased from 4 to 7 for multiple kind + zookeeperClusterCreateTimeout = 4 * time.Minute zookeeperClusterResourceCleanupTimeout = 60 * time.Second externalConsumerTimeout = 5 * time.Second externalProducerTimeout = 5 * time.Second From 70296817821d302e24d169a592beb412f2dedccc Mon Sep 17 00:00:00 2001 From: marbarta Date: Wed, 16 Aug 2023 11:21:21 +0200 Subject: [PATCH 31/36] test 2 kind cluster setup --- .github/workflows/e2e-test.yaml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/e2e-test.yaml b/.github/workflows/e2e-test.yaml index 596b667c7..a6f1f31a9 100644 --- a/.github/workflows/e2e-test.yaml +++ b/.github/workflows/e2e-test.yaml @@ -30,14 +30,13 @@ jobs: kind_k8s_version: v1.24.13 kubeconfig_dir_path: tests/e2e/kubeconfigs - # TODO: ZookeeperCluster create time out always on the second kind cluster - # - name: Setup Kind cluster2 - # id: setup-kind2 - # uses: ./.github/actions/kind-create - # with: - # kind_cluster_name: kind2 - # kind_k8s_version: v1.25.9 - # kubeconfig_dir_path: tests/e2e/kubeconfigs + - name: Setup Kind cluster2 + id: setup-kind2 + uses: ./.github/actions/kind-create + with: + kind_cluster_name: kind2 + kind_k8s_version: v1.23.17 + kubeconfig_dir_path: tests/e2e/kubeconfigs - name: Run E2E tests env: From 7bc2a2d5a0923344392cac1cd8c59985b52e9096 Mon Sep 17 00:00:00 2001 From: marbarta Date: Wed, 16 Aug 2023 11:21:46 +0200 Subject: [PATCH 32/36] default strategy versionComplete --- tests/e2e/pkg/common/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/pkg/common/config/config.go b/tests/e2e/pkg/common/config/config.go index 5760e7664..64b02f7c7 100644 --- a/tests/e2e/pkg/common/config/config.go +++ b/tests/e2e/pkg/common/config/config.go @@ -34,7 +34,7 @@ const ( defaultCreateTestReportFile = "true" defaultMaxTimeout = "1h" defaultAllowedOverrunDuration = "10m" - defaultTestStrategy = TestStrategyMinimal + defaultTestStrategy = TestStrategyVersionComplete defaultKubeConfigDirectoryPath = "kubeconfigs" ) From 842273f9ff7437f23c95b35f9d70f8de8cb939bc Mon Sep 17 00:00:00 2001 From: marbarta Date: Wed, 16 Aug 2023 12:09:18 +0200 Subject: [PATCH 33/36] go with parallel --- .github/workflows/e2e-test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-test.yaml b/.github/workflows/e2e-test.yaml index a6f1f31a9..f280301c4 100644 --- a/.github/workflows/e2e-test.yaml +++ b/.github/workflows/e2e-test.yaml @@ -46,7 +46,7 @@ jobs: run: | go work init go work use -r . - make test-e2e + make test-e2e-parallel - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action/composite@master From ba66727b7c571d6dffd1d4fa072e0c232a9415c2 Mon Sep 17 00:00:00 2001 From: marbarta Date: Wed, 16 Aug 2023 13:01:58 +0200 Subject: [PATCH 34/36] increase zookeeper and kafka cluster creation timeout --- tests/e2e/const.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/const.go b/tests/e2e/const.go index 9b8487d9d..09839029d 100644 --- a/tests/e2e/const.go +++ b/tests/e2e/const.go @@ -53,10 +53,10 @@ const ( defaultTopicCreationWaitTime = 10 * time.Second defaultUserCreationWaitTime = 10 * time.Second - kafkaClusterCreateTimeout = 600 * time.Second + kafkaClusterCreateTimeout = 800 * time.Second kafkaClusterResourceCleanupTimeout = 120 * time.Second kcatDeleetionTimeout = 40 * time.Second - zookeeperClusterCreateTimeout = 4 * time.Minute + zookeeperClusterCreateTimeout = 5 * time.Minute zookeeperClusterResourceCleanupTimeout = 60 * time.Second externalConsumerTimeout = 5 * time.Second externalProducerTimeout = 5 * time.Second From a13805fc9f8b4fc6005ee936fdb1fa7ed12dccb7 Mon Sep 17 00:00:00 2001 From: marbarta Date: Wed, 16 Aug 2023 13:56:36 +0200 Subject: [PATCH 35/36] default pod readiness timeout increased to 60 --- tests/e2e/const.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/const.go b/tests/e2e/const.go index 09839029d..1747bc86d 100644 --- a/tests/e2e/const.go +++ b/tests/e2e/const.go @@ -49,7 +49,7 @@ const ( cruiseControlPodReadinessTimeout = 50 * time.Second kafkaClusterResourceReadinessTimeout = 60 * time.Second defaultDeletionTimeout = 20 * time.Second - defaultPodReadinessWaitTime = 10 * time.Second + defaultPodReadinessWaitTime = 60 * time.Second defaultTopicCreationWaitTime = 10 * time.Second defaultUserCreationWaitTime = 10 * time.Second From a947c8c75d98408e4a69cb64a81a9b529007c4b7 Mon Sep 17 00:00:00 2001 From: marbarta Date: Thu, 17 Aug 2023 13:51:14 +0200 Subject: [PATCH 36/36] go with one kind cluster by default --- .github/workflows/e2e-test.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/e2e-test.yaml b/.github/workflows/e2e-test.yaml index f280301c4..ff1d8464e 100644 --- a/.github/workflows/e2e-test.yaml +++ b/.github/workflows/e2e-test.yaml @@ -22,7 +22,7 @@ jobs: - name: Checkout code uses: actions/checkout@v3 - - name: Setup Kind cluster1 + - name: Setup Kind cluster id: setup-kind1 uses: ./.github/actions/kind-create with: @@ -30,13 +30,13 @@ jobs: kind_k8s_version: v1.24.13 kubeconfig_dir_path: tests/e2e/kubeconfigs - - name: Setup Kind cluster2 - id: setup-kind2 - uses: ./.github/actions/kind-create - with: - kind_cluster_name: kind2 - kind_k8s_version: v1.23.17 - kubeconfig_dir_path: tests/e2e/kubeconfigs + # - name: Setup Kind cluster2 + # id: setup-kind2 + # uses: ./.github/actions/kind-create + # with: + # kind_cluster_name: kind2 + # kind_k8s_version: v1.23.17 + # kubeconfig_dir_path: tests/e2e/kubeconfigs - name: Run E2E tests env: