diff --git a/cmd/flags.go b/cmd/flags.go index d1fb2298..9eadd9a2 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -53,10 +53,10 @@ func stringSliceFlag(flags *pflag.FlagSet, name, usage string, opts ...DocOpt[[] } func addFlag[D any]( - name, usage string, - opts []DocOpt[D], - onlyLong func(string, D, string) *D, - longAndShort func(string, string, D, string) *D, + name, usage string, + opts []DocOpt[D], + onlyLong func(string, D, string) *D, + longAndShort func(string, string, D, string) *D, ) { var opt DocOpt[D] for _, o := range opts { diff --git a/cmd/root.go b/cmd/root.go index 403f3a54..eca9bb6f 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -11,6 +11,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" + "github.com/zapier/kubechecks/pkg" "github.com/zapier/kubechecks/telemetry" ) @@ -97,7 +98,7 @@ func initTelemetry(ctx context.Context) (*telemetry.OperatorTelemetry, error) { enableOtel := viper.GetBool("otel-enabled") otelHost := viper.GetString("otel-collector-host") otelPort := viper.GetString("otel-collector-port") - return telemetry.Init(ctx, "kubechecks", enableOtel, otelHost, otelPort) + return telemetry.Init(ctx, "kubechecks", pkg.GitTag, pkg.GitCommit, enableOtel, otelHost, otelPort) } func setupLogOutput() { diff --git a/go.mod b/go.mod index b3a7d8a2..32cf5865 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,6 @@ require ( github.com/go-logr/zerologr v1.2.3 github.com/google/go-github/v53 v53.2.0 github.com/heptiolabs/healthcheck v0.0.0-20211123025425-613501dd5deb - github.com/kubescape/go-git-url v0.0.25 github.com/labstack/echo-contrib v0.14.0 github.com/labstack/echo/v4 v4.10.2 github.com/masterminds/semver v1.5.0 @@ -28,6 +27,7 @@ require ( github.com/shurcooL/githubv4 v0.0.0-20230704064427-599ae7bbf278 github.com/sirupsen/logrus v1.9.0 github.com/spf13/cobra v1.6.1 + github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.2 github.com/whilp/git-urls v1.0.0 @@ -42,21 +42,22 @@ require ( go.opentelemetry.io/otel/sdk v1.11.1 go.opentelemetry.io/otel/sdk/metric v0.33.0 go.opentelemetry.io/otel/trace v1.11.1 - golang.org/x/net v0.10.0 - golang.org/x/oauth2 v0.8.0 - golang.org/x/sync v0.1.0 - google.golang.org/grpc v1.53.0 + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa + golang.org/x/net v0.17.0 + golang.org/x/oauth2 v0.13.0 + golang.org/x/sync v0.5.0 + google.golang.org/grpc v1.59.0 gopkg.in/dealancer/validate.v2 v2.1.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/apimachinery v0.24.4 ) require ( - cloud.google.com/go v0.107.0 // indirect - cloud.google.com/go/compute v1.15.1 // indirect + cloud.google.com/go v0.110.8 // indirect + cloud.google.com/go/compute v1.23.1 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v0.8.0 // indirect - cloud.google.com/go/storage v1.27.0 // indirect + cloud.google.com/go/iam v1.1.3 // indirect + cloud.google.com/go/storage v1.35.1 // indirect cuelang.org/go v0.4.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/BurntSushi/toml v0.3.1 // indirect @@ -120,18 +121,19 @@ require ( github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang-jwt/jwt/v4 v4.4.3 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/google/btree v1.1.2 // indirect github.com/google/gnostic v0.6.9 // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-github/v45 v45.2.0 // indirect github.com/google/go-jsonnet v0.19.1 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/s2a-go v0.1.7 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.1 // indirect - github.com/googleapis/gax-go/v2 v2.7.0 // indirect + github.com/google/uuid v1.4.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect @@ -207,7 +209,6 @@ require ( github.com/spf13/afero v1.9.3 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/tchap/go-patricia/v2 v2.3.1 // indirect github.com/tmccombs/hcl2json v0.3.1 // indirect @@ -231,17 +232,18 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.1 // indirect go.opentelemetry.io/proto/otlp v0.19.0 // indirect go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd // indirect - golang.org/x/crypto v0.7.0 // indirect - golang.org/x/exp v0.0.0-20210901193431-a062eea981d2 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/term v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/term v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/api v0.107.0 // indirect + google.golang.org/api v0.150.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect - google.golang.org/protobuf v1.28.1 // indirect + google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/go.sum b/go.sum index cd1d457e..3a26c30c 100644 --- a/go.sum +++ b/go.sum @@ -39,8 +39,8 @@ cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w9 cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.107.0 h1:qkj22L7bgkl6vIeZDlOY2po43Mx/TIa2Wsa7VR+PEww= -cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= +cloud.google.com/go v0.110.8 h1:tyNdfIxjzaWctIiLYOTalaLKZ17SI44SKFW26QbOhME= +cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk= cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= @@ -77,8 +77,8 @@ cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.15.1 h1:7UGq3QknM33pw5xATlpzeoomNxsacIVvTqTTvbfajmE= -cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= +cloud.google.com/go/compute v1.23.1 h1:V97tBoDaZHb6leicZ1G6DLK2BAaZLJ/7+9BB/En3hR0= +cloud.google.com/go/compute v1.23.1/go.mod h1:CqB3xpmPKKt3OJpW2ndFIXnA9A4xAy/F3Xp1ixncW78= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= @@ -119,13 +119,12 @@ cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y97 cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= -cloud.google.com/go/iam v0.8.0 h1:E2osAkZzxI/+8pZcxVLcDtAQx/u+hZXVryUaYQ5O0Kk= -cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= +cloud.google.com/go/iam v1.1.3 h1:18tKG7DzydKWUnLjonWcJO6wjSCAtzh4GcRKlH/Hrzc= +cloud.google.com/go/iam v1.1.3/go.mod h1:3khUlaBXfPKKe7huYgEpDn6FtgRyMEqbkvBxrQyY5SE= cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= -cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= @@ -182,8 +181,9 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= -cloud.google.com/go/storage v1.27.0 h1:YOO045NZI9RKfCj1c5A/ZtuuENUc8OAW+gHdGnDgyMQ= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cloud.google.com/go/storage v1.35.1 h1:B59ahL//eDfx2IIKFBeT5Atm9wnNmj3+8xG/W4WB//w= +cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= @@ -487,6 +487,7 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/apd/v2 v2.0.1 h1:y1Rh3tEU89D+7Tgbw+lp52T6p/GJLpDmNvr10UWqLTE= @@ -728,6 +729,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= github.com/euank/go-kmsg-parser v2.0.0+incompatible/go.mod h1:MhmAMZ8V4CYH4ybgdRwPr2TU5ThnS43puaKEMpja1uw= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -887,8 +889,8 @@ github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5 github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -962,8 +964,9 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 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/go-containerregistry v0.0.0-20191010200024-a3d713f9b7f8/go.mod h1:KyKXa9ciM8+lgMXwOVsXi7UxGrsf9mM61Mzs+xKUrKE= github.com/google/go-containerregistry v0.1.2/go.mod h1:GPivBPgdAyd2SU+vf6EpsgOtWDuPqjW0hJZt4rNdTZ4= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= @@ -989,8 +992,8 @@ github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible h1:x github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -1008,6 +1011,8 @@ github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/rpmpack v0.0.0-20191226140753-aa36bfddb3a0/go.mod h1:RaTPr0KUf2K7fnZYLNDrr8rxAamWs3iNywJLtQ2AzBg= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= @@ -1015,15 +1020,16 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s= github.com/google/wire v0.4.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= -github.com/googleapis/enterprise-certificate-proxy v0.2.1 h1:RY7tHKZcRlk788d5WSo/e83gOyyy742E8GSs771ySpg= -github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -1035,8 +1041,8 @@ github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99 github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= @@ -1239,8 +1245,6 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kubescape/go-git-url v0.0.25 h1:i7SSSC1+1m/Dg+4LV3erp0YklnWj1Z0cVlRxCT3Zy/0= -github.com/kubescape/go-git-url v0.0.25/go.mod h1:IbVT7Wsxlghsa+YxI5KOx4k9VQJaa3z0kTaQz5D3nKM= github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= @@ -2010,8 +2014,8 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -2029,8 +2033,9 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20210126221216-84987778548c/go.mod h1:I6l2HNBLBZEcrOoCpyKLdY2lHoRZ8lI4x60KMCQDft4= golang.org/x/exp v0.0.0-20210220032938-85be41e4509f/go.mod h1:I6l2HNBLBZEcrOoCpyKLdY2lHoRZ8lI4x60KMCQDft4= -golang.org/x/exp v0.0.0-20210901193431-a062eea981d2 h1:Or4Ra3AAlhUlNn8WmIzw2Yq2vUHSkrP6E2e/FIESpF8= golang.org/x/exp v0.0.0-20210901193431-a062eea981d2/go.mod h1:a3o/VtDNHN+dCVLEpzjjUHOzR+Ln3DHX056ZPzoZGGA= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -2142,8 +2147,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180724155351-3d292e4d0cdc/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -2176,8 +2181,8 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= +golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= 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= @@ -2193,8 +2198,8 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2340,14 +2345,14 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2358,8 +2363,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2539,8 +2544,8 @@ google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.107.0 h1:I2SlFjD8ZWabaIFOfeEDg3pf0BHJDh6iYQ1ic3Yu/UU= -google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.150.0 h1:Z9k22qD289SZ8gCJrk4DrWXkNjtfvKAUo/l1ma8eBYE= +google.golang.org/api v0.150.0/go.mod h1:ccy+MJ6nrYFgE3WgRx/AMXOxOmU8Q4hSa+jjibzhxcg= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -2670,8 +2675,12 @@ google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqw google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b h1:+YaDE2r2OG8t/z5qmsh7Y+XXwCbvadxxZ0YY6mTdrVA= +google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= +google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b h1:CIC2YMXmIhYw6evmhPxBKJ4fmLbOFtXQN/GV3XOZR8k= +google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= @@ -2717,8 +2726,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 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= @@ -2729,8 +2738,9 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0 h1:FVCohIoYO7IJoDDVpV2pdq7SgrMH6wHnuTyrdrxJNoY= gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0/go.mod h1:OdE7CF6DbADk7lN8LIKRzRJTTZXIjtWgA5THM5lhBAw= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= diff --git a/pkg/vcs_clients/client.go b/pkg/client.go similarity index 68% rename from pkg/vcs_clients/client.go rename to pkg/client.go index 0d072ced..76311e8f 100644 --- a/pkg/vcs_clients/client.go +++ b/pkg/client.go @@ -1,4 +1,4 @@ -package vcs_clients +package pkg import ( "context" @@ -8,46 +8,6 @@ import ( "github.com/zapier/kubechecks/pkg/repo" ) -// Enum for represnting the state of a commit for posting via CommitStatus -type CommitState int - -const ( - Pending CommitState = iota - Running - Failure - Success -) - -// Return literal string representation of this state for use in the request -func (s CommitState) String() string { - switch s { - case Pending: - return "pending" - case Running: - return "running" - case Failure: - return "error" - case Success: - return "success" - } - return "unknown" -} - -// Return more informative description message based on the enum state -func (s CommitState) StateToDesc() string { - switch s { - case Pending: - return "pending..." - case Running: - return "in progress..." - case Failure: - return "failed." - case Success: - return "succeeded." - } - return "unknown" -} - var ( // ErrInvalidType is a sentinel error for use in client implementations ErrInvalidType = errors.New("invalid event type") diff --git a/pkg/commitState.go b/pkg/commitState.go new file mode 100644 index 00000000..a1270e5d --- /dev/null +++ b/pkg/commitState.go @@ -0,0 +1,70 @@ +package pkg + +import "fmt" + +// CommitState is an enum for represnting the state of a commit for posting via CommitStatus +type CommitState uint8 + +const ( + StateNone CommitState = iota + StateSuccess + StateRunning + StateWarning + StateFailure + StateError + StatePanic +) + +// Emoji returns a string representation of this state for use in the request +func (s CommitState) Emoji() string { + if emoji, ok := stateEmoji[s]; ok { + return emoji + } else { + return defaultEmoji + } +} + +func (s CommitState) String() string { + text, ok := stateString[s] + if !ok { + text = defaultString + } + + if text == "" { + return "" + } + + return fmt.Sprintf("%s %s", text, s.Emoji()) +} + +var stateEmoji = map[CommitState]string{ + StateNone: "", + StateSuccess: ":white_check_mark:", + StateRunning: ":running:", + StateWarning: ":warning:", + StateFailure: ":red_circle:", + StateError: ":heavy_exclamation_mark:", + StatePanic: ":skull:", +} + +const defaultEmoji = ":interrobang:" + +var stateString = map[CommitState]string{ + StateNone: "", + StateSuccess: "Passed", + StateRunning: "Running", + StateWarning: "Warning", + StateFailure: "Failed", + StateError: "Error", + StatePanic: "Panic", +} + +const defaultString = "Unknown" + +func WorstState(l1, l2 CommitState) CommitState { + if l2 > l1 { + return l2 + } + + return l1 +} diff --git a/pkg/conftest/conftest.go b/pkg/conftest/conftest.go index 6806b797..cb766822 100644 --- a/pkg/conftest/conftest.go +++ b/pkg/conftest/conftest.go @@ -21,17 +21,6 @@ import ( "github.com/zapier/kubechecks/telemetry" ) -var gitLabCommentFormat = ` -
Show Conftest Validation result: %s - -%s - -> This check validates resources against conftest policies. -> Currently this is informational only and a warning or error does not block deploy. - -
-` - const passedMessage = "\nPassed all policy checks." var reposCache = local.NewReposDirectory() @@ -41,7 +30,7 @@ var reposCache = local.NewReposDirectory() // as a GitLab comment. The validation checks resources against Zapier policies and // provides feedback for warnings or errors as informational messages. Failure to // pass a policy check currently does not block deploy. -func Conftest(ctx context.Context, app *v1alpha1.Application, repoPath string) (string, error) { +func Conftest(ctx context.Context, app *v1alpha1.Application, repoPath string) (pkg.CheckResult, error) { _, span := otel.Tracer("Kubechecks").Start(ctx, "Conftest") defer span.End() @@ -60,7 +49,10 @@ func Conftest(ctx context.Context, app *v1alpha1.Application, repoPath string) ( } if len(locations) == 0 { - return "no policies locations configured", nil + return pkg.CheckResult{ + State: pkg.StateWarning, + Summary: "no policies locations configured", + }, nil } var r runner.TestRunner @@ -80,7 +72,7 @@ func Conftest(ctx context.Context, app *v1alpha1.Application, repoPath string) ( results, err := r.Run(ctx, []string{confTestDir}) if err != nil { telemetry.SetError(span, err, "ConfTest Run") - return "", err + return pkg.CheckResult{}, err } var b bytes.Buffer @@ -104,16 +96,19 @@ func Conftest(ctx context.Context, app *v1alpha1.Application, repoPath string) ( innerStrings = append(innerStrings, passedMessage) } - resultString := pkg.PassString() - if warnings { - resultString = pkg.WarningString() - } + var cr pkg.CheckResult if failures { - resultString = pkg.FailedString() + cr.State = pkg.StateFailure + } else if warnings { + cr.State = pkg.StateWarning + } else { + cr.State = pkg.StateSuccess } - comment := fmt.Sprintf(gitLabCommentFormat, resultString, strings.Join(innerStrings, "\n")) - return comment, nil + cr.Summary = "Show Conftest Validation result" + cr.Details = strings.Join(innerStrings, "\n") + + return cr, nil } // formatConftestResults writes the check results from an array of output.CheckResult objects into a formatted table. @@ -134,11 +129,11 @@ func formatConftestResults(w io.Writer, checkResults []output.CheckResult) { } for _, result := range checkResult.Exceptions { - tableData = append(tableData, []string{pkg.FailedEmoji(), code(checkResult.FileName), result.Message}) + tableData = append(tableData, []string{pkg.StateError.Emoji(), code(checkResult.FileName), result.Message}) } for _, result := range checkResult.Warnings { - tableData = append(tableData, []string{pkg.WarningEmoji(), code(checkResult.FileName), result.Message}) + tableData = append(tableData, []string{pkg.StateWarning.Emoji(), code(checkResult.FileName), result.Message}) } for _, result := range checkResult.Skipped { @@ -146,7 +141,7 @@ func formatConftestResults(w io.Writer, checkResults []output.CheckResult) { } for _, result := range checkResult.Failures { - tableData = append(tableData, []string{pkg.FailedEmoji(), code(checkResult.FileName), result.Message}) + tableData = append(tableData, []string{pkg.StateFailure.Emoji(), code(checkResult.FileName), result.Message}) } } diff --git a/pkg/diff/ai_summary.go b/pkg/diff/ai_summary.go index 814cafc2..fa31a2dc 100644 --- a/pkg/diff/ai_summary.go +++ b/pkg/diff/ai_summary.go @@ -1,25 +1,16 @@ package diff import ( - "fmt" - "github.com/rs/zerolog/log" - "github.com/zapier/kubechecks/pkg/aisummary" - "github.com/zapier/kubechecks/pkg/vcs_clients" - "github.com/zapier/kubechecks/telemetry" "go.opentelemetry.io/otel" "golang.org/x/net/context" -) - -const diffAISummaryCommentFormat = ` -
Show AI Summary Diff -%s - -
-` + "github.com/zapier/kubechecks/pkg" + "github.com/zapier/kubechecks/pkg/aisummary" + "github.com/zapier/kubechecks/telemetry" +) -func AIDiffSummary(ctx context.Context, mrNote *vcs_clients.Message, name string, manifests []string, diff string) { +func AIDiffSummary(ctx context.Context, mrNote *pkg.Message, name string, manifests []string, diff string) { ctx, span := otel.Tracer("Kubechecks").Start(ctx, "AIDiffSummary") defer span.End() @@ -32,11 +23,13 @@ func AIDiffSummary(ctx context.Context, mrNote *vcs_clients.Message, name string if err != nil { telemetry.SetError(span, err, "OpenAI SummarizeDiff") log.Error().Err(err).Msg("failed to summarize diff") - mrNote.AddToAppMessage(ctx, name, fmt.Sprintf("failed to summarize diff: %s", err)) + cr := pkg.CheckResult{State: pkg.StateWarning, Summary: "failed to summarize diff", Details: err.Error()} + mrNote.AddToAppMessage(ctx, name, cr) return } if aiSummary != "" { - mrNote.AddToAppMessage(ctx, name, fmt.Sprintf(diffAISummaryCommentFormat, aiSummary)) + cr := pkg.CheckResult{State: pkg.StateNone, Summary: "Show AI Summary Diff", Details: aiSummary} + mrNote.AddToAppMessage(ctx, name, cr) } } diff --git a/pkg/diff/diff.go b/pkg/diff/diff.go index cb8d4495..5b643f02 100644 --- a/pkg/diff/diff.go +++ b/pkg/diff/diff.go @@ -11,11 +11,6 @@ import ( "github.com/argoproj/argo-cd/v2/controller" "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" "github.com/argoproj/argo-cd/v2/pkg/apiclient/settings" - "github.com/ghodss/yaml" - "github.com/go-logr/zerologr" - "github.com/rs/zerolog/log" - "go.opentelemetry.io/otel" - argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/util/argo" argodiff "github.com/argoproj/argo-cd/v2/util/argo/diff" @@ -23,12 +18,17 @@ import ( "github.com/argoproj/gitops-engine/pkg/sync/ignore" "github.com/argoproj/gitops-engine/pkg/utils/kube" "github.com/argoproj/pkg/errors" - "github.com/zapier/kubechecks/pkg/argo_client" - "github.com/zapier/kubechecks/telemetry" + "github.com/ghodss/yaml" + "github.com/go-logr/zerologr" + "github.com/pmezard/go-difflib/difflib" + "github.com/rs/zerolog/log" + "go.opentelemetry.io/otel" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" - "github.com/pmezard/go-difflib/difflib" + "github.com/zapier/kubechecks/pkg" + "github.com/zapier/kubechecks/pkg/argo_client" + "github.com/zapier/kubechecks/telemetry" ) // from https://github.com/argoproj/argo-cd/blob/d3ff9757c460ae1a6a11e1231251b5d27aadcdd1/cmd/argocd/commands/app.go#L879 @@ -38,16 +38,6 @@ type objKeyLiveTarget struct { target *unstructured.Unstructured } -const diffCommentFormat = ` -
Show Diff: %s - -` + "```diff" + ` -%s -` + "```" + ` - -
-` - /* Take cli output and return as a string or an array of strings instead of printing @@ -55,7 +45,7 @@ changedFilePath should be the root of the changed folder from https://github.com/argoproj/argo-cd/blob/d3ff9757c460ae1a6a11e1231251b5d27aadcdd1/cmd/argocd/commands/app.go#L879 */ -func GetDiff(ctx context.Context, name string, manifests []string) (string, string, error) { +func GetDiff(ctx context.Context, name string, manifests []string) (pkg.CheckResult, string, error) { ctx, span := otel.Tracer("Kubechecks").Start(ctx, "Diff") defer span.End() @@ -77,7 +67,7 @@ func GetDiff(ctx context.Context, name string, manifests []string) (string, stri }) if err != nil { telemetry.SetError(span, err, "Get Argo App") - return "", "", err + return pkg.CheckResult{}, "", err } resources, err := appClient.ManagedResources(ctx, &application.ResourcesQuery{ @@ -85,7 +75,7 @@ func GetDiff(ctx context.Context, name string, manifests []string) (string, stri }) if err != nil { telemetry.SetError(span, err, "Get Argo Managed Resources") - return "", "", err + return pkg.CheckResult{}, "", err } errors.CheckError(err) @@ -99,13 +89,13 @@ func GetDiff(ctx context.Context, name string, manifests []string) (string, stri argoSettings, err := settingsClient.Get(ctx, &settings.SettingsQuery{}) if err != nil { telemetry.SetError(span, err, "Get Argo Cluster Settings") - return "", "", err + return pkg.CheckResult{}, "", err } liveObjs, err := cmdutil.LiveObjects(resources.Items) if err != nil { telemetry.SetError(span, err, "Get Argo Live Objects") - return "", "", err + return pkg.CheckResult{}, "", err } groupedObjs := groupObjsByKey(unstructureds, liveObjs, app.Spec.Destination.Namespace) @@ -154,7 +144,7 @@ func GetDiff(ctx context.Context, name string, manifests []string) (string, stri err := PrintDiff(diffBuffer, live, target) if err != nil { telemetry.SetError(span, err, "Print Diff") - return "", "", err + return pkg.CheckResult{}, "", err } switch { case diffRes.Modified: @@ -166,14 +156,20 @@ func GetDiff(ctx context.Context, name string, manifests []string) (string, stri } } } - summary := "No changes" + var summary string if added != 0 || modified != 0 || removed != 0 { summary = fmt.Sprintf("%d added, %d modified, %d removed", added, modified, removed) + } else { + summary = "No changes" } diff := diffBuffer.String() - return fmt.Sprintf(diffCommentFormat, summary, diff), diff, nil + var cr pkg.CheckResult + cr.Summary = summary + cr.Details = fmt.Sprintf("```diff\n%s\n```", diff) + + return cr, diff, nil } // from https://github.com/argoproj/argo-cd/blob/d3ff9757c460ae1a6a11e1231251b5d27aadcdd1/cmd/argocd/commands/app.go#L879 diff --git a/pkg/events/check.go b/pkg/events/check.go index 07eddb1d..cf5c6c28 100644 --- a/pkg/events/check.go +++ b/pkg/events/check.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "strings" + "sync" "sync/atomic" "time" @@ -15,7 +16,6 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" - "golang.org/x/sync/errgroup" "github.com/zapier/kubechecks/pkg" "github.com/zapier/kubechecks/pkg/affected_apps" @@ -26,46 +26,27 @@ import ( "github.com/zapier/kubechecks/pkg/repo" "github.com/zapier/kubechecks/pkg/repo_config" "github.com/zapier/kubechecks/pkg/validate" - "github.com/zapier/kubechecks/pkg/vcs_clients" "github.com/zapier/kubechecks/telemetry" ) type CheckEvent struct { - client vcs_clients.Client // Client exposing methods to communicate with platform of user choice - fileList []string // What files have changed in this PR/MR - repoFiles []string // All files in this repository - TempWorkingDir string // Location of the local repo + client pkg.Client // Client exposing methods to communicate with platform of user choice + fileList []string // What files have changed in this PR/MR + repoFiles []string // All files in this repository + TempWorkingDir string // Location of the local repo repo *repo.Repo logger zerolog.Logger workerLimits int - vcsNote *vcs_clients.Message + vcsNote *pkg.Message affectedItems affected_apps.AffectedItems cfg *pkg.ServerConfig } -const ( - errorCommentFormat = ` -:warning: **Error while %s** :warning: -` + "```" + ` -%v -` + "```" + ` +var inFlight int32 -Check kubechecks application logs for more information. -` -) - -var ( - hostname = "" - inFlight int32 -) - -func init() { - hostname, _ = os.Hostname() -} - -func NewCheckEvent(repo *repo.Repo, client vcs_clients.Client, cfg *pkg.ServerConfig) *CheckEvent { +func NewCheckEvent(repo *repo.Repo, client pkg.Client, cfg *pkg.ServerConfig) *CheckEvent { ce := &CheckEvent{ cfg: cfg, client: client, @@ -76,7 +57,7 @@ func NewCheckEvent(repo *repo.Repo, client vcs_clients.Client, cfg *pkg.ServerCo return ce } -// Get the Repo from a CheckEvent. In normal operations a CheckEvent can only be made by the VCSHookHandler +// GetRepo gets the repo from a CheckEvent. In normal operations a CheckEvent can only be made by the VCSHookHandler // As the Repo is built from a webhook payload via the VCSClient, it should always be present. If not, error func (ce *CheckEvent) GetRepo(ctx context.Context) (*repo.Repo, error) { _, span := otel.Tracer("Kubechecks").Start(ctx, "CheckEventGetRepo") @@ -232,7 +213,7 @@ func (ce *CheckEvent) ProcessApps(ctx context.Context) { // Concurrently process all apps, with a corresponding error channel for reporting back failures appChannel := make(chan appStruct, len(ce.affectedItems.Applications)) - errChannel := make(chan error, len(ce.affectedItems.Applications)) + doneChannel := make(chan bool, len(ce.affectedItems.Applications)) // If the number of affected apps that we have is less than our worker limit, lower the worker limit if ce.workerLimits > len(ce.affectedItems.Applications) { @@ -243,7 +224,7 @@ func (ce *CheckEvent) ProcessApps(ctx context.Context) { ce.vcsNote = ce.createNote(ctx) for w := 0; w <= ce.workerLimits; w++ { - go ce.appWorkers(ctx, w, appChannel, errChannel) + go ce.appWorkers(ctx, w, appChannel, doneChannel) } // Produce apps onto channel @@ -257,33 +238,37 @@ func (ce *CheckEvent) ProcessApps(ctx context.Context) { } returnCount := 0 - resultError := false - for err := range errChannel { - returnCount++ - if err != nil { - resultError = true - ce.logger.Error().Err(err).Msg("error running tool") + commitStatus := true + for appStatus := range doneChannel { + if !appStatus { + commitStatus = false } + + returnCount++ if returnCount == len(ce.affectedItems.Applications) { ce.logger.Debug().Msg("Closing channels") close(appChannel) - close(errChannel) + close(doneChannel) } } ce.logger.Info().Msg("Finished") - if resultError { - ce.CommitStatus(ctx, vcs_clients.Failure) + if err = ce.vcsNote.PushComment(ctx, ce.client); err != nil { + ce.logger.Error().Err(err).Msg("failed to push comment") + } + + if !commitStatus { + ce.CommitStatus(ctx, pkg.StateFailure) ce.logger.Error().Msg("Errors found") return } - ce.CommitStatus(ctx, vcs_clients.Success) + ce.CommitStatus(ctx, pkg.StateSuccess) } -// CommitStatus takes one of "success", "failure", "pending" or "error" and pass off to client +// CommitStatus sets the commit status on the MR // To set the PR/MR status -func (ce *CheckEvent) CommitStatus(ctx context.Context, status vcs_clients.CommitState) { +func (ce *CheckEvent) CommitStatus(ctx context.Context, status pkg.CommitState) { _, span := otel.Tracer("Kubechecks").Start(ctx, "CommitStatus") defer span.End() @@ -293,10 +278,11 @@ func (ce *CheckEvent) CommitStatus(ctx context.Context, status vcs_clients.Commi } // Process all apps on the provided channel -func (ce *CheckEvent) appWorkers(ctx context.Context, workerID int, appChannel chan appStruct, resultChannel chan error) { +func (ce *CheckEvent) appWorkers(ctx context.Context, workerID int, appChannel chan appStruct, resultChannel chan bool) { for app := range appChannel { ce.logger.Info().Int("workerID", workerID).Str("app", app.name).Msg("Processing App") - resultChannel <- ce.processApp(ctx, app.name, app.dir) + isSuccess := ce.processApp(ctx, app.name, app.dir) + resultChannel <- isSuccess } } @@ -305,7 +291,7 @@ func (ce *CheckEvent) appWorkers(ctx context.Context, workerID int, appChannel c // It takes a context (ctx), application name (app), directory (dir) as input and returns an error if any check fails. // The processing is performed concurrently using Go routines and error groups. Any check results are sent through // the returnChan. The function also manages the inFlight atomic counter to track active processing routines. -func (ce *CheckEvent) processApp(ctx context.Context, app, dir string) error { +func (ce *CheckEvent) processApp(ctx context.Context, app, dir string) bool { ctx, span := otel.Tracer("Kubechecks").Start(ctx, "processApp", trace.WithAttributes( attribute.String("app", app), attribute.String("dir", dir), @@ -324,8 +310,9 @@ func (ce *CheckEvent) processApp(ctx context.Context, app, dir string) error { manifests, err := argo_client.GetManifestsLocal(ctx, app, ce.TempWorkingDir, dir) if err != nil { ce.logger.Error().Err(err).Msgf("Unable to get manifests for %s in %s", app, dir) - ce.vcsNote.AddToAppMessage(ctx, app, fmt.Sprintf("Unable to get manifests for application: \n\n ```\n%s\n```", ce.cleanupGetManifestsError(err))) - return nil + cr := pkg.CheckResult{State: pkg.StateError, Summary: "Unable to get manifests", Details: fmt.Sprintf("```\n%s\n```", ce.cleanupGetManifestsError(err))} + ce.vcsNote.AddToAppMessage(ctx, app, cr) + return false } // Argo diff logic wants unformatted manifests but everything else wants them as YAML, so we prepare both @@ -341,117 +328,133 @@ func (ce *CheckEvent) processApp(ctx context.Context, app, dir string) error { ce.logger.Info().Msgf("Kubernetes version: %s", k8sVersion) } - grp, grpCtx := errgroup.WithContext(ctx) - wrap := ce.createWrapper(span, grpCtx, app) + var wg sync.WaitGroup + + run := ce.createRunner(span, ctx, app, &wg) - grp.Go(wrap("validating app against schema", ce.validateSchemas(grpCtx, app, k8sVersion, ce.TempWorkingDir, formattedManifests))) - grp.Go(wrap("generating diff for app", ce.generateDiff(grpCtx, app, manifests))) + run("validating app against schema", ce.validateSchemas(ctx, app, k8sVersion, ce.TempWorkingDir, formattedManifests)) + run("generating diff for app", ce.generateDiff(ctx, app, manifests)) if viper.GetBool("enable-conftest") { - grp.Go(wrap("validation policy", ce.validatePolicy(grpCtx, app))) + run("validation policy", ce.validatePolicy(ctx, app)) } - grp.Go(wrap("running pre-upgrade check", ce.runPreupgradeCheck(grpCtx, app, k8sVersion, formattedManifests))) + run("running pre-upgrade check", ce.runPreupgradeCheck(ctx, app, k8sVersion, formattedManifests)) - err = grp.Wait() - if err != nil { - telemetry.SetError(span, err, "running checks") - } + wg.Wait() - ce.vcsNote.AddToAppMessage(ctx, app, renderInfoFooter(time.Since(start), ce.repo.SHA)) + ce.vcsNote.SetFooter(start, ce.repo.SHA) - return err + commitStatus := ce.vcsNote.IsSuccess() + return commitStatus } -type checkFunction func() (string, error) +type checkFunction func() (pkg.CheckResult, error) -func (ce *CheckEvent) createWrapper(span trace.Span, grpCtx context.Context, app string) func(string, checkFunction) func() error { - return func(desc string, fn checkFunction) func() error { - return func() error { +const ( + errorCommentFormat = ` +:warning: **Error while %s** :warning: +` + "```" + ` +%v +` + "```" + ` + +Check kubechecks application logs for more information. +` +) + +func (ce *CheckEvent) createRunner(span trace.Span, grpCtx context.Context, app string, wg *sync.WaitGroup) func(string, checkFunction) { + return func(desc string, fn checkFunction) { + wg.Add(1) + + go func() { defer func() { + wg.Done() + if r := recover(); r != nil { + ce.logger.Error().Str("app", app).Str("check", desc).Msgf("panic while running check") + telemetry.SetError(span, fmt.Errorf("%v", r), desc) - ce.vcsNote.AddToAppMessage(grpCtx, app, fmt.Sprintf(errorCommentFormat, desc, r)) + result := pkg.CheckResult{State: pkg.StatePanic, Summary: desc, Details: fmt.Sprintf(errorCommentFormat, desc, r)} + ce.vcsNote.AddToAppMessage(grpCtx, app, result) } }() - s, err := fn() + ce.logger.Info().Str("app", app).Str("check", desc).Msgf("running check") + + cr, err := fn() if err != nil { telemetry.SetError(span, err, desc) - ce.vcsNote.AddToAppMessage(grpCtx, app, fmt.Sprintf(errorCommentFormat, desc, err)) - return errors.Wrapf(err, "error while %s", desc) + result := pkg.CheckResult{State: pkg.StateError, Summary: desc, Details: fmt.Sprintf(errorCommentFormat, desc, err)} + ce.vcsNote.AddToAppMessage(grpCtx, app, result) + return } - if s != "" { - ce.vcsNote.AddToAppMessage(grpCtx, app, s) - } + ce.vcsNote.AddToAppMessage(grpCtx, app, cr) - return nil - } + ce.logger.Info().Str("app", app).Str("check", desc).Str("result", cr.State.String()).Msgf("check done") + }() } } -func (ce *CheckEvent) runPreupgradeCheck(grpCtx context.Context, app string, k8sVersion string, formattedManifests []string) func() (string, error) { - return func() (string, error) { +func (ce *CheckEvent) runPreupgradeCheck(grpCtx context.Context, app string, k8sVersion string, formattedManifests []string) func() (pkg.CheckResult, error) { + return func() (pkg.CheckResult, error) { s, err := kubepug.CheckApp(grpCtx, app, k8sVersion, formattedManifests) if err != nil { - return "", err + return pkg.CheckResult{}, err } return s, nil } } -func (ce *CheckEvent) validatePolicy(ctx context.Context, app string) func() (string, error) { - return func() (string, error) { +func (ce *CheckEvent) validatePolicy(ctx context.Context, app string) func() (pkg.CheckResult, error) { + return func() (pkg.CheckResult, error) { argoApp, err := argo_client.GetArgoClient().GetApplicationByName(ctx, app) if err != nil { - return "", errors.Wrapf(err, "could not retrieve ArgoCD App data: %q", app) + return pkg.CheckResult{}, errors.Wrapf(err, "could not retrieve ArgoCD App data: %q", app) } - s, err := conftest.Conftest(ctx, argoApp, ce.TempWorkingDir) + cr, err := conftest.Conftest(ctx, argoApp, ce.TempWorkingDir) if err != nil { - return "", err + return pkg.CheckResult{}, err } - return s, nil + return cr, nil } } -func (ce *CheckEvent) generateDiff(ctx context.Context, app string, manifests []string) func() (string, error) { - return func() (string, error) { - s, rawDiff, err := diff.GetDiff(ctx, app, manifests) +func (ce *CheckEvent) generateDiff(ctx context.Context, app string, manifests []string) func() (pkg.CheckResult, error) { + return func() (pkg.CheckResult, error) { + cr, rawDiff, err := diff.GetDiff(ctx, app, manifests) if err != nil { - return "", err + return pkg.CheckResult{}, err } diff.AIDiffSummary(ctx, ce.vcsNote, app, manifests, rawDiff) - return s, nil + return cr, nil } } -func (ce *CheckEvent) validateSchemas(ctx context.Context, app, k8sVersion, tempRepoPath string, formattedManifests []string) func() (string, error) { - return func() (string, error) { - s, err := validate.ArgoCdAppValidate(ctx, app, k8sVersion, tempRepoPath, formattedManifests) +func (ce *CheckEvent) validateSchemas(ctx context.Context, app, k8sVersion, tempRepoPath string, formattedManifests []string) func() (pkg.CheckResult, error) { + return func() (pkg.CheckResult, error) { + cr, err := validate.ArgoCdAppValidate(ctx, app, k8sVersion, tempRepoPath, formattedManifests) if err != nil { - return "", err + return pkg.CheckResult{}, err } - return s, nil + return cr, nil } } // Creates a generic Note struct that we can write into across all worker threads -func (ce *CheckEvent) createNote(ctx context.Context) *vcs_clients.Message { +func (ce *CheckEvent) createNote(ctx context.Context) *pkg.Message { ctx, span := otel.Tracer("check").Start(ctx, "createNote") defer span.End() - var sb strings.Builder - _, _ = fmt.Fprintf(&sb, "# Kubechecks Report:\n") ce.logger.Info().Msgf("Creating note") - return ce.client.PostMessage(ctx, ce.repo, ce.repo.CheckID, sb.String()) + return ce.client.PostMessage(ctx, ce.repo, ce.repo.CheckID, "kubechecks running ... ") } // cleanupGetManifestsError takes an error as input and returns a simplified and more user-friendly error message. @@ -469,16 +472,3 @@ func (ce *CheckEvent) cleanupGetManifestsError(err error) string { return errStr } - -func renderInfoFooter(duration time.Duration, commitSHA string) string { - if viper.GetBool("show-debug-info") { - label := viper.GetString("label-filter") - envStr := "" - if label != "" { - envStr = fmt.Sprintf(", Env: %s", label) - } - return fmt.Sprintf("_Done: Pod: %s, Dur: %v, SHA: %s%s_\n", hostname, duration, pkg.GitCommit, envStr) - } else { - return fmt.Sprintf("_Done. CommitSHA: %s_\n", commitSHA) - } -} diff --git a/pkg/github_client/client.go b/pkg/github_client/client.go index 74f40a09..feb6b157 100644 --- a/pkg/github_client/client.go +++ b/pkg/github_client/client.go @@ -12,10 +12,10 @@ import ( "github.com/rs/zerolog/log" "github.com/shurcooL/githubv4" "github.com/spf13/viper" + "golang.org/x/oauth2" + "github.com/zapier/kubechecks/pkg" "github.com/zapier/kubechecks/pkg/repo" - "github.com/zapier/kubechecks/pkg/vcs_clients" - "golang.org/x/oauth2" ) var githubClient *Client @@ -27,7 +27,7 @@ type Client struct { *github.Client } -var _ vcs_clients.Client = new(Client) +var _ pkg.Client = new(Client) func GetGithubClient() (*Client, string) { once.Do(func() { @@ -116,11 +116,11 @@ func (c *Client) CreateRepo(_ context.Context, payload interface{}) (*repo.Repo, return buildRepoFromEvent(p), nil default: log.Info().Str("action", p.GetAction()).Msg("ignoring Github pull request event due to non commit based action") - return nil, vcs_clients.ErrInvalidType + return nil, pkg.ErrInvalidType } default: log.Error().Msg("invalid event provided to Github client") - return nil, vcs_clients.ErrInvalidType + return nil, pkg.ErrInvalidType } } @@ -171,13 +171,13 @@ func buildRepoFromEvent(event *github.PullRequestEvent) *repo.Repo { } } -func (c *Client) CommitStatus(ctx context.Context, repo *repo.Repo, status vcs_clients.CommitState) error { +func (c *Client) CommitStatus(ctx context.Context, repo *repo.Repo, status pkg.CommitState) error { log.Info().Str("repo", repo.Name).Str("sha", repo.SHA).Str("status", status.String()).Msg("setting Github commit status") repoStatus, _, err := c.Repositories.CreateStatus(ctx, repo.Owner, repo.Name, repo.SHA, &github.RepoStatus{ - State: github.String(status.String()), - Description: github.String(status.StateToDesc()), - ID: github.Int64(int64(repo.CheckID)), - Context: github.String("kubechecks"), + State: pkg.Pointer(status.String()), + Description: pkg.Pointer(status.String()), + ID: pkg.Pointer(int64(repo.CheckID)), + Context: pkg.Pointer("kubechecks"), }) if err != nil { log.Err(err).Msg("could not set Github commit status") @@ -200,7 +200,7 @@ func parseRepo(cloneUrl string) (string, string) { panic(cloneUrl) } -func (c *Client) GetHookByUrl(ctx context.Context, ownerAndRepoName, webhookUrl string) (*vcs_clients.WebHookConfig, error) { +func (c *Client) GetHookByUrl(ctx context.Context, ownerAndRepoName, webhookUrl string) (*pkg.WebHookConfig, error) { owner, repoName := parseRepo(ownerAndRepoName) items, _, err := c.Repositories.ListHooks(ctx, owner, repoName, nil) if err != nil { @@ -209,14 +209,14 @@ func (c *Client) GetHookByUrl(ctx context.Context, ownerAndRepoName, webhookUrl for _, item := range items { if item.URL != nil && *item.URL == webhookUrl { - return &vcs_clients.WebHookConfig{ + return &pkg.WebHookConfig{ Url: item.GetURL(), Events: item.Events, // TODO: translate GH specific event names to VCS agnostic }, nil } } - return nil, vcs_clients.ErrHookNotFound + return nil, pkg.ErrHookNotFound } func (c *Client) CreateHook(ctx context.Context, ownerAndRepoName, webhookUrl, webhookSecret string) error { diff --git a/pkg/github_client/message.go b/pkg/github_client/message.go index f0013d20..73e817ce 100644 --- a/pkg/github_client/message.go +++ b/pkg/github_client/message.go @@ -4,19 +4,19 @@ import ( "context" "fmt" "strings" - "sync" "github.com/google/go-github/v53/github" "github.com/rs/zerolog/log" "github.com/shurcooL/githubv4" "github.com/spf13/viper" + "go.opentelemetry.io/otel" + + "github.com/zapier/kubechecks/pkg" "github.com/zapier/kubechecks/pkg/repo" - "github.com/zapier/kubechecks/pkg/vcs_clients" "github.com/zapier/kubechecks/telemetry" - "go.opentelemetry.io/otel" ) -func (c *Client) PostMessage(ctx context.Context, repo *repo.Repo, prID int, msg string) *vcs_clients.Message { +func (c *Client) PostMessage(ctx context.Context, repo *repo.Repo, prID int, msg string) *pkg.Message { _, span := otel.Tracer("Kubechecks").Start(ctx, "PostMessageToMergeRequest") defer span.End() @@ -34,18 +34,10 @@ func (c *Client) PostMessage(ctx context.Context, repo *repo.Repo, prID int, msg log.Error().Err(err).Msg("could not post message to PR") } - return &vcs_clients.Message{ - Lock: sync.Mutex{}, - Name: repo.FullName, - CheckID: prID, - NoteID: int(*comment.ID), - Msg: msg, - Client: c, - Apps: make(map[string]string), - } + return pkg.NewMessage(repo.FullName, prID, int(*comment.ID)) } -func (c *Client) UpdateMessage(ctx context.Context, m *vcs_clients.Message, msg string) error { +func (c *Client) UpdateMessage(ctx context.Context, m *pkg.Message, msg string) error { _, span := otel.Tracer("Kubechecks").Start(ctx, "UpdateMessage") defer span.End() @@ -135,8 +127,8 @@ func (c *Client) TidyOutdatedComments(ctx context.Context, repo *repo.Repo) erro for { comments, resp, err := c.Issues.ListComments(ctx, repo.Owner, repo.Name, repo.CheckID, &github.IssueListCommentsOptions{ - Sort: github.String("created"), - Direction: github.String("asc"), + Sort: pkg.Pointer("created"), + Direction: pkg.Pointer("asc"), ListOptions: github.ListOptions{Page: nextPage}, }) if err != nil { diff --git a/pkg/gitlab_client/client.go b/pkg/gitlab_client/client.go index 7434ce20..4ebca3cb 100644 --- a/pkg/gitlab_client/client.go +++ b/pkg/gitlab_client/client.go @@ -13,9 +13,9 @@ import ( "github.com/spf13/viper" "github.com/whilp/git-urls" "github.com/xanzy/go-gitlab" + "github.com/zapier/kubechecks/pkg" "github.com/zapier/kubechecks/pkg/repo" - "github.com/zapier/kubechecks/pkg/vcs_clients" ) var gitlabClient *Client @@ -29,7 +29,7 @@ type Client struct { *gitlab.Client } -var _ vcs_clients.Client = new(Client) +var _ pkg.Client = new(Client) func GetGitlabClient() (*Client, string) { once.Do(func() { @@ -108,13 +108,13 @@ func (c *Client) CreateRepo(ctx context.Context, eventRequest interface{}) (*rep return buildRepoFromEvent(event), nil default: log.Trace().Msgf("Unhandled Action %s", event.ObjectAttributes.Action) - return nil, vcs_clients.ErrInvalidType + return nil, pkg.ErrInvalidType } default: log.Trace().Msgf("Unhandled Event: %T", event) - return nil, vcs_clients.ErrInvalidType + return nil, pkg.ErrInvalidType } - return nil, vcs_clients.ErrInvalidType + return nil, pkg.ErrInvalidType } func parseRepoName(url string) (string, error) { @@ -129,7 +129,7 @@ func parseRepoName(url string) (string, error) { return path, nil } -func (c *Client) GetHookByUrl(ctx context.Context, repoName, webhookUrl string) (*vcs_clients.WebHookConfig, error) { +func (c *Client) GetHookByUrl(ctx context.Context, repoName, webhookUrl string) (*pkg.WebHookConfig, error) { pid, err := parseRepoName(repoName) if err != nil { return nil, errors.Wrap(err, "failed to parse repo url") @@ -146,14 +146,14 @@ func (c *Client) GetHookByUrl(ctx context.Context, repoName, webhookUrl string) if hook.MergeRequestsEvents { events = append(events, string(gitlab.MergeRequestEventTargetType)) } - return &vcs_clients.WebHookConfig{ + return &pkg.WebHookConfig{ Url: hook.URL, Events: events, }, nil } } - return nil, vcs_clients.ErrHookNotFound + return nil, pkg.ErrHookNotFound } func (c *Client) CreateHook(ctx context.Context, repoName, webhookUrl, webhookSecret string) error { diff --git a/pkg/gitlab_client/message.go b/pkg/gitlab_client/message.go index f8f1a1f4..ec3696f2 100644 --- a/pkg/gitlab_client/message.go +++ b/pkg/gitlab_client/message.go @@ -4,40 +4,32 @@ import ( "context" "fmt" "strings" - "sync" "github.com/rs/zerolog/log" "github.com/spf13/viper" "github.com/xanzy/go-gitlab" + "go.opentelemetry.io/otel" + + "github.com/zapier/kubechecks/pkg" "github.com/zapier/kubechecks/pkg/repo" - "github.com/zapier/kubechecks/pkg/vcs_clients" "github.com/zapier/kubechecks/telemetry" - "go.opentelemetry.io/otel" ) -func (c *Client) PostMessage(ctx context.Context, repo *repo.Repo, mergeRequestID int, msg string) *vcs_clients.Message { +func (c *Client) PostMessage(ctx context.Context, repo *repo.Repo, mergeRequestID int, msg string) *pkg.Message { _, span := otel.Tracer("Kubechecks").Start(ctx, "PostMessageToMergeRequest") defer span.End() n, _, err := c.Notes.CreateMergeRequestNote( repo.FullName, mergeRequestID, &gitlab.CreateMergeRequestNoteOptions{ - Body: gitlab.String(msg), + Body: pkg.Pointer(msg), }) if err != nil { telemetry.SetError(span, err, "Create Merge Request Note") log.Error().Err(err).Msg("could not post message to MR") } - return &vcs_clients.Message{ - Lock: sync.Mutex{}, - Name: repo.FullName, - CheckID: mergeRequestID, - NoteID: n.ID, - Msg: msg, - Client: c, - Apps: make(map[string]string), - } + return pkg.NewMessage(repo.FullName, mergeRequestID, n.ID) } func (c *Client) hideOutdatedMessages(ctx context.Context, projectName string, mergeRequestID int, notes []*gitlab.Note) error { @@ -80,11 +72,11 @@ func (c *Client) hideOutdatedMessages(ctx context.Context, projectName string, m return nil } -func (c *Client) UpdateMessage(ctx context.Context, m *vcs_clients.Message, msg string) error { +func (c *Client) UpdateMessage(ctx context.Context, m *pkg.Message, msg string) error { log.Debug().Msgf("Updating message %d for %s", m.NoteID, m.Name) n, _, err := c.Notes.UpdateMergeRequestNote(m.Name, m.CheckID, m.NoteID, &gitlab.UpdateMergeRequestNoteOptions{ - Body: gitlab.String(msg), + Body: pkg.Pointer(msg), }) if err != nil { @@ -129,8 +121,8 @@ func (c *Client) TidyOutdatedComments(ctx context.Context, repo *repo.Repo) erro for { // list merge request notes notes, resp, err := c.Notes.ListMergeRequestNotes(repo.FullName, repo.CheckID, &gitlab.ListMergeRequestNotesOptions{ - Sort: gitlab.String("asc"), - OrderBy: gitlab.String("created_at"), + Sort: pkg.Pointer("asc"), + OrderBy: pkg.Pointer("created_at"), ListOptions: gitlab.ListOptions{ Page: nextPage, }, diff --git a/pkg/gitlab_client/pipeline.go b/pkg/gitlab_client/pipeline.go index 4cb14cc4..41e26bb4 100644 --- a/pkg/gitlab_client/pipeline.go +++ b/pkg/gitlab_client/pipeline.go @@ -3,11 +3,13 @@ package gitlab_client import ( "github.com/rs/zerolog/log" "github.com/xanzy/go-gitlab" + + "github.com/zapier/kubechecks/pkg" ) func (c *Client) GetPipelinesForCommit(projectName string, commitSHA string) ([]*gitlab.PipelineInfo, error) { pipelines, _, err := c.Pipelines.ListProjectPipelines(projectName, &gitlab.ListProjectPipelinesOptions{ - SHA: gitlab.String(commitSHA), + SHA: pkg.Pointer(commitSHA), }) if err != nil { log.Error().Err(err).Msg("gitlab client: could not get pipelines for commit") diff --git a/pkg/gitlab_client/project.go b/pkg/gitlab_client/project.go index 0e275916..67489b12 100644 --- a/pkg/gitlab_client/project.go +++ b/pkg/gitlab_client/project.go @@ -6,8 +6,10 @@ import ( "github.com/cenkalti/backoff/v4" "github.com/xanzy/go-gitlab" - "github.com/zapier/kubechecks/pkg/repo_config" "go.opentelemetry.io/otel" + + "github.com/zapier/kubechecks/pkg" + "github.com/zapier/kubechecks/pkg/repo_config" ) // GetProjectByIDorName gets a project by the given Project Name or ID @@ -31,7 +33,7 @@ func (c *Client) GetRepoConfigFile(ctx context.Context, projectId int, mergeReqI b, _, err := c.RepositoryFiles.GetRawFile( projectId, file, - &gitlab.GetRawFileOptions{Ref: gitlab.String("HEAD")}, + &gitlab.GetRawFileOptions{Ref: pkg.Pointer("HEAD")}, ) if err != nil { continue diff --git a/pkg/gitlab_client/status.go b/pkg/gitlab_client/status.go index e9526b1f..5a974662 100644 --- a/pkg/gitlab_client/status.go +++ b/pkg/gitlab_client/status.go @@ -3,24 +3,26 @@ package gitlab_client import ( "context" "errors" + "strconv" "time" "github.com/cenkalti/backoff/v4" "github.com/rs/zerolog/log" "github.com/xanzy/go-gitlab" + + "github.com/zapier/kubechecks/pkg" "github.com/zapier/kubechecks/pkg/repo" - "github.com/zapier/kubechecks/pkg/vcs_clients" ) const GitlabCommitStatusContext = "kubechecks" var errNoPipelineStatus = errors.New("nil pipeline status") -func (c *Client) CommitStatus(ctx context.Context, repo *repo.Repo, state vcs_clients.CommitState) error { +func (c *Client) CommitStatus(ctx context.Context, repo *repo.Repo, state pkg.CommitState) error { status := &gitlab.SetCommitStatusOptions{ - Name: gitlab.String(GitlabCommitStatusContext), - Context: gitlab.String(GitlabCommitStatusContext), - Description: gitlab.String(state.StateToDesc()), + Name: pkg.Pointer(GitlabCommitStatusContext), + Context: pkg.Pointer(GitlabCommitStatusContext), + Description: pkg.Pointer(state.String()), State: convertState(state), } // Get pipelineStatus so we can attach new status to existing pipeline. We @@ -53,17 +55,17 @@ func (c *Client) CommitStatus(ctx context.Context, repo *repo.Repo, state vcs_cl return nil } -func convertState(state vcs_clients.CommitState) gitlab.BuildStateValue { +func convertState(state pkg.CommitState) gitlab.BuildStateValue { switch state { - case vcs_clients.Pending: - return gitlab.Pending - case vcs_clients.Running: + case pkg.StateRunning: return gitlab.Running - case vcs_clients.Failure: + case pkg.StateFailure, pkg.StateError, pkg.StatePanic: return gitlab.Failed - case vcs_clients.Success: + case pkg.StateSuccess, pkg.StateWarning, pkg.StateNone: return gitlab.Success } + + log.Warn().Str("state", strconv.FormatUint(uint64(state), 10)).Msg("cannot convert to gitlab state") return gitlab.Failed } diff --git a/pkg/kubepug/kubepug.go b/pkg/kubepug/kubepug.go index 5c62ebc0..092627e4 100644 --- a/pkg/kubepug/kubepug.go +++ b/pkg/kubepug/kubepug.go @@ -7,27 +7,19 @@ import ( "os" "strings" - "github.com/zapier/kubechecks/pkg" - "go.opentelemetry.io/otel" - "github.com/masterminds/semver" "github.com/olekukonko/tablewriter" "github.com/rikatz/kubepug/lib" "github.com/rikatz/kubepug/pkg/results" "github.com/rs/zerolog/log" + "go.opentelemetry.io/otel" + + "github.com/zapier/kubechecks/pkg" ) const docLinkFmt = "[%s Deprecation Notes](https://kubernetes.io/docs/reference/using-api/deprecation-guide/#%s-v%d%d)" -const kubepugCommentFormat = ` -
Show kubepug report: %s - - > This provides a list of Kubernetes resources in this application that are either deprecated or deleted from the **next** version (%s) of Kubernetes. - -%s -
-` -func CheckApp(ctx context.Context, appName, targetKubernetesVersion string, manifests []string) (string, error) { +func CheckApp(ctx context.Context, appName, targetKubernetesVersion string, manifests []string) (pkg.CheckResult, error) { _, span := otel.Tracer("Kubechecks").Start(ctx, "KubePug") defer span.End() @@ -41,7 +33,7 @@ func CheckApp(ctx context.Context, appName, targetKubernetesVersion string, mani if err != nil { log.Error().Err(err).Msg("could not create temp directory to write manifests for kubepug check") //return "", err - return fmt.Sprintf("Error: %v", err), err + return pkg.CheckResult{}, err } defer os.RemoveAll(tempDir) @@ -52,7 +44,7 @@ func CheckApp(ctx context.Context, appName, targetKubernetesVersion string, mani nextVersion, err := nextKubernetesVersion(targetKubernetesVersion) if err != nil { - return fmt.Sprintf("Error: %v", err), err + return pkg.CheckResult{}, err } config := lib.Config{ K8sVersion: fmt.Sprintf("v%s", nextVersion.String()), @@ -65,7 +57,7 @@ func CheckApp(ctx context.Context, appName, targetKubernetesVersion string, mani result, err := kubepug.GetDeprecated() if err != nil { - return fmt.Sprintf("Error: %v", err), err + return pkg.CheckResult{}, err } if len(result.DeprecatedAPIs) > 0 || len(result.DeletedAPIs) > 0 { @@ -120,18 +112,26 @@ func CheckApp(ctx context.Context, appName, targetKubernetesVersion string, mani outputString = append(outputString, "No Deprecated or Deleted APIs found.") } - return fmt.Sprintf(kubepugCommentFormat, checkStatus(result), "`v"+nextVersion.String()+"`", strings.Join(outputString, "\n")), nil + return pkg.CheckResult{ + State: checkStatus(result), + Summary: "Show kubepug report:", + Details: fmt.Sprintf( + "> This provides a list of Kubernetes resources in this application that are either deprecated or deleted from the **next** version (v%s) of Kubernetes.\n\n%s", + nextVersion.String(), + strings.Join(outputString, "\n"), + ), + }, nil } -func checkStatus(result *results.Result) string { +func checkStatus(result *results.Result) pkg.CommitState { switch { case len(result.DeletedAPIs) > 0: // for now only ever a warning - return pkg.WarningString() + return pkg.StateWarning case len(result.DeprecatedAPIs) > 0: - return pkg.WarningString() + return pkg.StateWarning default: - return pkg.PassString() + return pkg.StateSuccess } } diff --git a/pkg/message.go b/pkg/message.go new file mode 100644 index 00000000..c708e2a3 --- /dev/null +++ b/pkg/message.go @@ -0,0 +1,165 @@ +package pkg + +import ( + "context" + "fmt" + "os" + "strings" + "sync" + "time" + + "github.com/spf13/viper" + "go.opentelemetry.io/otel" + "golang.org/x/exp/constraints" + "golang.org/x/exp/slices" +) + +type CheckResult struct { + State CommitState + Summary, Details string +} + +type AppResults struct { + results []CheckResult +} + +func (ar *AppResults) AddCheckResult(result CheckResult) { + ar.results = append(ar.results, result) +} + +func NewMessage(name string, prId, commentId int) *Message { + return &Message{ + Name: name, + CheckID: prId, + NoteID: commentId, + apps: make(map[string]*AppResults), + } +} + +// Message type that allows concurrent updates +// Has a reference to the owner/repo (ie zapier/kubechecks), +// the PR/MR id, and the actual messsage +type Message struct { + Name string + Owner string + CheckID int + NoteID int + + // Key = Appname, value = Results + apps map[string]*AppResults + footer string + lock sync.Mutex +} + +func (m *Message) IsSuccess() bool { + isSuccess := true + + for _, r := range m.apps { + for _, result := range r.results { + switch result.State { + case StateSuccess, StateWarning, StateNone: + isSuccess = false + } + } + } + + return isSuccess +} + +func (m *Message) AddNewApp(ctx context.Context, app string) { + _, span := otel.Tracer("Kubechecks").Start(ctx, "AddNewApp") + defer span.End() + m.lock.Lock() + defer m.lock.Unlock() + + m.apps[app] = new(AppResults) +} + +func (m *Message) AddToAppMessage(ctx context.Context, app string, result CheckResult) { + _, span := otel.Tracer("Kubechecks").Start(ctx, "AddToAppMessage") + defer span.End() + m.lock.Lock() + defer m.lock.Unlock() + + m.apps[app].AddCheckResult(result) +} + +var hostname = "" + +func init() { + hostname, _ = os.Hostname() +} + +func (m *Message) SetFooter(start time.Time, commitSha string) { + m.footer = buildFooter(start, commitSha) +} + +func (m *Message) PushComment(ctx context.Context, client Client) error { + return client.UpdateMessage(ctx, m, buildComment(ctx, m.apps)) +} + +func buildFooter(start time.Time, commitSHA string) string { + showDebug := viper.GetBool("show-debug-info") + if !showDebug { + return fmt.Sprintf("_Done. CommitSHA: %s_\n", commitSHA) + } + + label := viper.GetString("label-filter") + envStr := "" + if label != "" { + envStr = fmt.Sprintf(", Env: %s", label) + } + duration := time.Since(start) + + return fmt.Sprintf("_Done: Pod: %s, Dur: %v, SHA: %s%s_\n", hostname, duration, GitCommit, envStr) +} + +// Iterate the map of all apps in this message, building a final comment from their current state +func buildComment(ctx context.Context, apps map[string]*AppResults) string { + _, span := otel.Tracer("Kubechecks").Start(ctx, "buildComment") + defer span.End() + + names := getSortedKeys(apps) + + var sb strings.Builder + sb.WriteString("# Kubechecks Report\n") + + for _, appName := range names { + var checkStrings []string + results := apps[appName] + + appState := StateSuccess + for _, check := range results.results { + var summary string + if check.State == StateNone { + summary = check.Summary + } else { + summary = fmt.Sprintf("%s %s", check.Summary, check.State.String()) + } + + msg := fmt.Sprintf("
\n%s\n\n%s\n
", summary, check.Details) + checkStrings = append(checkStrings, msg) + appState = WorstState(appState, check.State) + } + + sb.WriteString("
\n") + sb.WriteString("\n\n") + sb.WriteString(fmt.Sprintf("## ArgoCD Application Checks: `%s` %s\n", appName, appState.Emoji())) + sb.WriteString("\n\n") + sb.WriteString(strings.Join(checkStrings, "---\n")) + sb.WriteString("
") + } + + return sb.String() +} + +func getSortedKeys[K constraints.Ordered, V any](m map[K]V) []K { + var keys []K + for key := range m { + keys = append(keys, key) + } + + slices.Sort(keys) + + return keys +} diff --git a/pkg/message_test.go b/pkg/message_test.go new file mode 100644 index 00000000..fc08a92a --- /dev/null +++ b/pkg/message_test.go @@ -0,0 +1,35 @@ +package pkg + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestBuildComment(t *testing.T) { + appResults := map[string]*AppResults{ + "myapp": { + results: []CheckResult{ + { + State: StateError, + Summary: "this failed bigly", + Details: "should add some important details here", + }, + }, + }, + } + comment := buildComment(context.TODO(), appResults) + assert.Equal(t, `# Kubechecks Report +
+ + +## ArgoCD Application Checks: `+"`myapp`"+` :heavy_exclamation_mark: + + +
+this failed bigly Error :heavy_exclamation_mark: + +should add some important details here +
`, comment) +} diff --git a/pkg/server/hook_handler.go b/pkg/server/hook_handler.go index b2c9825f..8e063ca1 100644 --- a/pkg/server/hook_handler.go +++ b/pkg/server/hook_handler.go @@ -10,20 +10,20 @@ import ( "github.com/labstack/echo/v4" "github.com/rs/zerolog/log" "github.com/spf13/viper" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + "github.com/zapier/kubechecks/pkg" "github.com/zapier/kubechecks/pkg/events" "github.com/zapier/kubechecks/pkg/github_client" "github.com/zapier/kubechecks/pkg/gitlab_client" "github.com/zapier/kubechecks/pkg/repo" - "github.com/zapier/kubechecks/pkg/vcs_clients" "github.com/zapier/kubechecks/telemetry" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" ) type VCSHookHandler struct { - client vcs_clients.Client + client pkg.Client tokenUser string cfg *pkg.ServerConfig // labelFilter is a string specifying the required label name to filter merge events by; if empty, all merge events will pass the filter. @@ -31,19 +31,19 @@ type VCSHookHandler struct { } var once sync.Once -var vcsClient vcs_clients.Client // Currently, only allow one client at a time +var vcsClient pkg.Client // Currently, only allow one client at a time var tokenUser string var ProjectHookPath = "/gitlab/project" // High level type representing the fields we care about from an arbitrary Git repository -func GetVCSClient() (vcs_clients.Client, string) { +func GetVCSClient() (pkg.Client, string) { once.Do(func() { vcsClient, tokenUser = createVCSClient() }) return vcsClient, tokenUser } -func createVCSClient() (vcs_clients.Client, string) { +func createVCSClient() (pkg.Client, string) { // Determine what client to use based on set config (default Gitlab) clientType := viper.GetString("vcs-type") // All hooks set up follow the convention /VCS_PROVIDER/project @@ -99,7 +99,7 @@ func (h *VCSHookHandler) groupHandler(c echo.Context) error { repo, err := h.client.CreateRepo(ctx, eventRequest) if err != nil { switch err { - case vcs_clients.ErrInvalidType: + case pkg.ErrInvalidType: log.Debug().Msg("Ignoring event, not a merge request") return c.String(http.StatusOK, "Skipped") default: diff --git a/pkg/server/server.go b/pkg/server/server.go index 9e6c2e7a..cb044b7d 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -12,10 +12,10 @@ import ( "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/spf13/viper" + "github.com/ziflex/lecho/v3" + "github.com/zapier/kubechecks/pkg" "github.com/zapier/kubechecks/pkg/argo_client" - "github.com/zapier/kubechecks/pkg/vcs_clients" - "github.com/ziflex/lecho/v3" ) const KubeChecksHooksPathPrefix = "/hooks" @@ -105,7 +105,7 @@ func (s *Server) ensureWebhooks() error { for _, repo := range s.cfg.GetVcsRepos() { wh, err := vcsClient.GetHookByUrl(ctx, repo, fullUrl) - if err != nil && err != vcs_clients.ErrHookNotFound { + if err != nil && !errors.Is(err, pkg.ErrHookNotFound) { log.Error().Err(err).Msgf("failed to get hook for %s:", repo) continue } diff --git a/pkg/utils.go b/pkg/utils.go index 3d542a84..fd77724e 100644 --- a/pkg/utils.go +++ b/pkg/utils.go @@ -5,29 +5,6 @@ var ( GitCommit = "" ) -func PassEmoji() string { - return " :white_check_mark: " -} -func PassString() string { - return " Passed" + PassEmoji() -} - -func WarningEmoji() string { - return " :warning: " -} - -func WarningString() string { - return " Warning" + WarningEmoji() -} - -func FailedEmoji() string { - return " :red_circle: " -} - -func FailedString() string { - return " Failed" + FailedEmoji() -} - func Pointer[T interface{}](item T) *T { return &item } diff --git a/pkg/validate/validate.go b/pkg/validate/validate.go index 7d058566..8c74b95d 100644 --- a/pkg/validate/validate.go +++ b/pkg/validate/validate.go @@ -19,16 +19,6 @@ import ( var reposCache = local.NewReposDirectory() -const kubeconformCommentFormat = ` -
Show kubeconform report: %s - ->Validated against Kubernetes Version: %s - -%s - -
-` - func getSchemaLocations(ctx context.Context, tempRepoPath string) []string { locations := []string{ // schemas included in kubechecks @@ -62,7 +52,7 @@ func getSchemaLocations(ctx context.Context, tempRepoPath string) []string { return locations } -func ArgoCdAppValidate(ctx context.Context, appName, targetKubernetesVersion, tempRepoPath string, appManifests []string) (string, error) { +func ArgoCdAppValidate(ctx context.Context, appName, targetKubernetesVersion, tempRepoPath string, appManifests []string) (pkg.CheckResult, error) { _, span := otel.Tracer("Kubechecks").Start(ctx, "ArgoCdAppValidate") defer span.End() @@ -94,7 +84,7 @@ func ArgoCdAppValidate(ctx context.Context, appName, targetKubernetesVersion, te v, err := validator.New(schemaLocations, vOpts) if err != nil { log.Error().Err(err).Msg("could not create kubeconform validator") - return "", fmt.Errorf("could not create kubeconform validator: %v", err) + return pkg.CheckResult{}, fmt.Errorf("could not create kubeconform validator: %v", err) } result := v.Validate("-", io.NopCloser(strings.NewReader(strings.Join(appManifests, "\n")))) var invalid, failedValidation bool @@ -118,12 +108,18 @@ func ArgoCdAppValidate(ctx context.Context, appName, targetKubernetesVersion, te outputString = append(outputString, fmt.Sprintf(" * :white_check_mark: Passed: %s", sig)) } } - summary := pkg.PassString() + + var cr pkg.CheckResult if invalid { - summary = pkg.WarningString() + cr.State = pkg.StateWarning } else if failedValidation { - summary = pkg.FailedString() + cr.State = pkg.StateFailure + } else { + cr.State = pkg.StateSuccess } - return fmt.Sprintf(kubeconformCommentFormat, summary, targetKubernetesVersion, strings.Join(outputString, "\n")), nil + cr.Summary = "Show kubeconform report:" + cr.Details = fmt.Sprintf(">Validated against Kubernetes Version: %s\n\n%s", targetKubernetesVersion, strings.Join(outputString, "\n")) + + return cr, nil } diff --git a/pkg/vcs_clients/message.go b/pkg/vcs_clients/message.go deleted file mode 100644 index 86c66a10..00000000 --- a/pkg/vcs_clients/message.go +++ /dev/null @@ -1,102 +0,0 @@ -package vcs_clients - -import ( - "context" - "fmt" - "regexp" - "sort" - "strings" - "sync" - - "github.com/zapier/kubechecks/pkg" - "go.opentelemetry.io/otel" -) - -const ( - appFormat = `
- -## ArgoCD Application Checks:` + "`%s` %s" + - ` - -%s -
-` -) - -// Used to test messages quickly if we have to update internal emoji -var summaryEmojiRegex = regexp.MustCompile(pkg.FailedEmoji() + "|" + pkg.WarningEmoji()) - -// Message type that allows concurrent updates -// Has a reference to the owner/repo (ie zapier/kubechecks), -// the PR/MR id, and the actual messsage -type Message struct { - Lock sync.Mutex - Name string - Owner string - CheckID int - NoteID int - Msg string - // Key = Appname, value = Msg - Apps map[string]string - Client Client -} - -func (m *Message) AddToMessage(ctx context.Context, msg string) { - _, span := otel.Tracer("Kubechecks").Start(ctx, "AddToMessage") - defer span.End() - m.Lock.Lock() - defer m.Lock.Unlock() - - m.Msg = fmt.Sprintf("%s \n\n---\n\n%s", m.Msg, msg) - m.Client.UpdateMessage(ctx, m, m.Msg) - -} - -func (m *Message) AddNewApp(ctx context.Context, app string) { - _, span := otel.Tracer("Kubechecks").Start(ctx, "AddNewApp") - defer span.End() - m.Lock.Lock() - defer m.Lock.Unlock() - - m.Apps[app] = "" - - m.Client.UpdateMessage(ctx, m, m.buildComment(ctx)) -} - -func (m *Message) AddToAppMessage(ctx context.Context, app string, msg string) { - _, span := otel.Tracer("Kubechecks").Start(ctx, "AddToAppMessage") - defer span.End() - m.Lock.Lock() - defer m.Lock.Unlock() - - m.Apps[app] = fmt.Sprintf("%s \n\n---\n\n%s", m.Apps[app], msg) - m.Client.UpdateMessage(ctx, m, m.buildComment(ctx)) -} - -// Iterate the map of all apps in this message, building a final comment from their current state -func (m *Message) buildComment(ctx context.Context) string { - _, span := otel.Tracer("Kubechecks").Start(ctx, "buildComment") - defer span.End() - - var names []string - for name := range m.Apps { - names = append(names, name) - } - sort.Strings(names) - - var sb strings.Builder - fmt.Fprintf(&sb, "# Kubechecks Report\n") - // m.Msg = fmt.Sprintf("%s \n\n---\n\n%s", m.Msg, msg) - for _, name := range names { - msg := m.Apps[name] - appEmoji := pkg.PassEmoji() - - // Test the message for failures, since we'll be showing this at the top - if summaryEmojiRegex.MatchString(msg) { - appEmoji = pkg.FailedEmoji() - } - - fmt.Fprintf(&sb, appFormat, name, appEmoji, msg) - } - return sb.String() -} diff --git a/telemetry/telemetry.go b/telemetry/telemetry.go index b01f2a0d..0299559c 100644 --- a/telemetry/telemetry.go +++ b/telemetry/telemetry.go @@ -19,8 +19,6 @@ import ( semconv "go.opentelemetry.io/otel/semconv/v1.4.0" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - - "github.com/zapier/kubechecks/pkg" ) const DefaultMetricInterval = 2 @@ -54,7 +52,7 @@ func (ot *OperatorTelemetry) StartMetricCollectors() error { return nil } -func Init(ctx context.Context, serviceName string, otelEnabled bool, otelHost string, otelPort string) (*OperatorTelemetry, error) { +func Init(ctx context.Context, serviceName, gitTag, gitCommit string, otelEnabled bool, otelHost, otelPort string) (*OperatorTelemetry, error) { log.Info().Msg("Initializing telemetry") bt := &OperatorTelemetry{ BaseTelemetry: &BaseTelemetry{ @@ -64,8 +62,8 @@ func Init(ctx context.Context, serviceName string, otelEnabled bool, otelHost st res, err := resource.New(ctx, resource.WithAttributes( semconv.ServiceNameKey.String(serviceName), - semconv.ServiceVersionKey.String(pkg.GitTag), - attribute.String("SHA", pkg.GitCommit), + semconv.ServiceVersionKey.String(gitTag), + attribute.String("SHA", gitCommit), ), ) if err != nil {