diff --git a/go.mod b/go.mod index 30e5f0d832..6fc6dc0aa4 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/grandcat/zeroconf v1.0.0 github.com/gruntwork-io/gruntwork-cli v0.7.0 - github.com/gruntwork-io/terratest v0.30.7 + github.com/gruntwork-io/terratest v0.35.6 github.com/jinzhu/copier v0.0.0-20201025035756-632e723a6687 github.com/julienschmidt/httprouter v1.3.0 github.com/metal-stack/go-ipam v1.8.4-0.20210322080203-5a9da5064b27 diff --git a/go.sum b/go.sum index c9a7467502..7d665fb8e7 100644 --- a/go.sum +++ b/go.sum @@ -92,15 +92,14 @@ github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb0 github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -112,6 +111,9 @@ github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:C github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= +github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= +github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -126,8 +128,9 @@ github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQ github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.35.24 h1:U3GNTg8+7xSM6OAJ8zksiSM4bRqxBWmVwwehvOSNG3A= github.com/aws/aws-sdk-go v1.35.24/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= +github.com/aws/aws-sdk-go v1.38.28 h1:2ZzgEupSluR18ClxUnHwXKyuADheZpMblXRAsHqF0tI= +github.com/aws/aws-sdk-go v1.38.28/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -368,12 +371,10 @@ github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= -github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= -github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= @@ -387,7 +388,6 @@ github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsd github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/spec v0.19.5 h1:Xm0Ao53uqnk9QE/LlYV5DEU09UAgpliA85QoT9LzqPw= github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= @@ -397,7 +397,6 @@ github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= @@ -411,6 +410,7 @@ github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gG github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= @@ -441,6 +441,7 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -485,7 +486,6 @@ github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -495,7 +495,6 @@ github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ 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/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/googleapis/gnostic v0.5.4 h1:ynbQIWjLw7iv6HAFdixb30U7Uvcmx+f4KlLJpmhkTK0= @@ -518,21 +517,25 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/gruntwork-io/go-commons v0.8.0 h1:k/yypwrPqSeYHevLlEDmvmgQzcyTwrlZGRaxEM6G0ro= +github.com/gruntwork-io/go-commons v0.8.0/go.mod h1:gtp0yTtIBExIZp7vyIV9I0XQkVwiQZze678hvDXof78= github.com/gruntwork-io/gruntwork-cli v0.7.0 h1:YgSAmfCj9c61H+zuvHwKfYUwlMhu5arnQQLM4RH+CYs= github.com/gruntwork-io/gruntwork-cli v0.7.0/go.mod h1:jp6Z7NcLF2avpY8v71fBx6hds9eOFPELSuD/VPv7w00= -github.com/gruntwork-io/terratest v0.30.7 h1:RmvfWTngHv9Mat2AF4++Znte8Znkz585/IzDvUgz84o= -github.com/gruntwork-io/terratest v0.30.7/go.mod h1:7dNmTD2zDKUEVqfmvcUU5c9mZi+986mcXNzhzqPYPg8= +github.com/gruntwork-io/terratest v0.35.6 h1:Q7pUd3JI4i5mmR/KgYkZJJ4q9ZbV8ru9KydwjA/ohaA= +github.com/gruntwork-io/terratest v0.35.6/go.mod h1:GIVJGBV1WIv1vxIG31Ycy0CuHYfXuvvkilNQuC9Wi+o= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= @@ -548,10 +551,12 @@ github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uG github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/hcl/v2 v2.8.2/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/terraform-json v0.9.0/go.mod h1:3defM4kkMfttwiE7VakJDwCd4R+umhSQnvJwORXbprE= github.com/heketi/heketi v10.2.0+incompatible/go.mod h1:bB9ly3RchcQqsQ9CpyaQwvva7RS5ytVoSoholZQON6o= github.com/heketi/tests v0.0.0-20151005000721-f3775cbcefd6/go.mod h1:xGMAM8JLi7UkZt1i4FQeQy0R2T8GLUwQhOP5M1gBhy4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -569,6 +574,7 @@ github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod github.com/ishidawataru/sctp v0.0.0-20190723014705-7c296d48a2b5/go.mod h1:DM4VvS+hD/kDi1U1QsX2fnZowwBhqD0Dk3bRPKF/Oc8= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a/go.mod h1:wK6yTYYcgjHE1Z1QtXACPDjcFJyBskHEdagmnq3vsP8= +github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= github.com/jinzhu/copier v0.0.0-20201025035756-632e723a6687 h1:bWXum+xWafUxxJpcXnystwg5m3iVpPYtrGJFc1rjfLc= github.com/jinzhu/copier v0.0.0-20201025035756-632e723a6687/go.mod h1:24xnZezI2Yqac9J61UC6/dG/k76ttpq0DdJI3QmUvro= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -639,7 +645,6 @@ github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/markbates/pkger v0.17.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= @@ -673,6 +678,7 @@ github.com/mholt/certmagic v0.6.2-0.20190624175158-6a42ef9fe8c2/go.mod h1:g4cOPx github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.35 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs= github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws= @@ -684,6 +690,7 @@ github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk 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/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= @@ -707,7 +714,6 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mohae/deepcopy v0.0.0-20170603005431-491d3605edfb/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= @@ -808,7 +814,6 @@ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.0/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/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= @@ -913,6 +918,7 @@ github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0 github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 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= @@ -964,6 +970,7 @@ github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmF github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= @@ -971,7 +978,6 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2 github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI= github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -980,6 +986,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= +github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= +github.com/zclconf/go-cty v1.2.1/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= @@ -996,7 +1004,6 @@ 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.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -1031,6 +1038,7 @@ golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -1086,6 +1094,7 @@ 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.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180811021610-c39426892332/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= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1170,6 +1179,7 @@ golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1201,7 +1211,6 @@ golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1293,7 +1302,6 @@ golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200113040837-eac381796e91/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -1306,6 +1314,7 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201110201400-7099162a900a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= @@ -1364,7 +1373,6 @@ google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= @@ -1470,7 +1478,6 @@ k8s.io/apimachinery v0.21.0 h1:3Fx+41if+IRavNcKOz09FwEXDBG6ORh6iMsTSelhkMA= k8s.io/apimachinery v0.21.0/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY= k8s.io/apiserver v0.21.0 h1:1hWMfsz+cXxB77k6/y0XxWxwl6l9OF26PC9QneUVn1Q= k8s.io/apiserver v0.21.0/go.mod h1:w2YSn4/WIwYuxG5zJmcqtRdtqgW/J2JRgFAqps3bBpg= -k8s.io/cli-runtime v0.21.0 h1:/V2Kkxtf6x5NI2z+Sd/mIrq4FQyQ8jzZAUD6N5RnN7Y= k8s.io/cli-runtime v0.21.0/go.mod h1:XoaHP93mGPF37MkLbjGVYqg3S1MnsFdKtiA/RZzzxOo= k8s.io/client-go v0.21.0 h1:n0zzzJsAQmJngpC0IhgFcApZyoGXPrDIAD601HD09ag= k8s.io/client-go v0.21.0/go.mod h1:nNBytTF9qPFDEhoqgEPaarobC8QPae13bElIVHzIglA= @@ -1530,11 +1537,9 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyz sigs.k8s.io/controller-runtime v0.8.3/go.mod h1:U/l+DUopBc1ecfRZ5aviA9JDmGFQKvLf5YkZNx2e0sU= sigs.k8s.io/controller-runtime v0.9.0-beta.2 h1:T2sG4AGBWKRsUJyEeMRsIpAdn/1Tqk+3J7KSJB4pWPo= sigs.k8s.io/controller-runtime v0.9.0-beta.2/go.mod h1:ufPDuvefw2Y1KnBgHQrLdOjueYlj+XJV2AszbT+WTxs= -sigs.k8s.io/kustomize/api v0.8.5 h1:bfCXGXDAbFbb/Jv5AhMj2BB8a5VAJuuQ5/KU69WtDjQ= sigs.k8s.io/kustomize/api v0.8.5/go.mod h1:M377apnKT5ZHJS++6H4rQoCHmWtt6qTpp3mbe7p6OLY= sigs.k8s.io/kustomize/cmd/config v0.9.7/go.mod h1:MvXCpHs77cfyxRmCNUQjIqCmZyYsbn5PyQpWiq44nW0= sigs.k8s.io/kustomize/kustomize/v4 v4.0.5/go.mod h1:C7rYla7sI8EnxHE/xEhRBSHMNfcL91fx0uKmUlUhrBk= -sigs.k8s.io/kustomize/kyaml v0.10.15 h1:dSLgG78KyaxN4HylPXdK+7zB3k7sW6q3IcCmcfKA+aI= sigs.k8s.io/kustomize/kyaml v0.10.15/go.mod h1:mlQFagmkm1P+W4lZJbJ/yaxMd8PqMRSC4cPcfUVt5Hg= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.1.0 h1:C4r9BgJ98vrKnnVCjwCSXcWjWe0NKcUQkmzDXZXGwH8= diff --git a/pkg/clusterid/clusterID.go b/pkg/clusterid/clusterID.go index 79dec4f7ff..2c286b5023 100644 --- a/pkg/clusterid/clusterID.go +++ b/pkg/clusterid/clusterID.go @@ -46,7 +46,7 @@ func NewClusterIDFromClient(client kubernetes.Interface) (ClusterID, error) { namespace, found := os.LookupEnv("POD_NAMESPACE") if !found { klog.Info("POD_NAMESPACE not set") - data, err := ioutil.ReadFile(consts.ServiceAccount) + data, err := ioutil.ReadFile(consts.ServiceAccountNamespacePath) if err != nil { klog.Error(err, "Unable to get namespace") os.Exit(1) @@ -112,12 +112,12 @@ func getClusterID(cm *v1.ConfigMap) string { if cm == nil { return "" } - return cm.Data[consts.ConfigMapKey] + return cm.Data[consts.ClusterIDConfigMapKey] } // SetupClusterID sets a new clusterid. func (cId *ClusterIDImpl) SetupClusterID(namespace string) error { - cm, err := cId.client.CoreV1().ConfigMaps(namespace).Get(context.TODO(), consts.ClusterIDconfigMapName, + cm, err := cId.client.CoreV1().ConfigMaps(namespace).Get(context.TODO(), consts.ClusterIDConfigMapName, metav1.GetOptions{}) if err != nil && !k8serror.IsNotFound(err) { klog.Error(err) @@ -166,17 +166,17 @@ func (cId *ClusterIDImpl) getMasterID() (string, error) { // saveToConfigMap stores the clusterid in the detailed configMap. func (cId *ClusterIDImpl) saveToConfigMap(id, namespace string) error { - cm, err := cId.client.CoreV1().ConfigMaps(namespace).Get(context.TODO(), consts.ClusterIDconfigMapName, + cm, err := cId.client.CoreV1().ConfigMaps(namespace).Get(context.TODO(), consts.ClusterIDConfigMapName, metav1.GetOptions{}) if err != nil { if k8serror.IsNotFound(err) { // does not exist cm = &v1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ - Name: consts.ClusterIDconfigMapName, + Name: consts.ClusterIDConfigMapName, }, Data: map[string]string{ - consts.ConfigMapKey: id, + consts.ClusterIDConfigMapKey: id, }, } cId.id = id @@ -187,8 +187,8 @@ func (cId *ClusterIDImpl) saveToConfigMap(id, namespace string) error { return err } // already exists, update it if needed - if cm.Data[consts.ConfigMapKey] != id { - cm.Data[consts.ConfigMapKey] = id + if cm.Data[consts.ClusterIDConfigMapKey] != id { + cm.Data[consts.ClusterIDConfigMapKey] = id _, err := cId.client.CoreV1().ConfigMaps(namespace).Update(context.TODO(), cm, metav1.UpdateOptions{}) return err } @@ -197,7 +197,7 @@ func (cId *ClusterIDImpl) saveToConfigMap(id, namespace string) error { // updateClusterID updates the clusterid values. func (cId *ClusterIDImpl) updateClusterID(obj interface{}) { - tmp := obj.(*v1.ConfigMap).Data[consts.ClusterIDconfigMapName] + tmp := obj.(*v1.ConfigMap).Data[consts.ClusterIDConfigMapKey] cId.m.RLock() curr := cId.id if curr != tmp { diff --git a/pkg/consts/clusterid.go b/pkg/consts/clusterid.go index 1eca320710..151049e5a8 100644 --- a/pkg/consts/clusterid.go +++ b/pkg/consts/clusterid.go @@ -1,9 +1,14 @@ package consts const ( - ClusterIDconfigMapName = "cluster-id" - MasterLabel = "node-role.kubernetes.io/master" - ServiceAccount = "/var/run/secrets/kubernetes.io/serviceaccount/namespace" - ConfigMapKey = "cluster-id" - ClusterIDLabelName = "clusterID" + // ClusterIDConfigMapName is the name of the configmap where the cluster-id is stored. + ClusterIDConfigMapName = "cluster-id" + // MasterLabel contains the label used to identify the master nodes. + MasterLabel = "node-role.kubernetes.io/master" + // ServiceAccountNamespacePath contains the path where the namespace is stored in the serviceaccount volume mount. + ServiceAccountNamespacePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace" + // ClusterIDLabelName is the name of the label key to use with Cluster ID. + ClusterIDLabelName = "cluster-id" + // ClusterIDConfigMapKey is the key of the configmap where the cluster-id is stored. + ClusterIDConfigMapKey = "cluster-id" ) diff --git a/pkg/virtualKubelet/test/e2e/main_test.go b/pkg/virtualKubelet/test/e2e/main_test.go index 91a9786065..8a8a86fa53 100644 --- a/pkg/virtualKubelet/test/e2e/main_test.go +++ b/pkg/virtualKubelet/test/e2e/main_test.go @@ -9,7 +9,7 @@ import ( v1 "k8s.io/api/core/v1" - vke2e "github.com/liqotech/liqo/test/e2e" + vke2e "github.com/liqotech/liqo/pkg/virtualKubelet/test/e2e/framework" ) const ( diff --git a/test/e2e/deploy_app_test.go b/test/e2e/deploy_app_test.go deleted file mode 100644 index 7132c65b35..0000000000 --- a/test/e2e/deploy_app_test.go +++ /dev/null @@ -1,116 +0,0 @@ -package e2e - -import ( - "context" - "fmt" - "os" - "testing" - "time" - - http_helper "github.com/gruntwork-io/terratest/modules/http-helper" - "github.com/gruntwork-io/terratest/modules/k8s" - "gotest.tools/assert" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - liqoconst "github.com/liqotech/liqo/pkg/consts" -) - -const ( - retries = 36 - sleepBetweenRetries = 5 * time.Second -) - -func testDeployApp(t *testing.T) { - kubeResourcePath := "https://raw.githubusercontent.com/liqotech/microservices-demo/master/release/kubernetes-manifests.yaml" - - namespace := "test-app" - configPath, ok := os.LookupEnv("KUBECONFIG_1") - assert.Assert(t, ok) - options := k8s.NewKubectlOptions("", configPath, namespace) - - defer cleanup(t, options, kubeResourcePath, namespace) - - k8s.CreateNamespace(t, options, namespace) - liqoEnable(t, options, namespace) - k8s.KubectlApply(t, options, kubeResourcePath) - - // load generator pods is in error state - pods := k8s.ListPods(t, options, metav1.ListOptions{ - LabelSelector: "app!=loadgenerator", - }) - for _, pod := range pods { - k8s.WaitUntilPodAvailable(t, options, pod.Name, retries, sleepBetweenRetries) - } - - svcs := k8s.ListServices(t, options, metav1.ListOptions{}) - for _, svc := range svcs { - // load balancer services will be never available in kind - if svc.Spec.Type != v1.ServiceTypeLoadBalancer { - k8s.WaitUntilServiceAvailable(t, options, svc.Name, retries, sleepBetweenRetries) - } - } - - service := k8s.GetService(t, options, "frontend-external") - assert.Assert(t, len(service.Spec.Ports) > 0) - - nodes := getNodes(t, options) - assert.Assert(t, len(nodes) > 0) - - url := fmt.Sprintf("http://%s:%d", getAddress(t, nodes[0].Status.Addresses), service.Spec.Ports[0].NodePort) - http_helper.HttpGetWithRetryWithCustomValidation(t, url, nil, retries, sleepBetweenRetries, func(code int, body string) bool { - return code == 200 - }) -} - -func getAddress(t *testing.T, addrs []v1.NodeAddress) string { - for _, addr := range addrs { - if addr.Type == v1.NodeInternalIP { - return addr.Address - } - } - t.Fail() - return "" -} - -func cleanup(t *testing.T, options *k8s.KubectlOptions, configPath string, namespace string) { - k8s.KubectlDelete(t, options, configPath) - clientset, err := k8s.GetKubernetesClientFromOptionsE(t, options) - assert.NilError(t, err) - for { - pods, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{}) - assert.NilError(t, err) - if len(pods.Items) == 0 { - break - } - time.Sleep(1 * time.Second) - } - k8s.DeleteNamespace(t, options, namespace) -} - -func getNodes(t *testing.T, options *k8s.KubectlOptions) []v1.Node { - clientset, err := k8s.GetKubernetesClientFromOptionsE(t, options) - assert.NilError(t, err) - - nodes, err := clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{ - LabelSelector: fmt.Sprintf("%v!=%v,!net.liqo.io/gateway", liqoconst.TypeLabel, liqoconst.TypeNode), - }) - assert.NilError(t, err) - return nodes.Items -} - -func liqoEnable(t *testing.T, options *k8s.KubectlOptions, namespace string) { - clientset, err := k8s.GetKubernetesClientFromOptionsE(t, options) - assert.NilError(t, err) - - ns, err := clientset.CoreV1().Namespaces().Get(context.TODO(), namespace, metav1.GetOptions{}) - assert.NilError(t, err) - - if ns.Labels == nil { - ns.Labels = map[string]string{} - } - ns.Labels["liqo.io/enabled"] = "true" - - _, err = clientset.CoreV1().Namespaces().Update(context.TODO(), ns, metav1.UpdateOptions{}) - assert.NilError(t, err) -} diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go deleted file mode 100644 index 1e51e1d0a5..0000000000 --- a/test/e2e/e2e_test.go +++ /dev/null @@ -1,9 +0,0 @@ -package e2e - -import "testing" - -func TestE2E(t *testing.T) { - t.Run("joinTest", testJoin) - t.Run("netTest", testNet) - t.Run("testDeployApp", testDeployApp) -} diff --git a/test/e2e/join_test.go b/test/e2e/join_test.go deleted file mode 100644 index 72c2cc2a2a..0000000000 --- a/test/e2e/join_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package e2e - -import ( - context2 "context" - "testing" - - "github.com/stretchr/testify/assert" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/klog" - - "github.com/liqotech/liqo/test/e2e/util" -) - -func testJoin(t *testing.T) { - t.Run("testPodsUp1", testPodsUp1) - t.Run("testPodsUp2", testPodsUp2) - t.Run("testNodeVK1", testNodeVK1) - t.Run("testNodeVK2", testNodeVK2) -} - -func testPodsUp1(t *testing.T) { - context := util.GetTester() - util.ArePodsUp(context.Client1, context.Namespace, t, "cluster1") -} - -func testPodsUp2(t *testing.T) { - context := util.GetTester() - util.ArePodsUp(context.Client2, context.Namespace, t, "cluster2") -} - -func testNodeVK1(t *testing.T) { - context := util.GetTester() - CheckVkNode(context.Client1, context.Client2, context.Namespace, t) -} - -func testNodeVK2(t *testing.T) { - context := util.GetTester() - CheckVkNode(context.Client2, context.Client1, context.Namespace, t) -} - -func CheckVkNode(client1 *kubernetes.Clientset, client2 *kubernetes.Clientset, namespace string, t *testing.T) { - id, err := client1.CoreV1().ConfigMaps(namespace).Get(context2.TODO(), "cluster-id", metav1.GetOptions{}) - if err != nil { - klog.Error(err) - t.Fail() - } - node, err := client2.CoreV1().Nodes().Get(context2.TODO(), "liqo-"+id.Data["cluster-id"], metav1.GetOptions{}) - if err != nil { - klog.Error(err) - t.Fail() - } - for _, condition := range node.Status.Conditions { - if condition.Type == "Ready" { - assert.Equal(t, "True", string(condition.Status), "Assert the node"+node.Name+"is ready") - } else { - assert.Equal(t, "False", string(condition.Status), "Assert the other node conditions on node "+node.Name+"are not True") - } - } -} diff --git a/test/e2e/net_test.go b/test/e2e/net_test.go deleted file mode 100644 index 1401dd09e7..0000000000 --- a/test/e2e/net_test.go +++ /dev/null @@ -1,345 +0,0 @@ -package e2e - -import ( - "bytes" - "context" - "fmt" - "os/exec" - "strconv" - "testing" - "time" - - "github.com/stretchr/testify/assert" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/klog" - - liqoconst "github.com/liqotech/liqo/pkg/consts" - "github.com/liqotech/liqo/pkg/virtualKubelet" - "github.com/liqotech/liqo/test/e2e/util" -) - -var ( - image = "nginx" - waitTime = 2 * time.Minute - podTesterLocalCl1 = "tester-local-cl1" - podTesterRemoteCl1 = "tester-remote-cl1" - podTesterLocalCl2 = "tester-local-cl2" - podTesterRemoteCl2 = "tester-remote-cl2" - namespaceNameCl1 = "test-connectivity-cl1" - namespaceNameCl2 = "test-connectivity-cl2" - //label to list only the real nodes excluding the virtual ones - labelSelectorNodes = fmt.Sprintf("%v!=%v", liqoconst.TypeLabel, liqoconst.TypeNode) - //TODO: use the retry mechanism of curl without sleeping before running the command - command = "curl -s -o /dev/null -w '%{http_code}' " -) - -func testNet(t *testing.T) { - t.Run("testPodConnectivity1to2", testPodConnectivity1to2) - t.Run("testPodConnectivity2to1", testPodConnectivity2to1) -} - -func testPodConnectivity1to2(t *testing.T) { - context := util.GetTester() - ConnectivityCheckPodToPodCluster1ToCluster2(context, t) - ConnectivityCheckNodeToPodCluster1ToCluster2(context, t) - err := util.DeleteNamespace(context.Client1, namespaceNameCl1) - assert.Nil(t, err, "error should be nil while deleting namespace %s in cluster %s", namespaceNameCl1, context.ClusterID1) - -} - -func testPodConnectivity2to1(t *testing.T) { - context := util.GetTester() - ConnectivityCheckPodToPodCluster2ToCluster1(context, t) - ConnectivityCheckNodeToPodCluster2ToCluster1(context, t) - err := util.DeleteNamespace(context.Client2, namespaceNameCl2) - assert.Nil(t, err, "error should be nil while deleting namespace %s in cluster %s", namespaceNameCl2, context.ClusterID2) -} - -func ConnectivityCheckPodToPodCluster1ToCluster2(con *util.Tester, t *testing.T) { - localNodes, err := util.GetNodes(con.Client1, con.ClusterID2, labelSelectorNodes) - if err != nil { - klog.Error(err) - t.Fail() - } - remoteNodes, err := util.GetNodes(con.Client2, con.ClusterID1, labelSelectorNodes) - if err != nil { - klog.Error(err) - t.Fail() - } - - //testing connection from cluster1 to cluster2 - //we expect for the pod to be created on cluster one and also on cluster 2 - //and to communicate with each other - ns, err := util.CreateNamespace(con.Client1, con.ClusterID1, namespaceNameCl1) - if err != nil { - t.Fail() - } - reflectedNamespace := ns.Name + "-" + con.ClusterID1 - podRemote := DeployRemotePod(image, podTesterRemoteCl1, ns.Name) - _, err = con.Client1.CoreV1().Pods(ns.Name).Create(context.TODO(), podRemote, metav1.CreateOptions{}) - if err != nil { - klog.Error(err) - t.Fail() - } - podLocal := DeployLocalPod(image, podTesterLocalCl1, ns.Name) - _, err = con.Client1.CoreV1().Pods(ns.Name).Create(context.TODO(), podLocal, metav1.CreateOptions{}) - if err != nil { - klog.Error(err) - t.Fail() - } - if !util.WaitForPodToBeReady("home", con.Client1, waitTime, con.ClusterID1, podLocal.Namespace, podLocal.Name) { - t.Fail() - } - if !util.WaitForPodToBeReady("home", con.Client1, waitTime, con.ClusterID1, podRemote.Namespace, podRemote.Name) { - t.Fail() - } - if !util.WaitForPodToBeReady("foreign", con.Client2, waitTime, con.ClusterID2, reflectedNamespace, podRemote.Name) { - t.Fail() - } - podRemoteUpdateCluster2, err := con.Client2.CoreV1().Pods(reflectedNamespace).List(context.TODO(), metav1.ListOptions{ - LabelSelector: fmt.Sprintf("%s=%s", virtualKubelet.ReflectedpodKey, podRemote.Name), - }) - if err != nil { - klog.Error(err) - t.Fail() - } - if len(podRemoteUpdateCluster2.Items) != 1 { - t.Fatalf("there should be exactly one pod with the label %s", fmt.Sprintf("%s=%s", virtualKubelet.ReflectedpodKey, podRemote.Name)) - } - - podRemoteUpdateCluster1, err := con.Client1.CoreV1().Pods(podRemote.Namespace).Get(context.TODO(), podRemote.Name, metav1.GetOptions{}) - if err != nil { - klog.Error(err) - t.Fail() - } - - podLocalUpdate, err := con.Client1.CoreV1().Pods(podLocal.Namespace).Get(context.TODO(), podLocal.Name, metav1.GetOptions{}) - if err != nil { - klog.Error(err) - t.Fail() - } - assert.True(t, isContained(remoteNodes, podRemoteUpdateCluster2.Items[0].Spec.NodeName), "remotepod should be running on one of the local nodes") - assert.True(t, isContained(localNodes, podLocalUpdate.Spec.NodeName), "localpod should be running on one of the remote pods") - cmd := command + podRemoteUpdateCluster1.Status.PodIP - stdout, _, err := util.ExecCmd(con.Config1, con.Client1, podLocalUpdate.Name, podLocalUpdate.Namespace, cmd) - assert.Equal(t, "200", stdout, "status code should be 200") - if err != nil { - t.Fail() - } -} - -func ConnectivityCheckPodToPodCluster2ToCluster1(con *util.Tester, t *testing.T) { - localNodes, err := util.GetNodes(con.Client2, con.ClusterID2, labelSelectorNodes) - if err != nil { - t.Fail() - } - remoteNodes, err := util.GetNodes(con.Client1, con.ClusterID1, labelSelectorNodes) - if err != nil { - t.Fail() - } - - //testing connection from cluster2 to cluster1 - //we expect for the pod to be created on cluster one and also on cluster 2 - //and to communicate with each other - ns, err := util.CreateNamespace(con.Client2, con.ClusterID2, namespaceNameCl2) - if err != nil { - t.Fail() - } - reflectedNamespace := ns.Name + "-" + con.ClusterID2 - podRemote := DeployRemotePod(image, podTesterRemoteCl2, ns.Name) - _, err = con.Client2.CoreV1().Pods(ns.Name).Create(context.TODO(), podRemote, metav1.CreateOptions{}) - if err != nil { - klog.Error(err) - t.Fail() - } - podLocal := DeployLocalPod(image, podTesterLocalCl2, ns.Name) - _, err = con.Client2.CoreV1().Pods(ns.Name).Create(context.TODO(), podLocal, metav1.CreateOptions{}) - if err != nil { - klog.Error(err) - t.Fail() - } - if !util.WaitForPodToBeReady("home", con.Client2, waitTime, con.ClusterID2, podLocal.Namespace, podLocal.Name) { - t.Fail() - } - if !util.WaitForPodToBeReady("home", con.Client2, waitTime, con.ClusterID2, podRemote.Namespace, podRemote.Name) { - t.Fail() - } - if !util.WaitForPodToBeReady("foreign", con.Client1, waitTime, con.ClusterID1, reflectedNamespace, podRemote.Name) { - t.Fail() - } - podRemoteUpdateCluster1, err := con.Client1.CoreV1().Pods(reflectedNamespace).List(context.TODO(), metav1.ListOptions{ - LabelSelector: fmt.Sprintf("%s=%s", virtualKubelet.ReflectedpodKey, podRemote.Name), - }) - if err != nil { - klog.Error(err) - t.Fail() - } - if len(podRemoteUpdateCluster1.Items) != 1 { - klog.Errorf("there should be exactly one pod with the label %s", fmt.Sprintf("%s=%s", virtualKubelet.ReflectedpodKey, podRemote.Name)) - t.Fail() - } - - podRemoteUpdateCluster2, err := con.Client2.CoreV1().Pods(podRemote.Namespace).Get(context.TODO(), podRemote.Name, metav1.GetOptions{}) - if err != nil { - klog.Error(err) - t.Fail() - } - - podLocalUpdate, err := con.Client2.CoreV1().Pods(podLocal.Namespace).Get(context.TODO(), podLocal.Name, metav1.GetOptions{}) - if err != nil { - klog.Error(err) - t.Fail() - } - - assert.True(t, isContained(remoteNodes, podRemoteUpdateCluster1.Items[0].Spec.NodeName), "remotepod should be running on one of the local nodes") - assert.True(t, isContained(localNodes, podLocalUpdate.Spec.NodeName), "localpod should be running on one of the remote pods") - cmd := command + podRemoteUpdateCluster2.Status.PodIP - stdout, _, err := util.ExecCmd(con.Config2, con.Client2, podLocalUpdate.Name, podLocalUpdate.Namespace, cmd) - assert.Nil(t, err, "error should be nil") - assert.Equal(t, "200", stdout, "status code should be 200") - if err != nil { - t.Fail() - } -} - -func ConnectivityCheckNodeToPodCluster1ToCluster2(con *util.Tester, t *testing.T) { - nodePort, err := util.CreateNodePort(con.Client1, con.ClusterID1, podTesterRemoteCl1, "nodeport-cl1", namespaceNameCl1) - if err != nil { - t.Fail() - } - localNodes, err := util.GetNodes(con.Client1, con.ClusterID1, labelSelectorNodes) - if err != nil { - t.Fail() - } - time.Sleep(10 * time.Second) - for _, node := range localNodes.Items { - cmd := command + node.Status.Addresses[0].Address + ":" + strconv.Itoa(int(nodePort.Spec.Ports[0].NodePort)) - c := exec.Command("sh", "-c", cmd) - output := &bytes.Buffer{} - errput := &bytes.Buffer{} - c.Stdout = output - c.Stderr = errput - klog.Infof("running command %s", cmd) - err := c.Run() - if err != nil { - klog.Error(err) - klog.Infof(errput.String()) - t.Fail() - } - assert.Nil(t, err, "error should be nil") - assert.Equal(t, "200", output.String(), "status code should be 200") - } -} - -func ConnectivityCheckNodeToPodCluster2ToCluster1(con *util.Tester, t *testing.T) { - nodePort, err := util.CreateNodePort(con.Client2, con.ClusterID2, podTesterRemoteCl2, "nodeport-cl2", namespaceNameCl2) - if err != nil { - t.Fail() - } - localNodes, err := util.GetNodes(con.Client2, con.ClusterID2, labelSelectorNodes) - if err != nil { - t.Fail() - } - time.Sleep(10 * time.Second) - for _, node := range localNodes.Items { - cmd := command + node.Status.Addresses[0].Address + ":" + strconv.Itoa(int(nodePort.Spec.Ports[0].NodePort)) - c := exec.Command("sh", "-c", cmd) - output := &bytes.Buffer{} - errput := &bytes.Buffer{} - c.Stdout = output - c.Stderr = errput - klog.Infof("running command %s", cmd) - err := c.Run() - if err != nil { - klog.Error(err) - klog.Infof(errput.String()) - t.Fail() - } - assert.Nil(t, err, "error should be nil") - assert.Equal(t, "200", output.String(), "status code should be 200") - } -} - -func DeployRemotePod(image, podName, namespace string) *v1.Pod { - pod1 := v1.Pod{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: podName, - Namespace: namespace, - Labels: map[string]string{"app": podName}, - }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ - { - Name: "tester", - Image: image, - Resources: v1.ResourceRequirements{}, - ImagePullPolicy: "IfNotPresent", - Ports: []v1.ContainerPort{{ - ContainerPort: 80, - }}, - }, - }, - Affinity: &v1.Affinity{ - NodeAffinity: &v1.NodeAffinity{ - RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{NodeSelectorTerms: []v1.NodeSelectorTerm{{ - MatchExpressions: []v1.NodeSelectorRequirement{{ - Key: liqoconst.TypeLabel, - Operator: "In", - Values: []string{liqoconst.TypeNode}, - }}, - MatchFields: nil, - }}}, - }, - }, - }, - Status: v1.PodStatus{}, - } - return &pod1 -} - -func DeployLocalPod(image, podName, namespace string) *v1.Pod { - pod2 := v1.Pod{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: podName, - Namespace: namespace, - }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ - { - Name: "tester", - Image: image, - ImagePullPolicy: "IfNotPresent", - Ports: []v1.ContainerPort{{ - ContainerPort: 80, - }, - }, - }}, - - Affinity: &v1.Affinity{ - NodeAffinity: &v1.NodeAffinity{ - RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{NodeSelectorTerms: []v1.NodeSelectorTerm{{ - MatchExpressions: []v1.NodeSelectorRequirement{{ - Key: liqoconst.TypeLabel, - Operator: "NotIn", - Values: []string{liqoconst.TypeNode}, - }}, - MatchFields: nil, - }}}, - }, - }, - }, - } - return &pod2 -} - -func isContained(nodes *v1.NodeList, nodeName string) bool { - for _, node := range nodes.Items { - if nodeName == node.Name { - return true - } - } - return false -} diff --git a/test/e2e/peering_e2e/basic_test.go b/test/e2e/peering_e2e/basic_test.go new file mode 100644 index 0000000000..101af51b14 --- /dev/null +++ b/test/e2e/peering_e2e/basic_test.go @@ -0,0 +1,125 @@ +package peeringe2e + +import ( + "context" + "testing" + "time" + + "github.com/gruntwork-io/terratest/modules/k8s" + . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/extensions/table" + . "github.com/onsi/gomega" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + + "github.com/liqotech/liqo/test/e2e/testutils" + "github.com/liqotech/liqo/test/e2e/testutils/microservices" + "github.com/liqotech/liqo/test/e2e/testutils/net" + "github.com/liqotech/liqo/test/e2e/testutils/tester" + "github.com/liqotech/liqo/test/e2e/testutils/util" +) + +func TestE2E(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Liqo E2E Suite") +} + +var _ = Describe("Liqo E2E", func() { + var ( + ctx = context.Background() + testContext = tester.GetTester(ctx) + namespace = "liqo" + interval = 3 * time.Second + timeout = 2 * time.Minute + ) + + Describe("Assert that Liqo is up, pod offloading and network connectivity are working", func() { + Context("Check Join Status", func() { + DescribeTable("Liqo pods are up and running", + + func(cluster tester.ClusterContext, namespace string) { + readyPods, notReadyPods, err := util.ArePodsUp(ctx, cluster.Client, testContext.Namespace) + Expect(err).ToNot(HaveOccurred()) + Expect(notReadyPods).To(BeNil()) + Expect(len(readyPods)).Should(BeNumerically(">", 0)) + }, + Entry("Pods UP on cluster 1", testContext.Clusters[0], namespace), + Entry("Pods UP on cluster 2", testContext.Clusters[1], namespace), + ) + + DescribeTable("Liqo Virtual Nodes are ready", + + func(homeCluster tester.ClusterContext, foreignCluster tester.ClusterContext, namespace string) { + nodeReady := util.CheckVirtualNodes(ctx, homeCluster.Client) + Expect(nodeReady).To(BeTrue()) + }, + + Entry("VirtualNode is Ready on cluster 2", testContext.Clusters[0], testContext.Clusters[1], namespace), + Entry("VirtualNode is Ready on cluster 1", testContext.Clusters[1], testContext.Clusters[0], namespace), + ) + + DescribeTable("Liqo Pod to Pod Connectivity Check", + + func(homeCluster tester.ClusterContext, foreignCluster tester.ClusterContext, namespace string) { + + By("Deploy Tester Pod", func() { + err := net.EnsureNetTesterPods(ctx, homeCluster.Client, homeCluster.ClusterID) + Expect(err).ToNot(HaveOccurred()) + Eventually(func() bool { + check := net.CheckTesterPods(ctx, homeCluster.Client, foreignCluster.Client, homeCluster.ClusterID) + return check + }, timeout, interval).Should(BeTrue()) + }) + + By("Check Pod to Pod Connectivity", func() { + Eventually(func() error { + return net.CheckPodConnectivity(homeCluster.Config, homeCluster.Client) + }, timeout, interval).ShouldNot(HaveOccurred()) + }) + + By("Check Service NodePort Connectivity", func() { + err := net.ConnectivityCheckNodeToPod(ctx, homeCluster.Client, homeCluster.ClusterID) + Expect(err).ToNot(HaveOccurred()) + }) + + }, + + Entry("Check Pod to Pod connectivity from cluster 1", testContext.Clusters[0], testContext.Clusters[1], namespace), + Entry("Check Pod to Pod connectivity from cluster 2", testContext.Clusters[1], testContext.Clusters[0], namespace), + ) + }) + + Context("E2E Testing with Online Boutique", func() { + + options := k8s.NewKubectlOptions("", testContext.Clusters[0].KubeconfigPath, microservices.TestNamespaceName) + defer GinkgoRecover() + err := microservices.DeployApp(GinkgoT(), testContext.Clusters[0].KubeconfigPath) + Expect(err).ShouldNot(HaveOccurred()) + microservices.WaitDemoApp(GinkgoT(), options) + + By("Verify Online Boutique Connectivity") + err = microservices.CheckApplicationIsWorking(GinkgoT(), options) + Expect(err).ShouldNot(HaveOccurred()) + }) + + AfterSuite(func() { + + for i := range testContext.Clusters { + err := util.DeleteNamespace(ctx, testContext.Clusters[i].Client, testutils.LiqoTestNamespaceLabels) + Expect(err).ShouldNot(HaveOccurred()) + } + Eventually(func() bool { + for i := range testContext.Clusters { + list, err := testContext.Clusters[i].Client.CoreV1().Namespaces().List(ctx, v1.ListOptions{ + LabelSelector: labels.SelectorFromSet(testutils.LiqoTestNamespaceLabels).String(), + }) + if err != nil || len(list.Items) > 0 { + return false + } + } + return true + }, timeout, interval).Should(BeTrue()) + }) + }) + +}) diff --git a/test/e2e/testutils/consts.go b/test/e2e/testutils/consts.go new file mode 100644 index 0000000000..119bdcbfb0 --- /dev/null +++ b/test/e2e/testutils/consts.go @@ -0,0 +1,12 @@ +// Package testutils encapsulates all methods and constants to perform E2E tests +package testutils + +import "github.com/liqotech/liqo/pkg/consts" + +const liqoTestingLabelKey = "liqo.io/testing-namespace" + +// LiqoTestNamespaceLabels is a set of labels that has to be attached to test namespaces to simplify garbage collection. +var LiqoTestNamespaceLabels = map[string]string{ + liqoTestingLabelKey: "true", + consts.EnablingLiqoLabel: consts.EnablingLiqoLabelValue, +} diff --git a/test/e2e/testutils/microservices/deploy_app.go b/test/e2e/testutils/microservices/deploy_app.go new file mode 100644 index 0000000000..3e7804e48d --- /dev/null +++ b/test/e2e/testutils/microservices/deploy_app.go @@ -0,0 +1,105 @@ +package microservices + +import ( + "context" + "fmt" + "time" + + http_helper "github.com/gruntwork-io/terratest/modules/http-helper" + "github.com/gruntwork-io/terratest/modules/k8s" + "github.com/onsi/ginkgo" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + liqoconst "github.com/liqotech/liqo/pkg/consts" + "github.com/liqotech/liqo/test/e2e/testutils" +) + +const ( + retries = 60 + sleepBetweenRetries = 3 * time.Second + kubeResourcePath = "https://raw.githubusercontent.com/liqotech/microservices-demo/master/release/kubernetes-manifests.yaml" + // TestNamespaceName is the namespace name where the test is performed. + TestNamespaceName = "test-app" +) + +// DeployApp creates the namespace and deploy the applications. It returns an error in case of failures. +func DeployApp(t ginkgo.GinkgoTInterface, configPath string) error { + options := k8s.NewKubectlOptions("", configPath, TestNamespaceName) + if err := k8s.CreateNamespaceWithMetadataE(t, options, metav1.ObjectMeta{ + Name: "test-app", + Labels: testutils.LiqoTestNamespaceLabels, + }); err != nil { + return err + } + if err := k8s.KubectlApplyE(t, options, kubeResourcePath); err != nil { + return err + } + return nil +} + +// WaitDemoApp waits until each service of the application has ready endpoints. It fails if this does not happen +// within the timeout (retries*sleepBetweenRetries). +func WaitDemoApp(t ginkgo.GinkgoTInterface, options *k8s.KubectlOptions) { + pods := k8s.ListPods(t, options, metav1.ListOptions{}) + for index := range pods { + k8s.WaitUntilPodAvailable(t, options, pods[index].Name, retries, sleepBetweenRetries) + } + + svcs := k8s.ListServices(t, options, metav1.ListOptions{}) + for index := range svcs { + // load balancer services will be never available in kind + if svcs[index].Spec.Type != v1.ServiceTypeLoadBalancer { + k8s.WaitUntilServiceAvailable(t, options, svcs[index].Name, retries, sleepBetweenRetries) + } + } +} + +// CheckApplicationIsWorking performs HTTP requests to the micro-service application to assess its functionality and availability. +func CheckApplicationIsWorking(t ginkgo.GinkgoTInterface, options *k8s.KubectlOptions) error { + service := k8s.GetService(t, options, "frontend-external") + if len(service.Spec.Ports) == 0 { + return fmt.Errorf("frontend service not found") + } + + nodes, err := getNodes(t, options) + if err != nil { + return err + } + if len(nodes) == 0 { + return fmt.Errorf("no nodes retrieved from the cluster") + } + nodeAddress, err := getInternalAddress(nodes[0].Status.Addresses) + if err != nil { + return err + } + url := fmt.Sprintf("http://%s:%d", nodeAddress, service.Spec.Ports[0].NodePort) + return http_helper.HttpGetWithRetryWithCustomValidationE(t, url, nil, retries, sleepBetweenRetries, func(code int, body string) bool { + return code == 200 + }) +} + +func getInternalAddress(addrs []v1.NodeAddress) (string, error) { + for _, addr := range addrs { + if addr.Type == v1.NodeInternalIP { + return addr.Address, nil + } + } + return "", fmt.Errorf("unbale to retrieve an internalIP for the selected node") +} + +func getNodes(t ginkgo.GinkgoTInterface, options *k8s.KubectlOptions) ([]v1.Node, error) { + clientset, err := k8s.GetKubernetesClientFromOptionsE(t, options) + if err != nil { + return nil, err + } + + nodes, err := clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{ + LabelSelector: fmt.Sprintf("%v!=%v", liqoconst.TypeLabel, liqoconst.TypeNode), + }) + if err != nil { + return nil, err + } + + return nodes.Items, err +} diff --git a/test/e2e/testutils/microservices/doc.go b/test/e2e/testutils/microservices/doc.go new file mode 100644 index 0000000000..0736f4b099 --- /dev/null +++ b/test/e2e/testutils/microservices/doc.go @@ -0,0 +1,3 @@ +// Package microservices implements an E2E test based on the Google Online Boutique +// (https://github.com/GoogleCloudPlatform/microservices-demo) to assess the E2E functionality of Liqo +package microservices diff --git a/test/e2e/testutils/net/doc.go b/test/e2e/testutils/net/doc.go new file mode 100644 index 0000000000..17ba675c81 --- /dev/null +++ b/test/e2e/testutils/net/doc.go @@ -0,0 +1,2 @@ +// Package net implements basic network connecitivity test on Liqo to assess E2E functionality of the network +package net diff --git a/test/e2e/testutils/net/net.go b/test/e2e/testutils/net/net.go new file mode 100644 index 0000000000..5a12e1c97f --- /dev/null +++ b/test/e2e/testutils/net/net.go @@ -0,0 +1,75 @@ +package net + +import ( + "context" + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + restclient "k8s.io/client-go/rest" + "k8s.io/klog" + + liqoconst "github.com/liqotech/liqo/pkg/consts" + "github.com/liqotech/liqo/test/e2e/testutils/util" +) + +var ( + image = "nginx" + podTesterLocalCl = "tester-local" + podTesterRemoteCl = "tester-remote" + // TestNamespaceName is the namespace name where the test is performed. + TestNamespaceName = "test-connectivity" + // label to list only the real nodes excluding the virtual ones. + labelSelectorNodes = fmt.Sprintf("%v!=%v", liqoconst.TypeLabel, liqoconst.TypeNode) + //TODO: use the retry mechanism of curl without sleeping before running the command. + command = "curl --fail -s -o /dev/null -w '%{http_code}' " +) + +// ConnectivityCheckNodeToPod creates a NodePort Service and check its availability. +func ConnectivityCheckNodeToPod(ctx context.Context, homeClusterClient kubernetes.Interface, clusterID string) error { + nodePort, err := EnsureNodePortService(homeClusterClient, clusterID) + if err != nil { + return err + } + return CheckNodeToPortConnectivity(ctx, homeClusterClient, clusterID, nodePort) +} + +// EnsureNodePortService creates a nodePortService. It returns the port to contact to reach the service and occurred errors. +func EnsureNodePortService(homeClusterClient kubernetes.Interface, clusterID string) (int, error) { + nodePort, err := EnsureNodePort(homeClusterClient, clusterID, "tester-remote", TestNamespaceName) + if err != nil { + return 0, err + } + return int(nodePort.Spec.Ports[0].NodePort), nil +} + +// CheckNodeToPortConnectivity contacts the nodePortValue and returns the result. +func CheckNodeToPortConnectivity(ctx context.Context, homeClusterClient kubernetes.Interface, homeClusterID string, nodePortValue int) error { + localNodes, err := util.GetNodes(ctx, homeClusterClient, homeClusterID, labelSelectorNodes) + if err != nil { + return err + } + return util.TriggerCheckNodeConnectivity(localNodes, command, nodePortValue) +} + +// CheckPodConnectivity contacts the remote service by executing the command inside podRemoteUpdateCluster1. +func CheckPodConnectivity(homeConfig *restclient.Config, homeClient kubernetes.Interface) error { + podLocalUpdate, err := homeClient.CoreV1().Pods(TestNamespaceName).Get(context.TODO(), podTesterLocalCl, metav1.GetOptions{}) + if err != nil { + klog.Error(err) + return err + } + podRemoteUpdateCluster1, err := homeClient.CoreV1().Pods(TestNamespaceName).Get(context.TODO(), podTesterRemoteCl, metav1.GetOptions{}) + if err != nil { + klog.Error(err) + return err + } + cmd := command + podRemoteUpdateCluster1.Status.PodIP + stdout, stderr, err := util.ExecCmd(homeConfig, homeClient, podLocalUpdate.Name, podLocalUpdate.Namespace, cmd) + if stdout == "200" && err == nil { + return nil + } + klog.Infof("stdout: %s", stderr) + klog.Infof("stderr: %s", stderr) + return err +} diff --git a/test/e2e/testutils/net/pod.go b/test/e2e/testutils/net/pod.go new file mode 100644 index 0000000000..17b3ba9273 --- /dev/null +++ b/test/e2e/testutils/net/pod.go @@ -0,0 +1,88 @@ +package net + +import ( + "context" + + v1 "k8s.io/api/core/v1" + kerrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" + + liqoconst "github.com/liqotech/liqo/pkg/consts" + "github.com/liqotech/liqo/test/e2e/testutils/util" +) + +// EnsureNetTesterPods creates the NetTest pods and waits for them to be ready. +func EnsureNetTesterPods(ctx context.Context, homeClient kubernetes.Interface, homeID string) error { + ns, err := util.EnforceNamespace(ctx, homeClient, homeID, TestNamespaceName) + if err != nil && !kerrors.IsAlreadyExists(err) { + klog.Error(err) + return err + } + podRemote := ForgeTesterPod(image, podTesterRemoteCl, ns.Name, true) + _, err = homeClient.CoreV1().Pods(ns.Name).Create(ctx, podRemote, metav1.CreateOptions{}) + if err != nil && !kerrors.IsAlreadyExists(err) { + klog.Error(err) + return err + } + podLocal := ForgeTesterPod(image, podTesterLocalCl, ns.Name, false) + _, err = homeClient.CoreV1().Pods(ns.Name).Create(ctx, podLocal, metav1.CreateOptions{}) + if err != nil && !kerrors.IsAlreadyExists(err) { + klog.Error(err) + return err + } + return nil +} + +// CheckTesterPods retrieves the netTest pods and returns true if all the pods are up and ready. +func CheckTesterPods(ctx context.Context, homeClient, foreignClient kubernetes.Interface, homeClusterID string) bool { + reflectedNamespace := TestNamespaceName + "-" + homeClusterID + return util.IsPodUp(ctx, homeClient, TestNamespaceName, podTesterLocalCl, true) && + util.IsPodUp(ctx, homeClient, TestNamespaceName, podTesterRemoteCl, true) && + util.IsPodUp(ctx, foreignClient, reflectedNamespace, podTesterRemoteCl, false) +} + +// ForgeTesterPod deploys the Remote pod of the test. +func ForgeTesterPod(image, podName, namespace string, isRemote bool) *v1.Pod { + NodeAffinityOperator := v1.NodeSelectorOpNotIn + if isRemote { + NodeAffinityOperator = v1.NodeSelectorOpIn + } + + pod1 := v1.Pod{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: podName, + Namespace: namespace, + Labels: map[string]string{"app": podName}, + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "tester", + Image: image, + Resources: v1.ResourceRequirements{}, + ImagePullPolicy: "IfNotPresent", + Ports: []v1.ContainerPort{{ + ContainerPort: 80, + }}, + }, + }, + Affinity: &v1.Affinity{ + NodeAffinity: &v1.NodeAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{NodeSelectorTerms: []v1.NodeSelectorTerm{{ + MatchExpressions: []v1.NodeSelectorRequirement{{ + Key: liqoconst.TypeLabel, + Operator: NodeAffinityOperator, + Values: []string{liqoconst.TypeNode}, + }}, + MatchFields: nil, + }}}, + }, + }, + }, + Status: v1.PodStatus{}, + } + return &pod1 +} diff --git a/test/e2e/testutils/net/svc.go b/test/e2e/testutils/net/svc.go new file mode 100644 index 0000000000..7192abccf6 --- /dev/null +++ b/test/e2e/testutils/net/svc.go @@ -0,0 +1,48 @@ +package net + +import ( + "context" + + v1 "k8s.io/api/core/v1" + kerrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" +) + +// EnsureNodePort creates a Service of type NodePort for the netTest. +func EnsureNodePort(client kubernetes.Interface, clusterID, name, namespace string) (*v1.Service, error) { + nodePort := &v1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Spec: v1.ServiceSpec{ + Ports: []v1.ServicePort{{ + Name: "http", + Protocol: "TCP", + AppProtocol: nil, + Port: 80, + TargetPort: intstr.IntOrString{ + IntVal: 80, + }, + }}, + Selector: map[string]string{"app": name}, + Type: v1.ServiceTypeNodePort, + }, + Status: v1.ServiceStatus{}, + } + nodePort, err := client.CoreV1().Services(namespace).Create(context.TODO(), nodePort, metav1.CreateOptions{}) + if kerrors.IsAlreadyExists(err) { + _, err = client.CoreV1().Services(namespace).Update(context.TODO(), nodePort, metav1.UpdateOptions{}) + if err != nil { + klog.Errorf("%s -> an error occurred while updating nodePort service %s : %s", clusterID, name, err) + return nil, err + } + } + if err != nil { + klog.Errorf("%s -> an error occurred while creating nodePort service %s in namespace %s: %s", clusterID, name, namespace, err) + return nil, err + } + return nodePort, nil +} diff --git a/test/e2e/testutils/tester/doc.go b/test/e2e/testutils/tester/doc.go new file mode 100644 index 0000000000..159eb647bb --- /dev/null +++ b/test/e2e/testutils/tester/doc.go @@ -0,0 +1,2 @@ +// Package tester contains the logic to access the test context and the cluster included +package tester diff --git a/test/e2e/testutils/tester/tester.go b/test/e2e/testutils/tester/tester.go new file mode 100644 index 0000000000..e621adaada --- /dev/null +++ b/test/e2e/testutils/tester/tester.go @@ -0,0 +1,118 @@ +package tester + +import ( + "context" + "os" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/klog/v2" + + "github.com/liqotech/liqo/pkg/consts" +) + +// Tester is used to encapsulate the context where the test is executed. +type Tester struct { + Clusters []ClusterContext + Namespace string +} + +// ClusterContext encapsulate all information and objects used to access a test cluster. +type ClusterContext struct { + Config *rest.Config + Client *kubernetes.Clientset + ClusterID string + KubeconfigPath string +} + +var ( + tester *Tester +) + +// GetTester returns a Tester instance. +func GetTester(ctx context.Context) *Tester { + if tester == nil { + tester = createTester(ctx) + } + + return tester +} + +func createTester(ctx context.Context) *Tester { + kubeconfig1 := os.Getenv("KUBECONFIG_1") + if kubeconfig1 == "" { + klog.Error("KUBECONFIG_1 not set") + os.Exit(1) + } + kubeconfig2 := os.Getenv("KUBECONFIG_2") + if kubeconfig2 == "" { + klog.Error("KUBECONFIG_2 not set") + os.Exit(1) + } + namespace := os.Getenv("NAMESPACE") + if namespace == "" { + klog.Error("NAMESPACE not set") + os.Exit(1) + } + + config1, err := clientcmd.BuildConfigFromFlags("", kubeconfig1) + if err != nil { + klog.Error(err) + os.Exit(1) + } + config2, err := clientcmd.BuildConfigFromFlags("", kubeconfig2) + if err != nil { + klog.Error(err) + os.Exit(1) + } + clientset1, err := kubernetes.NewForConfig(config1) + if err != nil { + klog.Error(err) + os.Exit(1) + } + clientset2, err := kubernetes.NewForConfig(config2) + if err != nil { + klog.Error(err) + os.Exit(1) + } + clusterID1, err := getClusterID(ctx, clientset1, namespace) + if err != nil { + klog.Warningf("an error occurred while getting cluster-id configmap %s", err) + clusterID1 = "" + } + clusterID2, err := getClusterID(ctx, clientset2, namespace) + if err != nil { + klog.Warningf("an error occurred while getting cluster-id configmap %s", err) + clusterID2 = "" + } + return &Tester{ + Namespace: namespace, + Clusters: []ClusterContext{ + { + Config: config1, + KubeconfigPath: kubeconfig1, + Client: clientset1, + ClusterID: clusterID1, + }, + { + Config: config2, + KubeconfigPath: kubeconfig2, + Client: clientset2, + ClusterID: clusterID2, + }, + }, + } +} + +func getClusterID(ctx context.Context, client *kubernetes.Clientset, namespace string) (string, error) { + cmClient := client.CoreV1().ConfigMaps(namespace) + cm, err := cmClient.Get(ctx, consts.ClusterIDConfigMapName, metav1.GetOptions{}) + if err != nil { + return "", err + } + clusterID := cm.Data[consts.ClusterIDConfigMapName] + klog.Infof("got clusterid %s", clusterID) + return clusterID, nil +} diff --git a/test/e2e/testutils/util/doc.go b/test/e2e/testutils/util/doc.go new file mode 100644 index 0000000000..798184c45e --- /dev/null +++ b/test/e2e/testutils/util/doc.go @@ -0,0 +1,2 @@ +// Package util contains all the utility methods to execute the test (APIServer interaction, wait for condition, etc.). +package util diff --git a/test/e2e/testutils/util/exec.go b/test/e2e/testutils/util/exec.go new file mode 100644 index 0000000000..81179cdd3b --- /dev/null +++ b/test/e2e/testutils/util/exec.go @@ -0,0 +1,70 @@ +package util + +import ( + "bytes" + "os/exec" + "strconv" + + v1 "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + remotecommandclient "k8s.io/client-go/tools/remotecommand" + "k8s.io/klog/v2" +) + +// ExecCmd executes a command inside a pod. +func ExecCmd(config *rest.Config, client kubernetes.Interface, podName, namespace, command string) (stdOut, stdErr string, retErr error) { + cmd := []string{ + "sh", + "-c", + command, + } + req := client.CoreV1().RESTClient().Post(). + Resource("pods"). + Name(podName). + Namespace(namespace). + SubResource("exec") + req.VersionedParams(&v1.PodExecOptions{ + Stdin: false, + Stdout: true, + Stderr: true, + TTY: false, + Command: cmd, + }, scheme.ParameterCodec) + + var stdout, stderr bytes.Buffer + executor, err := remotecommandclient.NewSPDYExecutor(config, "POST", req.URL()) + if err != nil { + return "", "", err + } + err = executor.Stream(remotecommandclient.StreamOptions{ + Stdin: nil, + Stdout: &stdout, + Stderr: &stderr, + }) + if err != nil { + return "", "", err + } + return stdout.String(), stderr.String(), err +} + +// TriggerCheckNodeConnectivity checks nodePort service connectivity, executing a command for every node in the target cluster. +func TriggerCheckNodeConnectivity(localNodes *v1.NodeList, command string, nodePortValue int) error { + for index := range localNodes.Items { + cmd := command + localNodes.Items[index].Status.Addresses[0].Address + ":" + strconv.Itoa(nodePortValue) + c := exec.Command("sh", "-c", cmd) //nolint:gosec // Just a test, no need for this check + output := &bytes.Buffer{} + errput := &bytes.Buffer{} + c.Stdout = output + c.Stderr = errput + klog.Infof("running command %s", cmd) + err := c.Run() + if err != nil { + klog.Info(output.String()) + klog.Info(errput.String()) + return nil + } + } + return nil +} diff --git a/test/e2e/testutils/util/namespace.go b/test/e2e/testutils/util/namespace.go new file mode 100644 index 0000000000..cc1361eb27 --- /dev/null +++ b/test/e2e/testutils/util/namespace.go @@ -0,0 +1,55 @@ +package util + +import ( + "context" + + v1 "k8s.io/api/core/v1" + kerrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" + + "github.com/liqotech/liqo/test/e2e/testutils" +) + +// EnforceNamespace creates and returns a namespace. If it already exists, it just returns the namespace. +func EnforceNamespace(ctx context.Context, client kubernetes.Interface, clusterID, name string) (*v1.Namespace, error) { + ns := &v1.Namespace{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: testutils.LiqoTestNamespaceLabels, + }, + Spec: v1.NamespaceSpec{}, + Status: v1.NamespaceStatus{}, + } + ns, err := client.CoreV1().Namespaces().Create(ctx, ns, metav1.CreateOptions{}) + if kerrors.IsAlreadyExists(err) { + ns, err = client.CoreV1().Namespaces().Get(ctx, name, metav1.GetOptions{}) + if err != nil { + klog.Errorf("%s -> an error occurred while creating namespace %s : %s", clusterID, name, err) + return nil, err + } + } else if err != nil { + klog.Errorf("%s -> an error occurred while creating namespace %s : %s", clusterID, name, err) + return nil, err + } + return ns, nil +} + +// DeleteNamespace wrap the deletion of a namespace. +func DeleteNamespace(ctx context.Context, client kubernetes.Interface, labelSelector map[string]string) error { + list, err := client.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{ + LabelSelector: labels.SelectorFromSet(labelSelector).String(), + }) + if err != nil && !kerrors.IsNotFound(err) { + return err + } + for i := range list.Items { + if e := client.CoreV1().Namespaces().Delete(ctx, list.Items[i].Name, metav1.DeleteOptions{}); e != nil && !kerrors.IsNotFound(err) { + return e + } + } + return nil +} diff --git a/test/e2e/testutils/util/nodes.go b/test/e2e/testutils/util/nodes.go new file mode 100644 index 0000000000..33f67f3180 --- /dev/null +++ b/test/e2e/testutils/util/nodes.go @@ -0,0 +1,52 @@ +package util + +import ( + "context" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" + + "github.com/liqotech/liqo/pkg/consts" +) + +// GetNodes returns the list of nodes of the cluster matching the given labels. +func GetNodes(ctx context.Context, client kubernetes.Interface, clusterID, labelSelector string) (*v1.NodeList, error) { + remoteNodes, err := client.CoreV1().Nodes().List(ctx, metav1.ListOptions{ + LabelSelector: labelSelector, + }) + if err != nil { + klog.Errorf("%s -> an error occurred while listing nodes: %s", clusterID, err) + return nil, err + } + return remoteNodes, nil +} + +// CheckVirtualNodes checks if the Liqo virtual nodes of cluster C. +func CheckVirtualNodes(ctx context.Context, homeClusterClient kubernetes.Interface) (ready bool) { + var nodeLabel = make(map[string]string) + nodeLabel[consts.TypeLabel] = consts.TypeNode + virtualNodes, err := homeClusterClient.CoreV1().Nodes().List(ctx, metav1.ListOptions{ + LabelSelector: labels.SelectorFromSet(nodeLabel).String(), + }) + if err != nil { + klog.Error(err) + return false + } + for index := range virtualNodes.Items { + for _, condition := range virtualNodes.Items[index].Status.Conditions { + if condition.Type == "Ready" { + if string(condition.Status) == "False" { + return false + } + } else { + if string(condition.Status) == "True" { + return false + } + } + } + } + return true +} diff --git a/test/e2e/testutils/util/pod.go b/test/e2e/testutils/util/pod.go new file mode 100644 index 0000000000..f51cce0a5c --- /dev/null +++ b/test/e2e/testutils/util/pod.go @@ -0,0 +1,56 @@ +package util + +import ( + "context" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" + "k8s.io/kubernetes/pkg/api/v1/pod" + + "github.com/liqotech/liqo/pkg/virtualKubelet" +) + +// IsPodUp waits for a specific namespace/podName to be ready. It returns true if the pod within the timeout, false otherwise. +func IsPodUp(ctx context.Context, client kubernetes.Interface, namespace, podName string, isHomePod bool) bool { + var podToCheck *corev1.Pod + var err error + var labelSelector = map[string]string{ + virtualKubelet.ReflectedpodKey: podName, + } + if isHomePod { + podToCheck, err = client.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{}) + if err != nil { + return false + } + } else { + pods, err := client.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{ + LabelSelector: labels.SelectorFromSet(labelSelector).String(), + }) + if err != nil || len(pods.Items) == 0 { + return false + } + podToCheck = &pods.Items[0] + } + state := pod.IsPodReady(podToCheck) + return state +} + +// ArePodsUp check if all the pods of a specific namespace are ready. It returns a list of ready pods, a list of unready +// pods and occurred errors. +func ArePodsUp(ctx context.Context, clientset kubernetes.Interface, namespace string) (ready, notReady []string, retErr error) { + pods, retErr := clientset.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{}) + if retErr != nil { + klog.Error(retErr) + return nil, nil, retErr + } + for index := range pods.Items { + if !pod.IsPodReady(&pods.Items[index]) { + notReady = append(notReady, pods.Items[index].Name) + } + ready = append(ready, pods.Items[index].Name) + } + return ready, notReady, nil +} diff --git a/test/e2e/unjoin_e2e/unjoin_test.go b/test/e2e/unjoin_e2e/unjoin_test.go deleted file mode 100644 index f6a239debc..0000000000 --- a/test/e2e/unjoin_e2e/unjoin_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package unjoin_e2e - -import ( - context2 "context" - "fmt" - "testing" - - "gotest.tools/assert" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/klog" - - liqoconst "github.com/liqotech/liqo/pkg/consts" - "github.com/liqotech/liqo/test/e2e/util" -) - -func TestUnjoin(t *testing.T) { - context := util.GetTester() - NoPods(context.Client1, context.Namespace, t, "cluster1") - - NoJoined(context.Client2, t, "cluster2") - util.ArePodsUp(context.Client2, context.Namespace, t, "cluster2") -} - -func NoPods(clientset *kubernetes.Clientset, namespace string, t *testing.T, clustername string) { - pods, err := clientset.CoreV1().Pods(namespace).List(context2.TODO(), metav1.ListOptions{}) - if err != nil { - klog.Error(err) - t.Fail() - } - assert.Equal(t, len(pods.Items), 0, "There are still running pods on "+clustername) -} - -func NoJoined(clientset *kubernetes.Clientset, t *testing.T, clustername string) { - nodes, err := clientset.CoreV1().Nodes().List(context2.TODO(), metav1.ListOptions{ - LabelSelector: fmt.Sprintf("%v=%v", liqoconst.TypeLabel, liqoconst.TypeNode), - }) - if err != nil { - klog.Error(err) - t.Fail() - } - assert.Equal(t, len(nodes.Items), 0, "There are still virtual nodes on "+clustername) -} diff --git a/test/e2e/unjoin_e2e/unpeering_test.go b/test/e2e/unjoin_e2e/unpeering_test.go new file mode 100644 index 0000000000..b4cbafb729 --- /dev/null +++ b/test/e2e/unjoin_e2e/unpeering_test.go @@ -0,0 +1,71 @@ +package unjoine2e + +import ( + "context" + "fmt" + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/klog" + + liqoconst "github.com/liqotech/liqo/pkg/consts" + "github.com/liqotech/liqo/test/e2e/testutils/tester" + "github.com/liqotech/liqo/test/e2e/testutils/util" +) + +func Test_Unjoin(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Liqo E2E Suite") +} + +var _ = Describe("Liqo E2E", func() { + var ( + ctx = context.Background() + testContext = tester.GetTester(ctx) + ) + + Describe("Assert that Liqo is correctly uninstalled", func() { + Context("Test Unjoin", func() { + err := NoPods(testContext.Clusters[0].Client, testContext.Namespace) + Expect(err).ShouldNot(HaveOccurred()) + err = NoJoined(testContext.Clusters[0].Client) + Expect(err).ShouldNot(HaveOccurred()) + readyPods, notReadyPods, err := util.ArePodsUp(ctx, testContext.Clusters[1].Client, testContext.Namespace) + Expect(err).ShouldNot(HaveOccurred()) + Expect(notReadyPods).Should(BeZero()) + Expect(len(readyPods)).Should(BeNumerically(">", 0)) + }, + ) + }) +}) + +func NoPods(clientset *kubernetes.Clientset, namespace string) error { + pods, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{}) + if err != nil { + klog.Error(err) + return err + } + if len(pods.Items) > 0 { + return fmt.Errorf("There are still running pods in Liqo namespace") + } + return nil +} + +func NoJoined(clientset *kubernetes.Clientset) error { + nodes, err := clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{ + LabelSelector: fmt.Sprintf("%v=%v", liqoconst.TypeLabel, liqoconst.TypeNode), + }) + if err != nil { + klog.Error(err) + return err + } + + if len(nodes.Items) > 0 { + return fmt.Errorf("There are still virtual nodes in the cluster") + } + return nil + +} diff --git a/test/e2e/util/util.go b/test/e2e/util/util.go deleted file mode 100644 index 48321a68ab..0000000000 --- a/test/e2e/util/util.go +++ /dev/null @@ -1,307 +0,0 @@ -package util - -import ( - "bytes" - "context" - "fmt" - "os" - "sync" - "testing" - "time" - - "gotest.tools/assert" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" - remotecommandclient "k8s.io/client-go/tools/remotecommand" - "k8s.io/klog" - "k8s.io/kubernetes/pkg/api/v1/pod" - - "github.com/liqotech/liqo/pkg/virtualKubelet" -) - -type Tester struct { - Config1 *rest.Config - Config2 *rest.Config - Client1 *kubernetes.Clientset - Client2 *kubernetes.Clientset - ClusterID1 string - ClusterID2 string - Namespace string -} - -var l = &sync.Mutex{} - -var ( - t1 *Tester - clusterIDConfMap = "cluster-id" - namespaceLabels = map[string]string{"liqo.io/enabled": "true"} -) - -func GetTester() *Tester { - l.Lock() - defer l.Unlock() - - if t1 == nil { - t1 = createTester() - } - - return t1 -} - -func createTester() *Tester { - kubeconfig1 := os.Getenv("KUBECONFIG_1") - if kubeconfig1 == "" { - klog.Error("KUBECONFIG_1 not set") - os.Exit(1) - } - kubeconfig2 := os.Getenv("KUBECONFIG_2") - if kubeconfig2 == "" { - klog.Error("KUBECONFIG_2 not set") - os.Exit(1) - } - namespace := os.Getenv("NAMESPACE") - if namespace == "" { - klog.Error("NAMESPACE not set") - os.Exit(1) - } - - config1, err := clientcmd.BuildConfigFromFlags("", kubeconfig1) - if err != nil { - klog.Error(err) - os.Exit(1) - } - config2, err := clientcmd.BuildConfigFromFlags("", kubeconfig2) - if err != nil { - klog.Error(err) - os.Exit(1) - } - clientset1, err := kubernetes.NewForConfig(config1) - if err != nil { - klog.Error(err) - os.Exit(1) - } - clientset2, err := kubernetes.NewForConfig(config2) - if err != nil { - klog.Error(err) - os.Exit(1) - } - clusterID1, err := getClusterID(clientset1, namespace) - if err != nil { - klog.Warningf("an error occurred while getting cluster-id configmap %s", err) - clusterID1 = "" - } - clusterID2, err := getClusterID(clientset2, namespace) - if err != nil { - klog.Warningf("an error occurred while getting cluster-id configmap %s", err) - clusterID2 = "" - } - return &Tester{ - Config1: config1, - Config2: config2, - Client1: clientset1, - Client2: clientset2, - Namespace: namespace, - ClusterID1: clusterID1, - ClusterID2: clusterID2, - } -} - -func WaitForPodToBeReady(location string, client *kubernetes.Clientset, waitSeconds time.Duration, clusterID, namespace, podName string) bool { - stop := make(chan bool, 1) - ready := make(chan bool, 1) - klog.Infof("%s -> waiting for pod %s on namespace %s to become ready", clusterID, podName, namespace) - go PodWatcher(location, client, clusterID, namespace, podName, ready, stop) - select { - case isReady := <-ready: - return isReady - case <-time.After(waitSeconds): - klog.Infof("%s -> pod %s on namespace %s required more than %s to be ready", clusterID, podName, namespace, waitSeconds) - return false - } -} - -func PodWatcher(location string, client *kubernetes.Clientset, clusterID, namespace, podName string, podReady, stopCh chan bool) { - var watcher watch.Interface - var err error - - // If the location is home, we know the exact name of the pod - // otherwise, we know the name of the shadowed copy of a foreign pod by using a specific label. - // This happens because the foreign pod are created by the foreign kube-controller, starting from a replicaset. - // Hence, we do not know the exact foreign pod name. - if location == "home" { - watcher, err = client.CoreV1().Pods(namespace).Watch(context.TODO(), metav1.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(metav1.ObjectNameField, podName).String(), - }) - } else { - watcher, err = client.CoreV1().Pods(namespace).Watch(context.TODO(), metav1.ListOptions{ - LabelSelector: fmt.Sprintf("%s=%s", virtualKubelet.ReflectedpodKey, podName), - }) - } - - if err != nil { - klog.Errorf("%s -> an error occurred while launching the watcher for pod %s: %s", clusterID, podName, err) - return - } - event := watcher.ResultChan() - for { - select { - case <-stopCh: - klog.Infof("%s -> the watcher for pod %s timed out", clusterID, podName) - podReady <- false - case e := <-event: - obj, ok := e.Object.(*v1.Pod) - if !ok { - klog.Infof("object is not a pod") - continue - } - switch e.Type { - case watch.Added: - if pod.IsPodReady(obj) { - podReady <- true - return - } - case watch.Modified: - if pod.IsPodReady(obj) { - podReady <- true - return - } - case watch.Deleted: - podReady <- false - return - } - } - } -} - -func getClusterID(client *kubernetes.Clientset, namespace string) (string, error) { - cmClient := client.CoreV1().ConfigMaps(namespace) - cm, err := cmClient.Get(context.TODO(), clusterIDConfMap, metav1.GetOptions{}) - if err != nil { - return "", err - } - clusterID := cm.Data[clusterIDConfMap] - klog.Infof("got clusterid %s", clusterID) - return clusterID, nil -} - -func ExecCmd(config *rest.Config, client *kubernetes.Clientset, podName, namespace, command string) (string, string, error) { - cmd := []string{ - "sh", - "-c", - command, - } - req := client.CoreV1().RESTClient().Post(). - Resource("pods"). - Name(podName). - Namespace(namespace). - SubResource("exec") - req.VersionedParams(&v1.PodExecOptions{ - Stdin: false, - Stdout: true, - Stderr: true, - TTY: false, - Command: cmd, - }, scheme.ParameterCodec) - - var stdout, stderr bytes.Buffer - exec, err := remotecommandclient.NewSPDYExecutor(config, "POST", req.URL()) - if err != nil { - return "", "", err - } - err = exec.Stream(remotecommandclient.StreamOptions{ - Stdin: nil, - Stdout: &stdout, - Stderr: &stderr, - }) - if err != nil { - return "", "", err - } - return stdout.String(), stderr.String(), err -} - -func CreateNamespace(client *kubernetes.Clientset, clusterID string, name string) (*v1.Namespace, error) { - ns := &v1.Namespace{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: namespaceLabels, - }, - Spec: v1.NamespaceSpec{}, - Status: v1.NamespaceStatus{}, - } - ns, err := client.CoreV1().Namespaces().Create(context.TODO(), ns, metav1.CreateOptions{}) - if err != nil { - klog.Errorf("%s -> an error occurred while creating namespace %s : %s", clusterID, name, err) - return nil, err - } - return ns, nil -} - -func DeleteNamespace(client *kubernetes.Clientset, name string) error { - err := client.CoreV1().Namespaces().Delete(context.TODO(), name, metav1.DeleteOptions{}) - if err != nil { - return err - } - return nil -} - -func GetNodes(client *kubernetes.Clientset, clusterID string, labelSelector string) (*v1.NodeList, error) { - remoteNodes, err := client.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{ - LabelSelector: labelSelector, - }) - if err != nil { - klog.Errorf("%s -> an error occurred while listing nodes: %s", clusterID, err) - return nil, err - } - return remoteNodes, nil -} - -func CreateNodePort(client *kubernetes.Clientset, clusterID, appName, name, namespace string) (*v1.Service, error) { - nodePort := &v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - }, - Spec: v1.ServiceSpec{ - Ports: []v1.ServicePort{{ - Name: "http", - Protocol: "TCP", - AppProtocol: nil, - Port: 80, - TargetPort: intstr.IntOrString{ - IntVal: 80, - }, - }}, - Selector: map[string]string{"app": appName}, - Type: v1.ServiceTypeNodePort, - }, - Status: v1.ServiceStatus{}, - } - nodePort, err := client.CoreV1().Services(namespace).Create(context.TODO(), nodePort, metav1.CreateOptions{}) - if err != nil { - klog.Errorf("%s -> an error occurred while creating nodePort %s in namespace %s: %s", clusterID, name, namespace, err) - return nil, err - } - return nodePort, nil -} - -func ArePodsUp(clientset *kubernetes.Clientset, namespace string, t *testing.T, clustername string) { - pods, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{}) - if err != nil { - klog.Error(err) - t.Fail() - } - assert.Assert(t, len(pods.Items) > 0, "No pods in "+namespace+" on "+clustername) - for _, num := range pods.Items { - for _, container := range num.Status.ContainerStatuses { - assert.Equal(t, true, container.Ready, "Asserting "+container.Name+"pods is running "+ - "on "+clustername) - } - } -}