diff --git a/.github/semantic.yml b/.github/semantic.yml index 3e5f39a0e..6524a1572 100644 --- a/.github/semantic.yml +++ b/.github/semantic.yml @@ -1,4 +1,4 @@ -ititleOnly: true +titleOnly: true types: - fix diff --git a/.gitignore b/.gitignore index 4028d3ad1..3bba0a68f 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,13 @@ .vscode .env /cmd/external-app/devtron-ea + +#binaries +authenticator/authenticator +chart-sync/chart-sync +ci-runner/cirunner +git-sensor/git-sensor +kubelink/kubelink +kubewatch/kubewatch +lens/lens + diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6c69efe2e..000000000 --- a/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -language: go - -sudo: required - -go: - - 1.12.x - -branches: - only: - - master - -install: - - go get -u github.com/golang/dep/cmd/dep - - dep ensure \ No newline at end of file diff --git a/authenticator/client/oidcClient.go b/authenticator/client/oidcClient.go index 4c65a6ae9..b7dcba2f9 100644 --- a/authenticator/client/oidcClient.go +++ b/authenticator/client/oidcClient.go @@ -26,6 +26,7 @@ import ( "net/http" "net/url" "path" + "sync" "time" ) @@ -65,8 +66,8 @@ func getOidcClient(dexServerAddress string, settings *oidc.Settings, userVerifie }, } dexProxy := oidc.NewDexHTTPReverseProxy(dexServerAddress, dexClient.Transport) - cahecStore := &oidc.Cache{OidcState: map[string]*oidc.OIDCState{}} - oidcClient, err := oidc.NewClientApp(settings, cahecStore, "/", userVerifier, RedirectUrlSanitiser) + cacheStore := &oidc.Cache{OidcState: sync.Map{}} + oidcClient, err := oidc.NewClientApp(settings, cacheStore, "/", userVerifier, RedirectUrlSanitiser) if err != nil { return nil, nil, err } diff --git a/authenticator/oidc/oidc.go b/authenticator/oidc/oidc.go index 4ffdeb62f..a44e53c2c 100644 --- a/authenticator/oidc/oidc.go +++ b/authenticator/oidc/oidc.go @@ -32,6 +32,7 @@ import ( "path" "regexp" "strings" + "sync" "time" gooidc "github.com/coreos/go-oidc/v3/oidc" @@ -69,16 +70,23 @@ type OIDCStateStorage interface { } type Cache struct { - OidcState map[string]*OIDCState + OidcState sync.Map } func (c *Cache) GetOIDCState(key string) (*OIDCState, error) { - state := c.OidcState[key] + value, exists := c.OidcState.Load(key) + if !exists { + return nil, ErrCacheMiss + } + state, ok := value.(*OIDCState) + if !ok || state == nil { + return nil, ErrInvalidState + } return state, nil } func (c *Cache) SetOIDCState(key string, state *OIDCState) error { - c.OidcState[key] = state + c.OidcState.Store(key, state) return nil } @@ -287,12 +295,15 @@ func (a *ClientApp) generateAppState(returnURL string) string { } var ErrCacheMiss = errors.New("cache: key is missing") +var ErrInvalidState = errors.New("invalid app state") func (a *ClientApp) verifyAppState(state string) (*OIDCState, error) { res, err := a.cache.GetOIDCState(state) if err != nil { - if err == ErrCacheMiss { + if errors.Is(err, ErrCacheMiss) { return nil, fmt.Errorf("unknown app state %s", state) + } else if errors.Is(err, ErrInvalidState) { + return nil, fmt.Errorf("invalid app state %s", state) } else { return nil, fmt.Errorf("failed to verify app state %s: %v", state, err) } diff --git a/chart-sync/go.mod b/chart-sync/go.mod index 203914b11..d9a3f5632 100644 --- a/chart-sync/go.mod +++ b/chart-sync/go.mod @@ -4,11 +4,14 @@ go 1.22.4 toolchain go1.22.6 -replace helm.sh/helm/v3 v3.14.3 => github.com/devtron-labs/helm/v3 v3.14.1-0.20240401080259-90238cf69e42 +replace ( + github.com/devtron-labs/common-lib => github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da + helm.sh/helm/v3 v3.14.3 => github.com/devtron-labs/helm/v3 v3.14.1-0.20240401080259-90238cf69e42 +) require ( github.com/caarlos0/env v3.5.0+incompatible - github.com/devtron-labs/common-lib v0.18.0 + github.com/devtron-labs/common-lib v0.0.0 github.com/ghodss/yaml v1.0.0 github.com/go-pg/pg v6.15.1+incompatible github.com/google/wire v0.6.0 @@ -90,7 +93,7 @@ require ( go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/goleak v1.2.1 // indirect - go.uber.org/multierr v1.6.0 // indirect + go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.25.0 // indirect golang.org/x/net v0.27.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect @@ -105,10 +108,10 @@ require ( 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/api v0.29.0 // indirect - k8s.io/apimachinery v0.29.0 // indirect + k8s.io/api v0.29.7 // indirect + k8s.io/apimachinery v0.29.7 // indirect k8s.io/cli-runtime v0.29.0 // indirect - k8s.io/client-go v0.29.0 // indirect + k8s.io/client-go v0.29.7 // indirect k8s.io/klog/v2 v2.110.1 // indirect k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect diff --git a/chart-sync/go.sum b/chart-sync/go.sum index e7b5aaf83..1f85b6874 100644 --- a/chart-sync/go.sum +++ b/chart-sync/go.sum @@ -54,8 +54,8 @@ github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr 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/devtron-labs/common-lib v0.18.0 h1:3sjGGxzugZEtFJ1yevG+KBFlnOFmVqjYaIJdgWYG+zc= -github.com/devtron-labs/common-lib v0.18.0/go.mod h1:rAY9Xd6iz+OqNQ3nO3reVHapAVr1N6Osf4Irdc0A08Q= +github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da h1:vC6SMz6BM1doN+ZBGiDGyERJ/LphFQi5+Ab/YQkNJVo= +github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da/go.mod h1:KpKnF4OSpQNDJmb4wVZq3Za88ePBw4xec2GOAGRm5UQ= github.com/devtron-labs/helm/v3 v3.14.1-0.20240401080259-90238cf69e42 h1:pJmK44QaSztOiZe0iQHNf0sdy5KwkAeceydyhOG4RaY= github.com/devtron-labs/helm/v3 v3.14.1-0.20240401080259-90238cf69e42/go.mod h1:v6myVbyseSBJTzhmeE39UcPLNv6cQK6qss3dvgAySaE= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= @@ -360,8 +360,9 @@ go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -547,14 +548,14 @@ gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= 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.0 h1:NiCdQMY1QOp1H8lfRyeEf8eOwV6+0xA6XEE44ohDX2A= -k8s.io/api v0.29.0/go.mod h1:sdVmXoz2Bo/cb77Pxi71IPTSErEW32xa4aXwKH7gfBA= -k8s.io/apimachinery v0.29.0 h1:+ACVktwyicPz0oc6MTMLwa2Pw3ouLAfAon1wPLtG48o= -k8s.io/apimachinery v0.29.0/go.mod h1:eVBxQ/cwiJxH58eK/jd/vAk4mrxmVlnpBH5J2GbMeis= +k8s.io/api v0.29.7 h1:Q2/thp7YYESgy0MGzxT9RvA/6doLJHBXSFH8GGLxSbc= +k8s.io/api v0.29.7/go.mod h1:mPimdbyuIjwoLtBEVIGVUYb4BKOE+44XHt/n4IqKsLA= +k8s.io/apimachinery v0.29.7 h1:ICXzya58Q7hyEEfnTrbmdfX1n1schSepX2KUfC2/ykc= +k8s.io/apimachinery v0.29.7/go.mod h1:i3FJVwhvSp/6n8Fl4K97PJEP8C+MM+aoDq4+ZJBf70Y= k8s.io/cli-runtime v0.29.0 h1:q2kC3cex4rOBLfPOnMSzV2BIrrQlx97gxHJs21KxKS4= k8s.io/cli-runtime v0.29.0/go.mod h1:VKudXp3X7wR45L+nER85YUzOQIru28HQpXr0mTdeCrk= -k8s.io/client-go v0.29.0 h1:KmlDtFcrdUzOYrBhXHgKw5ycWzc3ryPX5mQe0SkG3y8= -k8s.io/client-go v0.29.0/go.mod h1:yLkXH4HKMAywcrD82KMSmfYg2DlE8mepPR4JGSo5n38= +k8s.io/client-go v0.29.7 h1:vTtiFrGBKlcBhxaeZC4eDrqui1e108nsTyue/KU63IY= +k8s.io/client-go v0.29.7/go.mod h1:69BvVqdRozgR/9TP45u/oO0tfrdbP+I8RqrcCJQshzg= 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= diff --git a/chart-sync/vendor/github.com/devtron-labs/common-lib/LICENSE b/chart-sync/vendor/github.com/devtron-labs/common-lib/LICENSE index 261eeb9e9..57bc88a15 100644 --- a/chart-sync/vendor/github.com/devtron-labs/common-lib/LICENSE +++ b/chart-sync/vendor/github.com/devtron-labs/common-lib/LICENSE @@ -199,3 +199,4 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + diff --git a/chart-sync/vendor/go.uber.org/multierr/.travis.yml b/chart-sync/vendor/go.uber.org/multierr/.travis.yml deleted file mode 100644 index 8636ab42a..000000000 --- a/chart-sync/vendor/go.uber.org/multierr/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -sudo: false -language: go -go_import_path: go.uber.org/multierr - -env: - global: - - GO111MODULE=on - -go: - - oldstable - - stable - -before_install: -- go version - -script: -- | - set -e - make lint - make cover - -after_success: -- bash <(curl -s https://codecov.io/bash) diff --git a/chart-sync/vendor/go.uber.org/multierr/CHANGELOG.md b/chart-sync/vendor/go.uber.org/multierr/CHANGELOG.md index 6f1db9ef4..f8177b978 100644 --- a/chart-sync/vendor/go.uber.org/multierr/CHANGELOG.md +++ b/chart-sync/vendor/go.uber.org/multierr/CHANGELOG.md @@ -1,6 +1,41 @@ Releases ======== +v1.11.0 (2023-03-28) +==================== +- `Errors` now supports any error that implements multiple-error + interface. +- Add `Every` function to allow checking if all errors in the chain + satisfies `errors.Is` against the target error. + +v1.10.0 (2023-03-08) +==================== + +- Comply with Go 1.20's multiple-error interface. +- Drop Go 1.18 support. + Per the support policy, only Go 1.19 and 1.20 are supported now. +- Drop all non-test external dependencies. + +v1.9.0 (2022-12-12) +=================== + +- Add `AppendFunc` that allow passsing functions to similar to + `AppendInvoke`. + +- Bump up yaml.v3 dependency to 3.0.1. + +v1.8.0 (2022-02-28) +=================== + +- `Combine`: perform zero allocations when there are no errors. + + +v1.7.0 (2021-05-06) +=================== + +- Add `AppendInvoke` to append into errors from `defer` blocks. + + v1.6.0 (2020-09-14) =================== diff --git a/chart-sync/vendor/go.uber.org/multierr/LICENSE.txt b/chart-sync/vendor/go.uber.org/multierr/LICENSE.txt index 858e02475..413e30f7c 100644 --- a/chart-sync/vendor/go.uber.org/multierr/LICENSE.txt +++ b/chart-sync/vendor/go.uber.org/multierr/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2017 Uber Technologies, Inc. +Copyright (c) 2017-2021 Uber Technologies, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/chart-sync/vendor/go.uber.org/multierr/Makefile b/chart-sync/vendor/go.uber.org/multierr/Makefile index 316004400..dcb6fe723 100644 --- a/chart-sync/vendor/go.uber.org/multierr/Makefile +++ b/chart-sync/vendor/go.uber.org/multierr/Makefile @@ -34,9 +34,5 @@ lint: gofmt golint staticcheck .PHONY: cover cover: - go test -coverprofile=cover.out -coverpkg=./... -v ./... + go test -race -coverprofile=cover.out -coverpkg=./... -v ./... go tool cover -html=cover.out -o cover.html - -update-license: - @cd tools && go install go.uber.org/tools/update-license - @$(GOBIN)/update-license $(GO_FILES) diff --git a/chart-sync/vendor/go.uber.org/multierr/README.md b/chart-sync/vendor/go.uber.org/multierr/README.md index 751bd65e5..5ab6ac40f 100644 --- a/chart-sync/vendor/go.uber.org/multierr/README.md +++ b/chart-sync/vendor/go.uber.org/multierr/README.md @@ -2,9 +2,29 @@ `multierr` allows combining one or more Go `error`s together. +## Features + +- **Idiomatic**: + multierr follows best practices in Go, and keeps your code idiomatic. + - It keeps the underlying error type hidden, + allowing you to deal in `error` values exclusively. + - It provides APIs to safely append into an error from a `defer` statement. +- **Performant**: + multierr is optimized for performance: + - It avoids allocations where possible. + - It utilizes slice resizing semantics to optimize common cases + like appending into the same error object from a loop. +- **Interoperable**: + multierr interoperates with the Go standard library's error APIs seamlessly: + - The `errors.Is` and `errors.As` functions *just work*. +- **Lightweight**: + multierr comes with virtually no dependencies. + ## Installation - go get -u go.uber.org/multierr +```bash +go get -u go.uber.org/multierr@latest +``` ## Status @@ -15,9 +35,9 @@ Stable: No breaking changes will be made before 2.0. Released under the [MIT License]. [MIT License]: LICENSE.txt -[doc-img]: https://godoc.org/go.uber.org/multierr?status.svg -[doc]: https://godoc.org/go.uber.org/multierr -[ci-img]: https://travis-ci.com/uber-go/multierr.svg?branch=master +[doc-img]: https://pkg.go.dev/badge/go.uber.org/multierr +[doc]: https://pkg.go.dev/go.uber.org/multierr +[ci-img]: https://github.com/uber-go/multierr/actions/workflows/go.yml/badge.svg [cov-img]: https://codecov.io/gh/uber-go/multierr/branch/master/graph/badge.svg -[ci]: https://travis-ci.com/uber-go/multierr +[ci]: https://github.com/uber-go/multierr/actions/workflows/go.yml [cov]: https://codecov.io/gh/uber-go/multierr diff --git a/chart-sync/vendor/go.uber.org/multierr/error.go b/chart-sync/vendor/go.uber.org/multierr/error.go index 5c9b67d53..3a828b2df 100644 --- a/chart-sync/vendor/go.uber.org/multierr/error.go +++ b/chart-sync/vendor/go.uber.org/multierr/error.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Uber Technologies, Inc. +// Copyright (c) 2017-2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -20,54 +20,109 @@ // Package multierr allows combining one or more errors together. // -// Overview +// # Overview // // Errors can be combined with the use of the Combine function. // -// multierr.Combine( -// reader.Close(), -// writer.Close(), -// conn.Close(), -// ) +// multierr.Combine( +// reader.Close(), +// writer.Close(), +// conn.Close(), +// ) // // If only two errors are being combined, the Append function may be used // instead. // -// err = multierr.Append(reader.Close(), writer.Close()) -// -// This makes it possible to record resource cleanup failures from deferred -// blocks with the help of named return values. -// -// func sendRequest(req Request) (err error) { -// conn, err := openConnection() -// if err != nil { -// return err -// } -// defer func() { -// err = multierr.Append(err, conn.Close()) -// }() -// // ... -// } +// err = multierr.Append(reader.Close(), writer.Close()) // // The underlying list of errors for a returned error object may be retrieved // with the Errors function. // -// errors := multierr.Errors(err) -// if len(errors) > 0 { -// fmt.Println("The following errors occurred:", errors) -// } +// errors := multierr.Errors(err) +// if len(errors) > 0 { +// fmt.Println("The following errors occurred:", errors) +// } +// +// # Appending from a loop +// +// You sometimes need to append into an error from a loop. +// +// var err error +// for _, item := range items { +// err = multierr.Append(err, process(item)) +// } +// +// Cases like this may require knowledge of whether an individual instance +// failed. This usually requires introduction of a new variable. +// +// var err error +// for _, item := range items { +// if perr := process(item); perr != nil { +// log.Warn("skipping item", item) +// err = multierr.Append(err, perr) +// } +// } +// +// multierr includes AppendInto to simplify cases like this. +// +// var err error +// for _, item := range items { +// if multierr.AppendInto(&err, process(item)) { +// log.Warn("skipping item", item) +// } +// } +// +// This will append the error into the err variable, and return true if that +// individual error was non-nil. // -// Advanced Usage +// See [AppendInto] for more information. +// +// # Deferred Functions +// +// Go makes it possible to modify the return value of a function in a defer +// block if the function was using named returns. This makes it possible to +// record resource cleanup failures from deferred blocks. +// +// func sendRequest(req Request) (err error) { +// conn, err := openConnection() +// if err != nil { +// return err +// } +// defer func() { +// err = multierr.Append(err, conn.Close()) +// }() +// // ... +// } +// +// multierr provides the Invoker type and AppendInvoke function to make cases +// like the above simpler and obviate the need for a closure. The following is +// roughly equivalent to the example above. +// +// func sendRequest(req Request) (err error) { +// conn, err := openConnection() +// if err != nil { +// return err +// } +// defer multierr.AppendInvoke(&err, multierr.Close(conn)) +// // ... +// } +// +// See [AppendInvoke] and [Invoker] for more information. +// +// NOTE: If you're modifying an error from inside a defer, you MUST use a named +// return value for that function. +// +// # Advanced Usage // // Errors returned by Combine and Append MAY implement the following // interface. // -// type errorGroup interface { -// // Returns a slice containing the underlying list of errors. -// // -// // This slice MUST NOT be modified by the caller. -// Errors() []error -// } +// type errorGroup interface { +// // Returns a slice containing the underlying list of errors. +// // +// // This slice MUST NOT be modified by the caller. +// Errors() []error +// } // // Note that if you need access to list of errors behind a multierr error, you // should prefer using the Errors function. That said, if you need cheap @@ -76,23 +131,23 @@ // because errors returned by Combine and Append are not guaranteed to // implement this interface. // -// var errors []error -// group, ok := err.(errorGroup) -// if ok { -// errors = group.Errors() -// } else { -// errors = []error{err} -// } +// var errors []error +// group, ok := err.(errorGroup) +// if ok { +// errors = group.Errors() +// } else { +// errors = []error{err} +// } package multierr // import "go.uber.org/multierr" import ( "bytes" + "errors" "fmt" "io" "strings" "sync" - - "go.uber.org/atomic" + "sync/atomic" ) var ( @@ -132,34 +187,15 @@ type errorGroup interface { // Errors returns a slice containing zero or more errors that the supplied // error is composed of. If the error is nil, a nil slice is returned. // -// err := multierr.Append(r.Close(), w.Close()) -// errors := multierr.Errors(err) +// err := multierr.Append(r.Close(), w.Close()) +// errors := multierr.Errors(err) // // If the error is not composed of other errors, the returned slice contains // just the error that was passed in. // // Callers of this function are free to modify the returned slice. func Errors(err error) []error { - if err == nil { - return nil - } - - // Note that we're casting to multiError, not errorGroup. Our contract is - // that returned errors MAY implement errorGroup. Errors, however, only - // has special behavior for multierr-specific error objects. - // - // This behavior can be expanded in the future but I think it's prudent to - // start with as little as possible in terms of contract and possibility - // of misuse. - eg, ok := err.(*multiError) - if !ok { - return []error{err} - } - - errors := eg.Errors() - result := make([]error, len(errors)) - copy(result, errors) - return result + return extractErrors(err) } // multiError is an error that holds one or more errors. @@ -174,8 +210,6 @@ type multiError struct { errors []error } -var _ errorGroup = (*multiError)(nil) - // Errors returns the list of underlying errors. // // This slice MUST NOT be modified. @@ -201,6 +235,17 @@ func (merr *multiError) Error() string { return result } +// Every compares every error in the given err against the given target error +// using [errors.Is], and returns true only if every comparison returned true. +func Every(err error, target error) bool { + for _, e := range extractErrors(err) { + if !errors.Is(e, target) { + return false + } + } + return true +} + func (merr *multiError) Format(f fmt.State, c rune) { if c == 'v' && f.Flag('+') { merr.writeMultiline(f) @@ -292,6 +337,14 @@ func inspect(errors []error) (res inspectResult) { // fromSlice converts the given list of errors into a single error. func fromSlice(errors []error) error { + // Don't pay to inspect small slices. + switch len(errors) { + case 0: + return nil + case 1: + return errors[0] + } + res := inspect(errors) switch res.Count { case 0: @@ -301,8 +354,12 @@ func fromSlice(errors []error) error { return errors[res.FirstErrorIdx] case len(errors): if !res.ContainsMultiError { - // already flat - return &multiError{errors: errors} + // Error list is flat. Make a copy of it + // Otherwise "errors" escapes to the heap + // unconditionally for all other cases. + // This lets us optimize for the "no errors" case. + out := append(([]error)(nil), errors...) + return &multiError{errors: out} } } @@ -327,32 +384,32 @@ func fromSlice(errors []error) error { // If zero arguments were passed or if all items are nil, a nil error is // returned. // -// Combine(nil, nil) // == nil +// Combine(nil, nil) // == nil // // If only a single error was passed, it is returned as-is. // -// Combine(err) // == err +// Combine(err) // == err // // Combine skips over nil arguments so this function may be used to combine // together errors from operations that fail independently of each other. // -// multierr.Combine( -// reader.Close(), -// writer.Close(), -// pipe.Close(), -// ) +// multierr.Combine( +// reader.Close(), +// writer.Close(), +// pipe.Close(), +// ) // // If any of the passed errors is a multierr error, it will be flattened along // with the other errors. // -// multierr.Combine(multierr.Combine(err1, err2), err3) -// // is the same as -// multierr.Combine(err1, err2, err3) +// multierr.Combine(multierr.Combine(err1, err2), err3) +// // is the same as +// multierr.Combine(err1, err2, err3) // // The returned error formats into a readable multi-line error message if // formatted with %+v. // -// fmt.Sprintf("%+v", multierr.Combine(err1, err2)) +// fmt.Sprintf("%+v", multierr.Combine(err1, err2)) func Combine(errors ...error) error { return fromSlice(errors) } @@ -362,16 +419,19 @@ func Combine(errors ...error) error { // This function is a specialization of Combine for the common case where // there are only two errors. // -// err = multierr.Append(reader.Close(), writer.Close()) +// err = multierr.Append(reader.Close(), writer.Close()) // // The following pattern may also be used to record failure of deferred // operations without losing information about the original error. // -// func doSomething(..) (err error) { -// f := acquireResource() -// defer func() { -// err = multierr.Append(err, f.Close()) -// }() +// func doSomething(..) (err error) { +// f := acquireResource() +// defer func() { +// err = multierr.Append(err, f.Close()) +// }() +// +// Note that the variable MUST be a named return to append an error to it from +// the defer statement. See also [AppendInvoke]. func Append(left error, right error) error { switch { case left == nil: @@ -401,37 +461,37 @@ func Append(left error, right error) error { // AppendInto appends an error into the destination of an error pointer and // returns whether the error being appended was non-nil. // -// var err error -// multierr.AppendInto(&err, r.Close()) -// multierr.AppendInto(&err, w.Close()) +// var err error +// multierr.AppendInto(&err, r.Close()) +// multierr.AppendInto(&err, w.Close()) // // The above is equivalent to, // -// err := multierr.Append(r.Close(), w.Close()) +// err := multierr.Append(r.Close(), w.Close()) // // As AppendInto reports whether the provided error was non-nil, it may be // used to build a multierr error in a loop more ergonomically. For example: // -// var err error -// for line := range lines { -// var item Item -// if multierr.AppendInto(&err, parse(line, &item)) { -// continue -// } -// items = append(items, item) -// } -// -// Compare this with a verison that relies solely on Append: -// -// var err error -// for line := range lines { -// var item Item -// if parseErr := parse(line, &item); parseErr != nil { -// err = multierr.Append(err, parseErr) -// continue -// } -// items = append(items, item) -// } +// var err error +// for line := range lines { +// var item Item +// if multierr.AppendInto(&err, parse(line, &item)) { +// continue +// } +// items = append(items, item) +// } +// +// Compare this with a version that relies solely on Append: +// +// var err error +// for line := range lines { +// var item Item +// if parseErr := parse(line, &item); parseErr != nil { +// err = multierr.Append(err, parseErr) +// continue +// } +// items = append(items, item) +// } func AppendInto(into *error, err error) (errored bool) { if into == nil { // We panic if 'into' is nil. This is not documented above @@ -447,3 +507,140 @@ func AppendInto(into *error, err error) (errored bool) { *into = Append(*into, err) return true } + +// Invoker is an operation that may fail with an error. Use it with +// AppendInvoke to append the result of calling the function into an error. +// This allows you to conveniently defer capture of failing operations. +// +// See also, [Close] and [Invoke]. +type Invoker interface { + Invoke() error +} + +// Invoke wraps a function which may fail with an error to match the Invoker +// interface. Use it to supply functions matching this signature to +// AppendInvoke. +// +// For example, +// +// func processReader(r io.Reader) (err error) { +// scanner := bufio.NewScanner(r) +// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err)) +// for scanner.Scan() { +// // ... +// } +// // ... +// } +// +// In this example, the following line will construct the Invoker right away, +// but defer the invocation of scanner.Err() until the function returns. +// +// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err)) +// +// Note that the error you're appending to from the defer statement MUST be a +// named return. +type Invoke func() error + +// Invoke calls the supplied function and returns its result. +func (i Invoke) Invoke() error { return i() } + +// Close builds an Invoker that closes the provided io.Closer. Use it with +// AppendInvoke to close io.Closers and append their results into an error. +// +// For example, +// +// func processFile(path string) (err error) { +// f, err := os.Open(path) +// if err != nil { +// return err +// } +// defer multierr.AppendInvoke(&err, multierr.Close(f)) +// return processReader(f) +// } +// +// In this example, multierr.Close will construct the Invoker right away, but +// defer the invocation of f.Close until the function returns. +// +// defer multierr.AppendInvoke(&err, multierr.Close(f)) +// +// Note that the error you're appending to from the defer statement MUST be a +// named return. +func Close(closer io.Closer) Invoker { + return Invoke(closer.Close) +} + +// AppendInvoke appends the result of calling the given Invoker into the +// provided error pointer. Use it with named returns to safely defer +// invocation of fallible operations until a function returns, and capture the +// resulting errors. +// +// func doSomething(...) (err error) { +// // ... +// f, err := openFile(..) +// if err != nil { +// return err +// } +// +// // multierr will call f.Close() when this function returns and +// // if the operation fails, its append its error into the +// // returned error. +// defer multierr.AppendInvoke(&err, multierr.Close(f)) +// +// scanner := bufio.NewScanner(f) +// // Similarly, this scheduled scanner.Err to be called and +// // inspected when the function returns and append its error +// // into the returned error. +// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err)) +// +// // ... +// } +// +// NOTE: If used with a defer, the error variable MUST be a named return. +// +// Without defer, AppendInvoke behaves exactly like AppendInto. +// +// err := // ... +// multierr.AppendInvoke(&err, mutltierr.Invoke(foo)) +// +// // ...is roughly equivalent to... +// +// err := // ... +// multierr.AppendInto(&err, foo()) +// +// The advantage of the indirection introduced by Invoker is to make it easy +// to defer the invocation of a function. Without this indirection, the +// invoked function will be evaluated at the time of the defer block rather +// than when the function returns. +// +// // BAD: This is likely not what the caller intended. This will evaluate +// // foo() right away and append its result into the error when the +// // function returns. +// defer multierr.AppendInto(&err, foo()) +// +// // GOOD: This will defer invocation of foo unutil the function returns. +// defer multierr.AppendInvoke(&err, multierr.Invoke(foo)) +// +// multierr provides a few Invoker implementations out of the box for +// convenience. See [Invoker] for more information. +func AppendInvoke(into *error, invoker Invoker) { + AppendInto(into, invoker.Invoke()) +} + +// AppendFunc is a shorthand for [AppendInvoke]. +// It allows using function or method value directly +// without having to wrap it into an [Invoker] interface. +// +// func doSomething(...) (err error) { +// w, err := startWorker(...) +// if err != nil { +// return err +// } +// +// // multierr will call w.Stop() when this function returns and +// // if the operation fails, it appends its error into the +// // returned error. +// defer multierr.AppendFunc(&err, w.Stop) +// } +func AppendFunc(into *error, fn func() error) { + AppendInvoke(into, Invoke(fn)) +} diff --git a/chart-sync/vendor/go.uber.org/multierr/error_post_go120.go b/chart-sync/vendor/go.uber.org/multierr/error_post_go120.go new file mode 100644 index 000000000..a173f9c25 --- /dev/null +++ b/chart-sync/vendor/go.uber.org/multierr/error_post_go120.go @@ -0,0 +1,48 @@ +// Copyright (c) 2017-2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build go1.20 +// +build go1.20 + +package multierr + +// Unwrap returns a list of errors wrapped by this multierr. +func (merr *multiError) Unwrap() []error { + return merr.Errors() +} + +type multipleErrors interface { + Unwrap() []error +} + +func extractErrors(err error) []error { + if err == nil { + return nil + } + + // check if the given err is an Unwrapable error that + // implements multipleErrors interface. + eg, ok := err.(multipleErrors) + if !ok { + return []error{err} + } + + return append(([]error)(nil), eg.Unwrap()...) +} diff --git a/git-sensor/vendor/go.uber.org/multierr/go113.go b/chart-sync/vendor/go.uber.org/multierr/error_pre_go120.go similarity index 66% rename from git-sensor/vendor/go.uber.org/multierr/go113.go rename to chart-sync/vendor/go.uber.org/multierr/error_pre_go120.go index 264b0eac0..93872a3fc 100644 --- a/git-sensor/vendor/go.uber.org/multierr/go113.go +++ b/chart-sync/vendor/go.uber.org/multierr/error_pre_go120.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Uber Technologies, Inc. +// Copyright (c) 2017-2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -18,12 +18,19 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -// +build go1.13 +//go:build !go1.20 +// +build !go1.20 package multierr import "errors" +// Versions of Go before 1.20 did not support the Unwrap() []error method. +// This provides a similar behavior by implementing the Is(..) and As(..) +// methods. +// See the errors.Join proposal for details: +// https://github.com/golang/go/issues/53435 + // As attempts to find the first error in the error list that matches the type // of the value that target points to. // @@ -50,3 +57,23 @@ func (merr *multiError) Is(target error) bool { } return false } + +func extractErrors(err error) []error { + if err == nil { + return nil + } + + // Note that we're casting to multiError, not errorGroup. Our contract is + // that returned errors MAY implement errorGroup. Errors, however, only + // has special behavior for multierr-specific error objects. + // + // This behavior can be expanded in the future but I think it's prudent to + // start with as little as possible in terms of contract and possibility + // of misuse. + eg, ok := err.(*multiError) + if !ok { + return []error{err} + } + + return append(([]error)(nil), eg.Errors()...) +} diff --git a/chart-sync/vendor/go.uber.org/multierr/glide.yaml b/chart-sync/vendor/go.uber.org/multierr/glide.yaml deleted file mode 100644 index 6ef084ec2..000000000 --- a/chart-sync/vendor/go.uber.org/multierr/glide.yaml +++ /dev/null @@ -1,8 +0,0 @@ -package: go.uber.org/multierr -import: -- package: go.uber.org/atomic - version: ^1 -testImport: -- package: github.com/stretchr/testify - subpackages: - - assert diff --git a/chart-sync/vendor/k8s.io/api/core/v1/generated.proto b/chart-sync/vendor/k8s.io/api/core/v1/generated.proto index cf9b6e6eb..d099238cd 100644 --- a/chart-sync/vendor/k8s.io/api/core/v1/generated.proto +++ b/chart-sync/vendor/k8s.io/api/core/v1/generated.proto @@ -3286,7 +3286,7 @@ message PersistentVolumeStatus { // lastPhaseTransitionTime is the time the phase transitioned from one to another // and automatically resets to current time everytime a volume phase transitions. - // This is an alpha field and requires enabling PersistentVolumeLastPhaseTransitionTime feature. + // This is a beta field and requires the PersistentVolumeLastPhaseTransitionTime feature to be enabled (enabled by default). // +featureGate=PersistentVolumeLastPhaseTransitionTime // +optional optional k8s.io.apimachinery.pkg.apis.meta.v1.Time lastPhaseTransitionTime = 4; diff --git a/chart-sync/vendor/k8s.io/api/core/v1/types.go b/chart-sync/vendor/k8s.io/api/core/v1/types.go index 1aade3806..61ba21bca 100644 --- a/chart-sync/vendor/k8s.io/api/core/v1/types.go +++ b/chart-sync/vendor/k8s.io/api/core/v1/types.go @@ -423,7 +423,7 @@ type PersistentVolumeStatus struct { Reason string `json:"reason,omitempty" protobuf:"bytes,3,opt,name=reason"` // lastPhaseTransitionTime is the time the phase transitioned from one to another // and automatically resets to current time everytime a volume phase transitions. - // This is an alpha field and requires enabling PersistentVolumeLastPhaseTransitionTime feature. + // This is a beta field and requires the PersistentVolumeLastPhaseTransitionTime feature to be enabled (enabled by default). // +featureGate=PersistentVolumeLastPhaseTransitionTime // +optional LastPhaseTransitionTime *metav1.Time `json:"lastPhaseTransitionTime,omitempty" protobuf:"bytes,4,opt,name=lastPhaseTransitionTime"` diff --git a/chart-sync/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go b/chart-sync/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go index 01152a096..fd6f7dc61 100644 --- a/chart-sync/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go +++ b/chart-sync/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go @@ -1478,7 +1478,7 @@ var map_PersistentVolumeStatus = map[string]string{ "phase": "phase indicates if a volume is available, bound to a claim, or released by a claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#phase", "message": "message is a human-readable message indicating details about why the volume is in this state.", "reason": "reason is a brief CamelCase string that describes any failure and is meant for machine parsing and tidy display in the CLI.", - "lastPhaseTransitionTime": "lastPhaseTransitionTime is the time the phase transitioned from one to another and automatically resets to current time everytime a volume phase transitions. This is an alpha field and requires enabling PersistentVolumeLastPhaseTransitionTime feature.", + "lastPhaseTransitionTime": "lastPhaseTransitionTime is the time the phase transitioned from one to another and automatically resets to current time everytime a volume phase transitions. This is a beta field and requires the PersistentVolumeLastPhaseTransitionTime feature to be enabled (enabled by default).", } func (PersistentVolumeStatus) SwaggerDoc() map[string]string { diff --git a/chart-sync/vendor/modules.txt b/chart-sync/vendor/modules.txt index 3c1bb0f15..9206c50c4 100644 --- a/chart-sync/vendor/modules.txt +++ b/chart-sync/vendor/modules.txt @@ -93,7 +93,7 @@ github.com/containerd/platforms # github.com/davecgh/go-spew v1.1.1 ## explicit github.com/davecgh/go-spew/spew -# github.com/devtron-labs/common-lib v0.18.0 +# github.com/devtron-labs/common-lib v0.0.0 => github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da ## explicit; go 1.21 github.com/devtron-labs/common-lib/helmLib/registry github.com/devtron-labs/common-lib/utils/http @@ -359,8 +359,8 @@ go.starlark.net/syntax go.uber.org/atomic # go.uber.org/goleak v1.2.1 ## explicit; go 1.18 -# go.uber.org/multierr v1.6.0 -## explicit; go 1.12 +# go.uber.org/multierr v1.11.0 +## explicit; go 1.19 go.uber.org/multierr # go.uber.org/zap v1.21.0 ## explicit; go 1.13 @@ -503,7 +503,7 @@ helm.sh/helm/v3/pkg/provenance helm.sh/helm/v3/pkg/registry helm.sh/helm/v3/pkg/repo helm.sh/helm/v3/pkg/time -# k8s.io/api v0.29.0 +# k8s.io/api v0.29.7 ## explicit; go 1.21 k8s.io/api/admissionregistration/v1 k8s.io/api/admissionregistration/v1alpha1 @@ -557,7 +557,7 @@ k8s.io/api/scheduling/v1beta1 k8s.io/api/storage/v1 k8s.io/api/storage/v1alpha1 k8s.io/api/storage/v1beta1 -# k8s.io/apimachinery v0.29.0 +# k8s.io/apimachinery v0.29.7 ## explicit; go 1.21 k8s.io/apimachinery/pkg/api/equality k8s.io/apimachinery/pkg/api/errors @@ -607,7 +607,7 @@ k8s.io/cli-runtime/pkg/genericclioptions k8s.io/cli-runtime/pkg/genericiooptions k8s.io/cli-runtime/pkg/printers k8s.io/cli-runtime/pkg/resource -# k8s.io/client-go v0.29.0 +# k8s.io/client-go v0.29.7 ## explicit; go 1.21 k8s.io/client-go/discovery k8s.io/client-go/discovery/cached/disk @@ -785,3 +785,4 @@ sigs.k8s.io/structured-merge-diff/v4/value # sigs.k8s.io/yaml v1.3.0 ## explicit; go 1.12 sigs.k8s.io/yaml +# github.com/devtron-labs/common-lib => github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da diff --git a/ci-runner/executor/stage/ciStages.go b/ci-runner/executor/stage/ciStages.go index 0690c76f3..d016f07b6 100644 --- a/ci-runner/executor/stage/ciStages.go +++ b/ci-runner/executor/stage/ciStages.go @@ -290,7 +290,6 @@ func (impl *CiStage) runCIStages(ciContext cicxt.CiContext, ciCdRequest *helper. log.Println(util.DEVTRON, " event") metrics.TotalDuration = time.Since(metrics.TotalStartTime).Seconds() - // When externalCiArtifact is provided (run time Env at time of build) then this image will be used further in the pipeline // imageDigest and ciProjectDetails are optional fields if scriptEnvs["externalCiArtifact"] != "" { diff --git a/ci-runner/go.mod b/ci-runner/go.mod index f9a681372..4aaad337d 100644 --- a/ci-runner/go.mod +++ b/ci-runner/go.mod @@ -4,11 +4,13 @@ go 1.21 toolchain go1.21.8 +replace github.com/devtron-labs/common-lib => github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da + require ( github.com/Knetic/govaluate v3.0.0+incompatible github.com/aws/aws-sdk-go v1.44.116 github.com/caarlos0/env v3.5.0+incompatible - github.com/devtron-labs/common-lib v0.18.1-0.20241003071930-acfb6a1b1e7f + github.com/devtron-labs/common-lib v0.19.0 github.com/go-resty/resty/v2 v2.7.0 github.com/joho/godotenv v1.4.0 github.com/otiai10/copy v1.7.0 diff --git a/ci-runner/go.sum b/ci-runner/go.sum index 73610737f..5e22eca02 100644 --- a/ci-runner/go.sum +++ b/ci-runner/go.sum @@ -57,8 +57,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/devtron-labs/common-lib v0.18.1-0.20241003071930-acfb6a1b1e7f h1:5PDhi7bAc9r67OpwXZDG7y1n9lhHX26Ke4Y9VE9+kd8= -github.com/devtron-labs/common-lib v0.18.1-0.20241003071930-acfb6a1b1e7f/go.mod h1:I+B+0ZeOV1Qv8dE/uNAFXOhw7lxfD6FqK6KzTBLBY7E= +github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da h1:vC6SMz6BM1doN+ZBGiDGyERJ/LphFQi5+Ab/YQkNJVo= +github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da/go.mod h1:KpKnF4OSpQNDJmb4wVZq3Za88ePBw4xec2GOAGRm5UQ= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/cli v24.0.6+incompatible h1:fF+XCQCgJjjQNIMjzaSmiKJSCcfcXb3TWTcc7GAneOY= diff --git a/ci-runner/vendor/modules.txt b/ci-runner/vendor/modules.txt index 632223beb..063cf3f93 100644 --- a/ci-runner/vendor/modules.txt +++ b/ci-runner/vendor/modules.txt @@ -118,7 +118,7 @@ github.com/cespare/xxhash/v2 # github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc ## explicit github.com/davecgh/go-spew/spew -# github.com/devtron-labs/common-lib v0.18.1-0.20241003071930-acfb6a1b1e7f +# github.com/devtron-labs/common-lib v0.19.0 => github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da ## explicit; go 1.21 github.com/devtron-labs/common-lib/blob-storage github.com/devtron-labs/common-lib/constants @@ -841,3 +841,4 @@ sigs.k8s.io/structured-merge-diff/v4/value # sigs.k8s.io/yaml v1.3.0 ## explicit; go 1.12 sigs.k8s.io/yaml +# github.com/devtron-labs/common-lib => github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da diff --git a/git-sensor/go.mod b/git-sensor/go.mod index e6066f739..186ab63e8 100644 --- a/git-sensor/go.mod +++ b/git-sensor/go.mod @@ -4,9 +4,11 @@ go 1.21 toolchain go1.22.4 +replace github.com/devtron-labs/common-lib => github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da + require ( github.com/caarlos0/env v3.5.0+incompatible - github.com/devtron-labs/common-lib v0.16.1-0.20240911071031-2625327bc7b4 + github.com/devtron-labs/common-lib v0.0.0 github.com/devtron-labs/protos v0.0.3-0.20240809072909-83171af34169 github.com/gammazero/workerpool v1.1.3 github.com/go-git/go-git/v5 v5.11.0 @@ -72,7 +74,7 @@ require ( github.com/tidwall/pretty v1.2.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect go.uber.org/atomic v1.10.0 // indirect - go.uber.org/multierr v1.6.0 // indirect + go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.25.0 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.27.0 // indirect diff --git a/git-sensor/go.sum b/git-sensor/go.sum index 0cff4da48..f8323fb17 100644 --- a/git-sensor/go.sum +++ b/git-sensor/go.sum @@ -29,8 +29,8 @@ github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxG 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/devtron-labs/common-lib v0.16.1-0.20240911071031-2625327bc7b4 h1:OWhV5B2SQRWZges8cltVsyUrdA/8EByBjjRxX95qN7o= -github.com/devtron-labs/common-lib v0.16.1-0.20240911071031-2625327bc7b4/go.mod h1:rAY9Xd6iz+OqNQ3nO3reVHapAVr1N6Osf4Irdc0A08Q= +github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da h1:vC6SMz6BM1doN+ZBGiDGyERJ/LphFQi5+Ab/YQkNJVo= +github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da/go.mod h1:KpKnF4OSpQNDJmb4wVZq3Za88ePBw4xec2GOAGRm5UQ= github.com/devtron-labs/protos v0.0.3-0.20240809072909-83171af34169 h1:9OMZv0/fOWKK9s9BLTofFL/BO79TdyvC1Sc1HsC4esQ= github.com/devtron-labs/protos v0.0.3-0.20240809072909-83171af34169/go.mod h1:1TqULGlTey+VNhAu/ag7NJuUvByJemkqodsc9L5PHJk= github.com/docker/cli v24.0.6+incompatible h1:fF+XCQCgJjjQNIMjzaSmiKJSCcfcXb3TWTcc7GAneOY= @@ -169,8 +169,9 @@ go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/git-sensor/vendor/github.com/devtron-labs/common-lib/LICENSE b/git-sensor/vendor/github.com/devtron-labs/common-lib/LICENSE index 261eeb9e9..57bc88a15 100644 --- a/git-sensor/vendor/github.com/devtron-labs/common-lib/LICENSE +++ b/git-sensor/vendor/github.com/devtron-labs/common-lib/LICENSE @@ -199,3 +199,4 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + diff --git a/git-sensor/vendor/github.com/devtron-labs/common-lib/constants/constants.go b/git-sensor/vendor/github.com/devtron-labs/common-lib/constants/constants.go index bd2f53a67..2b34bf833 100644 --- a/git-sensor/vendor/github.com/devtron-labs/common-lib/constants/constants.go +++ b/git-sensor/vendor/github.com/devtron-labs/common-lib/constants/constants.go @@ -38,13 +38,14 @@ const ( // metrics name constants const ( - NATS_PUBLISH_COUNT = "nats_publish_count" - NATS_CONSUMPTION_COUNT = "nats_consumption_count" - NATS_CONSUMING_COUNT = "nats_consuming_count" - NATS_EVENT_CONSUMPTION_TIME = "nats_event_consumption_time" - NATS_EVENT_PUBLISH_TIME = "nats_event_publish_time" - NATS_EVENT_DELIVERY_COUNT = "nats_event_delivery_count" - PANIC_RECOVERY_COUNT = "panic_recovery_count" + NATS_PUBLISH_COUNT = "nats_publish_count" + NATS_CONSUMPTION_COUNT = "nats_consumption_count" + NATS_CONSUMING_COUNT = "nats_consuming_count" + NATS_EVENT_CONSUMPTION_TIME = "nats_event_consumption_time" + NATS_EVENT_PUBLISH_TIME = "nats_event_publish_time" + NATS_EVENT_DELIVERY_COUNT = "nats_event_delivery_count" + PANIC_RECOVERY_COUNT = "panic_recovery_count" + REVERSE_PROXY_PANIC_RECOVERY_COUNT = "reverse_proxy_panic_recovery_count" ) // metrics labels constant diff --git a/git-sensor/vendor/github.com/devtron-labs/common-lib/middlewares/recovery.go b/git-sensor/vendor/github.com/devtron-labs/common-lib/middlewares/recovery.go index f4d73bfb1..39d760411 100644 --- a/git-sensor/vendor/github.com/devtron-labs/common-lib/middlewares/recovery.go +++ b/git-sensor/vendor/github.com/devtron-labs/common-lib/middlewares/recovery.go @@ -18,6 +18,7 @@ package middlewares import ( "encoding/json" + "errors" "github.com/devtron-labs/common-lib/constants" "github.com/devtron-labs/common-lib/pubsub-lib/metrics" "log" @@ -31,8 +32,15 @@ func Recovery(next http.Handler) http.Handler { defer func() { err := recover() if err != nil { - metrics.IncPanicRecoveryCount("handler", r.Host, r.Method, r.RequestURI) - log.Print(constants.PanicLogIdentifier, "recovered from panic", "err", err, "stack", string(debug.Stack())) + if errors.Is(err.(error), http.ErrAbortHandler) { + // suppress logging for http.ErrAbortHandler panic + // separate metric for reverse proxy panic recovery + metrics.IncReverseProxyPanicRecoveryCount("proxy", r.Host, r.Method, r.RequestURI) + } else { + // log and increment panic recovery count + metrics.IncPanicRecoveryCount("handler", r.Host, r.Method, r.RequestURI) + log.Print(constants.PanicLogIdentifier, "recovered from panic", "err", err, "stack", string(debug.Stack())) + } jsonBody, _ := json.Marshal(map[string]string{ "error": "internal server error", }) diff --git a/git-sensor/vendor/github.com/devtron-labs/common-lib/pubsub-lib/metrics/metrics.go b/git-sensor/vendor/github.com/devtron-labs/common-lib/pubsub-lib/metrics/metrics.go index f67c225f4..a0ba04500 100644 --- a/git-sensor/vendor/github.com/devtron-labs/common-lib/pubsub-lib/metrics/metrics.go +++ b/git-sensor/vendor/github.com/devtron-labs/common-lib/pubsub-lib/metrics/metrics.go @@ -53,6 +53,10 @@ var PanicRecoveryCount = promauto.NewCounterVec(prometheus.CounterOpts{ Name: constants.PANIC_RECOVERY_COUNT, }, []string{constants.PANIC_TYPE, constants.HOST, constants.METHOD, constants.PATH}) +var ReverseProxyPanicRecoveryCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: constants.REVERSE_PROXY_PANIC_RECOVERY_COUNT, +}, []string{constants.PANIC_TYPE, constants.HOST, constants.METHOD, constants.PATH}) + func IncPublishCount(topic, status string) { NatsPublishingCount.WithLabelValues(topic, status).Inc() } @@ -64,6 +68,11 @@ func IncConsumptionCount(topic string) { func IncConsumingCount(topic string) { NatsConsumingCount.WithLabelValues(topic).Inc() } + func IncPanicRecoveryCount(panicType, host, method, path string) { PanicRecoveryCount.WithLabelValues(panicType, host, method, path).Inc() } + +func IncReverseProxyPanicRecoveryCount(panicType, host, method, path string) { + ReverseProxyPanicRecoveryCount.WithLabelValues(panicType, host, method, path).Inc() +} diff --git a/git-sensor/vendor/github.com/devtron-labs/common-lib/utils/CommonUtils.go b/git-sensor/vendor/github.com/devtron-labs/common-lib/utils/CommonUtils.go index 95c0f3b74..1a6c8307c 100644 --- a/git-sensor/vendor/github.com/devtron-labs/common-lib/utils/CommonUtils.go +++ b/git-sensor/vendor/github.com/devtron-labs/common-lib/utils/CommonUtils.go @@ -22,6 +22,7 @@ import ( "github.com/devtron-labs/common-lib/utils/bean" "log" "math/rand" + "os" "path" "regexp" "strings" @@ -30,7 +31,11 @@ import ( var chars = []rune("abcdefghijklmnopqrstuvwxyz0123456789") -const DOCKER_REGISTRY_TYPE_DOCKERHUB = "docker-hub" +const ( + DOCKER_REGISTRY_TYPE_DOCKERHUB = "docker-hub" + DEVTRON_SELF_POD_UID = "DEVTRON_SELF_POD_UID" + DEVTRON_SELF_POD_NAME = "DEVTRON_SELF_POD_NAME" +) // Generates random string func Generate(size int) string { @@ -82,3 +87,11 @@ func BuildDockerImagePath(dockerInfo bean.DockerRegistryInfo) (string, error) { } return dest, nil } + +func GetSelfK8sUID() string { + return os.Getenv(DEVTRON_SELF_POD_UID) +} + +func GetSelfK8sPodName() string { + return os.Getenv(DEVTRON_SELF_POD_NAME) +} diff --git a/git-sensor/vendor/go.uber.org/multierr/.travis.yml b/git-sensor/vendor/go.uber.org/multierr/.travis.yml deleted file mode 100644 index 8636ab42a..000000000 --- a/git-sensor/vendor/go.uber.org/multierr/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -sudo: false -language: go -go_import_path: go.uber.org/multierr - -env: - global: - - GO111MODULE=on - -go: - - oldstable - - stable - -before_install: -- go version - -script: -- | - set -e - make lint - make cover - -after_success: -- bash <(curl -s https://codecov.io/bash) diff --git a/git-sensor/vendor/go.uber.org/multierr/CHANGELOG.md b/git-sensor/vendor/go.uber.org/multierr/CHANGELOG.md index 6f1db9ef4..f8177b978 100644 --- a/git-sensor/vendor/go.uber.org/multierr/CHANGELOG.md +++ b/git-sensor/vendor/go.uber.org/multierr/CHANGELOG.md @@ -1,6 +1,41 @@ Releases ======== +v1.11.0 (2023-03-28) +==================== +- `Errors` now supports any error that implements multiple-error + interface. +- Add `Every` function to allow checking if all errors in the chain + satisfies `errors.Is` against the target error. + +v1.10.0 (2023-03-08) +==================== + +- Comply with Go 1.20's multiple-error interface. +- Drop Go 1.18 support. + Per the support policy, only Go 1.19 and 1.20 are supported now. +- Drop all non-test external dependencies. + +v1.9.0 (2022-12-12) +=================== + +- Add `AppendFunc` that allow passsing functions to similar to + `AppendInvoke`. + +- Bump up yaml.v3 dependency to 3.0.1. + +v1.8.0 (2022-02-28) +=================== + +- `Combine`: perform zero allocations when there are no errors. + + +v1.7.0 (2021-05-06) +=================== + +- Add `AppendInvoke` to append into errors from `defer` blocks. + + v1.6.0 (2020-09-14) =================== diff --git a/git-sensor/vendor/go.uber.org/multierr/LICENSE.txt b/git-sensor/vendor/go.uber.org/multierr/LICENSE.txt index 858e02475..413e30f7c 100644 --- a/git-sensor/vendor/go.uber.org/multierr/LICENSE.txt +++ b/git-sensor/vendor/go.uber.org/multierr/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2017 Uber Technologies, Inc. +Copyright (c) 2017-2021 Uber Technologies, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/git-sensor/vendor/go.uber.org/multierr/Makefile b/git-sensor/vendor/go.uber.org/multierr/Makefile index 316004400..dcb6fe723 100644 --- a/git-sensor/vendor/go.uber.org/multierr/Makefile +++ b/git-sensor/vendor/go.uber.org/multierr/Makefile @@ -34,9 +34,5 @@ lint: gofmt golint staticcheck .PHONY: cover cover: - go test -coverprofile=cover.out -coverpkg=./... -v ./... + go test -race -coverprofile=cover.out -coverpkg=./... -v ./... go tool cover -html=cover.out -o cover.html - -update-license: - @cd tools && go install go.uber.org/tools/update-license - @$(GOBIN)/update-license $(GO_FILES) diff --git a/git-sensor/vendor/go.uber.org/multierr/README.md b/git-sensor/vendor/go.uber.org/multierr/README.md index 751bd65e5..5ab6ac40f 100644 --- a/git-sensor/vendor/go.uber.org/multierr/README.md +++ b/git-sensor/vendor/go.uber.org/multierr/README.md @@ -2,9 +2,29 @@ `multierr` allows combining one or more Go `error`s together. +## Features + +- **Idiomatic**: + multierr follows best practices in Go, and keeps your code idiomatic. + - It keeps the underlying error type hidden, + allowing you to deal in `error` values exclusively. + - It provides APIs to safely append into an error from a `defer` statement. +- **Performant**: + multierr is optimized for performance: + - It avoids allocations where possible. + - It utilizes slice resizing semantics to optimize common cases + like appending into the same error object from a loop. +- **Interoperable**: + multierr interoperates with the Go standard library's error APIs seamlessly: + - The `errors.Is` and `errors.As` functions *just work*. +- **Lightweight**: + multierr comes with virtually no dependencies. + ## Installation - go get -u go.uber.org/multierr +```bash +go get -u go.uber.org/multierr@latest +``` ## Status @@ -15,9 +35,9 @@ Stable: No breaking changes will be made before 2.0. Released under the [MIT License]. [MIT License]: LICENSE.txt -[doc-img]: https://godoc.org/go.uber.org/multierr?status.svg -[doc]: https://godoc.org/go.uber.org/multierr -[ci-img]: https://travis-ci.com/uber-go/multierr.svg?branch=master +[doc-img]: https://pkg.go.dev/badge/go.uber.org/multierr +[doc]: https://pkg.go.dev/go.uber.org/multierr +[ci-img]: https://github.com/uber-go/multierr/actions/workflows/go.yml/badge.svg [cov-img]: https://codecov.io/gh/uber-go/multierr/branch/master/graph/badge.svg -[ci]: https://travis-ci.com/uber-go/multierr +[ci]: https://github.com/uber-go/multierr/actions/workflows/go.yml [cov]: https://codecov.io/gh/uber-go/multierr diff --git a/git-sensor/vendor/go.uber.org/multierr/error.go b/git-sensor/vendor/go.uber.org/multierr/error.go index 5c9b67d53..3a828b2df 100644 --- a/git-sensor/vendor/go.uber.org/multierr/error.go +++ b/git-sensor/vendor/go.uber.org/multierr/error.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Uber Technologies, Inc. +// Copyright (c) 2017-2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -20,54 +20,109 @@ // Package multierr allows combining one or more errors together. // -// Overview +// # Overview // // Errors can be combined with the use of the Combine function. // -// multierr.Combine( -// reader.Close(), -// writer.Close(), -// conn.Close(), -// ) +// multierr.Combine( +// reader.Close(), +// writer.Close(), +// conn.Close(), +// ) // // If only two errors are being combined, the Append function may be used // instead. // -// err = multierr.Append(reader.Close(), writer.Close()) -// -// This makes it possible to record resource cleanup failures from deferred -// blocks with the help of named return values. -// -// func sendRequest(req Request) (err error) { -// conn, err := openConnection() -// if err != nil { -// return err -// } -// defer func() { -// err = multierr.Append(err, conn.Close()) -// }() -// // ... -// } +// err = multierr.Append(reader.Close(), writer.Close()) // // The underlying list of errors for a returned error object may be retrieved // with the Errors function. // -// errors := multierr.Errors(err) -// if len(errors) > 0 { -// fmt.Println("The following errors occurred:", errors) -// } +// errors := multierr.Errors(err) +// if len(errors) > 0 { +// fmt.Println("The following errors occurred:", errors) +// } +// +// # Appending from a loop +// +// You sometimes need to append into an error from a loop. +// +// var err error +// for _, item := range items { +// err = multierr.Append(err, process(item)) +// } +// +// Cases like this may require knowledge of whether an individual instance +// failed. This usually requires introduction of a new variable. +// +// var err error +// for _, item := range items { +// if perr := process(item); perr != nil { +// log.Warn("skipping item", item) +// err = multierr.Append(err, perr) +// } +// } +// +// multierr includes AppendInto to simplify cases like this. +// +// var err error +// for _, item := range items { +// if multierr.AppendInto(&err, process(item)) { +// log.Warn("skipping item", item) +// } +// } +// +// This will append the error into the err variable, and return true if that +// individual error was non-nil. // -// Advanced Usage +// See [AppendInto] for more information. +// +// # Deferred Functions +// +// Go makes it possible to modify the return value of a function in a defer +// block if the function was using named returns. This makes it possible to +// record resource cleanup failures from deferred blocks. +// +// func sendRequest(req Request) (err error) { +// conn, err := openConnection() +// if err != nil { +// return err +// } +// defer func() { +// err = multierr.Append(err, conn.Close()) +// }() +// // ... +// } +// +// multierr provides the Invoker type and AppendInvoke function to make cases +// like the above simpler and obviate the need for a closure. The following is +// roughly equivalent to the example above. +// +// func sendRequest(req Request) (err error) { +// conn, err := openConnection() +// if err != nil { +// return err +// } +// defer multierr.AppendInvoke(&err, multierr.Close(conn)) +// // ... +// } +// +// See [AppendInvoke] and [Invoker] for more information. +// +// NOTE: If you're modifying an error from inside a defer, you MUST use a named +// return value for that function. +// +// # Advanced Usage // // Errors returned by Combine and Append MAY implement the following // interface. // -// type errorGroup interface { -// // Returns a slice containing the underlying list of errors. -// // -// // This slice MUST NOT be modified by the caller. -// Errors() []error -// } +// type errorGroup interface { +// // Returns a slice containing the underlying list of errors. +// // +// // This slice MUST NOT be modified by the caller. +// Errors() []error +// } // // Note that if you need access to list of errors behind a multierr error, you // should prefer using the Errors function. That said, if you need cheap @@ -76,23 +131,23 @@ // because errors returned by Combine and Append are not guaranteed to // implement this interface. // -// var errors []error -// group, ok := err.(errorGroup) -// if ok { -// errors = group.Errors() -// } else { -// errors = []error{err} -// } +// var errors []error +// group, ok := err.(errorGroup) +// if ok { +// errors = group.Errors() +// } else { +// errors = []error{err} +// } package multierr // import "go.uber.org/multierr" import ( "bytes" + "errors" "fmt" "io" "strings" "sync" - - "go.uber.org/atomic" + "sync/atomic" ) var ( @@ -132,34 +187,15 @@ type errorGroup interface { // Errors returns a slice containing zero or more errors that the supplied // error is composed of. If the error is nil, a nil slice is returned. // -// err := multierr.Append(r.Close(), w.Close()) -// errors := multierr.Errors(err) +// err := multierr.Append(r.Close(), w.Close()) +// errors := multierr.Errors(err) // // If the error is not composed of other errors, the returned slice contains // just the error that was passed in. // // Callers of this function are free to modify the returned slice. func Errors(err error) []error { - if err == nil { - return nil - } - - // Note that we're casting to multiError, not errorGroup. Our contract is - // that returned errors MAY implement errorGroup. Errors, however, only - // has special behavior for multierr-specific error objects. - // - // This behavior can be expanded in the future but I think it's prudent to - // start with as little as possible in terms of contract and possibility - // of misuse. - eg, ok := err.(*multiError) - if !ok { - return []error{err} - } - - errors := eg.Errors() - result := make([]error, len(errors)) - copy(result, errors) - return result + return extractErrors(err) } // multiError is an error that holds one or more errors. @@ -174,8 +210,6 @@ type multiError struct { errors []error } -var _ errorGroup = (*multiError)(nil) - // Errors returns the list of underlying errors. // // This slice MUST NOT be modified. @@ -201,6 +235,17 @@ func (merr *multiError) Error() string { return result } +// Every compares every error in the given err against the given target error +// using [errors.Is], and returns true only if every comparison returned true. +func Every(err error, target error) bool { + for _, e := range extractErrors(err) { + if !errors.Is(e, target) { + return false + } + } + return true +} + func (merr *multiError) Format(f fmt.State, c rune) { if c == 'v' && f.Flag('+') { merr.writeMultiline(f) @@ -292,6 +337,14 @@ func inspect(errors []error) (res inspectResult) { // fromSlice converts the given list of errors into a single error. func fromSlice(errors []error) error { + // Don't pay to inspect small slices. + switch len(errors) { + case 0: + return nil + case 1: + return errors[0] + } + res := inspect(errors) switch res.Count { case 0: @@ -301,8 +354,12 @@ func fromSlice(errors []error) error { return errors[res.FirstErrorIdx] case len(errors): if !res.ContainsMultiError { - // already flat - return &multiError{errors: errors} + // Error list is flat. Make a copy of it + // Otherwise "errors" escapes to the heap + // unconditionally for all other cases. + // This lets us optimize for the "no errors" case. + out := append(([]error)(nil), errors...) + return &multiError{errors: out} } } @@ -327,32 +384,32 @@ func fromSlice(errors []error) error { // If zero arguments were passed or if all items are nil, a nil error is // returned. // -// Combine(nil, nil) // == nil +// Combine(nil, nil) // == nil // // If only a single error was passed, it is returned as-is. // -// Combine(err) // == err +// Combine(err) // == err // // Combine skips over nil arguments so this function may be used to combine // together errors from operations that fail independently of each other. // -// multierr.Combine( -// reader.Close(), -// writer.Close(), -// pipe.Close(), -// ) +// multierr.Combine( +// reader.Close(), +// writer.Close(), +// pipe.Close(), +// ) // // If any of the passed errors is a multierr error, it will be flattened along // with the other errors. // -// multierr.Combine(multierr.Combine(err1, err2), err3) -// // is the same as -// multierr.Combine(err1, err2, err3) +// multierr.Combine(multierr.Combine(err1, err2), err3) +// // is the same as +// multierr.Combine(err1, err2, err3) // // The returned error formats into a readable multi-line error message if // formatted with %+v. // -// fmt.Sprintf("%+v", multierr.Combine(err1, err2)) +// fmt.Sprintf("%+v", multierr.Combine(err1, err2)) func Combine(errors ...error) error { return fromSlice(errors) } @@ -362,16 +419,19 @@ func Combine(errors ...error) error { // This function is a specialization of Combine for the common case where // there are only two errors. // -// err = multierr.Append(reader.Close(), writer.Close()) +// err = multierr.Append(reader.Close(), writer.Close()) // // The following pattern may also be used to record failure of deferred // operations without losing information about the original error. // -// func doSomething(..) (err error) { -// f := acquireResource() -// defer func() { -// err = multierr.Append(err, f.Close()) -// }() +// func doSomething(..) (err error) { +// f := acquireResource() +// defer func() { +// err = multierr.Append(err, f.Close()) +// }() +// +// Note that the variable MUST be a named return to append an error to it from +// the defer statement. See also [AppendInvoke]. func Append(left error, right error) error { switch { case left == nil: @@ -401,37 +461,37 @@ func Append(left error, right error) error { // AppendInto appends an error into the destination of an error pointer and // returns whether the error being appended was non-nil. // -// var err error -// multierr.AppendInto(&err, r.Close()) -// multierr.AppendInto(&err, w.Close()) +// var err error +// multierr.AppendInto(&err, r.Close()) +// multierr.AppendInto(&err, w.Close()) // // The above is equivalent to, // -// err := multierr.Append(r.Close(), w.Close()) +// err := multierr.Append(r.Close(), w.Close()) // // As AppendInto reports whether the provided error was non-nil, it may be // used to build a multierr error in a loop more ergonomically. For example: // -// var err error -// for line := range lines { -// var item Item -// if multierr.AppendInto(&err, parse(line, &item)) { -// continue -// } -// items = append(items, item) -// } -// -// Compare this with a verison that relies solely on Append: -// -// var err error -// for line := range lines { -// var item Item -// if parseErr := parse(line, &item); parseErr != nil { -// err = multierr.Append(err, parseErr) -// continue -// } -// items = append(items, item) -// } +// var err error +// for line := range lines { +// var item Item +// if multierr.AppendInto(&err, parse(line, &item)) { +// continue +// } +// items = append(items, item) +// } +// +// Compare this with a version that relies solely on Append: +// +// var err error +// for line := range lines { +// var item Item +// if parseErr := parse(line, &item); parseErr != nil { +// err = multierr.Append(err, parseErr) +// continue +// } +// items = append(items, item) +// } func AppendInto(into *error, err error) (errored bool) { if into == nil { // We panic if 'into' is nil. This is not documented above @@ -447,3 +507,140 @@ func AppendInto(into *error, err error) (errored bool) { *into = Append(*into, err) return true } + +// Invoker is an operation that may fail with an error. Use it with +// AppendInvoke to append the result of calling the function into an error. +// This allows you to conveniently defer capture of failing operations. +// +// See also, [Close] and [Invoke]. +type Invoker interface { + Invoke() error +} + +// Invoke wraps a function which may fail with an error to match the Invoker +// interface. Use it to supply functions matching this signature to +// AppendInvoke. +// +// For example, +// +// func processReader(r io.Reader) (err error) { +// scanner := bufio.NewScanner(r) +// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err)) +// for scanner.Scan() { +// // ... +// } +// // ... +// } +// +// In this example, the following line will construct the Invoker right away, +// but defer the invocation of scanner.Err() until the function returns. +// +// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err)) +// +// Note that the error you're appending to from the defer statement MUST be a +// named return. +type Invoke func() error + +// Invoke calls the supplied function and returns its result. +func (i Invoke) Invoke() error { return i() } + +// Close builds an Invoker that closes the provided io.Closer. Use it with +// AppendInvoke to close io.Closers and append their results into an error. +// +// For example, +// +// func processFile(path string) (err error) { +// f, err := os.Open(path) +// if err != nil { +// return err +// } +// defer multierr.AppendInvoke(&err, multierr.Close(f)) +// return processReader(f) +// } +// +// In this example, multierr.Close will construct the Invoker right away, but +// defer the invocation of f.Close until the function returns. +// +// defer multierr.AppendInvoke(&err, multierr.Close(f)) +// +// Note that the error you're appending to from the defer statement MUST be a +// named return. +func Close(closer io.Closer) Invoker { + return Invoke(closer.Close) +} + +// AppendInvoke appends the result of calling the given Invoker into the +// provided error pointer. Use it with named returns to safely defer +// invocation of fallible operations until a function returns, and capture the +// resulting errors. +// +// func doSomething(...) (err error) { +// // ... +// f, err := openFile(..) +// if err != nil { +// return err +// } +// +// // multierr will call f.Close() when this function returns and +// // if the operation fails, its append its error into the +// // returned error. +// defer multierr.AppendInvoke(&err, multierr.Close(f)) +// +// scanner := bufio.NewScanner(f) +// // Similarly, this scheduled scanner.Err to be called and +// // inspected when the function returns and append its error +// // into the returned error. +// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err)) +// +// // ... +// } +// +// NOTE: If used with a defer, the error variable MUST be a named return. +// +// Without defer, AppendInvoke behaves exactly like AppendInto. +// +// err := // ... +// multierr.AppendInvoke(&err, mutltierr.Invoke(foo)) +// +// // ...is roughly equivalent to... +// +// err := // ... +// multierr.AppendInto(&err, foo()) +// +// The advantage of the indirection introduced by Invoker is to make it easy +// to defer the invocation of a function. Without this indirection, the +// invoked function will be evaluated at the time of the defer block rather +// than when the function returns. +// +// // BAD: This is likely not what the caller intended. This will evaluate +// // foo() right away and append its result into the error when the +// // function returns. +// defer multierr.AppendInto(&err, foo()) +// +// // GOOD: This will defer invocation of foo unutil the function returns. +// defer multierr.AppendInvoke(&err, multierr.Invoke(foo)) +// +// multierr provides a few Invoker implementations out of the box for +// convenience. See [Invoker] for more information. +func AppendInvoke(into *error, invoker Invoker) { + AppendInto(into, invoker.Invoke()) +} + +// AppendFunc is a shorthand for [AppendInvoke]. +// It allows using function or method value directly +// without having to wrap it into an [Invoker] interface. +// +// func doSomething(...) (err error) { +// w, err := startWorker(...) +// if err != nil { +// return err +// } +// +// // multierr will call w.Stop() when this function returns and +// // if the operation fails, it appends its error into the +// // returned error. +// defer multierr.AppendFunc(&err, w.Stop) +// } +func AppendFunc(into *error, fn func() error) { + AppendInvoke(into, Invoke(fn)) +} diff --git a/git-sensor/vendor/go.uber.org/multierr/error_post_go120.go b/git-sensor/vendor/go.uber.org/multierr/error_post_go120.go new file mode 100644 index 000000000..a173f9c25 --- /dev/null +++ b/git-sensor/vendor/go.uber.org/multierr/error_post_go120.go @@ -0,0 +1,48 @@ +// Copyright (c) 2017-2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build go1.20 +// +build go1.20 + +package multierr + +// Unwrap returns a list of errors wrapped by this multierr. +func (merr *multiError) Unwrap() []error { + return merr.Errors() +} + +type multipleErrors interface { + Unwrap() []error +} + +func extractErrors(err error) []error { + if err == nil { + return nil + } + + // check if the given err is an Unwrapable error that + // implements multipleErrors interface. + eg, ok := err.(multipleErrors) + if !ok { + return []error{err} + } + + return append(([]error)(nil), eg.Unwrap()...) +} diff --git a/lens/vendor/go.uber.org/multierr/go113.go b/git-sensor/vendor/go.uber.org/multierr/error_pre_go120.go similarity index 66% rename from lens/vendor/go.uber.org/multierr/go113.go rename to git-sensor/vendor/go.uber.org/multierr/error_pre_go120.go index 264b0eac0..93872a3fc 100644 --- a/lens/vendor/go.uber.org/multierr/go113.go +++ b/git-sensor/vendor/go.uber.org/multierr/error_pre_go120.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Uber Technologies, Inc. +// Copyright (c) 2017-2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -18,12 +18,19 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -// +build go1.13 +//go:build !go1.20 +// +build !go1.20 package multierr import "errors" +// Versions of Go before 1.20 did not support the Unwrap() []error method. +// This provides a similar behavior by implementing the Is(..) and As(..) +// methods. +// See the errors.Join proposal for details: +// https://github.com/golang/go/issues/53435 + // As attempts to find the first error in the error list that matches the type // of the value that target points to. // @@ -50,3 +57,23 @@ func (merr *multiError) Is(target error) bool { } return false } + +func extractErrors(err error) []error { + if err == nil { + return nil + } + + // Note that we're casting to multiError, not errorGroup. Our contract is + // that returned errors MAY implement errorGroup. Errors, however, only + // has special behavior for multierr-specific error objects. + // + // This behavior can be expanded in the future but I think it's prudent to + // start with as little as possible in terms of contract and possibility + // of misuse. + eg, ok := err.(*multiError) + if !ok { + return []error{err} + } + + return append(([]error)(nil), eg.Errors()...) +} diff --git a/git-sensor/vendor/go.uber.org/multierr/glide.yaml b/git-sensor/vendor/go.uber.org/multierr/glide.yaml deleted file mode 100644 index 6ef084ec2..000000000 --- a/git-sensor/vendor/go.uber.org/multierr/glide.yaml +++ /dev/null @@ -1,8 +0,0 @@ -package: go.uber.org/multierr -import: -- package: go.uber.org/atomic - version: ^1 -testImport: -- package: github.com/stretchr/testify - subpackages: - - assert diff --git a/git-sensor/vendor/modules.txt b/git-sensor/vendor/modules.txt index dfadf1bf3..c0b9bf03c 100644 --- a/git-sensor/vendor/modules.txt +++ b/git-sensor/vendor/modules.txt @@ -62,7 +62,7 @@ github.com/cyphar/filepath-securejoin # github.com/davecgh/go-spew v1.1.1 ## explicit github.com/davecgh/go-spew/spew -# github.com/devtron-labs/common-lib v0.16.1-0.20240911071031-2625327bc7b4 +# github.com/devtron-labs/common-lib v0.0.0 => github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da ## explicit; go 1.21 github.com/devtron-labs/common-lib/constants github.com/devtron-labs/common-lib/git-manager @@ -296,8 +296,8 @@ github.com/xanzy/ssh-agent # go.uber.org/atomic v1.10.0 ## explicit; go 1.18 go.uber.org/atomic -# go.uber.org/multierr v1.6.0 -## explicit; go 1.12 +# go.uber.org/multierr v1.11.0 +## explicit; go 1.19 go.uber.org/multierr # go.uber.org/zap v1.21.0 ## explicit; go 1.13 @@ -487,3 +487,4 @@ gopkg.in/yaml.v3 # mellium.im/sasl v0.3.1 ## explicit; go 1.18 mellium.im/sasl +# github.com/devtron-labs/common-lib => github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da diff --git a/kubelink/go.mod b/kubelink/go.mod index 8e10c820e..0b821aad3 100644 --- a/kubelink/go.mod +++ b/kubelink/go.mod @@ -6,7 +6,7 @@ toolchain go1.22.6 require ( github.com/caarlos0/env v3.5.0+incompatible - github.com/devtron-labs/common-lib v0.18.1-0.20241001061923-eda545dc839e + github.com/devtron-labs/common-lib v0.0.0 github.com/evanphx/json-patch v5.7.0+incompatible github.com/go-pg/pg v6.15.1+incompatible github.com/golang/protobuf v1.5.4 @@ -177,7 +177,7 @@ require ( ) replace ( - github.com/devtron-labs/common-lib => github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241007082211-c5886d8a9988 + github.com/devtron-labs/common-lib => github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da go.opentelemetry.io/otel/metric => go.opentelemetry.io/otel/metric v1.18.0 // https://github.com/kubernetes/kubernetes/issues/79384#issuecomment-505627280 k8s.io/api => k8s.io/api v0.29.0 diff --git a/kubelink/go.sum b/kubelink/go.sum index 85b3be31d..d09329251 100644 --- a/kubelink/go.sum +++ b/kubelink/go.sum @@ -79,8 +79,8 @@ github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxG 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/devtron-labs/devtron-services/common-lib v0.0.0-20241007082211-c5886d8a9988 h1:nb8tGcgpt2Eu7ceuWhwAKf+07s/0RZ1ASYsGhZ7swSQ= -github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241007082211-c5886d8a9988/go.mod h1:KpKnF4OSpQNDJmb4wVZq3Za88ePBw4xec2GOAGRm5UQ= +github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da h1:vC6SMz6BM1doN+ZBGiDGyERJ/LphFQi5+Ab/YQkNJVo= +github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da/go.mod h1:KpKnF4OSpQNDJmb4wVZq3Za88ePBw4xec2GOAGRm5UQ= 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/distribution/distribution/v3 v3.0.0-beta.1 h1:X+ELTxPuZ1Xe5MsD3kp2wfGUhc8I+MPfRis8dZ818Ic= diff --git a/kubelink/vendor/modules.txt b/kubelink/vendor/modules.txt index 99e19c4ea..e8b8ebca0 100644 --- a/kubelink/vendor/modules.txt +++ b/kubelink/vendor/modules.txt @@ -127,7 +127,7 @@ github.com/cyphar/filepath-securejoin # github.com/davecgh/go-spew v1.1.1 ## explicit github.com/davecgh/go-spew/spew -# github.com/devtron-labs/common-lib v0.18.1-0.20241001061923-eda545dc839e => github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241007082211-c5886d8a9988 +# github.com/devtron-labs/common-lib v0.0.0 => github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da ## explicit; go 1.21 github.com/devtron-labs/common-lib/async github.com/devtron-labs/common-lib/constants @@ -1353,7 +1353,7 @@ sigs.k8s.io/structured-merge-diff/v4/value # sigs.k8s.io/yaml v1.3.0 ## explicit; go 1.12 sigs.k8s.io/yaml -# github.com/devtron-labs/common-lib => github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241007082211-c5886d8a9988 +# github.com/devtron-labs/common-lib => github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da # go.opentelemetry.io/otel/metric => go.opentelemetry.io/otel/metric v1.18.0 # k8s.io/api => k8s.io/api v0.29.0 # k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.29.0 diff --git a/kubewatch/go.mod b/kubewatch/go.mod index 6afdbfe93..43da2a13b 100644 --- a/kubewatch/go.mod +++ b/kubewatch/go.mod @@ -36,7 +36,7 @@ require ( ) require ( - github.com/devtron-labs/common-lib v0.16.1-0.20240923063129-ff2dc035435e + github.com/devtron-labs/common-lib v0.0.0 github.com/google/go-querystring v1.1.0 // indirect github.com/google/wire v0.6.0 github.com/imdario/mergo v0.3.16 // indirect @@ -245,3 +245,5 @@ replace ( k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.29.7 k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.29.7 ) + +replace github.com/devtron-labs/common-lib => github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da diff --git a/kubewatch/go.sum b/kubewatch/go.sum index fc22dae2f..ebf0cc67f 100644 --- a/kubewatch/go.sum +++ b/kubewatch/go.sum @@ -719,8 +719,8 @@ github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxG 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/devtron-labs/common-lib v0.16.1-0.20240923063129-ff2dc035435e h1:siyAoJz9Nq26JrTzmSGd02EOmPV86tUgzgBW1Y5bSVU= -github.com/devtron-labs/common-lib v0.16.1-0.20240923063129-ff2dc035435e/go.mod h1:I+B+0ZeOV1Qv8dE/uNAFXOhw7lxfD6FqK6KzTBLBY7E= +github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da h1:vC6SMz6BM1doN+ZBGiDGyERJ/LphFQi5+Ab/YQkNJVo= +github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da/go.mod h1:KpKnF4OSpQNDJmb4wVZq3Za88ePBw4xec2GOAGRm5UQ= 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/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= diff --git a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/K8sUtil.go b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/K8sUtil.go index 7734f432b..e243148d0 100644 --- a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/K8sUtil.go +++ b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/K8sUtil.go @@ -113,8 +113,9 @@ type K8sService interface { CreateConfigMap(namespace string, cm *v1.ConfigMap, client *v12.CoreV1Client) (*v1.ConfigMap, error) GetConfigMap(namespace string, name string, client *v12.CoreV1Client) (*v1.ConfigMap, error) GetConfigMapWithCtx(ctx context.Context, namespace string, name string, client *v12.CoreV1Client) (*v1.ConfigMap, error) - CheckIfNsExists(namespace string, client *v12.CoreV1Client) (exists bool, err error) - CreateNsIfNotExists(namespace string, clusterConfig *ClusterConfig) (err error) + GetNsIfExists(namespace string, client *v12.CoreV1Client) (ns *v1.Namespace, exists bool, err error) + CreateNsIfNotExists(namespace string, clusterConfig *ClusterConfig) (ns *v1.Namespace, nsCreated bool, err error) + UpdateNSLabels(namespace *v1.Namespace, labels map[string]string, clusterConfig *ClusterConfig) (ns *v1.Namespace, err error) GetK8sDiscoveryClientInCluster() (*discovery.DiscoveryClient, error) GetK8sDiscoveryClient(clusterConfig *ClusterConfig) (*discovery.DiscoveryClient, error) GetClientForInCluster() (*v12.CoreV1Client, error) @@ -178,7 +179,7 @@ func (impl *K8sServiceImpl) GetRestConfigByClusterWithoutCustomTransport(cluster bearerToken := clusterConfig.BearerToken var restConfig *rest.Config var err error - if clusterConfig.Host == DefaultClusterUrl && len(bearerToken) == 0 { + if clusterConfig.Host == commonBean.DefaultClusterUrl && len(bearerToken) == 0 { restConfig, err = impl.GetK8sInClusterRestConfig() if err != nil { impl.logger.Errorw("error in getting rest config for default cluster", "err", err) @@ -290,37 +291,58 @@ func (impl *K8sServiceImpl) GetK8sDiscoveryClientInCluster() (*discovery.Discove return discoveryClient, err } -func (impl *K8sServiceImpl) CreateNsIfNotExists(namespace string, clusterConfig *ClusterConfig) (err error) { +func (impl *K8sServiceImpl) CreateNsIfNotExists(namespace string, clusterConfig *ClusterConfig) (ns *v1.Namespace, nsCreated bool, err error) { v12Client, err := impl.GetCoreV1Client(clusterConfig) if err != nil { impl.logger.Errorw("error", "error", err, "clusterConfig", clusterConfig) - return err + return nil, false, err } - exists, err := impl.CheckIfNsExists(namespace, v12Client) + ns, exists, err := impl.GetNsIfExists(namespace, v12Client) if err != nil { impl.logger.Errorw("error", "error", err, "clusterConfig", clusterConfig) - return err + return ns, false, err } if exists { + nsCreated = false impl.logger.Infow("namesapce already exist") - return nil + return ns, nsCreated, nil } impl.logger.Infow("ns not exists creating", "ns", namespace) - _, err = impl.CreateNs(namespace, v12Client) - return err + ns, err = impl.CreateNs(namespace, v12Client) + if err != nil { + impl.logger.Errorw("error in creating ns", "namespace", namespace, "err", err) + return nil, false, err + } + nsCreated = true + return ns, nsCreated, err +} + +func (impl *K8sServiceImpl) UpdateNSLabels(namespace *v1.Namespace, labels map[string]string, clusterConfig *ClusterConfig) (ns *v1.Namespace, err error) { + v12Client, err := impl.GetCoreV1Client(clusterConfig) + if err != nil { + impl.logger.Errorw("error", "error", err, "clusterConfig", clusterConfig) + return nil, err + } + namespace.Labels = labels + ns, err = v12Client.Namespaces().Update(context.Background(), namespace, metav1.UpdateOptions{}) + if err != nil { + impl.logger.Errorw("error in updating ns", "namespace", namespace, "err", err) + return nil, err + } + return ns, nil } -func (impl *K8sServiceImpl) CheckIfNsExists(namespace string, client *v12.CoreV1Client) (exists bool, err error) { - ns, err := client.Namespaces().Get(context.Background(), namespace, metav1.GetOptions{}) +func (impl *K8sServiceImpl) GetNsIfExists(namespace string, client *v12.CoreV1Client) (ns *v1.Namespace, exists bool, err error) { + ns, err = client.Namespaces().Get(context.Background(), namespace, metav1.GetOptions{}) //ns, err := impl.k8sClient.CoreV1().Namespaces().Get(namespace, metav1.GetOptions{}) impl.logger.Debugw("ns fetch", "name", namespace, "res", ns) if errors.IsNotFound(err) { - return false, nil + return nil, false, nil } else if err != nil { impl.logger.Errorw("error in checking if ns exist", "err", err) - return false, err + return nil, false, err } else { - return true, nil + return ns, true, nil } } @@ -610,7 +632,7 @@ func (impl *K8sServiceImpl) DiscoveryClientGetLiveZCall(cluster *ClusterConfig) return nil, err } //using livez path as healthz path is deprecated - response, err := impl.GetLiveZCall(LiveZ, k8sClientSet) + response, err := impl.GetLiveZCall(commonBean.LiveZ, k8sClientSet) if err != nil { impl.logger.Errorw("error in getting livez call", "err", err, "clusterName", cluster.ClusterName) return nil, err @@ -672,7 +694,7 @@ func (impl *K8sServiceImpl) DeletePodByLabel(namespace string, labels string, cl } for _, pod := range (*podList).Items { - if pod.Status.Phase != Running { + if pod.Status.Phase != commonBean.Running { podName := pod.ObjectMeta.Name err := pods.Delete(context.Background(), podName, metav1.DeleteOptions{}) if err != nil && !errors.IsNotFound(err) { @@ -729,7 +751,7 @@ func (impl *K8sServiceImpl) ListNamespaces(client *v12.CoreV1Client) (*v1.Namesp } func (impl *K8sServiceImpl) GetClientByToken(serverUrl string, token map[string]string) (*v12.CoreV1Client, error) { - bearerToken := token[BearerToken] + bearerToken := token[commonBean.BearerToken] clusterCfg := &ClusterConfig{Host: serverUrl, BearerToken: bearerToken} v12Client, err := impl.GetCoreV1Client(clusterCfg) if err != nil { @@ -861,16 +883,30 @@ func (impl *K8sServiceImpl) BuildK8sObjectListTableData(manifest *unstructured.U var cellObj map[string]interface{} if cellObjUncast != nil { cellObj = cellObjUncast.(map[string]interface{}) - if cellObj != nil && cellObj[commonBean.K8sClusterResourceMetadataKey] != nil { - metadata := cellObj[commonBean.K8sClusterResourceMetadataKey].(map[string]interface{}) - if metadata[commonBean.K8sClusterResourceNamespaceKey] != nil { - namespace = metadata[commonBean.K8sClusterResourceNamespaceKey].(string) - if namespaced { - rowIndex[commonBean.K8sClusterResourceNamespaceKey] = namespace + if cellObj != nil { + rowIndex[commonBean.K8sClusterResourceKindKey] = cellObj[commonBean.K8sClusterResourceKindKey].(string) + rowIndex[commonBean.K8sClusterResourceApiVersionKey] = cellObj[commonBean.K8sClusterResourceApiVersionKey].(string) + + if cellObj[commonBean.K8sClusterResourceMetadataKey] != nil { + metadata := cellObj[commonBean.K8sClusterResourceMetadataKey].(map[string]interface{}) + if metadata[commonBean.K8sClusterResourceNamespaceKey] != nil { + namespace = metadata[commonBean.K8sClusterResourceNamespaceKey].(string) + if namespaced { + rowIndex[commonBean.K8sClusterResourceNamespaceKey] = namespace + } + } + if includeMetadata { + rowIndex[commonBean.K8sClusterResourceMetadataKey] = metadata } } - if includeMetadata { - rowIndex[commonBean.K8sClusterResourceMetadataKey] = metadata + + if cellObj[commonBean.K8sClusterResourceSpecKey] != nil { + spec, ok := cellObj[commonBean.K8sClusterResourceSpecKey].(map[string]interface{}) + if ok { + rowIndex[commonBean.K8sClusterResourceSpecKey] = spec + } else { + impl.logger.Warnw("Not able to cast spec key of manifest to map") + } } } } @@ -1199,7 +1235,7 @@ func (impl *K8sServiceImpl) CreateK8sClientSet(restConfig *rest.Config) (*kubern func (impl *K8sServiceImpl) FetchConnectionStatusForCluster(k8sClientSet *kubernetes.Clientset) error { //using livez path as healthz path is deprecated - path := LiveZ + path := commonBean.LiveZ response, err := k8sClientSet.Discovery().RESTClient().Get().AbsPath(path).DoRaw(context.Background()) log.Println("received response for cluster livez status", "response", string(response), "err", err) if err != nil { @@ -1736,24 +1772,6 @@ func (impl *K8sServiceImpl) GetPodListByLabel(namespace, label string, clientSet return podList.Items, nil } -func IsService(gvk schema.GroupVersionKind) bool { - return gvk.Group == "" && gvk.Kind == commonBean.ServiceKind -} - -func IsPod(gvk schema.GroupVersionKind) bool { - return gvk.Group == "" && gvk.Kind == commonBean.PodKind && gvk.Version == "v1" -} - -func IsDevtronApp(labels map[string]string) bool { - isDevtronApp := false - if val, ok := labels[DEVTRON_APP_LABEL_KEY]; ok { - if val == DEVTRON_APP_LABEL_VALUE1 || val == DEVTRON_APP_LABEL_VALUE2 { - isDevtronApp = true - } - } - return isDevtronApp -} - //func GetHealthCheckFunc(gvk schema.GroupVersionKind) func(obj *unstructured.Unstructured) (*health.HealthStatus, error) { // return health.GetHealthCheckFunc(gvk) //} diff --git a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/adapter.go b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/adapter.go index d82cac194..f89681fa3 100644 --- a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/adapter.go +++ b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/adapter.go @@ -40,3 +40,14 @@ func (req *ResourceIdentifier) WithKind(kind string) *ResourceIdentifier { req.GroupVersionKind.Kind = kind return req } + +// +//func GetEmptyResourceForResourceKey(resourceKey *ResourceKey) *Resource { +// return &cache.Resource{ +// Ref: v1.ObjectReference{ +// Kind: resourceKey.Kind, +// Namespace: resourceKey.Namespace, +// Name: resourceKey.Name, +// }, +// } +//} diff --git a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/bean.go b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/bean.go index 05f5220ea..21f0a21be 100644 --- a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/bean.go +++ b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/bean.go @@ -35,22 +35,6 @@ import ( "time" ) -const ( - DEFAULT_CLUSTER = "default_cluster" - DEVTRON_SERVICE_NAME = "devtron-service" - DefaultClusterUrl = "https://kubernetes.default.svc" - BearerToken = "bearer_token" - CertificateAuthorityData = "cert_auth_data" - CertData = "cert_data" - TlsKey = "tls_key" - LiveZ = "/livez" - Running = "Running" - RestartingNotSupported = "restarting not supported" - DEVTRON_APP_LABEL_KEY = "app" - DEVTRON_APP_LABEL_VALUE1 = "devtron" - DEVTRON_APP_LABEL_VALUE2 = "orchestrator" -) - type ClusterConfig struct { ClusterName string Host string diff --git a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/commonBean/bean.go b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/commonBean/bean.go index 3383def20..621b39edf 100644 --- a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/commonBean/bean.go +++ b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/commonBean/bean.go @@ -17,7 +17,9 @@ package commonBean import ( + v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" ) @@ -73,6 +75,17 @@ const ( StatefulSetsResourceType = "statefulsets" ) +const ( + ContainersType = "Containers" + ContainersNamesType = "ContainerNames" + InitContainersNamesType = "InitContainerNames" + EphemeralContainersInfoType = "EphemeralContainerInfo" + EphemeralContainersStatusType = "EphemeralContainerStatuses" + StatusReason = "Status Reason" + Node = "Node" + RestartCount = "Restart Count" +) + const ( Group = "group" Version = "version" @@ -103,6 +116,7 @@ const K8sClusterResourceNameKey = "name" const K8sClusterResourcePriorityKey = "priority" const K8sClusterResourceNamespaceKey = "namespace" const K8sClusterResourceMetadataKey = "metadata" +const K8sClusterResourceSpecKey = "spec" const K8sClusterResourceMetadataNameKey = "name" const K8sClusterResourceOwnerReferenceKey = "ownerReferences" const K8sClusterResourceCreationTimestampKey = "creationTimestamp" @@ -126,6 +140,9 @@ const V1VERSION = "v1" const BatchGroup = "batch" const AppsGroup = "apps" +const HelmHookAnnotation = "helm.sh/hook" +const HibernateReplicaAnnotation = "hibernator.devtron.ai/replicas" + const ( K8sResourceColumnDefinitionName = "Name" K8sResourceColumnDefinitionSyncStatus = "Sync Status" @@ -200,3 +217,131 @@ type GvrAndScope struct { func GetGvkVsChildGvrAndScope() map[schema.GroupVersionKind][]*GvrAndScope { return gvkVsChildGvrAndScope } + +// ResourceNode contains information about live resource and its children +type ResourceNode struct { + *ResourceRef `json:",inline" protobuf:"bytes,1,opt,name=resourceRef"` + ParentRefs []*ResourceRef `json:"parentRefs,omitempty" protobuf:"bytes,2,opt,name=parentRefs"` + NetworkingInfo *ResourceNetworkingInfo `json:"networkingInfo,omitempty" protobuf:"bytes,4,opt,name=networkingInfo"` + ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,5,opt,name=resourceVersion"` + Health *HealthStatus `json:"health,omitempty" protobuf:"bytes,7,opt,name=health"` + IsHibernated bool `json:"isHibernated"` + CanBeHibernated bool `json:"canBeHibernated"` + Info []InfoItem `json:"info,omitempty"` + Port []int64 `json:"port,omitempty"` + CreatedAt string `json:"createdAt,omitempty"` + IsHook bool `json:"isHook,omitempty"` + HookType string `json:"hookType,omitempty"` + // UpdateRevision is used when a pod's owner is a StatefulSet for identifying if the pod is new or old + UpdateRevision string `json:"updateRevision,omitempty"` + // DeploymentPodHash is the podHash in deployment manifest and is used to compare replicaSet's podHash for identifying new vs old pod + DeploymentPodHash string `json:"deploymentPodHash,omitempty"` + DeploymentCollisionCount *int32 `json:"deploymentCollisionCount,omitempty"` + // RolloutCurrentPodHash is the podHash in rollout manifest and is used to compare replicaSet's podHash for identifying new vs old pod + RolloutCurrentPodHash string `json:"rolloutCurrentPodHash,omitempty"` +} + +// ResourceRef includes fields which unique identify resource +type ResourceRef struct { + Group string `json:"group,omitempty" protobuf:"bytes,1,opt,name=group"` + Version string `json:"version,omitempty" protobuf:"bytes,2,opt,name=version"` + Kind string `json:"kind,omitempty" protobuf:"bytes,3,opt,name=kind"` + Namespace string `json:"namespace,omitempty" protobuf:"bytes,4,opt,name=namespace"` + Name string `json:"name,omitempty" protobuf:"bytes,5,opt,name=name"` + UID string `json:"uid,omitempty" protobuf:"bytes,6,opt,name=uid"` + Manifest unstructured.Unstructured `json:"-"` +} + +func (r *ResourceRef) GetGvk() schema.GroupVersionKind { + if r == nil { + return schema.GroupVersionKind{} + } + return schema.GroupVersionKind{ + Group: r.Group, + Version: r.Version, + Kind: r.Kind, + } +} + +// ResourceNetworkingInfo holds networking resource related information +type ResourceNetworkingInfo struct { + Labels map[string]string `json:"labels,omitempty" protobuf:"bytes,3,opt,name=labels"` +} + +type HealthStatus struct { + Status HealthStatusCode `json:"status,omitempty" protobuf:"bytes,1,opt,name=status"` + Message string `json:"message,omitempty" protobuf:"bytes,2,opt,name=message"` +} + +type HealthStatusCode = string + +const ( + HealthStatusUnknown HealthStatusCode = "Unknown" + HealthStatusProgressing HealthStatusCode = "Progressing" + HealthStatusHealthy HealthStatusCode = "Healthy" + HealthStatusSuspended HealthStatusCode = "Suspended" + HealthStatusDegraded HealthStatusCode = "Degraded" + HealthStatusMissing HealthStatusCode = "Missing" + HealthStatusHibernated HealthStatusCode = "Hibernated" + HealthStatusPartiallyHibernated HealthStatusCode = "Partially Hibernated" +) + +type ResourceTreeResponse struct { + Nodes []*ResourceNode `json:"nodes,omitempty"` + PodMetadata []*PodMetadata `json:"podMetadata,omitempty"` +} + +type PodMetadata struct { + Name string `json:"name"` + UID string `json:"uid"` + Containers []string `json:"containers"` + InitContainers []string `json:"initContainers"` + IsNew bool `json:"isNew"` + EphemeralContainers []*EphemeralContainerData `json:"ephemeralContainers"` +} + +// use value field as generic type +// InfoItem contains arbitrary, human readable information about an application +type InfoItem struct { + // Name is a human readable title for this piece of information. + Name string `json:"name,omitempty"` + // Value is human readable content. + Value interface{} `json:"value,omitempty"` +} + +type EphemeralContainerData struct { + Name string `json:"name"` + IsExternal bool `json:"isExternal"` +} + +type EphemeralContainerStatusesInfo struct { + Name string + State v1.ContainerState +} + +type EphemeralContainerInfo struct { + Name string + Command []string +} +type ExtraNodeInfo struct { + // UpdateRevision is only used for StatefulSets, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence + UpdateRevision string + ResourceNetworkingInfo *ResourceNetworkingInfo + RolloutCurrentPodHash string +} + +const ( + DEFAULT_CLUSTER = "default_cluster" + DEVTRON_SERVICE_NAME = "devtron-service" + DefaultClusterUrl = "https://kubernetes.default.svc" + BearerToken = "bearer_token" + CertificateAuthorityData = "cert_auth_data" + CertData = "cert_data" + TlsKey = "tls_key" + LiveZ = "/livez" + Running = "Running" + RestartingNotSupported = "restarting not supported" + DEVTRON_APP_LABEL_KEY = "app" + DEVTRON_APP_LABEL_VALUE1 = "devtron" + DEVTRON_APP_LABEL_VALUE2 = "orchestrator" +) diff --git a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/bean.go b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/bean.go new file mode 100644 index 000000000..54eafc41d --- /dev/null +++ b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/bean.go @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package health + +// Represents resource health status +type HealthStatusCode string + +const ( + // Indicates that health assessment failed and actual health status is unknown + HealthStatusUnknown HealthStatusCode = "Unknown" + // Progressing health status means that resource is not healthy but still have a chance to reach healthy state + HealthStatusProgressing HealthStatusCode = "Progressing" + // Resource is 100% healthy + HealthStatusHealthy HealthStatusCode = "Healthy" + // Assigned to resources that are suspended or paused. The typical example is a + // [suspended](https://kubernetes.io/docs/tasks/job/automated-tasks-with-cron-jobs/#suspend) CronJob. + HealthStatusSuspended HealthStatusCode = "Suspended" + // Degrade status is used if resource status indicates failure or resource could not reach healthy state + // within some timeout. + HealthStatusDegraded HealthStatusCode = "Degraded" + // Indicates that resource is missing in the cluster. + HealthStatusMissing HealthStatusCode = "Missing" +) diff --git a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health.go b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health.go new file mode 100644 index 000000000..8118b49cb --- /dev/null +++ b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health.go @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package health + +import ( + "github.com/devtron-labs/common-lib/utils/k8s/commonBean" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// Implements custom health assessment that overrides built-in assessment +type HealthOverride interface { + GetResourceHealth(obj *unstructured.Unstructured) (*HealthStatus, error) +} + +// Holds health assessment results +type HealthStatus struct { + Status HealthStatusCode `json:"status,omitempty"` + Message string `json:"message,omitempty"` +} + +// healthOrder is a list of health codes in order of most healthy to least healthy +var healthOrder = []HealthStatusCode{ + HealthStatusHealthy, + HealthStatusSuspended, + HealthStatusProgressing, + HealthStatusMissing, + HealthStatusDegraded, + HealthStatusUnknown, +} +var healthOrderMap = map[HealthStatusCode]int{ + HealthStatusHealthy: 0, + HealthStatusSuspended: 1, + HealthStatusProgressing: 2, + HealthStatusMissing: 3, + HealthStatusDegraded: 4, + HealthStatusUnknown: 5, +} + +// IsWorse returns whether or not the new health status code is a worse condition than the current +func IsWorse(current, new HealthStatusCode) bool { + if new == HealthStatusHealthy && current == HealthStatusHealthy { + return false + } else if current == HealthStatusHealthy { + return true + } else { + currentIndex := 0 + newIndex := 0 + for i, code := range healthOrder { + if current == code { + currentIndex = i + } + if new == code { + newIndex = i + } + } + return newIndex > currentIndex + } +} + +func IsWorseStatus(current, new HealthStatusCode) bool { + return healthOrderMap[new] > healthOrderMap[current] +} + +// GetResourceHealth returns the health of a k8s resource +func GetResourceHealth(obj *unstructured.Unstructured, healthOverride HealthOverride) (health *HealthStatus, err error) { + if obj.GetDeletionTimestamp() != nil { + return &HealthStatus{ + Status: HealthStatusProgressing, + Message: "Pending deletion", + }, nil + } + + if healthOverride != nil { + health, err := healthOverride.GetResourceHealth(obj) + if err != nil { + health = &HealthStatus{ + Status: HealthStatusUnknown, + Message: err.Error(), + } + return health, err + } + if health != nil { + return health, nil + } + } + + if healthCheck := GetHealthCheckFunc(obj.GroupVersionKind()); healthCheck != nil { + if health, err = healthCheck(obj); err != nil { + health = &HealthStatus{ + Status: HealthStatusUnknown, + Message: err.Error(), + } + } + } + return health, err + +} + +// GetHealthCheckFunc returns built-in health check function or nil if health check is not supported +func GetHealthCheckFunc(gvk schema.GroupVersionKind) func(obj *unstructured.Unstructured) (*HealthStatus, error) { + switch gvk.Group { + case "apps": + switch gvk.Kind { + case commonBean.DeploymentKind: + return getDeploymentHealth + case commonBean.StatefulSetKind: + return getStatefulSetHealth + case commonBean.ReplicaSetKind: + return getReplicaSetHealth + case commonBean.DaemonSetKind: + return getDaemonSetHealth + } + case "extensions": + switch gvk.Kind { + case commonBean.IngressKind: + return getIngressHealth + } + case "argoproj.io": + switch gvk.Kind { + case "Workflow": + return getArgoWorkflowHealth + } + case "apiregistration.k8s.io": + switch gvk.Kind { + case commonBean.APIServiceKind: + return getAPIServiceHealth + } + case "networking.k8s.io": + switch gvk.Kind { + case commonBean.IngressKind: + return getIngressHealth + } + case "": + switch gvk.Kind { + case commonBean.ServiceKind: + return getServiceHealth + case commonBean.PersistentVolumeClaimKind: + return getPVCHealth + case commonBean.PodKind: + return getPodHealth + } + case "batch": + switch gvk.Kind { + case commonBean.JobKind: + return getJobHealth + } + case "autoscaling": + switch gvk.Kind { + case commonBean.HorizontalPodAutoscalerKind: + return getHPAHealth + } + } + return nil +} diff --git a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_apiservice.go b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_apiservice.go new file mode 100644 index 000000000..9a86e6e85 --- /dev/null +++ b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_apiservice.go @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package health + +import ( + "fmt" + "github.com/devtron-labs/common-lib/utils/k8s/commonBean" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" + apiregistrationv1beta1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1" +) + +func getAPIServiceHealth(obj *unstructured.Unstructured) (*HealthStatus, error) { + gvk := obj.GroupVersionKind() + switch gvk { + case apiregistrationv1.SchemeGroupVersion.WithKind(commonBean.APIServiceKind): + var apiService apiregistrationv1.APIService + err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &apiService) + if err != nil { + return nil, fmt.Errorf("failed to convert unstructured APIService to typed: %v", err) + } + return getApiregistrationv1APIServiceHealth(&apiService) + case apiregistrationv1beta1.SchemeGroupVersion.WithKind(commonBean.APIServiceKind): + var apiService apiregistrationv1beta1.APIService + err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &apiService) + if err != nil { + return nil, fmt.Errorf("failed to convert unstructured APIService to typed: %v", err) + } + return getApiregistrationv1beta1APIServiceHealth(&apiService) + default: + return nil, fmt.Errorf("unsupported APIService GVK: %s", gvk) + } +} + +func getApiregistrationv1APIServiceHealth(apiservice *apiregistrationv1.APIService) (*HealthStatus, error) { + for _, c := range apiservice.Status.Conditions { + switch c.Type { + case apiregistrationv1.Available: + if c.Status == apiregistrationv1.ConditionTrue { + return &HealthStatus{ + Status: HealthStatusHealthy, + Message: fmt.Sprintf("%s: %s", c.Reason, c.Message), + }, nil + } else { + return &HealthStatus{ + Status: HealthStatusProgressing, + Message: fmt.Sprintf("%s: %s", c.Reason, c.Message), + }, nil + } + } + } + return &HealthStatus{ + Status: HealthStatusProgressing, + Message: "Waiting to be processed", + }, nil +} + +func getApiregistrationv1beta1APIServiceHealth(apiservice *apiregistrationv1beta1.APIService) (*HealthStatus, error) { + for _, c := range apiservice.Status.Conditions { + switch c.Type { + case apiregistrationv1beta1.Available: + if c.Status == apiregistrationv1beta1.ConditionTrue { + return &HealthStatus{ + Status: HealthStatusHealthy, + Message: fmt.Sprintf("%s: %s", c.Reason, c.Message), + }, nil + } else { + return &HealthStatus{ + Status: HealthStatusProgressing, + Message: fmt.Sprintf("%s: %s", c.Reason, c.Message), + }, nil + } + } + } + return &HealthStatus{ + Status: HealthStatusProgressing, + Message: "Waiting to be processed", + }, nil +} diff --git a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_argo.go b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_argo.go new file mode 100644 index 000000000..c17c72045 --- /dev/null +++ b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_argo.go @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package health + +import ( + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" +) + +type nodePhase string + +// Workflow and node statuses +// See: https://github.com/argoproj/argo-workflows/blob/master/pkg/apis/workflow/v1alpha1/workflow_phase.go +const ( + nodePending nodePhase = "Pending" + nodeRunning nodePhase = "Running" + nodeSucceeded nodePhase = "Succeeded" + nodeFailed nodePhase = "Failed" + nodeError nodePhase = "Error" +) + +// An agnostic workflow object only considers Status.Phase and Status.Message. It is agnostic to the API version or any +// other fields. +type argoWorkflow struct { + Status struct { + Phase nodePhase + Message string + } +} + +func getArgoWorkflowHealth(obj *unstructured.Unstructured) (*HealthStatus, error) { + var wf argoWorkflow + err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &wf) + if err != nil { + return nil, err + } + switch wf.Status.Phase { + case "", nodePending, nodeRunning: + return &HealthStatus{Status: HealthStatusProgressing, Message: wf.Status.Message}, nil + case nodeSucceeded: + return &HealthStatus{Status: HealthStatusHealthy, Message: wf.Status.Message}, nil + case nodeFailed, nodeError: + return &HealthStatus{Status: HealthStatusDegraded, Message: wf.Status.Message}, nil + } + return &HealthStatus{Status: HealthStatusUnknown, Message: wf.Status.Message}, nil +} diff --git a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_daemonset.go b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_daemonset.go new file mode 100644 index 000000000..4e6d2472a --- /dev/null +++ b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_daemonset.go @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package health + +import ( + "fmt" + "github.com/devtron-labs/common-lib/utils/k8s/commonBean" + + appsv1 "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" +) + +func getDaemonSetHealth(obj *unstructured.Unstructured) (*HealthStatus, error) { + gvk := obj.GroupVersionKind() + switch gvk { + case appsv1.SchemeGroupVersion.WithKind(commonBean.DaemonSetKind): + var daemon appsv1.DaemonSet + err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &daemon) + if err != nil { + return nil, fmt.Errorf("failed to convert unstructured DaemonSet to typed: %v", err) + } + return getAppsv1DaemonSetHealth(&daemon) + default: + return nil, fmt.Errorf("unsupported DaemonSet GVK: %s", gvk) + } +} + +func getAppsv1DaemonSetHealth(daemon *appsv1.DaemonSet) (*HealthStatus, error) { + // Borrowed at kubernetes/kubectl/rollout_status.go https://github.com/kubernetes/kubernetes/blob/5232ad4a00ec93942d0b2c6359ee6cd1201b46bc/pkg/kubectl/rollout_status.go#L110 + if daemon.Generation <= daemon.Status.ObservedGeneration { + if daemon.Spec.UpdateStrategy.Type == appsv1.OnDeleteDaemonSetStrategyType { + return &HealthStatus{ + Status: HealthStatusHealthy, + Message: fmt.Sprintf("daemon set %d out of %d new pods have been updated", daemon.Status.UpdatedNumberScheduled, daemon.Status.DesiredNumberScheduled), + }, nil + } + if daemon.Status.UpdatedNumberScheduled < daemon.Status.DesiredNumberScheduled { + return &HealthStatus{ + Status: HealthStatusProgressing, + Message: fmt.Sprintf("Waiting for daemon set %q rollout to finish: %d out of %d new pods have been updated...", daemon.Name, daemon.Status.UpdatedNumberScheduled, daemon.Status.DesiredNumberScheduled), + }, nil + } + if daemon.Status.NumberAvailable < daemon.Status.DesiredNumberScheduled { + return &HealthStatus{ + Status: HealthStatusProgressing, + Message: fmt.Sprintf("Waiting for daemon set %q rollout to finish: %d of %d updated pods are available...", daemon.Name, daemon.Status.NumberAvailable, daemon.Status.DesiredNumberScheduled), + }, nil + } + } else { + return &HealthStatus{ + Status: HealthStatusProgressing, + Message: "Waiting for rollout to finish: observed daemon set generation less than desired generation", + }, nil + } + return &HealthStatus{ + Status: HealthStatusHealthy, + }, nil +} diff --git a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_deployment.go b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_deployment.go new file mode 100644 index 000000000..003d7565c --- /dev/null +++ b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_deployment.go @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package health + +import ( + "fmt" + "github.com/devtron-labs/common-lib/utils/k8s/commonBean" + + appsv1 "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" +) + +func getDeploymentHealth(obj *unstructured.Unstructured) (*HealthStatus, error) { + gvk := obj.GroupVersionKind() + switch gvk { + case appsv1.SchemeGroupVersion.WithKind(commonBean.DeploymentKind): + var deployment appsv1.Deployment + err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &deployment) + if err != nil { + return nil, fmt.Errorf("failed to convert unstructured Deployment to typed: %v", err) + } + return getAppsv1DeploymentHealth(&deployment) + default: + return nil, fmt.Errorf("unsupported Deployment GVK: %s", gvk) + } +} + +func getAppsv1DeploymentHealth(deployment *appsv1.Deployment) (*HealthStatus, error) { + if deployment.Spec.Paused { + return &HealthStatus{ + Status: HealthStatusSuspended, + Message: "Deployment is paused", + }, nil + } + // Borrowed at kubernetes/kubectl/rollout_status.go https://github.com/kubernetes/kubernetes/blob/5232ad4a00ec93942d0b2c6359ee6cd1201b46bc/pkg/kubectl/rollout_status.go#L80 + if deployment.Generation <= deployment.Status.ObservedGeneration { + cond := getAppsv1DeploymentCondition(deployment.Status, appsv1.DeploymentProgressing) + if cond != nil && cond.Reason == "ProgressDeadlineExceeded" { + return &HealthStatus{ + Status: HealthStatusDegraded, + Message: fmt.Sprintf("Deployment %q exceeded its progress deadline", deployment.Name), + }, nil + } else if deployment.Spec.Replicas != nil && deployment.Status.UpdatedReplicas < *deployment.Spec.Replicas { + return &HealthStatus{ + Status: HealthStatusProgressing, + Message: fmt.Sprintf("Waiting for rollout to finish: %d out of %d new replicas have been updated...", deployment.Status.UpdatedReplicas, *deployment.Spec.Replicas), + }, nil + } else if deployment.Status.Replicas > deployment.Status.UpdatedReplicas { + return &HealthStatus{ + Status: HealthStatusProgressing, + Message: fmt.Sprintf("Waiting for rollout to finish: %d old replicas are pending termination...", deployment.Status.Replicas-deployment.Status.UpdatedReplicas), + }, nil + } else if deployment.Status.AvailableReplicas < deployment.Status.UpdatedReplicas { + return &HealthStatus{ + Status: HealthStatusProgressing, + Message: fmt.Sprintf("Waiting for rollout to finish: %d of %d updated replicas are available...", deployment.Status.AvailableReplicas, deployment.Status.UpdatedReplicas), + }, nil + } + } else { + return &HealthStatus{ + Status: HealthStatusProgressing, + Message: "Waiting for rollout to finish: observed deployment generation less than desired generation", + }, nil + } + + return &HealthStatus{ + Status: HealthStatusHealthy, + }, nil +} + +func getAppsv1DeploymentCondition(status appsv1.DeploymentStatus, condType appsv1.DeploymentConditionType) *appsv1.DeploymentCondition { + for i := range status.Conditions { + c := status.Conditions[i] + if c.Type == condType { + return &c + } + } + return nil +} diff --git a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_hpa.go b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_hpa.go new file mode 100644 index 000000000..5465c9979 --- /dev/null +++ b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_hpa.go @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package health + +import ( + "encoding/json" + "fmt" + "github.com/devtron-labs/common-lib/utils/k8s/commonBean" + + autoscalingv1 "k8s.io/api/autoscaling/v1" + autoscalingv2 "k8s.io/api/autoscaling/v2" + autoscalingv2beta1 "k8s.io/api/autoscaling/v2beta1" + autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" +) + +var ( + progressingStatus = &HealthStatus{ + Status: HealthStatusProgressing, + Message: "Waiting to Autoscale", + } +) + +type hpaCondition struct { + Type string + Reason string + Message string + Status string +} + +func getHPAHealth(obj *unstructured.Unstructured) (*HealthStatus, error) { + gvk := obj.GroupVersionKind() + failedConversionMsg := "failed to convert unstructured HPA to typed: %v" + + switch gvk { + case autoscalingv1.SchemeGroupVersion.WithKind(commonBean.HorizontalPodAutoscalerKind): + var hpa autoscalingv1.HorizontalPodAutoscaler + err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &hpa) + if err != nil { + return nil, fmt.Errorf(failedConversionMsg, err) + } + return getAutoScalingV1HPAHealth(&hpa) + case autoscalingv2beta1.SchemeGroupVersion.WithKind(commonBean.HorizontalPodAutoscalerKind): + var hpa autoscalingv2beta1.HorizontalPodAutoscaler + err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &hpa) + if err != nil { + return nil, fmt.Errorf(failedConversionMsg, err) + } + return getAutoScalingV2beta1HPAHealth(&hpa) + case autoscalingv2beta2.SchemeGroupVersion.WithKind(commonBean.HorizontalPodAutoscalerKind): + var hpa autoscalingv2beta2.HorizontalPodAutoscaler + err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &hpa) + if err != nil { + return nil, fmt.Errorf(failedConversionMsg, err) + } + return getAutoScalingV2beta2HPAHealth(&hpa) + case autoscalingv2.SchemeGroupVersion.WithKind(commonBean.HorizontalPodAutoscalerKind): + var hpa autoscalingv2.HorizontalPodAutoscaler + err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &hpa) + if err != nil { + return nil, fmt.Errorf(failedConversionMsg, err) + } + return getAutoScalingV2HPAHealth(&hpa) + default: + return nil, fmt.Errorf("unsupported HPA GVK: %s", gvk) + } +} + +func getAutoScalingV2HPAHealth(hpa *autoscalingv2.HorizontalPodAutoscaler) (*HealthStatus, error) { + statusConditions := hpa.Status.Conditions + conditions := make([]hpaCondition, 0, len(statusConditions)) + for _, statusCondition := range statusConditions { + conditions = append(conditions, hpaCondition{ + Type: string(statusCondition.Type), + Reason: statusCondition.Reason, + Message: statusCondition.Message, + Status: string(statusCondition.Status), + }) + } + + return checkConditions(conditions, progressingStatus) +} + +func getAutoScalingV2beta2HPAHealth(hpa *autoscalingv2beta2.HorizontalPodAutoscaler) (*HealthStatus, error) { + statusConditions := hpa.Status.Conditions + conditions := make([]hpaCondition, 0, len(statusConditions)) + for _, statusCondition := range statusConditions { + conditions = append(conditions, hpaCondition{ + Type: string(statusCondition.Type), + Reason: statusCondition.Reason, + Message: statusCondition.Message, + Status: string(statusCondition.Status), + }) + } + + return checkConditions(conditions, progressingStatus) +} + +func getAutoScalingV2beta1HPAHealth(hpa *autoscalingv2beta1.HorizontalPodAutoscaler) (*HealthStatus, error) { + statusConditions := hpa.Status.Conditions + conditions := make([]hpaCondition, 0, len(statusConditions)) + for _, statusCondition := range statusConditions { + conditions = append(conditions, hpaCondition{ + Type: string(statusCondition.Type), + Reason: statusCondition.Reason, + Message: statusCondition.Message, + Status: string(statusCondition.Status), + }) + } + + return checkConditions(conditions, progressingStatus) +} + +func getAutoScalingV1HPAHealth(hpa *autoscalingv1.HorizontalPodAutoscaler) (*HealthStatus, error) { + annotation, ok := hpa.GetAnnotations()["autoscaling.alpha.kubernetes.io/conditions"] + if !ok { + return progressingStatus, nil + } + + var conditions []hpaCondition + err := json.Unmarshal([]byte(annotation), &conditions) + if err != nil { + failedMessage := "failed to convert conditions annotation to typed: %v" + return nil, fmt.Errorf(failedMessage, err) + } + + if len(conditions) == 0 { + return progressingStatus, nil + } + + return checkConditions(conditions, progressingStatus) +} + +func checkConditions(conditions []hpaCondition, progressingStatus *HealthStatus) (*HealthStatus, error) { + for _, condition := range conditions { + if isDegraded(&condition) { + return &HealthStatus{ + Status: HealthStatusDegraded, + Message: condition.Message, + }, nil + } + + if isHealthy(&condition) { + return &HealthStatus{ + Status: HealthStatusHealthy, + Message: condition.Message, + }, nil + } + } + + return progressingStatus, nil +} + +func isDegraded(condition *hpaCondition) bool { + degraded_states := []hpaCondition{ + {Type: "AbleToScale", Reason: "FailedGetScale"}, + {Type: "AbleToScale", Reason: "FailedUpdateScale"}, + {Type: "ScalingActive", Reason: "FailedGetResourceMetric"}, + {Type: "ScalingActive", Reason: "InvalidSelector"}, + } + for _, degraded_state := range degraded_states { + if condition.Type == degraded_state.Type && condition.Reason == degraded_state.Reason { + return true + } + } + return false +} + +func isHealthy(condition *hpaCondition) bool { + healthyConditionTypes := []string{"AbleToScale", "ScalingLimited"} + for _, conditionType := range healthyConditionTypes { + if condition.Type == conditionType && condition.Status == "True" { + return true + } + } + return false +} diff --git a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_ingress.go b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_ingress.go new file mode 100644 index 000000000..0f0ea3ab8 --- /dev/null +++ b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_ingress.go @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package health + +import ( + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +func getIngressHealth(obj *unstructured.Unstructured) (*HealthStatus, error) { + ingresses, _, _ := unstructured.NestedSlice(obj.Object, "status", "loadBalancer", "ingress") + health := HealthStatus{} + if len(ingresses) > 0 { + health.Status = HealthStatusHealthy + } else { + health.Status = HealthStatusProgressing + } + return &health, nil +} diff --git a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_job.go b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_job.go new file mode 100644 index 000000000..b49175f4e --- /dev/null +++ b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_job.go @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package health + +import ( + "fmt" + "github.com/devtron-labs/common-lib/utils/k8s/commonBean" + + batchv1 "k8s.io/api/batch/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" +) + +func getJobHealth(obj *unstructured.Unstructured) (*HealthStatus, error) { + gvk := obj.GroupVersionKind() + switch gvk { + case batchv1.SchemeGroupVersion.WithKind(commonBean.JobKind): + var job batchv1.Job + err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &job) + if err != nil { + return nil, fmt.Errorf("failed to convert unstructured Job to typed: %v", err) + } + return getBatchv1JobHealth(&job) + default: + return nil, fmt.Errorf("unsupported Job GVK: %s", gvk) + } +} + +func getBatchv1JobHealth(job *batchv1.Job) (*HealthStatus, error) { + failed := false + var failMsg string + complete := false + var message string + for _, condition := range job.Status.Conditions { + switch condition.Type { + case batchv1.JobFailed: + failed = true + complete = true + failMsg = condition.Message + case batchv1.JobComplete: + complete = true + message = condition.Message + } + } + if !complete { + return &HealthStatus{ + Status: HealthStatusProgressing, + Message: message, + }, nil + } else if failed { + return &HealthStatus{ + Status: HealthStatusDegraded, + Message: failMsg, + }, nil + } else { + return &HealthStatus{ + Status: HealthStatusHealthy, + Message: message, + }, nil + } +} diff --git a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_pod.go b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_pod.go new file mode 100644 index 000000000..d066f20cf --- /dev/null +++ b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_pod.go @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package health + +import ( + "fmt" + "github.com/devtron-labs/common-lib/utils/k8s/commonBean" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/kubectl/pkg/util/podutils" + "strings" +) + +func getPodHealth(obj *unstructured.Unstructured) (*HealthStatus, error) { + gvk := obj.GroupVersionKind() + switch gvk { + case corev1.SchemeGroupVersion.WithKind(commonBean.PodKind): + var pod corev1.Pod + err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &pod) + if err != nil { + return nil, fmt.Errorf("failed to convert unstructured Pod to typed: %v", err) + } + return getCorev1PodHealth(&pod) + default: + return nil, fmt.Errorf("unsupported Pod GVK: %s", gvk) + } +} + +func getCorev1PodHealth(pod *corev1.Pod) (*HealthStatus, error) { + // This logic cannot be applied when the pod.Spec.RestartPolicy is: corev1.RestartPolicyOnFailure, + // corev1.RestartPolicyNever, otherwise it breaks the resource hook logic. + // The issue is, if we mark a pod with ImagePullBackOff as Degraded, and the pod is used as a resource hook, + // then we will prematurely fail the PreSync/PostSync hook. Meanwhile, when that error condition is resolved + // (e.g. the image is available), the resource hook pod will unexpectedly be executed even though the sync has + // completed. + if pod.Spec.RestartPolicy == corev1.RestartPolicyAlways { + var status HealthStatusCode + var messages []string + + for _, containerStatus := range pod.Status.ContainerStatuses { + waiting := containerStatus.State.Waiting + // Article listing common container errors: https://medium.com/kokster/debugging-crashloopbackoffs-with-init-containers-26f79e9fb5bf + if waiting != nil && (strings.HasPrefix(waiting.Reason, "Err") || strings.HasSuffix(waiting.Reason, "Error") || strings.HasSuffix(waiting.Reason, "BackOff")) { + status = HealthStatusDegraded + messages = append(messages, waiting.Message) + } + } + + if status != "" { + return &HealthStatus{ + Status: status, + Message: strings.Join(messages, ", "), + }, nil + } + } + + getFailMessage := func(ctr *corev1.ContainerStatus) string { + if ctr.State.Terminated != nil { + if ctr.State.Terminated.Message != "" { + return ctr.State.Terminated.Message + } + if ctr.State.Terminated.Reason == "OOMKilled" { + return ctr.State.Terminated.Reason + } + if ctr.State.Terminated.ExitCode != 0 { + return fmt.Sprintf("container %q failed with exit code %d", ctr.Name, ctr.State.Terminated.ExitCode) + } + } + return "" + } + + switch pod.Status.Phase { + case corev1.PodPending: + return &HealthStatus{ + Status: HealthStatusProgressing, + Message: pod.Status.Message, + }, nil + case corev1.PodSucceeded: + return &HealthStatus{ + Status: HealthStatusHealthy, + Message: pod.Status.Message, + }, nil + case corev1.PodFailed: + if pod.Status.Message != "" { + // Pod has a nice error message. Use that. + return &HealthStatus{Status: HealthStatusDegraded, Message: pod.Status.Message}, nil + } + for _, ctr := range append(pod.Status.InitContainerStatuses, pod.Status.ContainerStatuses...) { + if msg := getFailMessage(&ctr); msg != "" { + return &HealthStatus{Status: HealthStatusDegraded, Message: msg}, nil + } + } + + return &HealthStatus{Status: HealthStatusDegraded, Message: ""}, nil + case corev1.PodRunning: + switch pod.Spec.RestartPolicy { + case corev1.RestartPolicyAlways: + // if pod is ready, it is automatically healthy + if podutils.IsPodReady(pod) { + return &HealthStatus{ + Status: HealthStatusHealthy, + Message: pod.Status.Message, + }, nil + } + // if it's not ready, check to see if any container terminated, if so, it's degraded + for _, ctrStatus := range pod.Status.ContainerStatuses { + if ctrStatus.LastTerminationState.Terminated != nil { + return &HealthStatus{ + Status: HealthStatusDegraded, + Message: pod.Status.Message, + }, nil + } + } + // otherwise we are progressing towards a ready state + return &HealthStatus{ + Status: HealthStatusProgressing, + Message: pod.Status.Message, + }, nil + case corev1.RestartPolicyOnFailure, corev1.RestartPolicyNever: + // pods set with a restart policy of OnFailure or Never, have a finite life. + // These pods are typically resource hooks. Thus, we consider these as Progressing + // instead of healthy. + return &HealthStatus{ + Status: HealthStatusProgressing, + Message: pod.Status.Message, + }, nil + } + } + return &HealthStatus{ + Status: HealthStatusUnknown, + Message: pod.Status.Message, + }, nil +} diff --git a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_pvc.go b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_pvc.go new file mode 100644 index 000000000..8dd95be65 --- /dev/null +++ b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_pvc.go @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package health + +import ( + "fmt" + "github.com/devtron-labs/common-lib/utils/k8s/commonBean" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" +) + +func getPVCHealth(obj *unstructured.Unstructured) (*HealthStatus, error) { + gvk := obj.GroupVersionKind() + switch gvk { + case corev1.SchemeGroupVersion.WithKind(commonBean.PersistentVolumeClaimKind): + var pvc corev1.PersistentVolumeClaim + err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &pvc) + if err != nil { + return nil, fmt.Errorf("failed to convert unstructured PersistentVolumeClaim to typed: %v", err) + } + return getCorev1PVCHealth(&pvc) + default: + return nil, fmt.Errorf("unsupported PersistentVolumeClaim GVK: %s", gvk) + } +} + +func getCorev1PVCHealth(pvc *corev1.PersistentVolumeClaim) (*HealthStatus, error) { + var status HealthStatusCode + switch pvc.Status.Phase { + case corev1.ClaimLost: + status = HealthStatusDegraded + case corev1.ClaimPending: + status = HealthStatusProgressing + case corev1.ClaimBound: + status = HealthStatusHealthy + default: + status = HealthStatusUnknown + } + return &HealthStatus{Status: status}, nil +} diff --git a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_replicaset.go b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_replicaset.go new file mode 100644 index 000000000..6a9a2972d --- /dev/null +++ b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_replicaset.go @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package health + +import ( + "fmt" + "github.com/devtron-labs/common-lib/utils/k8s/commonBean" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" +) + +func getReplicaSetHealth(obj *unstructured.Unstructured) (*HealthStatus, error) { + gvk := obj.GroupVersionKind() + switch gvk { + case appsv1.SchemeGroupVersion.WithKind(commonBean.ReplicaSetKind): + var replicaSet appsv1.ReplicaSet + err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &replicaSet) + if err != nil { + return nil, fmt.Errorf("failed to convert unstructured ReplicaSet to typed: %v", err) + } + return getAppsv1ReplicaSetHealth(&replicaSet) + default: + return nil, fmt.Errorf("unsupported ReplicaSet GVK: %s", gvk) + } +} + +func getAppsv1ReplicaSetHealth(replicaSet *appsv1.ReplicaSet) (*HealthStatus, error) { + if replicaSet.Generation <= replicaSet.Status.ObservedGeneration { + cond := getAppsv1ReplicaSetCondition(replicaSet.Status, appsv1.ReplicaSetReplicaFailure) + if cond != nil && cond.Status == corev1.ConditionTrue { + return &HealthStatus{ + Status: HealthStatusDegraded, + Message: cond.Message, + }, nil + } else if replicaSet.Spec.Replicas != nil && replicaSet.Status.AvailableReplicas < *replicaSet.Spec.Replicas { + return &HealthStatus{ + Status: HealthStatusProgressing, + Message: fmt.Sprintf("Waiting for rollout to finish: %d out of %d new replicas are available...", replicaSet.Status.AvailableReplicas, *replicaSet.Spec.Replicas), + }, nil + } + } else { + return &HealthStatus{ + Status: HealthStatusProgressing, + Message: "Waiting for rollout to finish: observed replica set generation less than desired generation", + }, nil + } + + return &HealthStatus{ + Status: HealthStatusHealthy, + }, nil +} + +func getAppsv1ReplicaSetCondition(status appsv1.ReplicaSetStatus, condType appsv1.ReplicaSetConditionType) *appsv1.ReplicaSetCondition { + for i := range status.Conditions { + c := status.Conditions[i] + if c.Type == condType { + return &c + } + } + return nil +} diff --git a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_service.go b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_service.go new file mode 100644 index 000000000..16e28a71e --- /dev/null +++ b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_service.go @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package health + +import ( + "fmt" + "github.com/devtron-labs/common-lib/utils/k8s/commonBean" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" +) + +func getServiceHealth(obj *unstructured.Unstructured) (*HealthStatus, error) { + gvk := obj.GroupVersionKind() + switch gvk { + case corev1.SchemeGroupVersion.WithKind(commonBean.ServiceKind): + var service corev1.Service + err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &service) + if err != nil { + return nil, fmt.Errorf("failed to convert unstructured Service to typed: %v", err) + } + return getCorev1ServiceHealth(&service) + default: + return nil, fmt.Errorf("unsupported Service GVK: %s", gvk) + } +} + +func getCorev1ServiceHealth(service *corev1.Service) (*HealthStatus, error) { + health := HealthStatus{Status: HealthStatusHealthy} + if service.Spec.Type == corev1.ServiceTypeLoadBalancer { + if len(service.Status.LoadBalancer.Ingress) > 0 { + health.Status = HealthStatusHealthy + } else { + health.Status = HealthStatusProgressing + } + } + return &health, nil +} diff --git a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_statefulset.go b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_statefulset.go new file mode 100644 index 000000000..2811d36e8 --- /dev/null +++ b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8s/health/health_statefulset.go @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package health + +import ( + "fmt" + "github.com/devtron-labs/common-lib/utils/k8s/commonBean" + + appsv1 "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" +) + +func getStatefulSetHealth(obj *unstructured.Unstructured) (*HealthStatus, error) { + gvk := obj.GroupVersionKind() + switch gvk { + case appsv1.SchemeGroupVersion.WithKind(commonBean.StatefulSetKind): + var sts appsv1.StatefulSet + err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &sts) + if err != nil { + return nil, fmt.Errorf("failed to convert unstructured StatefulSet to typed: %v", err) + } + return getAppsv1StatefulSetHealth(&sts) + default: + return nil, fmt.Errorf("unsupported StatefulSet GVK: %s", gvk) + } +} + +func getAppsv1StatefulSetHealth(sts *appsv1.StatefulSet) (*HealthStatus, error) { + // Borrowed at kubernetes/kubectl/rollout_status.go https://github.com/kubernetes/kubernetes/blob/5232ad4a00ec93942d0b2c6359ee6cd1201b46bc/pkg/kubectl/rollout_status.go#L131 + if sts.Status.ObservedGeneration == 0 || sts.Generation > sts.Status.ObservedGeneration { + return &HealthStatus{ + Status: HealthStatusProgressing, + Message: "Waiting for statefulset spec update to be observed...", + }, nil + } + if sts.Spec.Replicas != nil && sts.Status.ReadyReplicas < *sts.Spec.Replicas { + return &HealthStatus{ + Status: HealthStatusProgressing, + Message: fmt.Sprintf("Waiting for %d pods to be ready...", *sts.Spec.Replicas-sts.Status.ReadyReplicas), + }, nil + } + if sts.Spec.UpdateStrategy.Type == appsv1.RollingUpdateStatefulSetStrategyType && sts.Spec.UpdateStrategy.RollingUpdate != nil { + if sts.Spec.Replicas != nil && sts.Spec.UpdateStrategy.RollingUpdate.Partition != nil { + if sts.Status.UpdatedReplicas < (*sts.Spec.Replicas - *sts.Spec.UpdateStrategy.RollingUpdate.Partition) { + return &HealthStatus{ + Status: HealthStatusProgressing, + Message: fmt.Sprintf("Waiting for partitioned roll out to finish: %d out of %d new pods have been updated...", + sts.Status.UpdatedReplicas, (*sts.Spec.Replicas - *sts.Spec.UpdateStrategy.RollingUpdate.Partition)), + }, nil + } + } + return &HealthStatus{ + Status: HealthStatusHealthy, + Message: fmt.Sprintf("partitioned roll out complete: %d new pods have been updated...", sts.Status.UpdatedReplicas), + }, nil + } + if sts.Spec.UpdateStrategy.Type == appsv1.OnDeleteStatefulSetStrategyType { + return &HealthStatus{ + Status: HealthStatusHealthy, + Message: fmt.Sprintf("statefulset has %d ready pods", sts.Status.ReadyReplicas), + }, nil + } + if sts.Status.UpdateRevision != sts.Status.CurrentRevision { + return &HealthStatus{ + Status: HealthStatusProgressing, + Message: fmt.Sprintf("waiting for statefulset rolling update to complete %d pods at revision %s...", sts.Status.UpdatedReplicas, sts.Status.UpdateRevision), + }, nil + } + return &HealthStatus{ + Status: HealthStatusHealthy, + Message: fmt.Sprintf("statefulset rolling update complete %d pods at revision %s...", sts.Status.CurrentReplicas, sts.Status.CurrentRevision), + }, nil +} diff --git a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8sObjectsUtil/EphemeralContainersUtil.go b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8sObjectsUtil/EphemeralContainersUtil.go index 95c590e95..4522a5f96 100644 --- a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8sObjectsUtil/EphemeralContainersUtil.go +++ b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8sObjectsUtil/EphemeralContainersUtil.go @@ -74,7 +74,3 @@ func IsExternalEphemeralContainer(cmds []string, name string) bool { } return isExternal } - -func IsPod(kind string, group string) bool { - return kind == "Pod" && group == "" -} diff --git a/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8sObjectsUtil/ResourceTreeUtil.go b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8sObjectsUtil/ResourceTreeUtil.go new file mode 100644 index 000000000..c2ce6260a --- /dev/null +++ b/kubewatch/vendor/github.com/devtron-labs/common-lib/utils/k8sObjectsUtil/ResourceTreeUtil.go @@ -0,0 +1,700 @@ +package k8sObjectsUtil + +import ( + "encoding/binary" + "fmt" + "github.com/davecgh/go-spew/spew" + "github.com/devtron-labs/common-lib/utils/k8s/commonBean" + "github.com/devtron-labs/common-lib/utils/k8s/health" + "hash" + "hash/fnv" + v1 "k8s.io/api/core/v1" + "k8s.io/api/extensions/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/rand" +) + +func GetNodeFromResource(manifest *unstructured.Unstructured, resourceReference v1.ObjectReference, ownerRefs []metav1.OwnerReference, requestedNamespace string) (*commonBean.ResourceNode, error) { + gvk := manifest.GroupVersionKind() + _namespace := manifest.GetNamespace() + if _namespace == "" { + _namespace = requestedNamespace + } + ports := GetPorts(manifest, gvk) + resourceRef := BuildResourceRef(gvk, *manifest, _namespace) + + creationTimeStamp := "" + val, found, err := unstructured.NestedString(manifest.Object, "metadata", "creationTimestamp") + if found && err == nil { + creationTimeStamp = val + } + node := &commonBean.ResourceNode{ + ResourceRef: resourceRef, + ResourceVersion: manifest.GetResourceVersion(), + NetworkingInfo: &commonBean.ResourceNetworkingInfo{ + Labels: manifest.GetLabels(), + }, + CreatedAt: creationTimeStamp, + Port: ports, + } + node.IsHook, node.HookType = GetHookMetadata(manifest) + + // set health of Node + SetHealthStatusForNode(node, manifest, gvk) + + // hibernate set starts + if len(ownerRefs) == 0 { + + // set CanBeHibernated + SetHibernationRules(node, &node.Manifest) + } + // hibernate set ends + + if IsPod(gvk.Kind, gvk.Group) { + infoItems, _ := PopulatePodInfo(manifest) + node.Info = infoItems + } + AddSelectiveInfoInResourceNode(node, gvk, manifest.Object) + + return node, nil +} + +func GetPorts(manifest *unstructured.Unstructured, gvk schema.GroupVersionKind) []int64 { + ports := make([]int64, 0) + if gvk.Kind == commonBean.ServiceKind { + ports = append(ports, getPortsFromService(manifest)...) + } + if gvk.Kind == commonBean.EndPointsSlice { + ports = append(ports, getPortsFromEndPointsSlice(manifest)...) + } + if gvk.Kind == commonBean.EndpointsKind { + ports = append(ports, getPortsFromEndpointsKind(manifest)...) + } + return ports +} + +func getPortsFromService(manifest *unstructured.Unstructured) []int64 { + var ports []int64 + if manifest.Object["spec"] != nil { + spec := manifest.Object["spec"].(map[string]interface{}) + if spec["ports"] != nil { + portList := spec["ports"].([]interface{}) + for _, portItem := range portList { + if portItem.(map[string]interface{}) != nil { + _portNumber := portItem.(map[string]interface{})["port"] + portNumber := _portNumber.(int64) + if portNumber != 0 { + ports = append(ports, portNumber) + } + } + } + } + } + return ports +} + +func getPortsFromEndPointsSlice(manifest *unstructured.Unstructured) []int64 { + var ports []int64 + if manifest.Object["ports"] != nil { + endPointsSlicePorts := manifest.Object["ports"].([]interface{}) + for _, val := range endPointsSlicePorts { + _portNumber := val.(map[string]interface{})["port"] + portNumber := _portNumber.(int64) + if portNumber != 0 { + ports = append(ports, portNumber) + } + } + } + return ports +} + +func getPortsFromEndpointsKind(manifest *unstructured.Unstructured) []int64 { + var ports []int64 + if manifest.Object["subsets"] != nil { + subsets := manifest.Object["subsets"].([]interface{}) + for _, subset := range subsets { + subsetObj := subset.(map[string]interface{}) + if subsetObj != nil { + portsIfs := subsetObj["ports"].([]interface{}) + for _, portsIf := range portsIfs { + portsIfObj := portsIf.(map[string]interface{}) + if portsIfObj != nil { + port := portsIfObj["port"].(int64) + ports = append(ports, port) + } + } + } + } + } + return ports +} + +func BuildResourceRef(gvk schema.GroupVersionKind, manifest unstructured.Unstructured, namespace string) *commonBean.ResourceRef { + resourceRef := &commonBean.ResourceRef{ + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind, + Namespace: namespace, + Name: manifest.GetName(), + UID: string(manifest.GetUID()), + Manifest: manifest, + } + return resourceRef +} + +func GetHookMetadata(manifest *unstructured.Unstructured) (bool, string) { + annotations, found, _ := unstructured.NestedStringMap(manifest.Object, "metadata", "annotations") + if found { + if hookType, ok := annotations[commonBean.HelmHookAnnotation]; ok { + return true, hookType + } + } + return false, "" +} + +func SetHealthStatusForNode(res *commonBean.ResourceNode, un *unstructured.Unstructured, gvk schema.GroupVersionKind) { + if IsService(gvk) && un.GetName() == commonBean.DEVTRON_SERVICE_NAME && IsDevtronApp(res.NetworkingInfo.Labels) { + res.Health = &commonBean.HealthStatus{ + Status: commonBean.HealthStatusHealthy, + } + } else { + if healthCheck := health.GetHealthCheckFunc(gvk); healthCheck != nil { + health, err := healthCheck(un) + if err != nil { + res.Health = &commonBean.HealthStatus{ + Status: commonBean.HealthStatusUnknown, + Message: err.Error(), + } + } else if health != nil { + res.Health = &commonBean.HealthStatus{ + Status: string(health.Status), + Message: health.Message, + } + } + } + } +} + +func SetHibernationRules(res *commonBean.ResourceNode, un *unstructured.Unstructured) { + if un.GetOwnerReferences() == nil { + // set CanBeHibernated + replicas, found, _ := unstructured.NestedInt64(un.UnstructuredContent(), "spec", "replicas") + if found { + res.CanBeHibernated = true + } + + // set IsHibernated + annotations := un.GetAnnotations() + if annotations != nil { + if val, ok := annotations[commonBean.HibernateReplicaAnnotation]; ok { + if val != "0" && replicas == 0 { + res.IsHibernated = true + } + } + } + } +} + +func PopulatePodInfo(un *unstructured.Unstructured) ([]commonBean.InfoItem, error) { + var infoItems []commonBean.InfoItem + + pod := v1.Pod{} + err := runtime.DefaultUnstructuredConverter.FromUnstructured(un.Object, &pod) + if err != nil { + return nil, err + } + restarts := 0 + totalContainers := len(pod.Spec.Containers) + readyContainers := 0 + + reason := string(pod.Status.Phase) + if pod.Status.Reason != "" { + reason = pod.Status.Reason + } + + initializing := false + for i := range pod.Status.InitContainerStatuses { + container := pod.Status.InitContainerStatuses[i] + restarts += int(container.RestartCount) + switch { + case container.State.Terminated != nil && container.State.Terminated.ExitCode == 0: + continue + case container.State.Terminated != nil: + // initialization is failed + if len(container.State.Terminated.Reason) == 0 { + if container.State.Terminated.Signal != 0 { + reason = fmt.Sprintf("Init:Signal:%d", container.State.Terminated.Signal) + } else { + reason = fmt.Sprintf("Init:ExitCode:%d", container.State.Terminated.ExitCode) + } + } else { + reason = "Init:" + container.State.Terminated.Reason + } + initializing = true + case container.State.Waiting != nil && len(container.State.Waiting.Reason) > 0 && container.State.Waiting.Reason != "PodInitializing": + reason = "Init:" + container.State.Waiting.Reason + initializing = true + default: + reason = fmt.Sprintf("Init:%d/%d", i, len(pod.Spec.InitContainers)) + initializing = true + } + break + } + if !initializing { + restarts = 0 + hasRunning := false + for i := len(pod.Status.ContainerStatuses) - 1; i >= 0; i-- { + container := pod.Status.ContainerStatuses[i] + + restarts += int(container.RestartCount) + if container.State.Waiting != nil && container.State.Waiting.Reason != "" { + reason = container.State.Waiting.Reason + } else if container.State.Terminated != nil && container.State.Terminated.Reason != "" { + reason = container.State.Terminated.Reason + } else if container.State.Terminated != nil && container.State.Terminated.Reason == "" { + if container.State.Terminated.Signal != 0 { + reason = fmt.Sprintf("Signal:%d", container.State.Terminated.Signal) + } else { + reason = fmt.Sprintf("ExitCode:%d", container.State.Terminated.ExitCode) + } + } else if container.Ready && container.State.Running != nil { + hasRunning = true + readyContainers++ + } + } + + // change pod status back to "Running" if there is at least one container still reporting as "Running" status + if reason == "Completed" && hasRunning { + reason = "Running" + } + } + + // "NodeLost" = https://github.com/kubernetes/kubernetes/blob/cb8ad64243d48d9a3c26b11b2e0945c098457282/pkg/util/node/node.go#L46 + // But depending on the k8s.io/kubernetes package just for a constant + // is not worth it. + // See https://github.com/argoproj/argo-cd/issues/5173 + // and https://github.com/kubernetes/kubernetes/issues/90358#issuecomment-617859364 + if pod.DeletionTimestamp != nil && pod.Status.Reason == "NodeLost" { + reason = "Unknown" + } else if pod.DeletionTimestamp != nil { + reason = "Terminating" + } + infoItems = getAllInfoItems(infoItems, reason, restarts, readyContainers, totalContainers, pod) + return infoItems, nil +} + +func getAllInfoItems(infoItems []commonBean.InfoItem, reason string, restarts int, readyContainers int, totalContainers int, pod v1.Pod) []commonBean.InfoItem { + if reason != "" { + infoItems = append(infoItems, commonBean.InfoItem{Name: commonBean.StatusReason, Value: reason}) + } + infoItems = append(infoItems, commonBean.InfoItem{Name: commonBean.Node, Value: pod.Spec.NodeName}) + + containerNames, initContainerNames, ephemeralContainersInfo, ephemeralContainerStatus := getContainersInfo(pod) + + infoItems = append(infoItems, commonBean.InfoItem{Name: commonBean.ContainersType, Value: fmt.Sprintf("%d/%d", readyContainers, totalContainers)}) + infoItems = append(infoItems, commonBean.InfoItem{Name: commonBean.ContainersNamesType, Value: containerNames}) + infoItems = append(infoItems, commonBean.InfoItem{Name: commonBean.InitContainersNamesType, Value: initContainerNames}) + infoItems = append(infoItems, commonBean.InfoItem{Name: commonBean.EphemeralContainersInfoType, Value: ephemeralContainersInfo}) + infoItems = append(infoItems, commonBean.InfoItem{Name: commonBean.EphemeralContainersStatusType, Value: ephemeralContainerStatus}) + + if restarts > 0 { + infoItems = append(infoItems, commonBean.InfoItem{Name: commonBean.RestartCount, Value: fmt.Sprintf("%d", restarts)}) + } + return infoItems +} + +func getContainersInfo(pod v1.Pod) ([]string, []string, []commonBean.EphemeralContainerInfo, []commonBean.EphemeralContainerStatusesInfo) { + containerNames := make([]string, 0, len(pod.Spec.Containers)) + initContainerNames := make([]string, 0, len(pod.Spec.InitContainers)) + ephemeralContainers := make([]commonBean.EphemeralContainerInfo, 0, len(pod.Spec.EphemeralContainers)) + ephemeralContainerStatus := make([]commonBean.EphemeralContainerStatusesInfo, 0, len(pod.Status.EphemeralContainerStatuses)) + for _, container := range pod.Spec.Containers { + containerNames = append(containerNames, container.Name) + } + for _, initContainer := range pod.Spec.InitContainers { + initContainerNames = append(initContainerNames, initContainer.Name) + } + for _, ec := range pod.Spec.EphemeralContainers { + ecData := commonBean.EphemeralContainerInfo{ + Name: ec.Name, + Command: ec.Command, + } + ephemeralContainers = append(ephemeralContainers, ecData) + } + for _, ecStatus := range pod.Status.EphemeralContainerStatuses { + status := commonBean.EphemeralContainerStatusesInfo{ + Name: ecStatus.Name, + State: ecStatus.State, + } + ephemeralContainerStatus = append(ephemeralContainerStatus, status) + } + return containerNames, initContainerNames, ephemeralContainers, ephemeralContainerStatus +} + +func AddSelectiveInfoInResourceNode(resourceNode *commonBean.ResourceNode, gvk schema.GroupVersionKind, obj map[string]interface{}) { + if gvk.Kind == commonBean.StatefulSetKind { + resourceNode.UpdateRevision = GetUpdateRevisionForStatefulSet(obj) + } + if gvk.Kind == commonBean.DeploymentKind { + deployment, _ := ConvertToV1Deployment(obj) + if deployment == nil { + return + } + deploymentPodHash := ComputePodHash(&deployment.Spec.Template, deployment.Status.CollisionCount) + resourceNode.DeploymentPodHash = deploymentPodHash + resourceNode.DeploymentCollisionCount = deployment.Status.CollisionCount + } + if gvk.Kind == commonBean.K8sClusterResourceRolloutKind { + rolloutPodHash, found, _ := unstructured.NestedString(obj, "status", "currentPodHash") + if found { + resourceNode.RolloutCurrentPodHash = rolloutPodHash + } + } +} + +func ComputePodHash(template *v1.PodTemplateSpec, collisionCount *int32) string { + podTemplateSpecHasher := fnv.New32a() + DeepHashObject(podTemplateSpecHasher, *template) + + // Add collisionCount in the hash if it exists. + if collisionCount != nil { + collisionCountBytes := make([]byte, 8) + binary.LittleEndian.PutUint32(collisionCountBytes, uint32(*collisionCount)) + _, err := podTemplateSpecHasher.Write(collisionCountBytes) + if err != nil { + fmt.Println(err) + } + } + return rand.SafeEncodeString(fmt.Sprint(podTemplateSpecHasher.Sum32())) +} + +func ConvertToV1Deployment(nodeObj map[string]interface{}) (*v1beta1.Deployment, error) { + deploymentObj := v1beta1.Deployment{} + err := runtime.DefaultUnstructuredConverter.FromUnstructured(nodeObj, &deploymentObj) + if err != nil { + return nil, err + } + return &deploymentObj, nil +} + +func GetUpdateRevisionForStatefulSet(obj map[string]interface{}) string { + updateRevisionFromManifest, found, _ := unstructured.NestedString(obj, "status", "updateRevision") + if found { + return updateRevisionFromManifest + } + return "" +} + +// DeepHashObject writes specified object to hash using the spew library +// which follows pointers and prints actual values of the nested objects +// ensuring the hash does not change when a pointer changes. +func DeepHashObject(hasher hash.Hash, objectToWrite interface{}) { + hasher.Reset() + printer := spew.ConfigState{ + Indent: " ", + SortKeys: true, + DisableMethods: true, + SpewKeys: true, + } + _, err := printer.Fprintf(hasher, "%#v", objectToWrite) + if err != nil { + fmt.Println(err) + } +} + +func BuildPodMetadata(nodes []*commonBean.ResourceNode) ([]*commonBean.PodMetadata, error) { + podsMetadata := make([]*commonBean.PodMetadata, 0, len(nodes)) + for _, node := range nodes { + + if node.Kind != commonBean.PodKind { + continue + } + // set containers,initContainers and ephemeral container names + var containerNames []string + var initContainerNames []string + var ephemeralContainersInfo []commonBean.EphemeralContainerInfo + var ephemeralContainerStatus []commonBean.EphemeralContainerStatusesInfo + + for _, nodeInfo := range node.Info { + switch nodeInfo.Name { + case commonBean.ContainersNamesType: + containerNames = nodeInfo.Value.([]string) + case commonBean.InitContainersNamesType: + initContainerNames = nodeInfo.Value.([]string) + case commonBean.EphemeralContainersInfoType: + ephemeralContainersInfo = nodeInfo.Value.([]commonBean.EphemeralContainerInfo) + case commonBean.EphemeralContainersStatusType: + ephemeralContainerStatus = nodeInfo.Value.([]commonBean.EphemeralContainerStatusesInfo) + default: + continue + } + } + + ephemeralContainerStatusMap := make(map[string]bool) + for _, c := range ephemeralContainerStatus { + // c.state contains three states running,waiting and terminated + // at any point of time only one state will be there + if c.State.Running != nil { + ephemeralContainerStatusMap[c.Name] = true + } + } + ephemeralContainers := make([]*commonBean.EphemeralContainerData, 0, len(ephemeralContainersInfo)) + // sending only running ephemeral containers in the list + for _, ec := range ephemeralContainersInfo { + if _, ok := ephemeralContainerStatusMap[ec.Name]; ok { + containerData := &commonBean.EphemeralContainerData{ + Name: ec.Name, + IsExternal: IsExternalEphemeralContainer(ec.Command, ec.Name), + } + ephemeralContainers = append(ephemeralContainers, containerData) + } + } + + podMetadata := &commonBean.PodMetadata{ + Name: node.Name, + UID: node.UID, + Containers: containerNames, + InitContainers: initContainerNames, + EphemeralContainers: ephemeralContainers, + } + + podsMetadata = append(podsMetadata, podMetadata) + + } + return podsMetadata, nil +} + +func GetExtraNodeInfoMappings(nodes []*commonBean.ResourceNode) (map[string]string, map[string]*commonBean.ExtraNodeInfo, map[string]*commonBean.ExtraNodeInfo) { + deploymentPodHashMap := make(map[string]string) + rolloutNameVsExtraNodeInfoMapping := make(map[string]*commonBean.ExtraNodeInfo) + uidVsExtraNodeInfoMapping := make(map[string]*commonBean.ExtraNodeInfo) + for _, node := range nodes { + if node.Kind == commonBean.DeploymentKind { + deploymentPodHashMap[node.Name] = node.DeploymentPodHash + } else if node.Kind == commonBean.K8sClusterResourceRolloutKind { + rolloutNameVsExtraNodeInfoMapping[node.Name] = &commonBean.ExtraNodeInfo{ + RolloutCurrentPodHash: node.RolloutCurrentPodHash, + } + } else if node.Kind == commonBean.StatefulSetKind || node.Kind == commonBean.DaemonSetKind { + if _, ok := uidVsExtraNodeInfoMapping[node.UID]; !ok { + uidVsExtraNodeInfoMapping[node.UID] = &commonBean.ExtraNodeInfo{UpdateRevision: node.UpdateRevision, ResourceNetworkingInfo: node.NetworkingInfo} + } + } + } + return deploymentPodHashMap, rolloutNameVsExtraNodeInfoMapping, uidVsExtraNodeInfoMapping +} + +func IsPodNew(nodes []*commonBean.ResourceNode, node *commonBean.ResourceNode, deploymentPodHashMap map[string]string, rolloutMap map[string]*commonBean.ExtraNodeInfo, + uidVsExtraNodeInfoMap map[string]*commonBean.ExtraNodeInfo) (bool, error) { + + isNew := false + parentRef := node.ParentRefs[0] + parentKind := parentRef.Kind + + // if parent is StatefulSet - then pod label controller-revision-hash should match StatefulSet's update revision + if parentKind == commonBean.StatefulSetKind && node.NetworkingInfo != nil { + isNew = uidVsExtraNodeInfoMap[parentRef.UID].UpdateRevision == node.NetworkingInfo.Labels["controller-revision-hash"] + } + + // if parent is Job - then pod label controller-revision-hash should match StatefulSet's update revision + if parentKind == commonBean.JobKind { + // TODO - new or old logic not built in orchestrator for Job's pods. hence not implementing here. as don't know the logic :) + isNew = true + } + + // if parent kind is replica set then + if parentKind == commonBean.ReplicaSetKind { + replicaSetNode := GetMatchingNode(nodes, parentKind, parentRef.Name) + + // if parent of replicaset is deployment, compare label pod-template-hash + if replicaSetParent := replicaSetNode.ParentRefs[0]; replicaSetNode != nil && len(replicaSetNode.ParentRefs) > 0 && replicaSetParent.Kind == commonBean.DeploymentKind { + deploymentPodHash := deploymentPodHashMap[replicaSetParent.Name] + replicaSetObj, err := GetReplicaSetObject(replicaSetNode) + if err != nil { + return isNew, err + } + deploymentNode := GetMatchingNode(nodes, replicaSetParent.Kind, replicaSetParent.Name) + // TODO: why do we need deployment object for collisionCount ?? + var deploymentCollisionCount *int32 + if deploymentNode != nil && deploymentNode.DeploymentCollisionCount != nil { + deploymentCollisionCount = deploymentNode.DeploymentCollisionCount + } else { + deploymentCollisionCount, err = getDeploymentCollisionCount(replicaSetParent) + if err != nil { + return isNew, err + } + } + replicaSetPodHash := GetReplicaSetPodHash(replicaSetObj, deploymentCollisionCount) + isNew = replicaSetPodHash == deploymentPodHash + } else if replicaSetParent.Kind == commonBean.K8sClusterResourceRolloutKind { + + rolloutExtraInfo := rolloutMap[replicaSetParent.Name] + rolloutPodHash := rolloutExtraInfo.RolloutCurrentPodHash + replicasetPodHash := GetRolloutPodTemplateHash(replicaSetNode) + + isNew = rolloutPodHash == replicasetPodHash + + } + + } + + // if parent kind is DaemonSet then compare DaemonSet's Child ControllerRevision's label controller-revision-hash with pod label controller-revision-hash + if parentKind == commonBean.DaemonSetKind { + controllerRevisionNodes := GetMatchingNodes(nodes, "ControllerRevision") + for _, controllerRevisionNode := range controllerRevisionNodes { + if len(controllerRevisionNode.ParentRefs) > 0 && controllerRevisionNode.ParentRefs[0].Kind == parentKind && + controllerRevisionNode.ParentRefs[0].Name == parentRef.Name && uidVsExtraNodeInfoMap[parentRef.UID].ResourceNetworkingInfo != nil && + node.NetworkingInfo != nil { + + isNew = uidVsExtraNodeInfoMap[parentRef.UID].ResourceNetworkingInfo.Labels["controller-revision-hash"] == node.NetworkingInfo.Labels["controller-revision-hash"] + } + } + } + return isNew, nil +} + +func GetRolloutPodTemplateHash(replicasetNode *commonBean.ResourceNode) string { + if rolloutPodTemplateHash, ok := replicasetNode.NetworkingInfo.Labels["rollouts-pod-template-hash"]; ok { + return rolloutPodTemplateHash + } + return "" +} + +func getDeploymentCollisionCount(deploymentInfo *commonBean.ResourceRef) (*int32, error) { + + var deploymentNodeObj map[string]interface{} + var err error + deploymentNodeObj = deploymentInfo.Manifest.Object + + deploymentObj, err := ConvertToV1Deployment(deploymentNodeObj) + if err != nil { + return nil, err + } + return deploymentObj.Status.CollisionCount, nil +} + +func GetMatchingNode(nodes []*commonBean.ResourceNode, kind string, name string) *commonBean.ResourceNode { + for _, node := range nodes { + if node.Kind == kind && node.Name == name { + return node + } + } + return nil +} + +func GetMatchingNodes(nodes []*commonBean.ResourceNode, kind string) []*commonBean.ResourceNode { + nodesRes := make([]*commonBean.ResourceNode, 0, len(nodes)) + for _, node := range nodes { + if node.Kind == kind { + nodesRes = append(nodesRes, node) + } + } + return nodesRes +} + +func GetReplicaSetObject(replicaSetNode *commonBean.ResourceNode) (*v1beta1.ReplicaSet, error) { + var replicaSetNodeObj map[string]interface{} + var err error + replicaSetNodeObj = replicaSetNode.Manifest.Object + + replicaSetObj, err := ConvertToV1ReplicaSet(replicaSetNodeObj) + if err != nil { + return nil, err + } + return replicaSetObj, nil +} + +func ConvertToV1ReplicaSet(nodeObj map[string]interface{}) (*v1beta1.ReplicaSet, error) { + replicaSetObj := v1beta1.ReplicaSet{} + err := runtime.DefaultUnstructuredConverter.FromUnstructured(nodeObj, &replicaSetObj) + if err != nil { + return nil, err + } + return &replicaSetObj, nil +} + +func GetReplicaSetPodHash(replicasetObj *v1beta1.ReplicaSet, collisionCount *int32) string { + labels := make(map[string]string) + for k, v := range replicasetObj.Spec.Template.Labels { + if k != "pod-template-hash" { + labels[k] = v + } + } + replicasetObj.Spec.Template.Labels = labels + podHash := ComputePodHash(&replicasetObj.Spec.Template, collisionCount) + return podHash +} + +func IsDevtronApp(labels map[string]string) bool { + isDevtronApp := false + if val, ok := labels[commonBean.DEVTRON_APP_LABEL_KEY]; ok { + if val == commonBean.DEVTRON_APP_LABEL_VALUE1 || val == commonBean.DEVTRON_APP_LABEL_VALUE2 { + isDevtronApp = true + } + } + return isDevtronApp +} + +func IsService(gvk schema.GroupVersionKind) bool { + return gvk.Group == "" && gvk.Kind == commonBean.ServiceKind +} + +func IsPod(kind string, group string) bool { + return kind == "Pod" && group == "" +} + +func GetMatchingPodMetadataForUID(podMetadatas []*commonBean.PodMetadata, uid string) *commonBean.PodMetadata { + if len(podMetadatas) == 0 { + return nil + } + for _, podMetadata := range podMetadatas { + if podMetadata.UID == uid { + return podMetadata + } + } + return nil +} + +// app health is worst of the nodes health +// or if app status is healthy then check for hibernation status +func BuildAppHealthStatus(nodes []*commonBean.ResourceNode) *commonBean.HealthStatusCode { + appHealthStatus := commonBean.HealthStatusHealthy + isAppFullyHibernated := true + var isAppPartiallyHibernated bool + var isAnyNodeCanByHibernated bool + + for _, node := range nodes { + if node.IsHook { + continue + } + nodeHealth := node.Health + if node.CanBeHibernated { + isAnyNodeCanByHibernated = true + if !node.IsHibernated { + isAppFullyHibernated = false + } else { + isAppPartiallyHibernated = true + } + } + if nodeHealth == nil { + continue + } + if health.IsWorseStatus(health.HealthStatusCode(appHealthStatus), health.HealthStatusCode(nodeHealth.Status)) { + appHealthStatus = nodeHealth.Status + } + } + + // override hibernate status on app level if status is healthy and hibernation done + if appHealthStatus == commonBean.HealthStatusHealthy && isAnyNodeCanByHibernated { + if isAppFullyHibernated { + appHealthStatus = commonBean.HealthStatusHibernated + } else if isAppPartiallyHibernated { + appHealthStatus = commonBean.HealthStatusPartiallyHibernated + } + } + + return &appHealthStatus +} diff --git a/kubewatch/vendor/modules.txt b/kubewatch/vendor/modules.txt index cb566123d..ec8496a2f 100644 --- a/kubewatch/vendor/modules.txt +++ b/kubewatch/vendor/modules.txt @@ -211,7 +211,7 @@ github.com/cyphar/filepath-securejoin # github.com/davecgh/go-spew v1.1.1 ## explicit github.com/davecgh/go-spew/spew -# github.com/devtron-labs/common-lib v0.16.1-0.20240923063129-ff2dc035435e +# github.com/devtron-labs/common-lib v0.0.0 => github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da ## explicit; go 1.21 github.com/devtron-labs/common-lib/constants github.com/devtron-labs/common-lib/git-manager/util @@ -226,6 +226,7 @@ github.com/devtron-labs/common-lib/utils/bean github.com/devtron-labs/common-lib/utils/http github.com/devtron-labs/common-lib/utils/k8s github.com/devtron-labs/common-lib/utils/k8s/commonBean +github.com/devtron-labs/common-lib/utils/k8s/health github.com/devtron-labs/common-lib/utils/k8sObjectsUtil github.com/devtron-labs/common-lib/utils/remoteConnection/bean github.com/devtron-labs/common-lib/utils/yaml @@ -1753,3 +1754,4 @@ upper.io/db.v3/postgresql # k8s.io/mount-utils => k8s.io/mount-utils v0.29.7 # k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.29.7 # k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.29.7 +# github.com/devtron-labs/common-lib => github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da diff --git a/lens/go.mod b/lens/go.mod index e2defd7a4..f32149dd2 100644 --- a/lens/go.mod +++ b/lens/go.mod @@ -43,7 +43,7 @@ require ( go.opentelemetry.io/otel/metric v1.20.0 // indirect go.opentelemetry.io/otel/trace v1.20.0 // indirect go.uber.org/atomic v1.10.0 // indirect - go.uber.org/multierr v1.6.0 // indirect + go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.25.0 // indirect golang.org/x/net v0.27.0 // indirect golang.org/x/sys v0.22.0 // indirect @@ -54,7 +54,9 @@ require ( ) require ( - github.com/devtron-labs/common-lib v0.16.1-0.20240911071031-2625327bc7b4 + github.com/devtron-labs/common-lib v0.0.0 github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/gomega v1.18.1 // indirect ) + +replace github.com/devtron-labs/common-lib => github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da diff --git a/lens/go.sum b/lens/go.sum index 7710d38c6..f2e8d6929 100644 --- a/lens/go.sum +++ b/lens/go.sum @@ -22,8 +22,8 @@ github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWH 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/devtron-labs/common-lib v0.16.1-0.20240911071031-2625327bc7b4 h1:OWhV5B2SQRWZges8cltVsyUrdA/8EByBjjRxX95qN7o= -github.com/devtron-labs/common-lib v0.16.1-0.20240911071031-2625327bc7b4/go.mod h1:rAY9Xd6iz+OqNQ3nO3reVHapAVr1N6Osf4Irdc0A08Q= +github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da h1:vC6SMz6BM1doN+ZBGiDGyERJ/LphFQi5+Ab/YQkNJVo= +github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da/go.mod h1:KpKnF4OSpQNDJmb4wVZq3Za88ePBw4xec2GOAGRm5UQ= github.com/devtron-labs/protos v0.0.3-0.20240130061723-7b2e12ab0abb h1:CkfQQgZc950/hTPqtQSiHV2RmZgkBLGCzwR02FZYjAU= github.com/devtron-labs/protos v0.0.3-0.20240130061723-7b2e12ab0abb/go.mod h1:pjLjgoa1GzbkOkvbMyP4SAKsaiK7eG6GoQCNauG03JA= github.com/docker/cli v24.0.6+incompatible h1:fF+XCQCgJjjQNIMjzaSmiKJSCcfcXb3TWTcc7GAneOY= @@ -166,8 +166,9 @@ go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= diff --git a/lens/vendor/github.com/devtron-labs/common-lib/LICENSE b/lens/vendor/github.com/devtron-labs/common-lib/LICENSE index 261eeb9e9..57bc88a15 100644 --- a/lens/vendor/github.com/devtron-labs/common-lib/LICENSE +++ b/lens/vendor/github.com/devtron-labs/common-lib/LICENSE @@ -199,3 +199,4 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + diff --git a/lens/vendor/github.com/devtron-labs/common-lib/constants/constants.go b/lens/vendor/github.com/devtron-labs/common-lib/constants/constants.go index bd2f53a67..2b34bf833 100644 --- a/lens/vendor/github.com/devtron-labs/common-lib/constants/constants.go +++ b/lens/vendor/github.com/devtron-labs/common-lib/constants/constants.go @@ -38,13 +38,14 @@ const ( // metrics name constants const ( - NATS_PUBLISH_COUNT = "nats_publish_count" - NATS_CONSUMPTION_COUNT = "nats_consumption_count" - NATS_CONSUMING_COUNT = "nats_consuming_count" - NATS_EVENT_CONSUMPTION_TIME = "nats_event_consumption_time" - NATS_EVENT_PUBLISH_TIME = "nats_event_publish_time" - NATS_EVENT_DELIVERY_COUNT = "nats_event_delivery_count" - PANIC_RECOVERY_COUNT = "panic_recovery_count" + NATS_PUBLISH_COUNT = "nats_publish_count" + NATS_CONSUMPTION_COUNT = "nats_consumption_count" + NATS_CONSUMING_COUNT = "nats_consuming_count" + NATS_EVENT_CONSUMPTION_TIME = "nats_event_consumption_time" + NATS_EVENT_PUBLISH_TIME = "nats_event_publish_time" + NATS_EVENT_DELIVERY_COUNT = "nats_event_delivery_count" + PANIC_RECOVERY_COUNT = "panic_recovery_count" + REVERSE_PROXY_PANIC_RECOVERY_COUNT = "reverse_proxy_panic_recovery_count" ) // metrics labels constant diff --git a/lens/vendor/github.com/devtron-labs/common-lib/pubsub-lib/metrics/metrics.go b/lens/vendor/github.com/devtron-labs/common-lib/pubsub-lib/metrics/metrics.go index f67c225f4..a0ba04500 100644 --- a/lens/vendor/github.com/devtron-labs/common-lib/pubsub-lib/metrics/metrics.go +++ b/lens/vendor/github.com/devtron-labs/common-lib/pubsub-lib/metrics/metrics.go @@ -53,6 +53,10 @@ var PanicRecoveryCount = promauto.NewCounterVec(prometheus.CounterOpts{ Name: constants.PANIC_RECOVERY_COUNT, }, []string{constants.PANIC_TYPE, constants.HOST, constants.METHOD, constants.PATH}) +var ReverseProxyPanicRecoveryCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: constants.REVERSE_PROXY_PANIC_RECOVERY_COUNT, +}, []string{constants.PANIC_TYPE, constants.HOST, constants.METHOD, constants.PATH}) + func IncPublishCount(topic, status string) { NatsPublishingCount.WithLabelValues(topic, status).Inc() } @@ -64,6 +68,11 @@ func IncConsumptionCount(topic string) { func IncConsumingCount(topic string) { NatsConsumingCount.WithLabelValues(topic).Inc() } + func IncPanicRecoveryCount(panicType, host, method, path string) { PanicRecoveryCount.WithLabelValues(panicType, host, method, path).Inc() } + +func IncReverseProxyPanicRecoveryCount(panicType, host, method, path string) { + ReverseProxyPanicRecoveryCount.WithLabelValues(panicType, host, method, path).Inc() +} diff --git a/lens/vendor/github.com/devtron-labs/common-lib/utils/CommonUtils.go b/lens/vendor/github.com/devtron-labs/common-lib/utils/CommonUtils.go index 95c0f3b74..1a6c8307c 100644 --- a/lens/vendor/github.com/devtron-labs/common-lib/utils/CommonUtils.go +++ b/lens/vendor/github.com/devtron-labs/common-lib/utils/CommonUtils.go @@ -22,6 +22,7 @@ import ( "github.com/devtron-labs/common-lib/utils/bean" "log" "math/rand" + "os" "path" "regexp" "strings" @@ -30,7 +31,11 @@ import ( var chars = []rune("abcdefghijklmnopqrstuvwxyz0123456789") -const DOCKER_REGISTRY_TYPE_DOCKERHUB = "docker-hub" +const ( + DOCKER_REGISTRY_TYPE_DOCKERHUB = "docker-hub" + DEVTRON_SELF_POD_UID = "DEVTRON_SELF_POD_UID" + DEVTRON_SELF_POD_NAME = "DEVTRON_SELF_POD_NAME" +) // Generates random string func Generate(size int) string { @@ -82,3 +87,11 @@ func BuildDockerImagePath(dockerInfo bean.DockerRegistryInfo) (string, error) { } return dest, nil } + +func GetSelfK8sUID() string { + return os.Getenv(DEVTRON_SELF_POD_UID) +} + +func GetSelfK8sPodName() string { + return os.Getenv(DEVTRON_SELF_POD_NAME) +} diff --git a/lens/vendor/go.uber.org/multierr/.travis.yml b/lens/vendor/go.uber.org/multierr/.travis.yml deleted file mode 100644 index 8636ab42a..000000000 --- a/lens/vendor/go.uber.org/multierr/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -sudo: false -language: go -go_import_path: go.uber.org/multierr - -env: - global: - - GO111MODULE=on - -go: - - oldstable - - stable - -before_install: -- go version - -script: -- | - set -e - make lint - make cover - -after_success: -- bash <(curl -s https://codecov.io/bash) diff --git a/lens/vendor/go.uber.org/multierr/CHANGELOG.md b/lens/vendor/go.uber.org/multierr/CHANGELOG.md index 6f1db9ef4..f8177b978 100644 --- a/lens/vendor/go.uber.org/multierr/CHANGELOG.md +++ b/lens/vendor/go.uber.org/multierr/CHANGELOG.md @@ -1,6 +1,41 @@ Releases ======== +v1.11.0 (2023-03-28) +==================== +- `Errors` now supports any error that implements multiple-error + interface. +- Add `Every` function to allow checking if all errors in the chain + satisfies `errors.Is` against the target error. + +v1.10.0 (2023-03-08) +==================== + +- Comply with Go 1.20's multiple-error interface. +- Drop Go 1.18 support. + Per the support policy, only Go 1.19 and 1.20 are supported now. +- Drop all non-test external dependencies. + +v1.9.0 (2022-12-12) +=================== + +- Add `AppendFunc` that allow passsing functions to similar to + `AppendInvoke`. + +- Bump up yaml.v3 dependency to 3.0.1. + +v1.8.0 (2022-02-28) +=================== + +- `Combine`: perform zero allocations when there are no errors. + + +v1.7.0 (2021-05-06) +=================== + +- Add `AppendInvoke` to append into errors from `defer` blocks. + + v1.6.0 (2020-09-14) =================== diff --git a/lens/vendor/go.uber.org/multierr/LICENSE.txt b/lens/vendor/go.uber.org/multierr/LICENSE.txt index 858e02475..413e30f7c 100644 --- a/lens/vendor/go.uber.org/multierr/LICENSE.txt +++ b/lens/vendor/go.uber.org/multierr/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2017 Uber Technologies, Inc. +Copyright (c) 2017-2021 Uber Technologies, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lens/vendor/go.uber.org/multierr/Makefile b/lens/vendor/go.uber.org/multierr/Makefile index 316004400..dcb6fe723 100644 --- a/lens/vendor/go.uber.org/multierr/Makefile +++ b/lens/vendor/go.uber.org/multierr/Makefile @@ -34,9 +34,5 @@ lint: gofmt golint staticcheck .PHONY: cover cover: - go test -coverprofile=cover.out -coverpkg=./... -v ./... + go test -race -coverprofile=cover.out -coverpkg=./... -v ./... go tool cover -html=cover.out -o cover.html - -update-license: - @cd tools && go install go.uber.org/tools/update-license - @$(GOBIN)/update-license $(GO_FILES) diff --git a/lens/vendor/go.uber.org/multierr/README.md b/lens/vendor/go.uber.org/multierr/README.md index 751bd65e5..5ab6ac40f 100644 --- a/lens/vendor/go.uber.org/multierr/README.md +++ b/lens/vendor/go.uber.org/multierr/README.md @@ -2,9 +2,29 @@ `multierr` allows combining one or more Go `error`s together. +## Features + +- **Idiomatic**: + multierr follows best practices in Go, and keeps your code idiomatic. + - It keeps the underlying error type hidden, + allowing you to deal in `error` values exclusively. + - It provides APIs to safely append into an error from a `defer` statement. +- **Performant**: + multierr is optimized for performance: + - It avoids allocations where possible. + - It utilizes slice resizing semantics to optimize common cases + like appending into the same error object from a loop. +- **Interoperable**: + multierr interoperates with the Go standard library's error APIs seamlessly: + - The `errors.Is` and `errors.As` functions *just work*. +- **Lightweight**: + multierr comes with virtually no dependencies. + ## Installation - go get -u go.uber.org/multierr +```bash +go get -u go.uber.org/multierr@latest +``` ## Status @@ -15,9 +35,9 @@ Stable: No breaking changes will be made before 2.0. Released under the [MIT License]. [MIT License]: LICENSE.txt -[doc-img]: https://godoc.org/go.uber.org/multierr?status.svg -[doc]: https://godoc.org/go.uber.org/multierr -[ci-img]: https://travis-ci.com/uber-go/multierr.svg?branch=master +[doc-img]: https://pkg.go.dev/badge/go.uber.org/multierr +[doc]: https://pkg.go.dev/go.uber.org/multierr +[ci-img]: https://github.com/uber-go/multierr/actions/workflows/go.yml/badge.svg [cov-img]: https://codecov.io/gh/uber-go/multierr/branch/master/graph/badge.svg -[ci]: https://travis-ci.com/uber-go/multierr +[ci]: https://github.com/uber-go/multierr/actions/workflows/go.yml [cov]: https://codecov.io/gh/uber-go/multierr diff --git a/lens/vendor/go.uber.org/multierr/error.go b/lens/vendor/go.uber.org/multierr/error.go index 5c9b67d53..3a828b2df 100644 --- a/lens/vendor/go.uber.org/multierr/error.go +++ b/lens/vendor/go.uber.org/multierr/error.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Uber Technologies, Inc. +// Copyright (c) 2017-2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -20,54 +20,109 @@ // Package multierr allows combining one or more errors together. // -// Overview +// # Overview // // Errors can be combined with the use of the Combine function. // -// multierr.Combine( -// reader.Close(), -// writer.Close(), -// conn.Close(), -// ) +// multierr.Combine( +// reader.Close(), +// writer.Close(), +// conn.Close(), +// ) // // If only two errors are being combined, the Append function may be used // instead. // -// err = multierr.Append(reader.Close(), writer.Close()) -// -// This makes it possible to record resource cleanup failures from deferred -// blocks with the help of named return values. -// -// func sendRequest(req Request) (err error) { -// conn, err := openConnection() -// if err != nil { -// return err -// } -// defer func() { -// err = multierr.Append(err, conn.Close()) -// }() -// // ... -// } +// err = multierr.Append(reader.Close(), writer.Close()) // // The underlying list of errors for a returned error object may be retrieved // with the Errors function. // -// errors := multierr.Errors(err) -// if len(errors) > 0 { -// fmt.Println("The following errors occurred:", errors) -// } +// errors := multierr.Errors(err) +// if len(errors) > 0 { +// fmt.Println("The following errors occurred:", errors) +// } +// +// # Appending from a loop +// +// You sometimes need to append into an error from a loop. +// +// var err error +// for _, item := range items { +// err = multierr.Append(err, process(item)) +// } +// +// Cases like this may require knowledge of whether an individual instance +// failed. This usually requires introduction of a new variable. +// +// var err error +// for _, item := range items { +// if perr := process(item); perr != nil { +// log.Warn("skipping item", item) +// err = multierr.Append(err, perr) +// } +// } +// +// multierr includes AppendInto to simplify cases like this. +// +// var err error +// for _, item := range items { +// if multierr.AppendInto(&err, process(item)) { +// log.Warn("skipping item", item) +// } +// } +// +// This will append the error into the err variable, and return true if that +// individual error was non-nil. // -// Advanced Usage +// See [AppendInto] for more information. +// +// # Deferred Functions +// +// Go makes it possible to modify the return value of a function in a defer +// block if the function was using named returns. This makes it possible to +// record resource cleanup failures from deferred blocks. +// +// func sendRequest(req Request) (err error) { +// conn, err := openConnection() +// if err != nil { +// return err +// } +// defer func() { +// err = multierr.Append(err, conn.Close()) +// }() +// // ... +// } +// +// multierr provides the Invoker type and AppendInvoke function to make cases +// like the above simpler and obviate the need for a closure. The following is +// roughly equivalent to the example above. +// +// func sendRequest(req Request) (err error) { +// conn, err := openConnection() +// if err != nil { +// return err +// } +// defer multierr.AppendInvoke(&err, multierr.Close(conn)) +// // ... +// } +// +// See [AppendInvoke] and [Invoker] for more information. +// +// NOTE: If you're modifying an error from inside a defer, you MUST use a named +// return value for that function. +// +// # Advanced Usage // // Errors returned by Combine and Append MAY implement the following // interface. // -// type errorGroup interface { -// // Returns a slice containing the underlying list of errors. -// // -// // This slice MUST NOT be modified by the caller. -// Errors() []error -// } +// type errorGroup interface { +// // Returns a slice containing the underlying list of errors. +// // +// // This slice MUST NOT be modified by the caller. +// Errors() []error +// } // // Note that if you need access to list of errors behind a multierr error, you // should prefer using the Errors function. That said, if you need cheap @@ -76,23 +131,23 @@ // because errors returned by Combine and Append are not guaranteed to // implement this interface. // -// var errors []error -// group, ok := err.(errorGroup) -// if ok { -// errors = group.Errors() -// } else { -// errors = []error{err} -// } +// var errors []error +// group, ok := err.(errorGroup) +// if ok { +// errors = group.Errors() +// } else { +// errors = []error{err} +// } package multierr // import "go.uber.org/multierr" import ( "bytes" + "errors" "fmt" "io" "strings" "sync" - - "go.uber.org/atomic" + "sync/atomic" ) var ( @@ -132,34 +187,15 @@ type errorGroup interface { // Errors returns a slice containing zero or more errors that the supplied // error is composed of. If the error is nil, a nil slice is returned. // -// err := multierr.Append(r.Close(), w.Close()) -// errors := multierr.Errors(err) +// err := multierr.Append(r.Close(), w.Close()) +// errors := multierr.Errors(err) // // If the error is not composed of other errors, the returned slice contains // just the error that was passed in. // // Callers of this function are free to modify the returned slice. func Errors(err error) []error { - if err == nil { - return nil - } - - // Note that we're casting to multiError, not errorGroup. Our contract is - // that returned errors MAY implement errorGroup. Errors, however, only - // has special behavior for multierr-specific error objects. - // - // This behavior can be expanded in the future but I think it's prudent to - // start with as little as possible in terms of contract and possibility - // of misuse. - eg, ok := err.(*multiError) - if !ok { - return []error{err} - } - - errors := eg.Errors() - result := make([]error, len(errors)) - copy(result, errors) - return result + return extractErrors(err) } // multiError is an error that holds one or more errors. @@ -174,8 +210,6 @@ type multiError struct { errors []error } -var _ errorGroup = (*multiError)(nil) - // Errors returns the list of underlying errors. // // This slice MUST NOT be modified. @@ -201,6 +235,17 @@ func (merr *multiError) Error() string { return result } +// Every compares every error in the given err against the given target error +// using [errors.Is], and returns true only if every comparison returned true. +func Every(err error, target error) bool { + for _, e := range extractErrors(err) { + if !errors.Is(e, target) { + return false + } + } + return true +} + func (merr *multiError) Format(f fmt.State, c rune) { if c == 'v' && f.Flag('+') { merr.writeMultiline(f) @@ -292,6 +337,14 @@ func inspect(errors []error) (res inspectResult) { // fromSlice converts the given list of errors into a single error. func fromSlice(errors []error) error { + // Don't pay to inspect small slices. + switch len(errors) { + case 0: + return nil + case 1: + return errors[0] + } + res := inspect(errors) switch res.Count { case 0: @@ -301,8 +354,12 @@ func fromSlice(errors []error) error { return errors[res.FirstErrorIdx] case len(errors): if !res.ContainsMultiError { - // already flat - return &multiError{errors: errors} + // Error list is flat. Make a copy of it + // Otherwise "errors" escapes to the heap + // unconditionally for all other cases. + // This lets us optimize for the "no errors" case. + out := append(([]error)(nil), errors...) + return &multiError{errors: out} } } @@ -327,32 +384,32 @@ func fromSlice(errors []error) error { // If zero arguments were passed or if all items are nil, a nil error is // returned. // -// Combine(nil, nil) // == nil +// Combine(nil, nil) // == nil // // If only a single error was passed, it is returned as-is. // -// Combine(err) // == err +// Combine(err) // == err // // Combine skips over nil arguments so this function may be used to combine // together errors from operations that fail independently of each other. // -// multierr.Combine( -// reader.Close(), -// writer.Close(), -// pipe.Close(), -// ) +// multierr.Combine( +// reader.Close(), +// writer.Close(), +// pipe.Close(), +// ) // // If any of the passed errors is a multierr error, it will be flattened along // with the other errors. // -// multierr.Combine(multierr.Combine(err1, err2), err3) -// // is the same as -// multierr.Combine(err1, err2, err3) +// multierr.Combine(multierr.Combine(err1, err2), err3) +// // is the same as +// multierr.Combine(err1, err2, err3) // // The returned error formats into a readable multi-line error message if // formatted with %+v. // -// fmt.Sprintf("%+v", multierr.Combine(err1, err2)) +// fmt.Sprintf("%+v", multierr.Combine(err1, err2)) func Combine(errors ...error) error { return fromSlice(errors) } @@ -362,16 +419,19 @@ func Combine(errors ...error) error { // This function is a specialization of Combine for the common case where // there are only two errors. // -// err = multierr.Append(reader.Close(), writer.Close()) +// err = multierr.Append(reader.Close(), writer.Close()) // // The following pattern may also be used to record failure of deferred // operations without losing information about the original error. // -// func doSomething(..) (err error) { -// f := acquireResource() -// defer func() { -// err = multierr.Append(err, f.Close()) -// }() +// func doSomething(..) (err error) { +// f := acquireResource() +// defer func() { +// err = multierr.Append(err, f.Close()) +// }() +// +// Note that the variable MUST be a named return to append an error to it from +// the defer statement. See also [AppendInvoke]. func Append(left error, right error) error { switch { case left == nil: @@ -401,37 +461,37 @@ func Append(left error, right error) error { // AppendInto appends an error into the destination of an error pointer and // returns whether the error being appended was non-nil. // -// var err error -// multierr.AppendInto(&err, r.Close()) -// multierr.AppendInto(&err, w.Close()) +// var err error +// multierr.AppendInto(&err, r.Close()) +// multierr.AppendInto(&err, w.Close()) // // The above is equivalent to, // -// err := multierr.Append(r.Close(), w.Close()) +// err := multierr.Append(r.Close(), w.Close()) // // As AppendInto reports whether the provided error was non-nil, it may be // used to build a multierr error in a loop more ergonomically. For example: // -// var err error -// for line := range lines { -// var item Item -// if multierr.AppendInto(&err, parse(line, &item)) { -// continue -// } -// items = append(items, item) -// } -// -// Compare this with a verison that relies solely on Append: -// -// var err error -// for line := range lines { -// var item Item -// if parseErr := parse(line, &item); parseErr != nil { -// err = multierr.Append(err, parseErr) -// continue -// } -// items = append(items, item) -// } +// var err error +// for line := range lines { +// var item Item +// if multierr.AppendInto(&err, parse(line, &item)) { +// continue +// } +// items = append(items, item) +// } +// +// Compare this with a version that relies solely on Append: +// +// var err error +// for line := range lines { +// var item Item +// if parseErr := parse(line, &item); parseErr != nil { +// err = multierr.Append(err, parseErr) +// continue +// } +// items = append(items, item) +// } func AppendInto(into *error, err error) (errored bool) { if into == nil { // We panic if 'into' is nil. This is not documented above @@ -447,3 +507,140 @@ func AppendInto(into *error, err error) (errored bool) { *into = Append(*into, err) return true } + +// Invoker is an operation that may fail with an error. Use it with +// AppendInvoke to append the result of calling the function into an error. +// This allows you to conveniently defer capture of failing operations. +// +// See also, [Close] and [Invoke]. +type Invoker interface { + Invoke() error +} + +// Invoke wraps a function which may fail with an error to match the Invoker +// interface. Use it to supply functions matching this signature to +// AppendInvoke. +// +// For example, +// +// func processReader(r io.Reader) (err error) { +// scanner := bufio.NewScanner(r) +// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err)) +// for scanner.Scan() { +// // ... +// } +// // ... +// } +// +// In this example, the following line will construct the Invoker right away, +// but defer the invocation of scanner.Err() until the function returns. +// +// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err)) +// +// Note that the error you're appending to from the defer statement MUST be a +// named return. +type Invoke func() error + +// Invoke calls the supplied function and returns its result. +func (i Invoke) Invoke() error { return i() } + +// Close builds an Invoker that closes the provided io.Closer. Use it with +// AppendInvoke to close io.Closers and append their results into an error. +// +// For example, +// +// func processFile(path string) (err error) { +// f, err := os.Open(path) +// if err != nil { +// return err +// } +// defer multierr.AppendInvoke(&err, multierr.Close(f)) +// return processReader(f) +// } +// +// In this example, multierr.Close will construct the Invoker right away, but +// defer the invocation of f.Close until the function returns. +// +// defer multierr.AppendInvoke(&err, multierr.Close(f)) +// +// Note that the error you're appending to from the defer statement MUST be a +// named return. +func Close(closer io.Closer) Invoker { + return Invoke(closer.Close) +} + +// AppendInvoke appends the result of calling the given Invoker into the +// provided error pointer. Use it with named returns to safely defer +// invocation of fallible operations until a function returns, and capture the +// resulting errors. +// +// func doSomething(...) (err error) { +// // ... +// f, err := openFile(..) +// if err != nil { +// return err +// } +// +// // multierr will call f.Close() when this function returns and +// // if the operation fails, its append its error into the +// // returned error. +// defer multierr.AppendInvoke(&err, multierr.Close(f)) +// +// scanner := bufio.NewScanner(f) +// // Similarly, this scheduled scanner.Err to be called and +// // inspected when the function returns and append its error +// // into the returned error. +// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err)) +// +// // ... +// } +// +// NOTE: If used with a defer, the error variable MUST be a named return. +// +// Without defer, AppendInvoke behaves exactly like AppendInto. +// +// err := // ... +// multierr.AppendInvoke(&err, mutltierr.Invoke(foo)) +// +// // ...is roughly equivalent to... +// +// err := // ... +// multierr.AppendInto(&err, foo()) +// +// The advantage of the indirection introduced by Invoker is to make it easy +// to defer the invocation of a function. Without this indirection, the +// invoked function will be evaluated at the time of the defer block rather +// than when the function returns. +// +// // BAD: This is likely not what the caller intended. This will evaluate +// // foo() right away and append its result into the error when the +// // function returns. +// defer multierr.AppendInto(&err, foo()) +// +// // GOOD: This will defer invocation of foo unutil the function returns. +// defer multierr.AppendInvoke(&err, multierr.Invoke(foo)) +// +// multierr provides a few Invoker implementations out of the box for +// convenience. See [Invoker] for more information. +func AppendInvoke(into *error, invoker Invoker) { + AppendInto(into, invoker.Invoke()) +} + +// AppendFunc is a shorthand for [AppendInvoke]. +// It allows using function or method value directly +// without having to wrap it into an [Invoker] interface. +// +// func doSomething(...) (err error) { +// w, err := startWorker(...) +// if err != nil { +// return err +// } +// +// // multierr will call w.Stop() when this function returns and +// // if the operation fails, it appends its error into the +// // returned error. +// defer multierr.AppendFunc(&err, w.Stop) +// } +func AppendFunc(into *error, fn func() error) { + AppendInvoke(into, Invoke(fn)) +} diff --git a/lens/vendor/go.uber.org/multierr/error_post_go120.go b/lens/vendor/go.uber.org/multierr/error_post_go120.go new file mode 100644 index 000000000..a173f9c25 --- /dev/null +++ b/lens/vendor/go.uber.org/multierr/error_post_go120.go @@ -0,0 +1,48 @@ +// Copyright (c) 2017-2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build go1.20 +// +build go1.20 + +package multierr + +// Unwrap returns a list of errors wrapped by this multierr. +func (merr *multiError) Unwrap() []error { + return merr.Errors() +} + +type multipleErrors interface { + Unwrap() []error +} + +func extractErrors(err error) []error { + if err == nil { + return nil + } + + // check if the given err is an Unwrapable error that + // implements multipleErrors interface. + eg, ok := err.(multipleErrors) + if !ok { + return []error{err} + } + + return append(([]error)(nil), eg.Unwrap()...) +} diff --git a/chart-sync/vendor/go.uber.org/multierr/go113.go b/lens/vendor/go.uber.org/multierr/error_pre_go120.go similarity index 66% rename from chart-sync/vendor/go.uber.org/multierr/go113.go rename to lens/vendor/go.uber.org/multierr/error_pre_go120.go index 264b0eac0..93872a3fc 100644 --- a/chart-sync/vendor/go.uber.org/multierr/go113.go +++ b/lens/vendor/go.uber.org/multierr/error_pre_go120.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Uber Technologies, Inc. +// Copyright (c) 2017-2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -18,12 +18,19 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -// +build go1.13 +//go:build !go1.20 +// +build !go1.20 package multierr import "errors" +// Versions of Go before 1.20 did not support the Unwrap() []error method. +// This provides a similar behavior by implementing the Is(..) and As(..) +// methods. +// See the errors.Join proposal for details: +// https://github.com/golang/go/issues/53435 + // As attempts to find the first error in the error list that matches the type // of the value that target points to. // @@ -50,3 +57,23 @@ func (merr *multiError) Is(target error) bool { } return false } + +func extractErrors(err error) []error { + if err == nil { + return nil + } + + // Note that we're casting to multiError, not errorGroup. Our contract is + // that returned errors MAY implement errorGroup. Errors, however, only + // has special behavior for multierr-specific error objects. + // + // This behavior can be expanded in the future but I think it's prudent to + // start with as little as possible in terms of contract and possibility + // of misuse. + eg, ok := err.(*multiError) + if !ok { + return []error{err} + } + + return append(([]error)(nil), eg.Errors()...) +} diff --git a/lens/vendor/go.uber.org/multierr/glide.yaml b/lens/vendor/go.uber.org/multierr/glide.yaml deleted file mode 100644 index 6ef084ec2..000000000 --- a/lens/vendor/go.uber.org/multierr/glide.yaml +++ /dev/null @@ -1,8 +0,0 @@ -package: go.uber.org/multierr -import: -- package: go.uber.org/atomic - version: ^1 -testImport: -- package: github.com/stretchr/testify - subpackages: - - assert diff --git a/lens/vendor/modules.txt b/lens/vendor/modules.txt index d93c2712b..5690b6412 100644 --- a/lens/vendor/modules.txt +++ b/lens/vendor/modules.txt @@ -7,7 +7,7 @@ github.com/caarlos0/env # github.com/cespare/xxhash/v2 v2.2.0 ## explicit; go 1.11 github.com/cespare/xxhash/v2 -# github.com/devtron-labs/common-lib v0.16.1-0.20240911071031-2625327bc7b4 +# github.com/devtron-labs/common-lib v0.0.0 => github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da ## explicit; go 1.21 github.com/devtron-labs/common-lib/constants github.com/devtron-labs/common-lib/git-manager/util @@ -147,8 +147,8 @@ go.opentelemetry.io/otel/trace/embedded # go.uber.org/atomic v1.10.0 ## explicit; go 1.18 go.uber.org/atomic -# go.uber.org/multierr v1.6.0 -## explicit; go 1.12 +# go.uber.org/multierr v1.11.0 +## explicit; go 1.19 go.uber.org/multierr # go.uber.org/zap v1.21.0 ## explicit; go 1.13 @@ -282,3 +282,4 @@ google.golang.org/protobuf/types/known/timestamppb # mellium.im/sasl v0.3.1 ## explicit; go 1.18 mellium.im/sasl +# github.com/devtron-labs/common-lib => github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241010131105-e2c23f9c80da