From 8da400090a0f81c389eb552837e951850cebfdeb Mon Sep 17 00:00:00 2001 From: Bharadwajshivam28 Date: Thu, 24 Oct 2024 02:38:42 +0530 Subject: [PATCH 1/7] feat: Adding gcp cloud provider --- go.mod | 46 ++++++++--- go.sum | 187 ++++++++++++++++++++++++++++++++++++++------- program/config.go | 2 +- program/gcp.go | 184 ++++++++++++++++++++++++++++++++++++++++++++ program/program.go | 2 +- program/stats.go | 2 +- 6 files changed, 382 insertions(+), 41 deletions(-) create mode 100644 program/gcp.go diff --git a/go.mod b/go.mod index 46cda07..c5436a4 100644 --- a/go.mod +++ b/go.mod @@ -1,23 +1,37 @@ module github.com/clouddrove/kuconf -go 1.19 +go 1.21 + +toolchain go1.22.5 require ( + cloud.google.com/go/container v1.40.0 + cloud.google.com/go/iam v1.2.1 github.com/alecthomas/kong v1.2.1 github.com/aws/aws-sdk-go v1.55.5 github.com/mattn/go-colorable v0.1.13 github.com/pkg/errors v0.9.1 github.com/rs/zerolog v1.33.0 github.com/stretchr/testify v1.9.0 + google.golang.org/api v0.203.0 + google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 k8s.io/client-go v0.29.1 ) require ( + cloud.google.com/go/auth v0.9.9 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect + cloud.google.com/go/compute/metadata v0.5.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/go-logr/logr v1.3.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/s2a-go v0.1.8 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect + github.com/googleapis/gax-go/v2 v2.13.0 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -26,14 +40,24 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/oauth2 v0.10.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/term v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.3.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.33.0 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect + go.opentelemetry.io/otel v1.29.0 // indirect + go.opentelemetry.io/otel/metric v1.29.0 // indirect + go.opentelemetry.io/otel/trace v1.29.0 // indirect + golang.org/x/crypto v0.28.0 // indirect + golang.org/x/net v0.30.0 // indirect + golang.org/x/oauth2 v0.23.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/term v0.25.0 // indirect + golang.org/x/text v0.19.0 // indirect + golang.org/x/time v0.7.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect + google.golang.org/grpc v1.67.1 // indirect + google.golang.org/protobuf v1.35.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 9489b9c..fbb4373 100644 --- a/go.sum +++ b/go.sum @@ -1,35 +1,96 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE= +cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U= +cloud.google.com/go/auth v0.9.9 h1:BmtbpNQozo8ZwW2t7QJjnrQtdganSdmqeIBxHxNkEZQ= +cloud.google.com/go/auth v0.9.9/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI= +cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY= +cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc= +cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo= +cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k= +cloud.google.com/go/container v1.40.0 h1:JVoEg/4RvoGW37r2Eja/cTBc3X9c2loGWYq7QDsRDuI= +cloud.google.com/go/container v1.40.0/go.mod h1:wNI1mOUivm+ZkpHMbouutgbD4sQxyphMwK31X5cThY4= +cloud.google.com/go/iam v1.2.1 h1:QFct02HRb7H12J/3utj0qf5tobFh9V4vR6h9eX5EBRU= +cloud.google.com/go/iam v1.2.1/go.mod h1:3VUIJDPpwT6p/amXRC5GY8fCCh70lxPygguVtI0Z4/g= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/alecthomas/assert/v2 v2.10.0 h1:jjRCHsj6hBJhkmhznrCzoNpbA3zqy0fYiUcYZP/GkPY= +github.com/alecthomas/assert/v2 v2.10.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/kong v1.2.1 h1:E8jH4Tsgv6wCRX2nGrdPyHDUCSG83WH2qE4XLACD33Q= github.com/alecthomas/kong v1.2.1/go.mod h1:rKTSFhbdp3Ryefn8x5MOEprnRFQ7nlmMC01GKhehhBM= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= -github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= +github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= +github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= +github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= +github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= @@ -37,11 +98,13 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -53,56 +116,96 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= +go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= +go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= +go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= +go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= +go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo= +go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= +go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= +go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 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/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= -golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -110,12 +213,37 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/api v0.203.0 h1:SrEeuwU3S11Wlscsn+LA1kb/Y5xT8uggJSkIhD08NAU= +google.golang.org/api v0.203.0/go.mod h1:BuOVyCSYEPwJb3npWvDnNmFI92f3GeRnHNkETneT3SI= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 h1:Q3nlH8iSQSRUwOskjbcSMcF2jiYMNiQYZ0c2KEJLKKU= +google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38/go.mod h1:xBI+tzfqGGN2JBeSebfKXFSdBpWVQ7sLW40PTupVRm4= +google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 h1:fVoAXEKA4+yufmbdVYv+SE73+cPZbbbe8paLsHfkK+U= +google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53/go.mod h1:riSXTwQ4+nqmPGtobMFyW5FqVAmIs0St6VPp4Ug7CE4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= @@ -123,10 +251,14 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.29.1 h1:DAjwWX/9YT7NQD4INu49ROJuZAAAP/Ijki48GUPzxqw= +k8s.io/api v0.29.1/go.mod h1:7Kl10vBRUXhnQQI8YR/R327zXC8eJ7887/+Ybta+RoQ= k8s.io/apimachinery v0.29.1 h1:KY4/E6km/wLBguvCZv8cKTeOwwOBqFNjwJIdMkMbbRc= k8s.io/apimachinery v0.29.1/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU= k8s.io/client-go v0.29.1 h1:19B/+2NGEwnFLzt0uB5kNJnfTsbV8w6TgQRz9l7ti7A= @@ -134,6 +266,7 @@ k8s.io/client-go v0.29.1/go.mod h1:TDG/psL9hdet0TI9mGyHJSgRkW3H9JZk2dNEUS7bRks= k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= diff --git a/program/config.go b/program/config.go index 536903b..10530e2 100644 --- a/program/config.go +++ b/program/config.go @@ -109,4 +109,4 @@ func (program *Options) WriteConfig(config *api.Config) error { } else { return err } -} +} \ No newline at end of file diff --git a/program/gcp.go b/program/gcp.go new file mode 100644 index 0000000..3442e45 --- /dev/null +++ b/program/gcp.go @@ -0,0 +1,184 @@ +package program + +import ( + "bufio" + "context" + "os" + "strings" + "sync" + + "cloud.google.com/go/container/apiv1" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + containerpb "cloud.google.com/go/container/apiv1/containerpb" + "google.golang.org/api/option" +) + +type gcpSessionInfo struct { + project string + zone string + session *container.ClusterManagerClient + log zerolog.Logger +} + +type GCPClusterInfo struct { + *containerpb.Cluster + log zerolog.Logger + session *gcpSessionInfo +} + +type Options2 struct { + Projects []string + ProjectFile string + Zones []string +} + +func (program *Options2) getProjects() <-chan string { + output := make(chan string) + + if len(program.Projects) < 1 { + go func() { + defer close(output) + if f, err := os.Open(program.ProjectFile); err == nil { + scanner := bufio.NewScanner(f) + scanner.Split(bufio.ScanLines) + + for scanner.Scan() { + s := strings.TrimSpace(scanner.Text()) + if s != "" { + output <- s + } + } + + } else { + log.Error().Str("file", program.ProjectFile).Err(err).Msg("Failed to open project file") + } + }() + } else { + go func() { + defer close(output) + for _, p := range program.Projects { + output <- p + } + }() + } + + return output +} + +func (program *Options2) getClustersFrom(s *gcpSessionInfo, clusters chan<- GCPClusterInfo) { + wg := sync.WaitGroup{} + defer wg.Wait() + + req := &containerpb.ListClustersRequest{ + Parent: "projects/" + s.project + "/locations/" + s.zone, + } + + s.log.Debug().Msg("Listing GKE clusters") + if out, err := s.session.ListClusters(context.Background(), req); err == nil { + s.log.Debug().Msg("Getting GKE Clusters") + + for _, c := range out.Clusters { + wg.Add(1) + go func(c *containerpb.Cluster) { + defer wg.Done() + s.log.Debug().Str("cluster_name", c.Name).Msg("Found GKE cluster") + + clusters <- GCPClusterInfo{ + Cluster: c, + log: s.log.With().Str("cluster_name", c.Name).Logger(), + session: s, + } + }(c) + } + } else { + s.log.Error().Err(err).Msg("Error listing GKE clusters") + } +} + +func (program *Options2) getUniqueGCPSessions() <-chan *gcpSessionInfo { + + sessions := make(chan *gcpSessionInfo) + + go func() { + wg := sync.WaitGroup{} + + defer close(sessions) + defer wg.Wait() + + projects := make(map[string]bool) + for info := range program.getProjectSessions() { + if _, found := projects[info.project]; found { + info.log.Debug().Msg("Project is duplicate") + } else { + info.log.Debug().Msg("Project is good for use") + projects[info.project] = true + + sessions <- info + + for _, zone := range program.Zones { + wg.Add(1) + go func(project, zone string) { + defer wg.Done() + if zone != info.zone { + log := log.With().Str("project", info.project).Str("zone", zone).Logger() + log.Debug().Msg("Creating regional session") + if s, err := container.NewClusterManagerClient(context.Background(), option.WithCredentialsFile("path/to/credentials.json")); err == nil { + sessions <- &gcpSessionInfo{ + project: project, + zone: zone, + session: s, + log: log, + } + } else { + log.Error().Err(err).Msg("Failed to create GCP session") + } + } + }(info.project, zone) + } + } + } + }() + + return sessions +} + +func (program *Options2) getProjectSessions() <-chan *gcpSessionInfo { + + sessions := make(chan *gcpSessionInfo) + wg := sync.WaitGroup{} + + go func() { + defer close(sessions) + defer wg.Wait() + + projects := program.getProjects() + + for p := range projects { + log := log.With().Str("project", p).Str("zone", program.Zones[0]).Logger() + wg.Add(1) + go func(p string) { + defer wg.Done() + if s, err := NewGCPSession(p, program.Zones[0], log); err == nil { + sessions <- s + } + }(p) + } + }() + + return sessions +} + +func NewGCPSession(project, zone string, log zerolog.Logger) (*gcpSessionInfo, error) { + if sess, err := container.NewClusterManagerClient(context.Background(), option.WithCredentialsFile("path/to/credentials.json")); err == nil { + log.Debug().Msg("GCP project session created") + return &gcpSessionInfo{ + project: project, + zone: zone, + session: sess, + log: log, + }, nil + } else { + return nil, err + } +} diff --git a/program/program.go b/program/program.go index ac32f51..2078928 100644 --- a/program/program.go +++ b/program/program.go @@ -142,4 +142,4 @@ func isTerminal(file *os.File) bool { } else { return (fileInfo.Mode() & os.ModeCharDevice) != 0 } -} +} \ No newline at end of file diff --git a/program/stats.go b/program/stats.go index f850051..25e310f 100644 --- a/program/stats.go +++ b/program/stats.go @@ -21,4 +21,4 @@ func (s *Stats) Log() { Msg("Statistics") } -var stats Stats +var stats Stats \ No newline at end of file From 591f09e5448d09da45bb9eefe18908c2fe2f2d65 Mon Sep 17 00:00:00 2001 From: Bharadwajshivam28 Date: Fri, 25 Oct 2024 00:08:29 +0530 Subject: [PATCH 2/7] Integrated kuconf for GCP cloud provider --- main.go | 4 +- program/{ => aws}/aws.go | 4 +- program/{ => aws}/config.go | 2 +- program/{ => aws}/program.go | 2 +- program/aws/program_test.go | 20 +++ program/{ => aws}/stats.go | 2 +- program/{ => aws}/version.go | 4 +- program/gcp.go | 184 ------------------------- program/gcp/config.go | 101 ++++++++++++++ program/gcp/gcp.go | 218 ++++++++++++++++++++++++++++++ program/gcp/program.go | 138 +++++++++++++++++++ program/{ => gcp}/program_test.go | 2 +- program/gcp/stats.go | 23 ++++ program/gcp/version.go | 13 ++ 14 files changed, 523 insertions(+), 194 deletions(-) rename program/{ => aws}/aws.go (99%) rename program/{ => aws}/config.go (99%) rename program/{ => aws}/program.go (99%) create mode 100644 program/aws/program_test.go rename program/{ => aws}/stats.go (97%) rename program/{ => aws}/version.go (95%) mode change 100755 => 100644 delete mode 100644 program/gcp.go create mode 100644 program/gcp/config.go create mode 100644 program/gcp/gcp.go create mode 100644 program/gcp/program.go rename program/{ => gcp}/program_test.go (96%) create mode 100644 program/gcp/stats.go create mode 100755 program/gcp/version.go diff --git a/main.go b/main.go index 022e3d9..80f7e25 100644 --- a/main.go +++ b/main.go @@ -2,14 +2,14 @@ package main import ( "fmt" - "github.com/clouddrove/kuconf/program" + "github.com/clouddrove/kuconf/program/gcp" "github.com/rs/zerolog/log" "os" ) func main() { - var options program.Options + var options gcp.Options context, err := options.Parse(os.Args[1:]) diff --git a/program/aws.go b/program/aws/aws.go similarity index 99% rename from program/aws.go rename to program/aws/aws.go index abc42cb..cdec206 100644 --- a/program/aws.go +++ b/program/aws/aws.go @@ -1,4 +1,4 @@ -package program +package aws import ( "bufio" @@ -202,4 +202,4 @@ func NewSession(profile, region string, log zerolog.Logger) (*sessionInfo, error } else { return nil, err } -} +} \ No newline at end of file diff --git a/program/config.go b/program/aws/config.go similarity index 99% rename from program/config.go rename to program/aws/config.go index 10530e2..07ca3dd 100644 --- a/program/config.go +++ b/program/aws/config.go @@ -1,4 +1,4 @@ -package program +package aws import ( "encoding/base64" diff --git a/program/program.go b/program/aws/program.go similarity index 99% rename from program/program.go rename to program/aws/program.go index 2078928..7cf31ff 100644 --- a/program/program.go +++ b/program/aws/program.go @@ -1,4 +1,4 @@ -package program +package aws import ( "fmt" diff --git a/program/aws/program_test.go b/program/aws/program_test.go new file mode 100644 index 0000000..bef48b7 --- /dev/null +++ b/program/aws/program_test.go @@ -0,0 +1,20 @@ +package aws + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestOptions_ReadConfig(t *testing.T) { + program := Options{KubeConfig: "testdata/config"} + + config, err := program.ReadConfig() + + assert.NoError(t, err) + assert.NotNil(t, config) + + assert.Equal(t, 6, len(config.Clusters)) + + c := config.Clusters["docker-desktop"] + assert.Equal(t, "https://kubernetes.docker.internal:6443", c.Server) +} \ No newline at end of file diff --git a/program/stats.go b/program/aws/stats.go similarity index 97% rename from program/stats.go rename to program/aws/stats.go index 25e310f..7e76bf0 100644 --- a/program/stats.go +++ b/program/aws/stats.go @@ -1,4 +1,4 @@ -package program +package aws import ( "github.com/rs/zerolog/log" diff --git a/program/version.go b/program/aws/version.go old mode 100755 new mode 100644 similarity index 95% rename from program/version.go rename to program/aws/version.go index 0c692cf..18490f7 --- a/program/version.go +++ b/program/aws/version.go @@ -1,4 +1,4 @@ -package program +package aws import "fmt" @@ -14,4 +14,4 @@ func (v *VersionCmd) Run(program *Options) error { _ = program _, _ = fmt.Println(Version) return nil -} +} \ No newline at end of file diff --git a/program/gcp.go b/program/gcp.go deleted file mode 100644 index 3442e45..0000000 --- a/program/gcp.go +++ /dev/null @@ -1,184 +0,0 @@ -package program - -import ( - "bufio" - "context" - "os" - "strings" - "sync" - - "cloud.google.com/go/container/apiv1" - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" - containerpb "cloud.google.com/go/container/apiv1/containerpb" - "google.golang.org/api/option" -) - -type gcpSessionInfo struct { - project string - zone string - session *container.ClusterManagerClient - log zerolog.Logger -} - -type GCPClusterInfo struct { - *containerpb.Cluster - log zerolog.Logger - session *gcpSessionInfo -} - -type Options2 struct { - Projects []string - ProjectFile string - Zones []string -} - -func (program *Options2) getProjects() <-chan string { - output := make(chan string) - - if len(program.Projects) < 1 { - go func() { - defer close(output) - if f, err := os.Open(program.ProjectFile); err == nil { - scanner := bufio.NewScanner(f) - scanner.Split(bufio.ScanLines) - - for scanner.Scan() { - s := strings.TrimSpace(scanner.Text()) - if s != "" { - output <- s - } - } - - } else { - log.Error().Str("file", program.ProjectFile).Err(err).Msg("Failed to open project file") - } - }() - } else { - go func() { - defer close(output) - for _, p := range program.Projects { - output <- p - } - }() - } - - return output -} - -func (program *Options2) getClustersFrom(s *gcpSessionInfo, clusters chan<- GCPClusterInfo) { - wg := sync.WaitGroup{} - defer wg.Wait() - - req := &containerpb.ListClustersRequest{ - Parent: "projects/" + s.project + "/locations/" + s.zone, - } - - s.log.Debug().Msg("Listing GKE clusters") - if out, err := s.session.ListClusters(context.Background(), req); err == nil { - s.log.Debug().Msg("Getting GKE Clusters") - - for _, c := range out.Clusters { - wg.Add(1) - go func(c *containerpb.Cluster) { - defer wg.Done() - s.log.Debug().Str("cluster_name", c.Name).Msg("Found GKE cluster") - - clusters <- GCPClusterInfo{ - Cluster: c, - log: s.log.With().Str("cluster_name", c.Name).Logger(), - session: s, - } - }(c) - } - } else { - s.log.Error().Err(err).Msg("Error listing GKE clusters") - } -} - -func (program *Options2) getUniqueGCPSessions() <-chan *gcpSessionInfo { - - sessions := make(chan *gcpSessionInfo) - - go func() { - wg := sync.WaitGroup{} - - defer close(sessions) - defer wg.Wait() - - projects := make(map[string]bool) - for info := range program.getProjectSessions() { - if _, found := projects[info.project]; found { - info.log.Debug().Msg("Project is duplicate") - } else { - info.log.Debug().Msg("Project is good for use") - projects[info.project] = true - - sessions <- info - - for _, zone := range program.Zones { - wg.Add(1) - go func(project, zone string) { - defer wg.Done() - if zone != info.zone { - log := log.With().Str("project", info.project).Str("zone", zone).Logger() - log.Debug().Msg("Creating regional session") - if s, err := container.NewClusterManagerClient(context.Background(), option.WithCredentialsFile("path/to/credentials.json")); err == nil { - sessions <- &gcpSessionInfo{ - project: project, - zone: zone, - session: s, - log: log, - } - } else { - log.Error().Err(err).Msg("Failed to create GCP session") - } - } - }(info.project, zone) - } - } - } - }() - - return sessions -} - -func (program *Options2) getProjectSessions() <-chan *gcpSessionInfo { - - sessions := make(chan *gcpSessionInfo) - wg := sync.WaitGroup{} - - go func() { - defer close(sessions) - defer wg.Wait() - - projects := program.getProjects() - - for p := range projects { - log := log.With().Str("project", p).Str("zone", program.Zones[0]).Logger() - wg.Add(1) - go func(p string) { - defer wg.Done() - if s, err := NewGCPSession(p, program.Zones[0], log); err == nil { - sessions <- s - } - }(p) - } - }() - - return sessions -} - -func NewGCPSession(project, zone string, log zerolog.Logger) (*gcpSessionInfo, error) { - if sess, err := container.NewClusterManagerClient(context.Background(), option.WithCredentialsFile("path/to/credentials.json")); err == nil { - log.Debug().Msg("GCP project session created") - return &gcpSessionInfo{ - project: project, - zone: zone, - session: sess, - log: log, - }, nil - } else { - return nil, err - } -} diff --git a/program/gcp/config.go b/program/gcp/config.go new file mode 100644 index 0000000..b1608a3 --- /dev/null +++ b/program/gcp/config.go @@ -0,0 +1,101 @@ +package gcp + +import ( + "encoding/base64" + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/tools/clientcmd/api" + "os" +) + +func captureConfig(c GCPClusterInfo, i *api.Config) error { + certificateData, err := base64.StdEncoding.DecodeString(c.MasterAuth.ClusterCaCertificate) + if err != nil { + c.log.Error().Err(err).Msg("Failed to decode certificate authority data from GCP") + stats.Errors.Add(1) + return err + } + + cluster := api.Cluster{ + Server: "https://" + c.Endpoint, + CertificateAuthorityData: certificateData, + } + + user := api.AuthInfo{ + Exec: &api.ExecConfig{ + APIVersion: "client.authentication.k8s.io/v1beta1", + Command: "gke-gcloud-auth-plugin", + Args: []string{ + "--project", + c.session.project, + "--location", + c.session.zone, + "--cluster", + c.Name, + }, + }, + } + + context := api.Context{ + Cluster: c.Name, + AuthInfo: c.Name, + } + + i.Clusters[c.Name] = &cluster + i.AuthInfos[c.Name] = &user + i.Contexts[c.Name] = &context + + return nil +} + +func (program *Options) ReadConfig() (*api.Config, error) { + if _, err := os.Stat(program.KubeConfig); err != nil { + c := api.NewConfig() + return c, nil + } else { + c, err := clientcmd.LoadFromFile(program.KubeConfig) + if err != nil { + return nil, err + } + return c, nil + } +} + +func (program *Options) WriteConfig(config *api.Config) error { + newFile := program.KubeConfig + ".tmp" + bakFile := program.KubeConfig + ".bak" + + err := clientcmd.WriteToFile(*config, newFile) + log := log.With().Str("kubeconfig_file", program.KubeConfig).Logger() + + if err != nil { + return err + } + + if _, err := os.Stat(bakFile); err == nil { + err = os.RemoveAll(bakFile) + if err != nil { + return errors.Wrap(err, "Failed to remove config backup file") + } + } + + if _, err := os.Stat(program.KubeConfig); err != nil { + log.Debug().Msg("No existing config file. Copying new to config") + return os.Rename(newFile, program.KubeConfig) + } + + if err := os.Rename(program.KubeConfig, bakFile); err == nil { + if e2 := os.Rename(newFile, program.KubeConfig); err == nil { + return nil + } else { + if err := os.Rename(bakFile, program.KubeConfig); err != nil { + return errors.Wrap(err, "Error restoring kubeconfig. Backup left in "+bakFile) + } else { + return errors.Wrap(e2, "Error saving new kubeconfig") + } + } + } else { + return err + } +} \ No newline at end of file diff --git a/program/gcp/gcp.go b/program/gcp/gcp.go new file mode 100644 index 0000000..79f77b4 --- /dev/null +++ b/program/gcp/gcp.go @@ -0,0 +1,218 @@ +package gcp + +import ( + "bufio" + "context" + "os" + "strings" + "sync" + + "cloud.google.com/go/container/apiv1" + containerpb "cloud.google.com/go/container/apiv1/containerpb" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "google.golang.org/api/option" +) + +type gcpSessionInfo struct { + project string + zone string + session *container.ClusterManagerClient + log zerolog.Logger +} + +type GCPClusterInfo struct { + *containerpb.Cluster + log zerolog.Logger + session *gcpSessionInfo +} + +func (program *Options) getProjects() <-chan string { + output := make(chan string) + + if len(program.Projects) < 1 { + go func() { + defer close(output) + if f, err := os.Open(program.ProjectFile); err == nil { + scanner := bufio.NewScanner(f) + scanner.Split(bufio.ScanLines) + + for scanner.Scan() { + s := strings.TrimSpace(scanner.Text()) + if s != "" { + output <- s + } + } + + } else { + log.Error().Str("file", program.ProjectFile).Err(err).Msg("Failed to open project file") + } + }() + } else { + go func() { + defer close(output) + for _, p := range program.Projects { + output <- p + } + }() + } + + return output +} + +func (program *Options) getClustersFrom(s *gcpSessionInfo, clusters chan<- GCPClusterInfo) { + wg := sync.WaitGroup{} + defer wg.Wait() + + req := &containerpb.ListClustersRequest{ + Parent: "projects/" + s.project + "/locations/" + s.zone, + } + + s.log.Debug().Str("request", req.Parent).Msg("Requesting cluster listing") + + out, err := s.session.ListClusters(context.Background(), req) + if err != nil { + s.log.Error().Err(err).Msg("Error listing GKE clusters") + return + } + + s.log.Debug().Int("number_of_clusters", len(out.Clusters)).Msg("GKE clusters found") + + if len(out.Clusters) > 0 { + stats.Clusters.Add(int32(len(out.Clusters))) + } + + if len(out.Clusters) == 0 { + s.log.Warn().Msg("No GKE clusters found in the specified project and zone") + } + + for _, c := range out.Clusters { + wg.Add(1) + go func(c *containerpb.Cluster) { + defer wg.Done() + s.log.Debug().Str("cluster_name", c.Name).Msg("Found GKE cluster") + clusters <- GCPClusterInfo{ + Cluster: c, + log: s.log.With().Str("cluster_name", c.Name).Logger(), + session: s, + } + }(c) + } +} +func (program *Options) getUniqueGCPSessions() <-chan *gcpSessionInfo { + sessions := make(chan *gcpSessionInfo) + + go func() { + wg := sync.WaitGroup{} + defer close(sessions) + defer wg.Wait() + + projects := make(map[string]bool) + for info := range program.getProjectSessions() { + if _, found := projects[info.project]; found { + info.log.Debug().Msg("Project is duplicate") + continue + } + + info.log.Debug().Msg("Project is good for use") + projects[info.project] = true + stats.UniqueProjects.Add(1) + sessions <- info + + if strings.Count(info.zone, "-") == 2 { + for _, zone := range program.Zones { + if zone != info.zone { + wg.Add(1) + go func(project, zone string) { + defer wg.Done() + log := log.With().Str("project", project).Str("zone", zone).Logger() + log.Debug().Msg("Creating regional session") + + if s, err := program.newGCPSession(project, zone); err == nil { + sessions <- s + } else { + log.Error().Err(err).Msg("Failed to create GCP session") + } + }(info.project, zone) + } + } + } + } + }() + + return sessions +} + +func (program *Options) getProjectSessions() <-chan *gcpSessionInfo { + + sessions := make(chan *gcpSessionInfo) + wg := sync.WaitGroup{} + + go func() { + defer close(sessions) + defer wg.Wait() + + projects := program.getProjects() + + for p := range projects { + log := log.With().Str("project", p).Str("zone", program.Zones[0]).Logger() + wg.Add(1) + go func(p string) { + defer wg.Done() + if s, err := NewGCPSession(p, program.Zones[0], log); err == nil { + sessions <- s + } + }(p) + } + }() + + return sessions +} + +func (program *Options) newGCPSession(project, zone string) (*gcpSessionInfo, error) { + opts := []option.ClientOption{} + if program.CredentialsFile != "" { + opts = append(opts, option.WithCredentialsFile(program.CredentialsFile)) + } + + sess, err := container.NewClusterManagerClient(context.Background(), opts...) + if err != nil { + return nil, err + } + + stats.Projects.Add(1) + stats.UsableProjects.Add(1) + + logger := log.With().Str("project", project).Str("zone", zone).Logger() + logger.Debug().Msg("GCP project session created") + + return &gcpSessionInfo{ + project: project, + zone: zone, + session: sess, + log: logger, + }, nil +} + +func NewGCPSession(project, zone string, log zerolog.Logger) (*gcpSessionInfo, error) { + opts := []option.ClientOption{} + if os.Getenv("GOOGLE_APPLICATION_CREDENTIALS") != "" { + opts = append(opts, option.WithCredentialsFile(os.Getenv("GOOGLE_APPLICATION_CREDENTIALS"))) + } + + sess, err := container.NewClusterManagerClient(context.Background(), opts...) + if err != nil { + log.Error().Err(err).Msg("Failed to create GCP ClusterManagerClient") + return nil, err + } + + log.Debug().Msg("GCP project session created successfully") + + return &gcpSessionInfo{ + project: project, + zone: zone, + session: sess, + log: log, + }, nil + +} \ No newline at end of file diff --git a/program/gcp/program.go b/program/gcp/program.go new file mode 100644 index 0000000..836456e --- /dev/null +++ b/program/gcp/program.go @@ -0,0 +1,138 @@ +package gcp + +import ( + "fmt" + "github.com/alecthomas/kong" + "github.com/mattn/go-colorable" + "github.com/pkg/errors" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "io" + "os" + "runtime" + "sync" +) + +type Options struct { + Version bool `help:"Show program version"` + + KubeConfig string `group:"Input" short:"k" help:"Kubeconfig file" type:"path" default:"~/.kube/config"` + CredentialsFile string `group:"Input" short:"c" help:"GCP Credentials File" type:"existingfile" default:"~/.config/gcloud/application_default_credentials.json"` + Projects []string `group:"Input" help:"List of GCP projects to check"` + ProjectFile string `group:"Input" help:"File containing list of GCP projects" type:"path"` + Zones []string `group:"Input" help:"List of GCP zones to check" env:"GCP_ZONES" default:"us-central1-a,us-east1-b,us-west1-a,europe-west1-b,asia-east1-a"` + + Debug bool `group:"Info" help:"Show debugging information"` + OutputFormat string `group:"Info" enum:"auto,jsonl,terminal" default:"auto" help:"How to show program output (auto|terminal|jsonl)"` + Quiet bool `group:"Info" help:"Be less verbose than usual"` +} + +func (program *Options) Parse(args []string) (*kong.Context, error) { + parser, err := kong.New(program, + kong.ShortUsageOnError(), + kong.Description("Download kubeconfigs in bulk by examining GKE clusters across multiple projects and zones"), + ) + if err != nil { + fmt.Println(err) + return nil, err + } + return parser.Parse(args) +} + +func (program *Options) Run(options *Options) error { + config, err := program.ReadConfig() + if err != nil { + log.Error().Err(err).Msg("Failed to read kubeconfig file") + return err + } + + clusters := make(chan GCPClusterInfo) + wg := sync.WaitGroup{} + + for sess := range program.getUniqueGCPSessions() { + wg.Add(1) + go func(sess *gcpSessionInfo) { + defer wg.Done() + program.getClustersFrom(sess, clusters) + }(sess) + } + + go func() { + wg.Wait() + close(clusters) + }() + + for c := range clusters { + if err := captureConfig(c, config); err != nil { + stats.Errors.Add(1) + log.Error().Err(err).Msg("Error capturing cluster configuration") + } + } + + if err := program.WriteConfig(config); err != nil { + stats.Errors.Add(1) + log.Error(). + Err(err). + Str("file", program.KubeConfig). + Msg("Error saving kubeconfig") + } + + stats.Log() + if stats.Errors.Load() > 0 { + return errors.New("Errors encountered during run") + } + return nil +} + +func (program *Options) AfterApply() error { + program.initLogging() + if len(program.Zones) < 1 { + return errors.New("Must specify at least one zone") + } + if len(program.Projects) < 1 && program.ProjectFile == "" { + return errors.New("Must specify either projects or project file") + } + return nil +} + +func (program *Options) initLogging() { + if program.Version { + fmt.Println(Version) + os.Exit(0) + } + + switch { + case program.Debug: + zerolog.SetGlobalLevel(zerolog.DebugLevel) + case program.Quiet: + zerolog.SetGlobalLevel(zerolog.WarnLevel) + default: + zerolog.SetGlobalLevel(zerolog.InfoLevel) + } + + var out io.Writer = os.Stdout + if os.Getenv("TERM") == "" && runtime.GOOS == "windows" { + out = colorable.NewColorableStdout() + } + + if program.OutputFormat == "terminal" || + (program.OutputFormat == "auto" && isTerminal(os.Stdout)) { + log.Logger = log.Output(zerolog.ConsoleWriter{Out: out}) + } else { + log.Logger = log.Output(out) + } + + log.Logger.Debug(). + Str("version", Version). + Str("program", os.Args[0]). + Msg("Starting") +} + +func isTerminal(file *os.File) bool { + if fileInfo, err := file.Stat(); err != nil { + log.Err(err).Msg("Error running stat") + return false + } else { + return (fileInfo.Mode() & os.ModeCharDevice) != 0 + } +} diff --git a/program/program_test.go b/program/gcp/program_test.go similarity index 96% rename from program/program_test.go rename to program/gcp/program_test.go index e407c74..9360f1c 100755 --- a/program/program_test.go +++ b/program/gcp/program_test.go @@ -1,4 +1,4 @@ -package program +package gcp import ( "github.com/stretchr/testify/assert" diff --git a/program/gcp/stats.go b/program/gcp/stats.go new file mode 100644 index 0000000..79c89c7 --- /dev/null +++ b/program/gcp/stats.go @@ -0,0 +1,23 @@ +package gcp + +import ( + "github.com/rs/zerolog/log" + "sync/atomic" +) + +type Stats struct { + Projects, UniqueProjects, UsableProjects, Zones, Clusters, Errors atomic.Int32 +} + +func (s *Stats) Log() { + log.Info(). + Int32("projects", s.Projects.Load()). + Int32("unique_projects", s.UniqueProjects.Load()). + Int32("usable_projects", s.UsableProjects.Load()). + Int32("zones", s.Zones.Load()). + Int32("clusters", s.Clusters.Load()). + Int32("fatal_errors", s.Errors.Load()). + Msg("Statistics") +} + +var stats Stats \ No newline at end of file diff --git a/program/gcp/version.go b/program/gcp/version.go new file mode 100755 index 0000000..c58b84d --- /dev/null +++ b/program/gcp/version.go @@ -0,0 +1,13 @@ +package gcp + +import "fmt" + +var Version = "unknown" + +type VersionCmd struct{} + +func (v *VersionCmd) Run(program *Options) error { + _ = program + _, _ = fmt.Println(Version) + return nil +} From e98d581ed544c922e126b42824cc6f037e3f0ae4 Mon Sep 17 00:00:00 2001 From: Bharadwajshivam28 Date: Fri, 25 Oct 2024 01:14:23 +0530 Subject: [PATCH 3/7] feat: Improved cli working --- main.go | 50 ++++++++++++++++++++++++++++++++++++++++---------- txt | 7 +++++++ 2 files changed, 47 insertions(+), 10 deletions(-) create mode 100644 txt diff --git a/main.go b/main.go index 80f7e25..57e94c7 100644 --- a/main.go +++ b/main.go @@ -2,25 +2,55 @@ package main import ( "fmt" + "os" + + "github.com/alecthomas/kong" + "github.com/clouddrove/kuconf/program/aws" "github.com/clouddrove/kuconf/program/gcp" "github.com/rs/zerolog/log" - "os" ) func main() { + if len(os.Args) < 2 { + fmt.Println("Please provide (aws or gcp) as the first argument.") + os.Exit(1) + } - var options gcp.Options + platform := os.Args[1] - context, err := options.Parse(os.Args[1:]) + var optionsGCP gcp.Options + var optionsAWS aws.Options - if err != nil { - fmt.Println(err) - os.Exit(1) - } + var ctx *kong.Context + var err error + + switch platform { + case "gcp": + ctx, err = optionsGCP.Parse(os.Args[2:]) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + if err := ctx.Run(&optionsGCP); err != nil { + log.Err(err).Msg("Program failed for GCP") + os.Exit(1) + } + + case "aws": + ctx, err = optionsAWS.Parse(os.Args[2:]) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + if err := ctx.Run(&optionsAWS); err != nil { + log.Err(err).Msg("Program failed for AWS") + os.Exit(1) + } - // This ends up calling options.Run() - if err := context.Run(&options); err != nil { - log.Err(err).Msg("Program failed") + default: + fmt.Println("Invalid cloud provider. Please choose either 'aws' or 'gcp'.") os.Exit(1) } } diff --git a/txt b/txt new file mode 100644 index 0000000..338d40c --- /dev/null +++ b/txt @@ -0,0 +1,7 @@ +apiVersion: v1 +clusters: null +contexts: null +current-context: "" +kind: Config +preferences: {} +users: null From 0d2a6cd4bae2c7d45d7744ae3209cbc41d7543e0 Mon Sep 17 00:00:00 2001 From: Bharadwajshivam28 Date: Fri, 25 Oct 2024 02:24:52 +0530 Subject: [PATCH 4/7] feat: removed unwanted file --- txt | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 txt diff --git a/txt b/txt deleted file mode 100644 index 338d40c..0000000 --- a/txt +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -clusters: null -contexts: null -current-context: "" -kind: Config -preferences: {} -users: null From c42ec30e11fbb196e0d47bf29151f09b3e7df1eb Mon Sep 17 00:00:00 2001 From: Bharadwajshivam28 Date: Fri, 25 Oct 2024 02:27:00 +0530 Subject: [PATCH 5/7] fix: changed go version to 1.23 --- .github/workflows/test-multiplatform.yaml | 2 +- go.mod | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/test-multiplatform.yaml b/.github/workflows/test-multiplatform.yaml index 11c0367..bfca945 100644 --- a/.github/workflows/test-multiplatform.yaml +++ b/.github/workflows/test-multiplatform.yaml @@ -9,7 +9,7 @@ on: name: Test on all platforms env: - GO_VERSION: 1.19 + GO_VERSION: 1.23 jobs: test: diff --git a/go.mod b/go.mod index c5436a4..ff39866 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ toolchain go1.22.5 require ( cloud.google.com/go/container v1.40.0 - cloud.google.com/go/iam v1.2.1 github.com/alecthomas/kong v1.2.1 github.com/aws/aws-sdk-go v1.55.5 github.com/mattn/go-colorable v0.1.13 From 46d7a7a77a81285e99f419fb197ac72f640d664e Mon Sep 17 00:00:00 2001 From: Bharadwajshivam28 Date: Fri, 25 Oct 2024 18:37:12 +0530 Subject: [PATCH 6/7] feat: Integrated Azure cloud provider in kuconf --- go.mod | 10 +- go.sum | 42 ++++++- main.go | 17 ++- program/aws/program_test.go | 20 ---- program/azure/azure.go | 222 ++++++++++++++++++++++++++++++++++++ program/azure/config.go | 92 +++++++++++++++ program/azure/program.go | 151 ++++++++++++++++++++++++ program/azure/stats.go | 23 ++++ program/azure/version.go | 13 +++ program/gcp/gcp.go | 1 + program/gcp/program_test.go | 20 ---- program/testdata/config | 64 ----------- 12 files changed, 563 insertions(+), 112 deletions(-) delete mode 100644 program/aws/program_test.go create mode 100644 program/azure/azure.go create mode 100644 program/azure/config.go create mode 100644 program/azure/program.go create mode 100644 program/azure/stats.go create mode 100644 program/azure/version.go delete mode 100755 program/gcp/program_test.go delete mode 100644 program/testdata/config diff --git a/go.mod b/go.mod index ff39866..5aa9b34 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,8 @@ toolchain go1.22.5 require ( cloud.google.com/go/container v1.40.0 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice v1.0.0 github.com/alecthomas/kong v1.2.1 github.com/aws/aws-sdk-go v1.55.5 github.com/mattn/go-colorable v0.1.13 @@ -13,7 +15,6 @@ require ( github.com/rs/zerolog v1.33.0 github.com/stretchr/testify v1.9.0 google.golang.org/api v0.203.0 - google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 k8s.io/client-go v0.29.1 ) @@ -21,22 +22,29 @@ require ( cloud.google.com/go/auth v0.9.9 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect cloud.google.com/go/compute/metadata v0.5.2 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/s2a-go v0.1.8 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect github.com/googleapis/gax-go/v2 v2.13.0 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect go.opencensus.io v0.24.0 // indirect diff --git a/go.sum b/go.sum index fbb4373..07c3cd2 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,20 @@ cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixA cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k= cloud.google.com/go/container v1.40.0 h1:JVoEg/4RvoGW37r2Eja/cTBc3X9c2loGWYq7QDsRDuI= cloud.google.com/go/container v1.40.0/go.mod h1:wNI1mOUivm+ZkpHMbouutgbD4sQxyphMwK31X5cThY4= -cloud.google.com/go/iam v1.2.1 h1:QFct02HRb7H12J/3utj0qf5tobFh9V4vR6h9eX5EBRU= -cloud.google.com/go/iam v1.2.1/go.mod h1:3VUIJDPpwT6p/amXRC5GY8fCCh70lxPygguVtI0Z4/g= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 h1:nyQWyZvwGTvunIMxi1Y9uXkcyr+I7TeNrr/foo4Kpk8= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0 h1:+m0M/LFxN43KvULkDNfdXOgrjtg6UYJPFBJyuEcRCAw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0/go.mod h1:PwOyop78lveYMRs6oCxjiVyBdyCgIYH6XHIVZO9/SFQ= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice v1.0.0 h1:figxyQZXzZQIcP3njhC68bYUiTw45J8/SsHaLW8Ax0M= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice v1.0.0/go.mod h1:TmlMW4W5OvXOmOyKNnor8nlMMiO1ctIyzmHme/VHsrA= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/alecthomas/assert/v2 v2.10.0 h1:jjRCHsj6hBJhkmhznrCzoNpbA3zqy0fYiUcYZP/GkPY= github.com/alecthomas/assert/v2 v2.10.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= @@ -21,12 +33,16 @@ github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW5 github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -50,6 +66,8 @@ github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= @@ -101,8 +119,16 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs= +github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -117,11 +143,17 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= +github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= @@ -189,6 +221,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= @@ -220,8 +253,6 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 h1:Q3nlH8iSQSRUwOskjbcSMcF2jiYMNiQYZ0c2KEJLKKU= -google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38/go.mod h1:xBI+tzfqGGN2JBeSebfKXFSdBpWVQ7sLW40PTupVRm4= google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 h1:fVoAXEKA4+yufmbdVYv+SE73+cPZbbbe8paLsHfkK+U= google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53/go.mod h1:riSXTwQ4+nqmPGtobMFyW5FqVAmIs0St6VPp4Ug7CE4= google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE= @@ -244,8 +275,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main.go b/main.go index 57e94c7..c45bab4 100644 --- a/main.go +++ b/main.go @@ -6,13 +6,14 @@ import ( "github.com/alecthomas/kong" "github.com/clouddrove/kuconf/program/aws" + "github.com/clouddrove/kuconf/program/azure" "github.com/clouddrove/kuconf/program/gcp" "github.com/rs/zerolog/log" ) func main() { if len(os.Args) < 2 { - fmt.Println("Please provide (aws or gcp) as the first argument.") + fmt.Println("Please provide (aws, gcp or azure) as the first argument.") os.Exit(1) } @@ -20,6 +21,7 @@ func main() { var optionsGCP gcp.Options var optionsAWS aws.Options + var optionsAZURE azure.Options var ctx *kong.Context var err error @@ -48,9 +50,20 @@ func main() { log.Err(err).Msg("Program failed for AWS") os.Exit(1) } + + case "azure": + ctx, err = optionsAZURE.Parse(os.Args[2:]) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + if err := ctx.Run(&optionsAZURE); err != nil { + log.Err(err).Msg("Program failed for AZURE") + os.Exit(1) + } default: - fmt.Println("Invalid cloud provider. Please choose either 'aws' or 'gcp'.") + fmt.Println("Invalid cloud provider. Please choose either 'aws', 'gcp' or 'azure") os.Exit(1) } } diff --git a/program/aws/program_test.go b/program/aws/program_test.go deleted file mode 100644 index bef48b7..0000000 --- a/program/aws/program_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package aws - -import ( - "github.com/stretchr/testify/assert" - "testing" -) - -func TestOptions_ReadConfig(t *testing.T) { - program := Options{KubeConfig: "testdata/config"} - - config, err := program.ReadConfig() - - assert.NoError(t, err) - assert.NotNil(t, config) - - assert.Equal(t, 6, len(config.Clusters)) - - c := config.Clusters["docker-desktop"] - assert.Equal(t, "https://kubernetes.docker.internal:6443", c.Server) -} \ No newline at end of file diff --git a/program/azure/azure.go b/program/azure/azure.go new file mode 100644 index 0000000..d6f7ce4 --- /dev/null +++ b/program/azure/azure.go @@ -0,0 +1,222 @@ +package azure + +import ( + "bufio" + "context" + "fmt" + "os" + "strings" + "sync" + + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" +) + +type azureSessionInfo struct { + subscription string + location string + client *armcontainerservice.ManagedClustersClient + log zerolog.Logger +} + +type AzureClusterInfo struct { + *armcontainerservice.ManagedCluster + log zerolog.Logger + session *azureSessionInfo +} + +func (program *Options) getSubscriptions() <-chan string { + output := make(chan string) + + if len(program.Subscriptions) < 1 { + go func() { + defer close(output) + if f, err := os.Open(program.SubscriptionFile); err == nil { + scanner := bufio.NewScanner(f) + scanner.Split(bufio.ScanLines) + + for scanner.Scan() { + s := strings.TrimSpace(scanner.Text()) + if s != "" { + output <- s + } + } + } else { + log.Error().Str("file", program.SubscriptionFile).Err(err).Msg("Failed to open subscription file") + } + }() + } else { + go func() { + defer close(output) + for _, s := range program.Subscriptions { + output <- s + } + }() + } + + return output +} + +func (program *Options) getClustersFrom(s *azureSessionInfo, clusters chan<- AzureClusterInfo) { + var wg sync.WaitGroup + defer wg.Wait() + + client := s.client + ctx := context.Background() + uniqueClusters := make(map[string]struct{}) + + pager := client.NewListPager(nil) + + for pager.More() { + page, err := pager.NextPage(ctx) + if err != nil { + s.log.Error().Err(err).Msg("Error listing AKS clusters") + return + } + + for _, c := range page.Value { + clusterKey := fmt.Sprintf("%s-%s", *c.Name, *c.Location) + if _, exists := uniqueClusters[clusterKey]; !exists { + uniqueClusters[clusterKey] = struct{}{} + stats.Clusters.Add(1) + + wg.Add(1) + go func(c *armcontainerservice.ManagedCluster) { + defer wg.Done() + + s.log.Debug(). + Str("cluster_name", *c.Name). + Str("location", *c.Location). + Msg("Found unique AKS cluster") + + clusters <- AzureClusterInfo{ + ManagedCluster: c, + log: s.log.With().Str("cluster_name", *c.Name).Str("location", *c.Location).Logger(), + session: s, + } + }(c) + } + } + } +} + +func (program *Options) getUniqueAzureSessions() <-chan *azureSessionInfo { + sessions := make(chan *azureSessionInfo) + + go func() { + wg := sync.WaitGroup{} + defer close(sessions) + defer wg.Wait() + + subscriptions := make(map[string]bool) + for info := range program.getSubscriptionSessions() { + if _, found := subscriptions[info.subscription]; found { + info.log.Debug().Msg("Subscription is duplicate") + continue + } + + stats.UniqueSubscriptions.Add(1) + info.log.Debug().Msg("Subscription is good for use") + subscriptions[info.subscription] = true + sessions <- info + + for _, location := range program.Locations { + if location != info.location { + stats.Locations.Add(1) + wg.Add(1) + go func(subscription, location string) { + defer wg.Done() + log := log.With().Str("subscription", subscription).Str("location", location).Logger() + log.Debug().Msg("Creating regional session") + + if s, err := program.newAzureSession(subscription, location); err == nil { + sessions <- s + } else { + log.Error().Err(err).Msg("Failed to create Azure session") + } + }(info.subscription, location) + } + } + } + }() + + return sessions +} + +func (program *Options) getSubscriptionSessions() <-chan *azureSessionInfo { + sessions := make(chan *azureSessionInfo) + wg := sync.WaitGroup{} + + go func() { + defer close(sessions) + defer wg.Wait() + + subscriptions := program.getSubscriptions() + + for s := range subscriptions { + stats.Subscriptions.Add(1) + log := log.With().Str("subscription", s).Str("location", program.Locations[0]).Logger() + wg.Add(1) + go func(s string) { + defer wg.Done() + if session, err := NewAzureSession(s, program.Locations[0], log); err == nil { + stats.UsableSubscriptions.Add(1) + sessions <- session + } + }(s) + } + }() + + return sessions +} + +func (program *Options) newAzureSession(subscription, location string) (*azureSessionInfo, error) { + cred, err := program.getAzureCredential() + if err != nil { + return nil, err + } + + client, err := armcontainerservice.NewManagedClustersClient(subscription, cred, nil) + if err != nil { + return nil, err + } + + logger := log.With().Str("subscription", subscription).Str("location", location).Logger() + logger.Debug().Msg("Azure subscription session created") + + return &azureSessionInfo{ + subscription: subscription, + location: location, + client: client, + log: logger, + }, nil +} + +func (program *Options) getAzureCredential() (*azidentity.DefaultAzureCredential, error) { + return azidentity.NewDefaultAzureCredential(nil) +} + +func NewAzureSession(subscription, location string, log zerolog.Logger) (*azureSessionInfo, error) { + cred, err := azidentity.NewDefaultAzureCredential(nil) + if err != nil { + log.Error().Err(err).Msg("Failed to create Azure credential") + return nil, err + } + + client, err := armcontainerservice.NewManagedClustersClient(subscription, cred, nil) + if err != nil { + log.Error().Err(err).Msg("Failed to create Azure client factory") + return nil, err + } + + log.Debug().Msg("Azure subscription session created successfully") + + return &azureSessionInfo{ + subscription: subscription, + location: location, + client: client, + log: log, + }, nil +} \ No newline at end of file diff --git a/program/azure/config.go b/program/azure/config.go new file mode 100644 index 0000000..01ecbbf --- /dev/null +++ b/program/azure/config.go @@ -0,0 +1,92 @@ +package azure + +import ( + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/tools/clientcmd/api" + "os" +) + +func captureConfig(c AzureClusterInfo, resourceGroup string, i *api.Config) error { + certificateData := []byte(*c.ManagedCluster.Properties.NetworkProfile.ServiceCidr) + + cluster := api.Cluster{ + Server: "https://" + *c.ManagedCluster.Properties.Fqdn, + CertificateAuthorityData: certificateData, + } + + user := api.AuthInfo{ + Exec: &api.ExecConfig{ + APIVersion: "client.authentication.k8s.io/v1beta1", + Command: "azure-cli", + Args: []string{ + "aks", "get-credentials", + "--resource-group", resourceGroup, + "--name", *c.ManagedCluster.Name, + }, + }, + } + + context := api.Context{ + Cluster: *c.ManagedCluster.Name, + AuthInfo: *c.ManagedCluster.Name, + } + + i.Clusters[*c.ManagedCluster.Name] = &cluster + i.AuthInfos[*c.ManagedCluster.Name] = &user + i.Contexts[*c.ManagedCluster.Name] = &context + + return nil +} + +func (program *Options) ReadConfig() (*api.Config, error) { + if _, err := os.Stat(program.KubeConfig); os.IsNotExist(err) { + c := api.NewConfig() + return c, nil + } else { + c, err := clientcmd.LoadFromFile(program.KubeConfig) + if err != nil { + return nil, err + } + return c, nil + } +} + +func (program *Options) WriteConfig(config *api.Config) error { + newFile := program.KubeConfig + ".tmp" + bakFile := program.KubeConfig + ".bak" + + err := clientcmd.WriteToFile(*config, newFile) + log := log.With().Str("kubeconfig_file", program.KubeConfig).Logger() + + if err != nil { + return err + } + + if _, err := os.Stat(bakFile); err == nil { + err = os.RemoveAll(bakFile) + if err != nil { + return errors.Wrap(err, "Failed to remove config backup file") + } + } + + if _, err := os.Stat(program.KubeConfig); os.IsNotExist(err) { + log.Debug().Msg("No existing config file. Copying new to config") + return os.Rename(newFile, program.KubeConfig) + } + + if err := os.Rename(program.KubeConfig, bakFile); err == nil { + if e2 := os.Rename(newFile, program.KubeConfig); e2 == nil { + return nil + } else { + if restoreErr := os.Rename(bakFile, program.KubeConfig); restoreErr != nil { + return errors.Wrap(restoreErr, "Error restoring kubeconfig. Backup left in "+bakFile) + } else { + return errors.Wrap(e2, "Error saving new kubeconfig") + } + } + } else { + return err + } +} diff --git a/program/azure/program.go b/program/azure/program.go new file mode 100644 index 0000000..5bcd01e --- /dev/null +++ b/program/azure/program.go @@ -0,0 +1,151 @@ +package azure + +import ( + "fmt" + "io" + "os" + "runtime" + "strings" + "sync" + + "github.com/alecthomas/kong" + "github.com/mattn/go-colorable" + "github.com/pkg/errors" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" +) + +type Options struct { + Version bool `help:"Show program version"` + + KubeConfig string `group:"Input" short:"k" help:"Kubeconfig file" type:"path" default:"~/.kube/config"` + Subscriptions []string `group:"Input" help:"List of Azure subscriptions to check"` + SubscriptionFile string `group:"Input" help:"File containing list of Azure subscriptions" type:"path"` + Locations []string `group:"Input" help:"List of Azure locations to check" env:"AZURE_LOCATIONS" default:"eastus,westus,centralus,northeurope,westeurope"` + ResourceGroups []string `group:"Input" help:"List of Azure resource groups to check"` + + Debug bool `group:"Info" help:"Show debugging information"` + OutputFormat string `group:"Info" enum:"auto,jsonl,terminal" default:"auto" help:"How to show program output (auto|terminal|jsonl)"` + Quiet bool `group:"Info" help:"Be less verbose than usual"` +} + +func (program *Options) Parse(args []string) (*kong.Context, error) { + parser, err := kong.New(program, + kong.ShortUsageOnError(), + kong.Description("Download kubeconfigs in bulk by examining AKS clusters across multiple subscriptions and locations"), + ) + if err != nil { + fmt.Println(err) + return nil, err + } + return parser.Parse(args) +} + +func (program *Options) Run(options *Options) error { + config, err := program.ReadConfig() + if err != nil { + log.Error().Err(err).Msg("Failed to read kubeconfig file") + return err + } + + clusters := make(chan AzureClusterInfo) + wg := sync.WaitGroup{} + + for sess := range program.getUniqueAzureSessions() { + wg.Add(1) + go func(sess *azureSessionInfo) { + defer wg.Done() + program.getClustersFrom(sess, clusters) + }(sess) + } + + go func() { + wg.Wait() + close(clusters) + }() + + for c := range clusters { + resourceGroup := "" + if id := *c.ManagedCluster.ID; id != "" { + parts := strings.Split(id, "/") + for i, part := range parts { + if part == "resourceGroups" && i+1 < len(parts) { + resourceGroup = parts[i+1] + break + } + } + } + + if err := captureConfig(c, resourceGroup, config); err != nil { + stats.Errors.Add(1) + log.Error().Err(err).Msg("Error capturing cluster configuration") + } + } + + if err := program.WriteConfig(config); err != nil { + stats.Errors.Add(1) + log.Error(). + Err(err). + Str("file", program.KubeConfig). + Msg("Error saving kubeconfig") + } + + stats.Log() + if stats.Errors.Load() > 0 { + return errors.New("Errors encountered during run") + } + return nil +} + +func (program *Options) AfterApply() error { + program.initLogging() + if len(program.Locations) < 1 { + return errors.New("Must specify at least one location") + } + if len(program.Subscriptions) < 1 && program.SubscriptionFile == "" { + return errors.New("Must specify either subscriptions or subscription file") + } + return nil +} + +func (program *Options) initLogging() { + if program.Version { + fmt.Println(Version) + os.Exit(0) + } + + switch { + case program.Debug: + zerolog.SetGlobalLevel(zerolog.DebugLevel) + case program.Quiet: + zerolog.SetGlobalLevel(zerolog.WarnLevel) + default: + zerolog.SetGlobalLevel(zerolog.InfoLevel) + } + + var out io.Writer = os.Stdout + if os.Getenv("TERM") == "" && runtime.GOOS == "windows" { + out = colorable.NewColorableStdout() + } + + if program.OutputFormat == "terminal" || + (program.OutputFormat == "auto" && isTerminal(os.Stdout)) { + log.Logger = log.Output(zerolog.ConsoleWriter{Out: out}) + } else { + log.Logger = log.Output(out) + } + + log.Logger.Debug(). + Str("version", Version). + Str("program", os.Args[0]). + Msg("Starting") +} + +func isTerminal(file *os.File) bool { + if fileInfo, err := file.Stat(); err != nil { + log.Err(err).Msg("Error running stat") + return false + } else { + return (fileInfo.Mode() & os.ModeCharDevice) != 0 + } +} \ No newline at end of file diff --git a/program/azure/stats.go b/program/azure/stats.go new file mode 100644 index 0000000..4ae8995 --- /dev/null +++ b/program/azure/stats.go @@ -0,0 +1,23 @@ +package azure + +import ( + "github.com/rs/zerolog/log" + "sync/atomic" +) + +type Stats struct { + Subscriptions, UniqueSubscriptions, UsableSubscriptions, Locations, Clusters, Errors atomic.Int32 +} + +func (s *Stats) Log() { + log.Info(). + Int32("subscriptions", s.Subscriptions.Load()). + Int32("unique_subscriptions", s.UniqueSubscriptions.Load()). + Int32("usable_subscriptions", s.UsableSubscriptions.Load()). + Int32("locations", s.Locations.Load()). + Int32("clusters", s.Clusters.Load()). + Int32("fatal_errors", s.Errors.Load()). + Msg("Statistics") +} + +var stats Stats diff --git a/program/azure/version.go b/program/azure/version.go new file mode 100644 index 0000000..116d04f --- /dev/null +++ b/program/azure/version.go @@ -0,0 +1,13 @@ +package azure + +import "fmt" + +var Version = "unknown" + +type VersionCmd struct{} + +func (v *VersionCmd) Run(program *Options) error { + _ = program + _, _ = fmt.Println(Version) + return nil +} diff --git a/program/gcp/gcp.go b/program/gcp/gcp.go index 79f77b4..3994780 100644 --- a/program/gcp/gcp.go +++ b/program/gcp/gcp.go @@ -99,6 +99,7 @@ func (program *Options) getClustersFrom(s *gcpSessionInfo, clusters chan<- GCPCl }(c) } } + func (program *Options) getUniqueGCPSessions() <-chan *gcpSessionInfo { sessions := make(chan *gcpSessionInfo) diff --git a/program/gcp/program_test.go b/program/gcp/program_test.go deleted file mode 100755 index 9360f1c..0000000 --- a/program/gcp/program_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package gcp - -import ( - "github.com/stretchr/testify/assert" - "testing" -) - -func TestOptions_ReadConfig(t *testing.T) { - program := Options{KubeConfig: "testdata/config"} - - config, err := program.ReadConfig() - - assert.NoError(t, err) - assert.NotNil(t, config) - - assert.Equal(t, 6, len(config.Clusters)) - - c := config.Clusters["docker-desktop"] - assert.Equal(t, "https://kubernetes.docker.internal:6443", c.Server) -} diff --git a/program/testdata/config b/program/testdata/config deleted file mode 100644 index f2b98fa..0000000 --- a/program/testdata/config +++ /dev/null @@ -1,64 +0,0 @@ -apiVersion: v1 -clusters: -- cluster: - certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM1ekNDQWMrZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJeE1EUXdPREF3TlRBMU9Wb1hEVE14TURRd05qQXdOVEExT1Zvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTEp4Cmp1TzhLK2plOSthb3M0MWNjbDZPRU9mUXFOQTlZcFpPb0hIdVNwcGR2R3pBZlpEak11ZFJJZGs0aDcyY0dkczYKU29HVU5IeFhjY3hodnBnT0FGVDJWcmhBdFpCRmJwQjdSZVJraTBCVU1JWm9GSjN0NzA2ZjdwaTZScTNJUC85LwovaVNweU1ST1c0Tk8zTVllbmNpY0JIN0NhZWovMTJZUjlMN0RZc3hBZU1QUDA4SkF2c043bU1BNXJreEhvOHJvCmFmN0hIbDN3Z2RTVXN2dmdJRm1vTFVpVWJxWlpkdE1CTzEyMVBlMXUvZUlVamIzbFhJNXdBUzB3VFkzemIzMnYKQm5vSXMzNkFnWnI1Z1QybEpzUFFZbVMrT2VsN3g1YURMb1dQQnhxSlQvUkZJLzQyRmViL2pQMUw1TGZZU0pLaQorVm5iUzFMMitBaFgxNmR5QUtNQ0F3RUFBYU5DTUVBd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZHTUJDQnFTVGMxbkJ0eXAydDF4M2xlbktqdDFNQTBHQ1NxR1NJYjMKRFFFQkN3VUFBNElCQVFCVi83ZldFdVhKVkFuRHJYbUFnQ0NZTENYeWFubU81eDRCS2lPVVpFNCs5Z3JGOWRRcgpiTWVCZ2c3WWJnUTJhMjBocmM2cDRRWnVlV09zK2FXdTd5blYyR2J2aEZLTVRCR1pGMHdUaCtJNDJLMnI1TVVoCk9FWk1VbTBJMU10aHpUWjQvQ1VCdk9FOE5GZ2dWc1g2VG1NRGRkNzYzVGpCeWhOYW1welhCK3J1MGNLRWN0SXYKTmdyOEpTVXpURTRHUFd2VG02NTladFdpUnlOSTJWUTNkNXhFUjdENkF1WkErekNrcDhCR3oyeHJZMytldVo3LwpWNTJiSU9COGJpdFdYUklSMHkyaStPVllFcE1QYkdJVE9rVG9nK2lNSGg3bHYyT0hCS29kSExYVVc1VDIzTFVlCmcyMmdRR1J4bk9IMm83eXJGVHBPaHpaUVY5NEp3MXNFOTg4YgotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== - server: https://kubernetes.docker.internal:6443 - name: docker-desktop -- cluster: - certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJlRENDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUyTlRjeU16RXlPREl3SGhjTk1qSXdOekEzTWpJd01USXlXaGNOTXpJd056QTBNakl3TVRJeQpXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUyTlRjeU16RXlPREl3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFTR3FXd2FSdS9iWkFPQW1xMkpZRk1FUllNYS9iVVNHeXBZRWp5d2xoSGEKcVg5ZmdWdGVzTFh4Ukp5ZW9CS0RDTU1jLzJPUjZiaDk4VmhYc3VTUjQ2bFVvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVUVsVFRNL1RuY2xDOFNRVG5hd3lSCmYrYS9RZW93Q2dZSUtvWkl6ajBFQXdJRFNRQXdSZ0loQU5NbzB2cDhDZkJoR2VzUlBKd1ZTcm1ldnhMdTMyeXIKZnBMWW41VUMzUjhrQWlFQWl6N2NXRU9oWllSU2JPNVZOTVU0ZzRBTkVuMFJwdjdyV0hCYi8vZWluUm89Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K - server: https://0.0.0.0:60876 - name: k3d-k3d-dev -- cluster: - certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkekNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUyTlRRMU5EYzNORGt3SGhjTk1qSXdOakEyTWpBek5UUTVXaGNOTXpJd05qQXpNakF6TlRRNQpXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUyTlRRMU5EYzNORGt3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFSdFFPOTBxOUxjREpVM3NoTThTUjJobG5CYmZFc0d0aFlwbkhHRWZ0d3AKYUxkZ0wyU21DcDkrdDdFc2x2bmRHUUFuZG5saE5xdG1tQWtZOGZ2bHRHOWVvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVTl2RWtjdUZaS0orKzNXdWwwYlZ4CkFXaThkSTR3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnTnJFSk1pc3JFSzNQUVNBWGFuRTkrV2VMSWlaUHRmTi8KRENWRzFWZ3JiWFVDSVFEMUo4cGZtU216NG9WTjhDOUtpWVVyOU05R0ZndXM5bDN6MWFxbGFadW93QT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K - server: https://0.0.0.0:63560 - name: k3d-k3d-notifications -- cluster: - certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJlRENDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUyTlRRMk16QXpOelV3SGhjTk1qSXdOakEzTVRrek1qVTFXaGNOTXpJd05qQTBNVGt6TWpVMQpXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUyTlRRMk16QXpOelV3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFTcFNuL0hWWWJpYkVrZFBZd0ZFVUNuVzM3MEl4MmwyTEMxUFQ2Y25BazMKOU1DbDFWcGt4VmxibDhuSGJEeE8wRVJOQVFSb0czWEp5QjhHTlc4NXUxTGVvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVXVyUWdmNmFBaDZmRWtERVZZRUp0CnFibHlJVnd3Q2dZSUtvWkl6ajBFQXdJRFNRQXdSZ0loQUlLd0MwRy9XRVpqdTIwaTJwcHRHcWNhZndaZFdrai8KM2o5UHYvdmdHTlFCQWlFQXBoenMwdE54bVNlcDUzaENXOWNoSUI1bVc5MGp0R0lab3M4ZUIzUU5wQzA9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K - server: https://0.0.0.0:62233 - name: k3d-notifications -- cluster: - certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvakNDQWVhZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJeU1EWXlOekl5TWpBMU4xb1hEVE15TURZeU5ESXlNakExTjFvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTDlWCkNDTFVhNzVsRmJpL1lBcUdpVlhSM013Ym5CTTFJT1V2c09OWlRtQ250dDk4cFV0WTZFZk9xc01hUHpja21wRmEKSHRIRFc0cERUWmtZRnp2THlNNW5xZldKTjUxV1VSdExMQW1Hd3lLUTZTc0tOL1czZG9MR2lNQW1mU2wxNDdLUAprWTZQdjB1Sld4ZTRzNm9zbVBKTzZvNC9FNk11OEt4dkduWndNUm54c3ZjZ3hMYXVIQ1h3Q2owckR5aFZZN0thClJJQndCSXZpNkdEeVp1USthdHhOWlVLSGNmQkxwemc4QU5DZnVScWY3aE5NWU04ZzFvRHZJMkFEQUZzbGVPeEwKVDV4SlhkK3IzeE82VnNxWFpNYUtmTG1PTURYZDBtcHlXcGlnNlhjQ2MvcnZnNDFEeFpqNmZMbk5EdWZMY0VXZwpxaEo2WXZLaVBMVXhVaDJ0MTVjQ0F3RUFBYU5aTUZjd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZOelUrNEdtcGRhb09VbjhRSi9mRGk1ZGtYalVNQlVHQTFVZEVRUU8KTUF5Q0NtdDFZbVZ5Ym1WMFpYTXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBSHRjd2tqcXNjZ0haakRKNWZESwoyOThqTkpNS29vY2VXWmQveXNIVHpsWEJ3NGJxWVBkcDEwSTJtVkpHbHRmR3QrZm9hY01INnlDV0RYdDdRZW41CnhRdmt1WVRndDh0OE4yQkMyUWFzVytkbUNueDFRZzhWWk5iOFoxeEhWUE1LQ2dmbzZPczBETVMxOHJOMHhsWXMKMnpHZTJ2dE8xTXBhT2djSDZES2M4T3RtVDdyUXVJRDdncXVDU3BuNk9yMlVHOVB3NFQzeG1WVVVzaHNPZVZCagpqRDBiL3JXUXhIbGhYNEo4MW9iOTZhdHlUcmhOVDdJWlI0VVdXNnVnOThPcUJ4amxXNWNrUXVlbXRPT29obVIwCnBRcGJReFppQVdNVFV5VWd5VGV1cmNBN095UWZVR1IvbGtlOFFtejU0aTczb3l6bkhiTUJocFU0aWFNM0pVRDgKeUw4PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== - server: https://127.0.0.1:51890 - name: kind-kind -- cluster: - certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkekNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUyTkRZek5EUTVOREl3SGhjTk1qSXdNekF6TWpJd01qSXlXaGNOTXpJd01qSTVNakl3TWpJeQpXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUyTkRZek5EUTVOREl3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFTTlVtZXNMZ2VMR25acXlQandEVVRMdHpxdXRnSi84S1JlK2g0dE56eVIKcDErcEtrUE9TNkF0cCsxeUNDR241NXdPaXFGMWhDWS9iQkE1dUlITGRhOG9vMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVWR6akgvYWVWZUorbEFHcHNHMlRzCmthbFJ3ZGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQU9DRWk2dDlMSU9uV3VWQzNsaHB1WFVrV09lYzkvY08KVHc2VHNnZnNSRGI3QWlBdDNRcDA4eFpyUExjdk80VVk2ZzRETG03MEoxYzRESEI2RlU1TWFlR2hYdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K - server: https://127.0.0.1:6443 - name: rancher-desktop -contexts: -- context: - cluster: docker-desktop - user: docker-desktop - name: docker-desktop -- context: - cluster: k3d-k3d-dev - namespace: annotation-expander - user: admin@k3d-k3d-dev - name: k3d-k3d-dev -- context: - cluster: kind-kind - user: kind-kind - name: kind-kind -- context: - cluster: rancher-desktop - user: rancher-desktop - name: rancher-desktop -current-context: docker -desktop -kind: Config -preferences: {} -users: -- name: admin@k3d-k3d-dev - user: - client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJrVENDQVRlZ0F3SUJBZ0lJRFdreXVvV0w2QzR3Q2dZSUtvWkl6ajBFQXdJd0l6RWhNQjhHQTFVRUF3d1kKYXpOekxXTnNhV1Z1ZEMxallVQXhOalUzTWpNeE1qZ3lNQjRYRFRJeU1EY3dOekl5TURFeU1sb1hEVEl6TURjdwpOekl5TURFeU1sb3dNREVYTUJVR0ExVUVDaE1PYzNsemRHVnRPbTFoYzNSbGNuTXhGVEFUQmdOVkJBTVRESE41CmMzUmxiVHBoWkcxcGJqQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJIbHo2QUdHR0t1SUtPU00KQTFycEZqSHFXYmdna2c1NCtxVFZ5bDVuZnRONmVWR2dSM05RdHJGN2Vna0xCTldxZHpiMjBLSDhVUTRObGhzYgpRMXZmUmlDalNEQkdNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUtCZ2dyQmdFRkJRY0RBakFmCkJnTlZIU01FR0RBV2dCUktyTFNJaElGcTlmNGNvcWl6TTU4Wlo0Um91REFLQmdncWhrak9QUVFEQWdOSUFEQkYKQWlFQWpUQWlBanNzVEFnNkw1N2IrK1BHNjBDV2NFYzBORTFTSEw1ZHV6SFVJN0lDSUd1TEIrVFY4bXNPR0V2Nwp1MFRiUG11M0ZXRXNDamswcXFnOEU0a0hlNzlMCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkakNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdFkyeHAKWlc1MExXTmhRREUyTlRjeU16RXlPREl3SGhjTk1qSXdOekEzTWpJd01USXlXaGNOTXpJd056QTBNakl3TVRJeQpXakFqTVNFd0h3WURWUVFEREJock0zTXRZMnhwWlc1MExXTmhRREUyTlRjeU16RXlPREl3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFSOU85QjMzVnpRSTBZY0FXOUdGczIxY21nY0FwYlNHUk9OaGpMOUdRSHYKempTMUVsdENUNXJxUFA5RTlOcjVIS2gzY2FpS0w1SjVIVmZTVVYxWHpBaUVvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVVNxeTBpSVNCYXZYK0hLS29zek9mCkdXZUVhTGd3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnVTI0ZTI0VWN0WkxVODhNNjZTbVFGK0xWTVpIQXBzRkMKSUtrdUcwZTB2OVVDSUdyeG9IdmFrZDdUeGlZUTFYcS9oK3c1ZE9lb3R6bE1LVFMrVmtyN3U3clEKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= - client-key-data: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUhacTFIVDc3S2dPdGtRbHNwSGZMOGRQaUc4SDlJaEh1R2NHaXBOVWduZlRvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFZVhQb0FZWVlxNGdvNUl3RFd1a1dNZXBadUNDU0RuajZwTlhLWG1kKzAzcDVVYUJIYzFDMgpzWHQ2Q1FzRTFhcDNOdmJRb2Z4UkRnMldHeHREVzk5R0lBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= -- name: docker-desktop - user: - client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURGVENDQWYyZ0F3SUJBZ0lJVmkveFpGMi93cDB3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TVRBME1EZ3dNRFV3TlRsYUZ3MHlNakEyTURReE9UQTFORFZhTURZeApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sc3dHUVlEVlFRREV4SmtiMk5yWlhJdFptOXlMV1JsCmMydDBiM0F3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQzc0bjFlZmwya0V0dGsKcVY4cGRaNGxwNkhkbnN5NVdNU2w0d1JxT3MzOTNMZG53NHRIM2w5YllxTWk0eHJ0TTJ4T1dlM0NTa0g0d2xaWgpGVlBsZG5TTW5aRDdZaTBSNHFteWdnWkxYeU4reWZoZFdDVGtscWd4R2RHNzVUQWM0WWpDN1Z6RE01UkcxVi84CkdJYVJac2h0NXNPWlRUaFowbHBTRnZIdzJRNlBzK0xuYXovSzlxalAxMC9CYVFocG55aWZpY3lkMHZNcGpCRSsKTTVkWjBUVklwUEI4TE5NWUV2RVhRUEpUbzF0QTFzUzhFL29mdy94dktjN1M2ZTJTbXlEZDlvZWJiWEhXUWV4ZgpOR1M5N1I2bnlNY29iQWl1dkdiUG1oTG9NSHRRM05WM01xZUxHb3I1SlJzSnJGTlc1dlE1YXRQZHJrVkRqc0dECkpVTjhUMkFoQWdNQkFBR2pTREJHTUE0R0ExVWREd0VCL3dRRUF3SUZvREFUQmdOVkhTVUVEREFLQmdnckJnRUYKQlFjREFqQWZCZ05WSFNNRUdEQVdnQlJqQVFnYWtrM05ad2JjcWRyZGNkNVhweW83ZFRBTkJna3Foa2lHOXcwQgpBUXNGQUFPQ0FRRUFzRjdvWUNYbks0R2g1L3p3TDhEVnR1Yk96L2ZRejNibXFsVmhLZUMvZG5RRUk0VVhRc1dSCjVxMkQrQjFlNXptRWtub0hlWE16blUwOVBubVhmTHhtSGRwcThOcjZJRjFwMjhwdHA0M28xR080L3RuZlNoU3MKSTBFbEtaY0tVOGxZWkIxUDVsM0g1Vms1a3lROTQyWEZYZ3VOZkJwTzROaU9nMUxnSllyVVp2NWdXM0hPbG9LKwowMFJ2ZVlHTUZ4bHROUWExc1llVy9pM1NOZGFacVBkK25BYStmZHhQa2t6YThGeERMYy9QQy82Tmw0dVNGRXVHClkvZEpSaGcxQy9QaWFQNFc5REw0Y2VnYWNBSzBqNUZsa25IQ0dXYXowaHZyQlhTSDBhODhWbkJrWU5rQXoxN0wKVVd6b3VHU1ovbExQaTFVemYrMlZpVGFmMURQbjRQT2pxQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K - client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBdStKOVhuNWRwQkxiWktsZktYV2VKYWVoM1o3TXVWakVwZU1FYWpyTi9keTNaOE9MClI5NWZXMktqSXVNYTdUTnNUbG50d2twQitNSldXUlZUNVhaMGpKMlErMkl0RWVLcHNvSUdTMThqZnNuNFhWZ2sKNUphb01SblJ1K1V3SE9HSXd1MWN3ek9VUnRWZi9CaUdrV2JJYmViRG1VMDRXZEphVWhieDhOa09qN1BpNTJzLwp5dmFvejlkUHdXa0lhWjhvbjRuTW5kTHpLWXdSUGpPWFdkRTFTS1R3ZkN6VEdCTHhGMER5VTZOYlFOYkV2QlA2Ckg4UDhieW5PMHVudGtwc2czZmFIbTIxeDFrSHNYelJrdmUwZXA4akhLR3dJcnJ4bXo1b1M2REI3VU56VmR6S24KaXhxSytTVWJDYXhUVnViME9XclQzYTVGUTQ3Qmd5VkRmRTlnSVFJREFRQUJBb0lCQVFDQU9udkYyZ2lleWgvaAorNVp1L1U4SjgvYU5BcXhHYzBjQzBJVmEydmRyb3crcFNkUHRJTitEeHpYL3dXL0hJNmx2Y0FETThWRkZMWjB3Ci8xVmE3aFZ0UXJ5SVZPRGRyQXlWUUJoN3FGclpWWTZrWmx3SGhxc1RsOE9Fc1U2ODUxQW5lVWd1RVMzSmlpY1EKTHN6b3YzVmJUSHVDVFNtQVpBZ3dybWhCTUdLM3Y2bkRoOTNOVG5LYnA1TE02SGwrQnRzSElMeEVmT0RQUjFBaAp5QnlETXhYZXlFTnE2dHV2N0ZVZDFmSkZvb3Bob2xPYWhnUTNDVk9LOE9CQVRUa0RJN0xQOVNMeGNlRTVCMHRqCkFBRStzODYvV3VTaVhiTXBYbzV6amUzQlY5R3lxQnA5dmYvU2IzY0JwVWhyOHpVY1hlaE9xRzVtMUhCVTEwZlkKM2FMSmg3b0JBb0dCQU0xbGQvbHFzeDVqazloQUdoZjN0ZHZ6QjZYOXlOeXZIMXdZRkFvb2VEaC9ST0syczNhVgpVQVRLNG01cDMyZXBPY0daR2VpSUFMK2hQV25EL0V5SFBsMldVYkJEaXdyTnIwMFNXSkNIRGpCZTJhOFF4MnpzCktvVDliSGxLRGdlRFBXOGVhYjUra3lyTTdGVUtTQU1EdDRYVTRPOHMzYmI3NExKcXFDRzVWSjBKQW9HQkFPb3MKaSt5T2NJRkEyQnBraU9wMGtLWHRERHBJdHlLNXY2SUEyYndiTy85aXhuZjdKYmNFZW5VeEQrV2FuYzlsVzJuKwpoVG1za0gxaSs4TGU5UDVQN1BYbWUzcG02OFlTalRHcndlODhrTDFkaDNRTTB0NUxqcTJRb1crV3IxNjZBOS90CkN3cS9EMC9HT2tYSTFuVDFLbDRvWG9EVTRSY0tnbkVzSlcxY3VJaFpBb0dCQUlQSENRMUZycnZkaVZwaDdodlkKZXA0dU00Y0U1eHpuWWtoVXptNmgvbGZKRjZvWks5VDJBUXZDR3pwWnhOS2VHUWNqc2lLbE4wMjdKa3E0U2lQVApWV2ozNjFzYVNwZlkxUkt1cFpHOGhKYjJFOVdrL3ozYjIzL05sQTNSUEdVSGg1L0dOSFBhZzVNZlBMVm16a0xZCjNLTUpySExuSnl5aU4weGc3NVFMY3NUNUFvR0FaMWl2aS8rMDRTWTMrVFhqUXUyY3BNMi85cnorcmNHaU5DSUIKOWNWakxpdHVnYWNDRndwUDdRWnhDSmk3Q3J1bG8xcWplemVRTVBZTlBBeGZ2UmlTOEZiK3FaOWdjSExoZzBsVQo5bnNaT2FsY1RUZ1htK2xtbDJ6Y0FqNVRPUkFKQTdzK2cwaWN2azB5akhlOVJiS3BOeExEMmtHWThTMC9NaWtVCmYzWkxMa2tDZ1lFQWdnT0R3bVR5MUMxVUNtZy8xUjVxenZFVEdjbi9uQkFZSkVOMjdGVUptbVhJMmF0K2JGUGsKQkVmWUVsSUhFNTRlbmZULzR3VnQ0eFprRnBSdHM4dzRZamR0N3Q1UWZ0Q0tTMXhzV3R6Yzk3Rk9tb0h3UlIxeAowb0ZEcks0YjRpTWFsb09VREF4eUUyV3JxaEc4OHhyMjVsNjR0T1ZwaDVzTFFnWkNROEVTRVZFPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo= -- name: kind-kind - user: - client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURJVENDQWdtZ0F3SUJBZ0lJR0ZTRXhOcTI3cG93RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TWpBMk1qY3lNakl3TlRkYUZ3MHlNekEyTWpjeU1qSXdOVGxhTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXZEUExRdVJtOHRkeUZlNjYKaWlVMjZaU2g2L2JicVBwSlQ4RDRqamlPN2xzOW9od2pLUVI3NG41YklGRW9ybXNSK0RlSFNpQ3ZpRXlTaWVzNAp6cEVQczJONGs4cTRVdzVzNjFxNm9Jdmd4ZHFKa1JLREFWU3JNaG50dmRtazJCZE01SkZ4OXkwSzhsN0ExWlRYClFEelJocE1YK2RLOU9VYWpSSHhvZjI4d0NHL1ZiOG5LNEZoUEFad0V4U0JOZ29kaFo3VWQ0VnRkU05TVVZBVC8KMGZ4SENWWEk4ak9jaHhkZzY1OTBOSndpbG95RVlyNUR1c0RqWitaemxMMVlvK1JtelE0YkRpb0ZWa3R3Z2U3TQpOcVZEZ0N6b1J2clQxWi9qdWd1dDRzVDZDMzIxazR4TVZPVU5ZVUFrV0NURm1XMVowSzZUaElGM1lXTWRDcHRmCnVVUVU2UUlEQVFBQm8xWXdWREFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RBWURWUjBUQVFIL0JBSXdBREFmQmdOVkhTTUVHREFXZ0JUYzFQdUJwcVhXcURsSi9FQ2YzdzR1WFpGNAoxREFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBSSs1dlVCajFYVEZ2RVovS25lM0tmUGZIc2l2TDFpMXFOSkpECldqMmJWakxWcWc1aVRPdDNXbVVmRFVOdUJwUzlybGhxVG53dFZlTHFEeFIrRjhrNDlPRE1Yb0NxTlcwRUtWVVIKdVB6aHNwYnhsK2pBRG8yWFpzcVorSWVjbDI2NmJXQVVjcWJBTTk4OVh3VlMzeHZZalB5c3RlbksrT3ZvZEdyZQpRck0zbXFiY2kzVUcydktNb1NSblBLa25NU0gzOE53dms2RW9mQlRHZjlkNi91RytjMzNOcy9FNDEzMnhNcWtxCk5DWFkrOFk3VERSYXNGT2cwU1d0Tzd0dEVWOGlwK0JXbDF3c2JoeEM4QTNlZ0Q0LzRQQlpNcklwTkRsUUJ0djUKNEF6TmdjSjhDd1VVWjNwY2J6b1BQVkRyYjQrNWJ3Snd2RnluZTFNSi9EdWhrc3pYaHc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== - client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBdkRQTFF1Um04dGR5RmU2NmlpVTI2WlNoNi9iYnFQcEpUOEQ0amppTzdsczlvaHdqCktRUjc0bjViSUZFb3Jtc1IrRGVIU2lDdmlFeVNpZXM0enBFUHMyTjRrOHE0VXc1czYxcTZvSXZneGRxSmtSS0QKQVZTck1obnR2ZG1rMkJkTTVKRng5eTBLOGw3QTFaVFhRRHpSaHBNWCtkSzlPVWFqUkh4b2YyOHdDRy9WYjhuSwo0RmhQQVp3RXhTQk5nb2RoWjdVZDRWdGRTTlNVVkFULzBmeEhDVlhJOGpPY2h4ZGc2NTkwTkp3aWxveUVZcjVECnVzRGpaK1p6bEwxWW8rUm16UTRiRGlvRlZrdHdnZTdNTnFWRGdDem9SdnJUMVovanVndXQ0c1Q2QzMyMWs0eE0KVk9VTllVQWtXQ1RGbVcxWjBLNlRoSUYzWVdNZENwdGZ1VVFVNlFJREFRQUJBb0lCQVFDQ1gvNzNIL0tSVjk2WQpnd0NHb3JFZnJ1OWMzMjNiNlViUUVRWWV2OStFRDREcXhFTTNMdndETXpVOWRmdVFtR3VpWUJWQXJOUHN2OTdqCjNDZEdNZ2VHWmVvcHQ4MW8vQjFJRFZvcnpicXdWV3ZuSzhQRk1EcElRdDRISHJhclc3alF2bEVNd2c5TFI0aWUKYkxXUXNwRVRnT0tqRmZJdnMxZDNJaGZoNzh4K2lvcFpTb3lkWjhMM2puV09EU3Bod2pOa1ZCb21QblRSdFd0WQp0emVVbXdXUFN6cEdiTlp1YUdBNWN0SDUyc0t4SzEyNlBoUi9tOEtEdTVIcnJHMkRVUEN2bkx3cTNqdENNTkxkClFiWU01UktCR1lETlNOWG8rTkJQbXVybTFWNnl5NUk0L045Zm8yY0lnRzVpd1FSM3g5M0szYlM4TmJxejc4SUsKbkJUcWdwZ0JBb0dCQU4wd2JTbnhCVmxzdUVXUVA2K0dZeVl6L2M1bzZPZXpKTGhIOElHU3ZWSmFNVWlUR1VsSwowL2kzOGllNVI1dXlTSmpZNmQvbXgrbkFkdm1NMGxwaDNrMUI0ZVJ3Si9NZndrb05RREMzbkFnK3B5ZVFTanYrCkdwSjFwRkF0TFh4QTNLUDlESUN0a0NRK0E0RDNMRnRwYmFSdTliSHFuQ1JQanh3dmhLWHhRUC9KQW9HQkFOblMKV1I4eXlMMWcweEtMYmF5UHd0ckR6UGpNK3VaekI1MkkrYjI5bGJDOE9GZFAzRkVCdDVMR2lkS29kVzZDa1ZYVQowNG82MG52NjVtWjFHQUdrZ2dvei9VY0tINkQ3OWJPcFRMNndvN3l2N2ZKTDU2ZVRPekc3bkpUMW1meUJSRzNuCkVpMEZYdU0weWFpamd0eTN0ZmVXaHNRaUtOeEpCM0MzTVJLY2NEd2hBb0dCQUpBeDFiaVY2NHJVbVh5Z0FTU0kKVTNMQVNpR1p1dHZ4cVdxSlczemRFSmJ6UFZXMnI3RmNHcnYyRHVOZW9oUWFNY0dUQjBSenlyZlVSTHVDMWs4ZApwakp2VkxmN2RjL0ZVSCsrQzY2MncvMkwrcVNNS0JqY0dxNklDc3BKNlhFclI2dVFkK29iTy9Da2pOd3lSclNCCkVuMHo2UEJIcGVyMkFyRzVPSnlGbm40aEFvR0FWNFpjWlZya3FxUWpiRW8yVWFhYXA0a1I3YzkvNmVYQzQ1eE0KcmQ5bUxOUVUxNUN4ZUtOMS9qWmszdzd0MFBBS01qRUlwM3luWTBZWUdvZG1sQkprWm5FcGpzZ1VtWDFrNTBKYQpJaTJjUFk5N3oyYU11UnRDSitPNU1Xblc3RTJORDVSUUMyTWpFNno4Y3hUdzJudS9WTE5aaDZvcVBRbmpRWGw2CjhIMkg0OEVDZ1lBMFNZRUdwQ21UUXFOYTBzVGNsSXpUUzVGVlNBQTVEUXh3MXFNUFNKK04rRnVydWZVTlVrUkIKbnlDVmppVVd3dnEyL0FxVnNOc0VjemhuVVBOR0txUkVOWlZsMmgxTDNzY0NoNFBUamI4MGl0UWtrMTR3dEZIeQpVeWdIUS9hZDdpdmhiYUwyMjM3YmJDdDBzV3o5Y0hrTG5vZFBkSnpjbXhKWWx3WG9wUmRhZGc9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo= -- name: rancher-desktop - user: - client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJrVENDQVRlZ0F3SUJBZ0lJSHpRSXpiWFgwRDh3Q2dZSUtvWkl6ajBFQXdJd0l6RWhNQjhHQTFVRUF3d1kKYXpOekxXTnNhV1Z1ZEMxallVQXhOalEyTXpRME9UUXlNQjRYRFRJeU1ETXdNekl5TURJeU1sb1hEVEl6TURNdwpNekl5TURJeU1sb3dNREVYTUJVR0ExVUVDaE1PYzNsemRHVnRPbTFoYzNSbGNuTXhGVEFUQmdOVkJBTVRESE41CmMzUmxiVHBoWkcxcGJqQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJEbGt5UG1XenBIamdDWFQKMHVLWlluTkxib25Cd2poMWd4NnZLZmRHSlN6Nnc2NENuUUZxZGU5TldmWEFmWG5VQVZmdGF2R1BUQjRwYmYzaQpYY09VRkh5alNEQkdNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUtCZ2dyQmdFRkJRY0RBakFmCkJnTlZIU01FR0RBV2dCU3VRb2ZXa2htZFNyZjFJV1dkcTlsN2NyMGtsekFLQmdncWhrak9QUVFEQWdOSUFEQkYKQWlFQXdVZUVXaDFNVzFpZGplSExLL3dQNkVpQkRTVkM0YkxkRHhwRDlYTlpSM1VDSUIxYzNpRjBrQ1lxSlBuOQpzdXF5YmVnY2plSWJ2NHMrNy9oWVdFUnB2blZ3Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkekNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdFkyeHAKWlc1MExXTmhRREUyTkRZek5EUTVOREl3SGhjTk1qSXdNekF6TWpJd01qSXlXaGNOTXpJd01qSTVNakl3TWpJeQpXakFqTVNFd0h3WURWUVFEREJock0zTXRZMnhwWlc1MExXTmhRREUyTkRZek5EUTVOREl3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFSTXZBbTNST3RDTHhsWVpDM25ObUFtSk1ZVGJNSzEyYXlUR3lzK2F3cGEKTjBhem1jVytVWjNhQ0VXREw3TG1lV2szR2NGZEtGbmlwR1I2b0xtVDhaS3hvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVXJrS0gxcElablVxMzlTRmxuYXZaCmUzSzlKSmN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnRGw5MlNrcUZBdGh6QUt6MGltV2REcEVTMkpkbDFnRjgKMTFLK1IranQrTTRDSVFEN3BuSnBsNDhGRVBXUXpYOU50ZjRZSCs0R1R1R2VyUVVYa3JlTDArb0ZaZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K - client-key-data: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU5qNjBXYzIyenIvZmMvMnVMdk5pa0VIbnpyd0dTbm5ZNDhpdWdKdEpodjRvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFT1dUSStaYk9rZU9BSmRQUzRwbGljMHR1aWNIQ09IV0RIcThwOTBZbExQckRyZ0tkQVdwMQo3MDFaOWNCOWVkUUJWKzFxOFk5TUhpbHQvZUpkdzVRVWZBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= From 476167c767737e0cfb84741c2071281ea19dde18 Mon Sep 17 00:00:00 2001 From: Bharadwajshivam28 Date: Fri, 25 Oct 2024 18:56:37 +0530 Subject: [PATCH 7/7] feat: removed unwanted packages from go.mod --- go.mod | 3 --- 1 file changed, 3 deletions(-) diff --git a/go.mod b/go.mod index 5aa9b34..019e891 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,6 @@ require ( github.com/mattn/go-colorable v0.1.13 github.com/pkg/errors v0.9.1 github.com/rs/zerolog v1.33.0 - github.com/stretchr/testify v1.9.0 google.golang.org/api v0.203.0 k8s.io/client-go v0.29.1 ) @@ -45,7 +44,6 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect @@ -67,7 +65,6 @@ require ( google.golang.org/protobuf v1.35.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apimachinery v0.29.1 // indirect k8s.io/klog/v2 v2.110.1 // indirect k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect