diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c7c8504..28ca10d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,18 +34,18 @@ jobs: if: needs.semantic-release.outputs.new-release-published == 'true' strategy: matrix: - repos: ['mission-control', 'canary-checker', 'commons', 'config-db', 'gomplate'] + repos: ["mission-control", "canary-checker", "commons", "config-db", "gomplate"] steps: - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 with: repository: flanksource/${{ matrix.repos }} token: ${{ secrets.FLANKBOT }} - + - name: Setup Go uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 with: go-version: v1.22.x - + - name: Create commits run: | # Sleep to let index refresh @@ -60,9 +60,9 @@ jobs: id: cpr uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e # v6.0.5 with: - branch: 'bump-is-healthy-auto-pr' - commit-message: 'chore: bump is-healthy to v${{ needs.semantic-release.outputs.release-version }}' - title: 'chore: bump is-healthy to v${{ needs.semantic-release.outputs.release-version }}' + branch: "bump-is-healthy-auto-pr" + commit-message: "chore: bump is-healthy to v${{ needs.semantic-release.outputs.release-version }}" + title: "chore: bump is-healthy to v${{ needs.semantic-release.outputs.release-version }}" token: ${{ secrets.FLANKBOT }} labels: dependencies @@ -74,3 +74,23 @@ jobs: pull-request-number: ${{ steps.cpr.outputs.pull-request-number }} merge-method: squash repository: flanksource/${{ matrix.repos }} + goreleaser: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: stable + + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v6 + with: + distribution: goreleaser + version: "~> v2" + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 3b735ec..4092658 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ # Go workspace file go.work +# Added by goreleaser init: +dist/ diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..e1777de --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,26 @@ +version: 2 + +before: + hooks: + - go mod tidy + +builds: + - env: + - CGO_ENABLED=0 + goos: + - linux + - windows + - darwin + goarch: + - amd64 + - arm64 + +changelog: + sort: asc + filters: + exclude: + - "^docs:" + - "^test:" + +release: + footer: >- diff --git a/Makefile b/Makefile index 42b1c21..57b30a9 100644 --- a/Makefile +++ b/Makefile @@ -1,19 +1,32 @@ + +NAME=is-healthy +YQ=yq +OS ?= $(shell uname -s | tr '[:upper:]' '[:lower:]') +ARCH ?= $(shell uname -m | sed 's/x86_64/amd64/') +LD_FLAGS=-ldflags "-w -s -X \"main.version=$(VERSION_TAG)\"" +ifeq ($(VERSION),) + VERSION_TAG=$(shell git describe --abbrev=0 --tags || echo latest) +else + VERSION_TAG=$(VERSION) +endif + + +# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) +ifeq (,$(shell go env GOBIN)) +GOBIN=$(shell go env GOPATH)/bin +else +GOBIN=$(shell go env GOBIN) +endif + .PHONY: test test: go test ./... -v -.PHONY: tidy -tidy: - go mod tidy -go=1.20 -compat=1.20 .PHONY: lint -lint: +lint: fmt golangci-lint run - - golines -m 120 -w pkg/ - golines -m 120 -w events/ - gofumpt -w . .PHONY: sync: @@ -21,3 +34,48 @@ sync: update-submodules: git submodule update --remote --merge && git submodule sync + + +tidy: + go mod tidy + +fmt: + golines -m 120 -w pkg/ + golines -m 120 -w events/ + gofumpt -w . + +.PHONY: compress +compress: + test -e ./$(RELEASE_DIR)/$(NAME)_linux_amd64 && upx -5 ./$(RELEASE_DIR)/$(NAME)_linux_amd64 || true + test -e ./$(RELEASE_DIR)/$(NAME)_linux_arm64 && upx -5 ./$(RELEASE_DIR)/$(NAME)_linux_arm64 || true + +.PHONY: compress-build +compress-build: + upx -5 ./$(RELEASE_DIR)/$(NAME) ./$(RELEASE_DIR)/$(NAME).test + +.PHONY: linux +linux: + GOOS=linux GOARCH=amd64 go build -o ./$(RELEASE_DIR)/$(NAME)_linux_amd64 $(LD_FLAGS) main.go + GOOS=linux GOARCH=arm64 go build -o ./$(RELEASE_DIR)/$(NAME)_linux_arm64 $(LD_FLAGS) main.go + +.PHONY: darwin +darwin: + GOOS=darwin GOARCH=amd64 go build -o ./$(RELEASE_DIR)/$(NAME)_darwin_amd64 $(LD_FLAGS) main.go + GOOS=darwin GOARCH=arm64 go build -o ./$(RELEASE_DIR)/$(NAME)_darwin_arm64 $(LD_FLAGS) main.go + +.PHONY: windows +windows: + GOOS=windows GOARCH=amd64 go build -o ./$(RELEASE_DIR)/$(NAME).exe $(LD_FLAGS) main.go + +.PHONY: binaries +binaries: linux darwin windows compress + + +.PHONY: build +build: + GOOS=$(OS) GOARCH=$(ARCH) go build -o ./.bin/$(NAME) $(LD_FLAGS) main.go + + +.PHONY: install +install: + cp ./.bin/$(NAME) /usr/local/bin/ diff --git a/README.md b/README.md index 8d15faa..fc723f5 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,62 @@ -# is-healthy +
+

+ + + +

+
+`is-healthy` is a heuristic library for checking the health of Kubernetes and Cloud resources, it supports basic, enhanced and heuristic mode. -`is-healthy` is a library and CLI to test the status of Kubernetes JSON objects, it is copied mostly from the Argocd project and modified to reduce the dependency tree + +* **Basic Mode**: Wraps Argo health check functionality. Does not distinguish between `health` and `status`, `lastUpdated` and `ready` are not supported + * **health**: One of `healthy`, `unhealthy`, `warning`, `unknown` + * **message**: A reason for the status e.g. `Back-off pulling image "nginx:invalid"` +* **Enhanced Mode**: returns the following fields: + + * ***ready***: Whether the resource is currently being reconciled / provisioned. + > `ready` is independent of `health`, e.g. A pod in a failure state can be ready if its state is terminal and will not change. + * **health**: One of `healthy`, `unhealthy`, `warning`, `unknown`, + > `health` can transition based on the age or last event time of a resource. + * **status**: A text description of the state of of resource e.g. `Running` or `ImagePullBackoff` + * **message**: A reason for the status e.g. `Back-off pulling image "nginx:invalid"` + * **lastUpdated**: The last time the resource was updated +* **Heuristic Mode**: Attempts to determine health using and fields named `state`, `status` etc.. + +|Object Type|Support Mode|| +|---|---|---| +|Core Kubernetes Resources|Enhanced|| +|Pod|Enhanced|Ignores pod restarts for the first `15m`
`warning` if restarted in in last 24h
`unhealthy` if restarted in last `1h`| +|Certificate|Enhanced|`unhealthy` if not issued with `1h`
`warning` if not issued with `15m`
`warning` if certificate expiry `< 2d`| +|CronJob|Enhanced|| +|Flux CRD's|Enhanced|| +|Argo CRD's|Enhanced|| +|Cert-Manager CRD's|Enhanced|Marks| +|Kubernetes Resources using [Conditions](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties)|Heuristic|| + + + + +## Example + +```shell +kubectl po -o json | is-healthy -j +``` + +Output: + +```json +{ + "ready": false, + "health": "unhealthy", + "status": "ImagePullBackOff", + "message": "Back-off pulling image \"nginx:invalid\"", + "lastUpdated": "2025-03-26T10:17:18Z" +} +``` + + +## Attribution + +This project builds upon the health check implementations from: +* [Argo CD](https://github.com/argoproj/argo-cd) +* [gitops-engine](https://github.com/argoproj/gitops-engine) diff --git a/go.mod b/go.mod index a0531a8..fba7810 100644 --- a/go.mod +++ b/go.mod @@ -23,10 +23,13 @@ require ( github.com/go-logr/logr v1.2.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/spf13/cobra v1.9.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect golang.org/x/net v0.23.0 // indirect golang.org/x/text v0.16.0 // indirect diff --git a/go.sum b/go.sum index 3befd97..8dfdf99 100644 --- a/go.sum +++ b/go.sum @@ -115,6 +115,7 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -308,6 +309,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1: github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= @@ -463,6 +466,8 @@ github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJ github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -470,6 +475,8 @@ github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= diff --git a/main.go b/main.go index 978f9b2..96c8a37 100644 --- a/main.go +++ b/main.go @@ -1,39 +1,105 @@ package main import ( + "encoding/json" "fmt" "io" "os" + "strings" "github.com/flanksource/is-healthy/pkg/health" "github.com/flanksource/is-healthy/pkg/lua" + "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "sigs.k8s.io/yaml" ) +var ( + version = "dev" + commit = "none" + date = "unknown" +) + +var jsonOut bool + func main() { - stdin, err := io.ReadAll(os.Stdin) - if err != nil { - fmt.Println(err) - os.Exit(1) + if len(commit) > 8 { + version = fmt.Sprintf("%v, commit %v, built at %v", version, commit[0:8], date) } - obj := make(map[string]interface{}) - err = yaml.Unmarshal([]byte(stdin), &obj) - if err != nil { - fmt.Println(err) - os.Exit(1) - } + root := &cobra.Command{ + Use: "is-healthy", + RunE: func(cmd *cobra.Command, args []string) error { + stdin, err := io.ReadAll(os.Stdin) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + err = yaml.Unmarshal([]byte(stdin), &obj) + if err != nil { + return err + } - _health, err := health.GetResourceHealth(&unstructured.Unstructured{Object: obj}, lua.ResourceHealthOverrides{}) - if err != nil { - fmt.Println(err) - os.Exit(1) + _health, err := health.GetResourceHealth(&unstructured.Unstructured{Object: obj}, lua.ResourceHealthOverrides{}) + if err != nil { + return err + } + + if jsonOut { + data, _ := json.MarshalIndent(_health, "", " ") + fmt.Println(string(data)) + } else { + fmt.Printf("%s\n", *_health) + } + + if _health.Health.IsWorseThan(health.HealthUnhealthy) { + os.Exit(1) + } + if _health.Health.IsWorseThan(health.HealthWarning) { + os.Exit(2) + } + return nil + }, } - fmt.Printf("%s\n", *_health) + root.AddCommand(&cobra.Command{ + Use: "version", + Short: "Print the version number of is-healthy", + Args: cobra.MinimumNArgs(0), + Run: func(cmd *cobra.Command, args []string) { + fmt.Println(version) + }, + }) + + root.AddCommand(&cobra.Command{ + Use: "supported-types", + Short: "Print a list of supported object types", + Args: cobra.MinimumNArgs(0), + Run: func(cmd *cobra.Command, args []string) { + for _, t := range lua.ListResourceTypes() { + fmt.Println(t) + } + for _, t := range health.ListResourceTypes() { + if strings.Contains(t, "/") { + fmt.Println(t) + } else { + fmt.Println(t + "/*") + } + } + }, + }) + + root.SetUsageTemplate(root.UsageTemplate() + fmt.Sprintf("\nversion: %s\n ", version)) - if _health.Health.IsWorseThan(health.HealthWarning) { - os.Exit(1) + root.Flags().BoolVarP(&jsonOut, "json", "j", false, "Output in json format") + if err := root.Execute(); err != nil { + if jsonOut { + data, _ := json.MarshalIndent(map[string]interface{}{"error": err.Error()}, "", " ") + fmt.Println(string(data)) + } else { + fmt.Println(err) + } + os.Exit(3) } } diff --git a/pkg/health/health.go b/pkg/health/health.go index 7c4326e..e0cc658 100644 --- a/pkg/health/health.go +++ b/pkg/health/health.go @@ -106,6 +106,7 @@ const ( HealthStatusRestart HealthStatusCode = "Restarting" HealthStatusStarting HealthStatusCode = "Starting" HealthStatusFailed HealthStatusCode = "Failed" + HealthStatusFailedCreate HealthStatusCode = "Failed Create" HealthStatusUnschedulable HealthStatusCode = "Unschedulable" HealthStatusUpgradeFailed HealthStatusCode = "UpgradeFailed" HealthStatusOOMKilled HealthStatusCode = "OOMKilled" diff --git a/pkg/health/health_deployment.go b/pkg/health/health_deployment.go index 69cae8f..7e12658 100644 --- a/pkg/health/health_deployment.go +++ b/pkg/health/health_deployment.go @@ -53,13 +53,28 @@ func getReplicaHealth(s ReplicaStatus) *HealthStatus { age := time.Since(s.Object.GetCreationTimestamp().Time).Truncate(time.Minute).Abs() gs := GetGenericStatus(s.Object) + available := gs.FindCondition("Available") + isAvailable := s.Ready > 0 + if available.Status != "" { + isAvailable = available.Status == "True" + } progressing := gs.FindCondition("Progressing") + + failure := gs.FindCondition("ReplicaFailure") + if failure.Status == "True" { + hs.Status = HealthStatusFailedCreate + hs.Health = HealthUnhealthy + hs.Message = failure.Message + hs.Ready = true + return hs + } + isStarting := age < startDeadline isProgressDeadlineExceeded := !isStarting && (progressing.Reason == "ProgressDeadlineExceeded") - hs.Ready = progressing.Status == "True" + hs.Ready = progressing.Status == "True" && progressing.Reason != "ReplicaSetUpdated" - hs.Health = lo.Ternary(s.Ready >= s.Desired, HealthHealthy, lo.Ternary(s.Ready > 0, HealthWarning, HealthUnhealthy)) + hs.Health = lo.Ternary(isAvailable, HealthHealthy, lo.Ternary(s.Ready > 0, HealthWarning, HealthUnhealthy)) if s.Desired == 0 && s.Replicas == 0 { hs.Ready = true @@ -75,26 +90,32 @@ func getReplicaHealth(s ReplicaStatus) *HealthStatus { hs.Status = "Pending" hs.Health = HealthUnknown } - } else if s.Ready == 0 && isStarting && !isProgressDeadlineExceeded { - hs.Health = HealthUnknown + } else if s.Ready == 0 && isStarting { hs.Status = HealthStatusStarting - } else if s.Ready == 0 && !isStarting { - hs.Health = HealthUnhealthy - hs.Status = HealthStatusCrashLoopBackoff + } else if s.Ready == 0 { + hs.Status = lo.Ternary(isAvailable, HealthStatusUpdating, HealthStatusCrashLoopBackoff) + } + + if isProgressDeadlineExceeded { + hs.Status = HealthStatusRolloutFailed + hs.Health = hs.Health.Worst(HealthWarning) } else if s.Desired == 0 && s.Replicas > 0 { hs.Status = HealthStatusScalingDown - hs.Health = lo.Ternary(isProgressDeadlineExceeded, HealthWarning, HealthHealthy) } else if s.Ready == s.Desired && s.Desired == s.Updated && s.Replicas == s.Desired { hs.Status = HealthStatusRunning - } else if s.Desired != s.Updated { - hs.Status = HealthStatusUpdating + } else if !isStarting && s.Desired != s.Updated { + hs.Status = HealthStatusRollingOut } else if s.Replicas > s.Desired { hs.Status = HealthStatusScalingDown } else if s.Replicas < s.Desired { hs.Status = HealthStatusScalingUp } - if isStarting && hs.Health == HealthUnhealthy { + if s.Replicas != s.Desired || s.Replicas != s.Updated { + hs.Ready = false + } + + if isStarting && (hs.Health == HealthUnhealthy || hs.Health == HealthWarning) { hs.Health = HealthUnknown } diff --git a/pkg/health/health_test.go b/pkg/health/health_test.go index 795d564..dd96002 100644 --- a/pkg/health/health_test.go +++ b/pkg/health/health_test.go @@ -143,21 +143,6 @@ func assertAppHealthWithOverwriteMsg( assert.Equal(t, expectedMsg, health.Message) } -func assertAppHealthWithOverwrite( - t *testing.T, - yamlPath string, - overwrites map[string]string, - expectedStatus health.HealthStatusCode, - expectedHealth health.Health, - expectedReady bool, -) { - health, _ := getHealthStatus(yamlPath, t, overwrites) - assert.NotNil(t, health) - assert.Equal(t, expectedHealth, health.Health) - assert.Equal(t, expectedReady, health.Ready) - assert.Equal(t, expectedStatus, health.Status) -} - func getHealthStatus( yamlPath string, t *testing.T, @@ -315,88 +300,10 @@ func TestExternalSecrets(t *testing.T) { assertAppHealthMsg(t, b+"healthy.yaml", "SecretSynced", health.HealthHealthy, true) } -func TestDeploymentHealth(t *testing.T) { - assertAppHealthMsg(t, "./testdata/nginx.yaml", health.HealthStatusRunning, health.HealthHealthy, true, "1/1 ready") - assertAppHealthMsg( - t, - "./deployment-scaled-up.yaml", - health.HealthStatusRunning, - health.HealthHealthy, - true, - "3/3 ready", - ) - - assertAppHealthMsg( - t, - "./deployment-rollout-failed.yaml", - health.HealthStatusUpdating, - health.HealthWarning, - true, - "1/2 ready, 1 updating", - ) - assertAppHealthMsg( - t, - "./testdata/deployment-progressing.yaml", - health.HealthStatusUpdating, - health.HealthWarning, - true, - "1/2 ready, 1 updating", - ) - assertAppHealthMsg( - t, - "./testdata/deployment-suspended.yaml", - health.HealthStatusSuspended, - health.HealthHealthy, - false, - "1/1 ready, 1 updating, 1 terminating", - ) - assertAppHealthMsg( - t, - "./testdata/deployment-degraded.yaml", - health.HealthStatusUpdating, - health.HealthWarning, - true, - "1/2 ready, 1 updating", - ) - - assertAppHealthMsg( - t, - "./testdata/deployment-starting.yaml", - health.HealthStatusStarting, - health.HealthUnknown, - true, - "0/2 ready, 1 updating", - ) - assertAppHealthMsg( - t, - "./testdata/deployment-scaling-down.yaml", - health.HealthStatusScalingDown, - health.HealthHealthy, - true, - "1/1 ready, 1 updating, 1 terminating", - ) - assertAppHealthMsg( - t, - "./testdata/deployment-failed.yaml", - "Failed Create", - health.HealthUnhealthy, - false, - "0/1 ready", - ) -} - func TestStatefulSetHealth(t *testing.T) { + starting := "./testdata/Kubernetes/StatefulSet/statefulset-starting.yaml" assertAppHealthMsg( - t, - "./testdata/statefulset.yaml", - health.HealthStatusRunning, - health.HealthHealthy, - true, - "1/1 ready", - ) - assertAppHealthMsg( - t, - "./testdata/statefulset-starting.yaml", + t, starting, health.HealthStatusStarting, health.HealthUnknown, true, @@ -406,7 +313,8 @@ func TestStatefulSetHealth(t *testing.T) { ) assertAppHealthMsg( t, - "./testdata/statefulset-starting.yaml", + starting, + health.HealthStatusStarting, health.HealthUnknown, true, @@ -416,7 +324,7 @@ func TestStatefulSetHealth(t *testing.T) { ) assertAppHealthMsg( t, - "./testdata/statefulset-starting.yaml", + starting, health.HealthStatusCrashLoopBackoff, health.HealthUnhealthy, true, @@ -426,7 +334,7 @@ func TestStatefulSetHealth(t *testing.T) { ) assertAppHealthMsg( t, - "./testdata/statefulset-starting.yaml", + starting, health.HealthStatusCrashLoopBackoff, health.HealthUnhealthy, true, @@ -439,7 +347,7 @@ func TestStatefulSetHealth(t *testing.T) { func TestStatefulSetOnDeleteHealth(t *testing.T) { assertAppHealthMsg( t, - "./testdata/statefulset-ondelete.yaml", + "./testdata/Kubernetes/StatefulSet/statefulset-ondelete.yaml", "TerminatingStalled", health.HealthWarning, false, @@ -536,16 +444,6 @@ func TestHPA(t *testing.T) { ) } -func TestReplicaSet(t *testing.T) { - assertAppHealthWithOverwrite(t, "./testdata/replicaset-ittools.yml", map[string]string{ - "2024-08-03T06:06:18Z": time.Now().Add(-time.Minute * 2).UTC().Format("2006-01-02T15:04:05Z"), - }, health.HealthStatusRunning, health.HealthHealthy, false) - - assertAppHealthWithOverwrite(t, "./testdata/replicaset-unhealthy-pods.yaml", map[string]string{ - "2024-10-21T11:20:19Z": time.Now().Add(-time.Minute * 2).UTC().Format("2006-01-02T15:04:05Z"), - }, health.HealthStatusStarting, health.HealthUnknown, false) -} - // func TestAPIService(t *testing.T) { // assertAppHealthMsg(t, "./testdata/apiservice-v1-true.yaml", HealthStatusHealthy, health.HealthHealthy, true) // assertAppHealthMsg(t, "./testdata/apiservice-v1-false.yaml", HealthStatusProgressing, health.HealthHealthy, true) diff --git a/pkg/health/status.go b/pkg/health/status.go index c9e02c2..fbe3cb2 100644 --- a/pkg/health/status.go +++ b/pkg/health/status.go @@ -257,3 +257,14 @@ func GetHealthFromStatus(k GenericStatus, statusMap StatusMap) (*HealthStatus, e return health, nil } + +func ListResourceTypes() []string { + types := []string{} + for k := range statusByKind { + if k == "default" { + continue + } + types = append(types, k) + } + return types +} diff --git a/pkg/health/testdata/deployment-degraded.yaml b/pkg/health/testdata/Kubernetes/Deployment/deployment-degraded.yaml similarity index 73% rename from pkg/health/testdata/deployment-degraded.yaml rename to pkg/health/testdata/Kubernetes/Deployment/deployment-degraded.yaml index 7f49967..21a9222 100644 --- a/pkg/health/testdata/deployment-degraded.yaml +++ b/pkg/health/testdata/Kubernetes/Deployment/deployment-degraded.yaml @@ -2,9 +2,10 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - deployment.kubernetes.io/revision: "4" - kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"guestbook-default"},"name":"guestbook-ui","namespace":"default"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"guestbook-ui"}},"template":{"metadata":{"labels":{"app":"guestbook-ui","app.kubernetes.io/instance":"guestbook-default"}},"spec":{"containers":[{"image":"gcr.io/heptio-images/ks-guestbook-demo:0.3","name":"guestbook-ui","ports":[{"containerPort":80}]}]}}}} + expected-ready: "false" + expected-status: "Rolling Out" + expected-health: "warning" + expected-message: "1/2 ready, 1 updating" creationTimestamp: 2018-07-18T04:40:44Z generation: 4 labels: @@ -55,7 +56,7 @@ status: lastUpdateTime: 2018-07-18T04:48:48Z message: Deployment has minimum availability. reason: MinimumReplicasAvailable - status: "True" + status: "false" type: Available - lastTransitionTime: 2018-07-18T06:29:23Z lastUpdateTime: 2018-07-18T06:29:23Z diff --git a/pkg/health/testdata/deployment-failed.yaml b/pkg/health/testdata/Kubernetes/Deployment/deployment-failed.yaml similarity index 88% rename from pkg/health/testdata/deployment-failed.yaml rename to pkg/health/testdata/Kubernetes/Deployment/deployment-failed.yaml index 6136a69..45ef505 100644 --- a/pkg/health/testdata/deployment-failed.yaml +++ b/pkg/health/testdata/Kubernetes/Deployment/deployment-failed.yaml @@ -7,6 +7,11 @@ metadata: control-plane: karina-operator namespace: platform-system annotations: + expected-status: Failed Create + expected-ready: "true" + expected-health: "unhealthy" + expected-message: 'pods "karina-c7585bd87-" is forbidden: error looking up service + account platform-system/karina: serviceaccount "karina" not found' deployment.kubernetes.io/revision: "1" creationTimestamp: 2023-05-10T08:11:03Z spec: diff --git a/pkg/health/testdata/deployment-progressing.yaml b/pkg/health/testdata/Kubernetes/Deployment/deployment-progressing.yaml similarity index 75% rename from pkg/health/testdata/deployment-progressing.yaml rename to pkg/health/testdata/Kubernetes/Deployment/deployment-progressing.yaml index cf61d45..a6ba5cc 100644 --- a/pkg/health/testdata/deployment-progressing.yaml +++ b/pkg/health/testdata/Kubernetes/Deployment/deployment-progressing.yaml @@ -2,9 +2,11 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - deployment.kubernetes.io/revision: "4" - kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"guestbook-default"},"name":"guestbook-ui","namespace":"default"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"guestbook-ui"}},"template":{"metadata":{"labels":{"app":"guestbook-ui","app.kubernetes.io/instance":"guestbook-default"}},"spec":{"containers":[{"image":"gcr.io/heptio-images/ks-guestbook-demo:0.3","name":"guestbook-ui","ports":[{"containerPort":80}]}]}}}} + expected-status: Rolling Out + expected-health: healthy + expected-message: "1/2 ready, 1 updating" + expected-replicas: "2" + expted-ready: "false" creationTimestamp: 2018-07-18T04:40:44Z generation: 4 labels: diff --git a/pkg/health/testdata/deployment-rollout-failed-unhealthy.yaml b/pkg/health/testdata/Kubernetes/Deployment/deployment-rollout-failed-unhealthy.yaml similarity index 94% rename from pkg/health/testdata/deployment-rollout-failed-unhealthy.yaml rename to pkg/health/testdata/Kubernetes/Deployment/deployment-rollout-failed-unhealthy.yaml index 512ee39..cbdab15 100644 --- a/pkg/health/testdata/deployment-rollout-failed-unhealthy.yaml +++ b/pkg/health/testdata/Kubernetes/Deployment/deployment-rollout-failed-unhealthy.yaml @@ -2,6 +2,9 @@ apiVersion: apps/v1 kind: Deployment metadata: name: guestbook-ui + annotations: + expected-health: warning + expected-status: Rollout Failed spec: progressDeadlineSeconds: 600 replicas: 2 diff --git a/pkg/health/testdata/deployment-rollout-failed.yaml b/pkg/health/testdata/Kubernetes/Deployment/deployment-rollout-failed.yaml similarity index 90% rename from pkg/health/testdata/deployment-rollout-failed.yaml rename to pkg/health/testdata/Kubernetes/Deployment/deployment-rollout-failed.yaml index 86f87e1..5bf2914 100644 --- a/pkg/health/testdata/deployment-rollout-failed.yaml +++ b/pkg/health/testdata/Kubernetes/Deployment/deployment-rollout-failed.yaml @@ -2,6 +2,11 @@ apiVersion: apps/v1 kind: Deployment metadata: name: guestbook-ui + annotations: + expected-ready: "false" + expected-status: "Rolling Out" + expected-health: "healthy" + expected-message: "1/2 ready, 1 updating" spec: progressDeadlineSeconds: 600 replicas: 2 diff --git a/pkg/health/testdata/deployment-scaled-up.yaml b/pkg/health/testdata/Kubernetes/Deployment/deployment-scaled-up.yaml similarity index 96% rename from pkg/health/testdata/deployment-scaled-up.yaml rename to pkg/health/testdata/Kubernetes/Deployment/deployment-scaled-up.yaml index 5e154b4..eaf8623 100644 --- a/pkg/health/testdata/deployment-scaled-up.yaml +++ b/pkg/health/testdata/Kubernetes/Deployment/deployment-scaled-up.yaml @@ -10,9 +10,10 @@ metadata: app.kubernetes.io/managed-by: Helm namespace: podinfo annotations: - meta.helm.sh/release-name: podinfo - meta.helm.sh/release-namespace: podinfo - deployment.kubernetes.io/revision: "1" + expected-ready: "true" + expected-status: "Running" + expected-health: "healthy" + expected-message: "3/3 ready" creationTimestamp: 2023-12-19T15:50:39Z spec: replicas: 3 diff --git a/pkg/health/testdata/deployment-scaling-down.yaml b/pkg/health/testdata/Kubernetes/Deployment/deployment-scaling-down.yaml similarity index 73% rename from pkg/health/testdata/deployment-scaling-down.yaml rename to pkg/health/testdata/Kubernetes/Deployment/deployment-scaling-down.yaml index ce7359f..0b073e3 100644 --- a/pkg/health/testdata/deployment-scaling-down.yaml +++ b/pkg/health/testdata/Kubernetes/Deployment/deployment-scaling-down.yaml @@ -2,9 +2,10 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - deployment.kubernetes.io/revision: "4" - kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"guestbook-default"},"name":"guestbook-ui","namespace":"default"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"guestbook-ui"}},"template":{"metadata":{"labels":{"app":"guestbook-ui","app.kubernetes.io/instance":"guestbook-default"}},"spec":{"containers":[{"image":"gcr.io/heptio-images/ks-guestbook-demo:0.3","name":"guestbook-ui","ports":[{"containerPort":80}]}]}}}} + expected-ready: "false" + expected-status: "Scaling Down" + expected-health: "healthy" + expected-message: "1/1 ready, 1 updating, 1 terminating" creationTimestamp: 2018-07-18T04:40:44Z generation: 4 labels: diff --git a/pkg/health/testdata/deployment-scaling-up.yaml b/pkg/health/testdata/Kubernetes/Deployment/deployment-scaling-up.yaml similarity index 93% rename from pkg/health/testdata/deployment-scaling-up.yaml rename to pkg/health/testdata/Kubernetes/Deployment/deployment-scaling-up.yaml index 9d18eca..9d1b46d 100644 --- a/pkg/health/testdata/deployment-scaling-up.yaml +++ b/pkg/health/testdata/Kubernetes/Deployment/deployment-scaling-up.yaml @@ -1,9 +1,3 @@ -Config -Changes1 -Insights0 -Relationships4 -Playbooks1 -Checks0 apiVersion: apps/v1 kind: Deployment metadata: @@ -16,9 +10,11 @@ metadata: app.kubernetes.io/managed-by: Helm namespace: podinfo annotations: - meta.helm.sh/release-name: podinfo - meta.helm.sh/release-namespace: podinfo - deployment.kubernetes.io/revision: "1" + annotations: + expected-ready: "false" + expected-status: "Rolling Out" + expected-health: "warning" + expected-message: "1/3 ready, 1 updating" creationTimestamp: 2023-12-19T15:50:39Z spec: replicas: 3 @@ -120,6 +116,6 @@ status: status: "True" message: ReplicaSet "podinfo-97c6d4b94" has successfully progressed. readyReplicas: 1 - updatedReplicas: 3 + updatedReplicas: 2 availableReplicas: 1 unavailableReplicas: 2 diff --git a/pkg/health/testdata/deployment-starting.yaml b/pkg/health/testdata/Kubernetes/Deployment/deployment-starting.yaml similarity index 95% rename from pkg/health/testdata/deployment-starting.yaml rename to pkg/health/testdata/Kubernetes/Deployment/deployment-starting.yaml index eec0a6d..a54a603 100644 --- a/pkg/health/testdata/deployment-starting.yaml +++ b/pkg/health/testdata/Kubernetes/Deployment/deployment-starting.yaml @@ -5,6 +5,9 @@ metadata: labels: app.kubernetes.io/instance: guestbook-default name: guestbook-ui + annotations: + expected-status: Starting + expected-health: unknown namespace: default spec: progressDeadlineSeconds: 600 diff --git a/pkg/health/testdata/deployment-suspended.yaml b/pkg/health/testdata/Kubernetes/Deployment/deployment-suspended.yaml similarity index 75% rename from pkg/health/testdata/deployment-suspended.yaml rename to pkg/health/testdata/Kubernetes/Deployment/deployment-suspended.yaml index e8c5a40..fe37baa 100644 --- a/pkg/health/testdata/deployment-suspended.yaml +++ b/pkg/health/testdata/Kubernetes/Deployment/deployment-suspended.yaml @@ -2,9 +2,10 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - deployment.kubernetes.io/revision: "4" - kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"guestbook-default"},"name":"guestbook-ui","namespace":"default"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"guestbook-ui"}},"template":{"metadata":{"labels":{"app":"guestbook-ui","app.kubernetes.io/instance":"guestbook-default"}},"spec":{"containers":[{"image":"gcr.io/heptio-images/ks-guestbook-demo:0.3","name":"guestbook-ui","ports":[{"containerPort":80}]}]}}}} + expected-ready: "false" + expected-status: "Suspended" + expected-health: "healthy" + expected-message: "1/1 ready, 1 updating, 1 terminating" creationTimestamp: 2018-07-18T04:40:44Z generation: 4 labels: diff --git a/pkg/health/testdata/nginx.yaml b/pkg/health/testdata/Kubernetes/Deployment/healthy.yaml similarity index 100% rename from pkg/health/testdata/nginx.yaml rename to pkg/health/testdata/Kubernetes/Deployment/healthy.yaml diff --git a/pkg/health/testdata/Kubernetes/Deployment/restart.yaml b/pkg/health/testdata/Kubernetes/Deployment/restart.yaml new file mode 100644 index 0000000..0fa5a1e --- /dev/null +++ b/pkg/health/testdata/Kubernetes/Deployment/restart.yaml @@ -0,0 +1,124 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + uid: 8d347012-945e-4e95-9b75-66aad2c923be + name: podinfo2 + labels: + helm.sh/chart: podinfo-6.7.1 + app.kubernetes.io/name: podinfo2 + app.kubernetes.io/version: 6.7.1 + helm.toolkit.fluxcd.io/name: podinfo2 + app.kubernetes.io/managed-by: Helm + helm.toolkit.fluxcd.io/namespace: flux-092532 + namespace: flux-092532 + annotations: + expected-status: Updating + expected-ready: "false" + expected-health: "healthy" + + meta.helm.sh/release-name: podinfo2 + meta.helm.sh/release-namespace: flux-092532 + deployment.kubernetes.io/revision: "2" + creationTimestamp: 2024-11-03T19:53:13Z +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: podinfo2 + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 25% + maxUnavailable: 1 + template: + spec: + volumes: + - name: data + emptyDir: {} + dnsPolicy: ClusterFirst + containers: + - env: + - name: PODINFO_UI_COLOR + value: "#34577c" + name: podinfo + image: ghcr.io/stefanprodan/podinfo:6.7.1 + ports: + - name: http + protocol: TCP + containerPort: 9898 + - name: http-metrics + protocol: TCP + containerPort: 9797 + - name: grpc + protocol: TCP + containerPort: 9999 + command: + - ./podinfo + - --port=9898 + - --cert-path=/data/cert + - --port-metrics=9797 + - --grpc-port=9999 + - --grpc-service-name=podinfo + - --level=info + - --random-delay=false + - --random-error=false + resources: + requests: + cpu: 1m + memory: 16Mi + volumeMounts: + - name: data + mountPath: /data + livenessProbe: + exec: + command: + - podcli + - check + - http + - localhost:9898/healthz + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + successThreshold: 1 + initialDelaySeconds: 1 + readinessProbe: + exec: + command: + - podcli + - check + - http + - localhost:9898/readyz + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + successThreshold: 1 + initialDelaySeconds: 1 + imagePullPolicy: IfNotPresent + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + terminationGracePeriodSeconds: 30 + metadata: + labels: + app.kubernetes.io/name: podinfo2 + annotations: + prometheus.io/port: "9898" + prometheus.io/scrape: "true" + kubectl.kubernetes.io/restartedAt: 2024-12-01T20:55:04Z + revisionHistoryLimit: 10 + progressDeadlineSeconds: 600 +status: + replicas: 1 + conditions: + - type: Available + reason: MinimumReplicasAvailable + status: "True" + message: Deployment has minimum availability. + - type: Progressing + reason: ReplicaSetUpdated + status: "True" + message: ReplicaSet "podinfo2-8c499d45b" is progressing. + updatedReplicas: 1 + unavailableReplicas: 1 diff --git a/pkg/health/testdata/replicaset-ittools.yml b/pkg/health/testdata/Kubernetes/ReplicaSet/healthy.yml similarity index 64% rename from pkg/health/testdata/replicaset-ittools.yml rename to pkg/health/testdata/Kubernetes/ReplicaSet/healthy.yml index a1db08f..bb5dda8 100644 --- a/pkg/health/testdata/replicaset-ittools.yml +++ b/pkg/health/testdata/Kubernetes/ReplicaSet/healthy.yml @@ -2,11 +2,10 @@ apiVersion: apps/v1 kind: ReplicaSet metadata: annotations: - deployment.kubernetes.io/desired-replicas: "1" - deployment.kubernetes.io/max-replicas: "1" - deployment.kubernetes.io/revision: "2" - meta.helm.sh/release-name: ittools - meta.helm.sh/release-namespace: default + expected-ready: "true" + expected-status: "Running" + expected-health: healthy" + expected-message: "1/1 ready" creationTimestamp: "2024-08-03T06:06:18Z" generation: 52 labels: @@ -17,12 +16,12 @@ metadata: name: ittools-5fbf458f49 namespace: default ownerReferences: - - apiVersion: apps/v1 - blockOwnerDeletion: true - controller: true - kind: Deployment - name: ittools - uid: d2beccff-8da9-42e8-8459-e7ff938b2ffd + - apiVersion: apps/v1 + blockOwnerDeletion: true + controller: true + kind: Deployment + name: ittools + uid: d2beccff-8da9-42e8-8459-e7ff938b2ffd resourceVersion: "96413911" uid: c044b250-2445-4813-b00f-22b696c5fcf2 spec: @@ -43,17 +42,17 @@ spec: spec: automountServiceAccountToken: true containers: - - image: corentinth/it-tools:latest - imagePullPolicy: Always - name: it-tools - resources: - limits: - memory: 50Mi - requests: - cpu: 25m - memory: 10Mi - terminationMessagePath: /dev/termination-log - terminationMessagePolicy: File + - image: corentinth/it-tools:latest + imagePullPolicy: Always + name: it-tools + resources: + limits: + memory: 50Mi + requests: + cpu: 25m + memory: 10Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File dnsPolicy: ClusterFirst enableServiceLinks: false restartPolicy: Always diff --git a/pkg/health/testdata/replicaset-unhealthy-pods.yaml b/pkg/health/testdata/Kubernetes/ReplicaSet/starting.yaml similarity index 100% rename from pkg/health/testdata/replicaset-unhealthy-pods.yaml rename to pkg/health/testdata/Kubernetes/ReplicaSet/starting.yaml diff --git a/pkg/health/testdata/Kubernetes/ReplicaSet/unhealthy.yaml b/pkg/health/testdata/Kubernetes/ReplicaSet/unhealthy.yaml new file mode 100644 index 0000000..e6fe2ab --- /dev/null +++ b/pkg/health/testdata/Kubernetes/ReplicaSet/unhealthy.yaml @@ -0,0 +1,54 @@ +apiVersion: apps/v1 +kind: ReplicaSet +metadata: + uid: f6579017-448f-425a-9645-ea3c93700948 + name: failing-deployment-866585899d + labels: + app: failing-app + pod-template-hash: 866585899d + namespace: default + annotations: + expected-ready: "false" + expected-status: "CrashLoopBackOff" + expected-health: "unhealthy" + expected-message: "0/1 ready" + ownerReferences: + - uid: 1ab20b2b-e2c8-4e85-b7b6-5709ba594c0d + kind: Deployment + name: failing-deployment + apiVersion: apps/v1 + controller: true + blockOwnerDeletion: true + creationTimestamp: "@now-1d" +spec: + replicas: 1 + selector: + matchLabels: + app: failing-app + pod-template-hash: 866585899d + template: + spec: + dnsPolicy: ClusterFirst + containers: + - args: + - -c + - sleep 5 && exit 1 + name: failing-container + image: busybox + command: + - /bin/sh + resources: {} + imagePullPolicy: Always + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + terminationGracePeriodSeconds: 30 + metadata: + labels: + app: failing-app + pod-template-hash: 866585899d +status: + replicas: 1 + fullyLabeledReplicas: 1 diff --git a/pkg/health/testdata/statefulset-ondelete.yaml b/pkg/health/testdata/Kubernetes/StatefulSet/statefulset-ondelete.yaml similarity index 63% rename from pkg/health/testdata/statefulset-ondelete.yaml rename to pkg/health/testdata/Kubernetes/StatefulSet/statefulset-ondelete.yaml index 9f2adfa..ac743ca 100644 --- a/pkg/health/testdata/statefulset-ondelete.yaml +++ b/pkg/health/testdata/Kubernetes/StatefulSet/statefulset-ondelete.yaml @@ -2,8 +2,9 @@ apiVersion: apps/v1 kind: StatefulSet metadata: annotations: - kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"apps/v1beta2","kind":"StatefulSet","metadata":{"annotations":{},"labels":{"app":"redis","app.kubernetes.io/instance":"redis","chart":"redis-3.6.5","heritage":"Tiller","release":"redis"},"name":"redis-master","namespace":"default"},"spec":{"selector":{"matchLabels":{"app":"redis","release":"redis","role":"master"}},"serviceName":"redis-master","template":{"metadata":{"labels":{"app":"redis","app.kubernetes.io/instance":"redis","release":"redis","role":"master"}},"spec":{"containers":[{"env":[{"name":"REDIS_REPLICATION_MODE","value":"master"},{"name":"REDIS_PASSWORD","valueFrom":{"secretKeyRef":{"key":"redis-password","name":"redis"}}},{"name":"REDIS_DISABLE_COMMANDS","value":"FLUSHDB,FLUSHALL"}],"image":"docker.io/bitnami/redis:4.0.10-debian-9","imagePullPolicy":"Always","livenessProbe":{"exec":{"command":["redis-cli","ping"]},"failureThreshold":5,"initialDelaySeconds":30,"periodSeconds":10,"successThreshold":1,"timeoutSeconds":5},"name":"redis","ports":[{"containerPort":6379,"name":"redis"}],"readinessProbe":{"exec":{"command":["redis-cli","ping"]},"failureThreshold":5,"initialDelaySeconds":5,"periodSeconds":10,"successThreshold":1,"timeoutSeconds":1},"resources":{},"volumeMounts":[{"mountPath":"/bitnami/redis/data","name":"redis-data"}]}],"securityContext":{"fsGroup":1001,"runAsUser":1001},"serviceAccountName":"default"}},"updateStrategy":{"type":"OnDelete"},"volumeClaimTemplates":[{"metadata":{"labels":{"app":"redis","chart":"redis-3.6.5","component":"master","heritage":"Tiller","release":"redis"},"name":"redis-data"},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"8Gi"}}}}]}} + expected-ready: "false" + expected-status: "Terminating" + expected-message: "1/1 ready, 1 updating" creationTimestamp: 2018-07-20T08:23:04Z generation: 1 labels: diff --git a/pkg/health/testdata/statefulset-starting.yaml b/pkg/health/testdata/Kubernetes/StatefulSet/statefulset-starting.yaml similarity index 100% rename from pkg/health/testdata/statefulset-starting.yaml rename to pkg/health/testdata/Kubernetes/StatefulSet/statefulset-starting.yaml diff --git a/pkg/health/testdata/statefulset.yaml b/pkg/health/testdata/Kubernetes/StatefulSet/statefulset.yaml similarity index 63% rename from pkg/health/testdata/statefulset.yaml rename to pkg/health/testdata/Kubernetes/StatefulSet/statefulset.yaml index 1ecd85e..a32b6bc 100644 --- a/pkg/health/testdata/statefulset.yaml +++ b/pkg/health/testdata/Kubernetes/StatefulSet/statefulset.yaml @@ -2,8 +2,10 @@ apiVersion: apps/v1 kind: StatefulSet metadata: annotations: - kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"apps/v1beta2","kind":"StatefulSet","metadata":{"annotations":{},"labels":{"app":"redis","app.kubernetes.io/instance":"redis","chart":"redis-3.6.5","heritage":"Tiller","release":"redis"},"name":"redis-master","namespace":"default"},"spec":{"selector":{"matchLabels":{"app":"redis","release":"redis","role":"master"}},"serviceName":"redis-master","template":{"metadata":{"labels":{"app":"redis","app.kubernetes.io/instance":"redis","release":"redis","role":"master"}},"spec":{"containers":[{"env":[{"name":"REDIS_REPLICATION_MODE","value":"master"},{"name":"REDIS_PASSWORD","valueFrom":{"secretKeyRef":{"key":"redis-password","name":"redis"}}},{"name":"REDIS_DISABLE_COMMANDS","value":"FLUSHDB,FLUSHALL"}],"image":"docker.io/bitnami/redis:4.0.10-debian-9","imagePullPolicy":"Always","livenessProbe":{"exec":{"command":["redis-cli","ping"]},"failureThreshold":5,"initialDelaySeconds":30,"periodSeconds":10,"successThreshold":1,"timeoutSeconds":5},"name":"redis","ports":[{"containerPort":6379,"name":"redis"}],"readinessProbe":{"exec":{"command":["redis-cli","ping"]},"failureThreshold":5,"initialDelaySeconds":5,"periodSeconds":10,"successThreshold":1,"timeoutSeconds":1},"resources":{},"volumeMounts":[{"mountPath":"/bitnami/redis/data","name":"redis-data"}]}],"securityContext":{"fsGroup":1001,"runAsUser":1001},"serviceAccountName":"default"}},"updateStrategy":{"type":"OnDelete"},"volumeClaimTemplates":[{"metadata":{"labels":{"app":"redis","chart":"redis-3.6.5","component":"master","heritage":"Tiller","release":"redis"},"name":"redis-data"},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"8Gi"}}}}]}} + expected-ready: "true" + expected-status: "Running" + expected-health: "healthy" + expected-message: "1/1 ready" creationTimestamp: 2018-07-20T08:23:04Z generation: 1 labels: diff --git a/pkg/lua/lua.go b/pkg/lua/lua.go index 1319e28..a50f8db 100644 --- a/pkg/lua/lua.go +++ b/pkg/lua/lua.go @@ -5,6 +5,7 @@ import ( "context" "encoding/json" "fmt" + "io/fs" "os" "path/filepath" "strings" @@ -428,6 +429,23 @@ func GetWildcardConfigMapKey(vm VM, gvk schema.GroupVersionKind) string { return "" } +func ListResourceTypes() []string { + types := []string{} + + _ = fs.WalkDir(resource_customizations.Embedded, ".", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() && !strings.HasSuffix(d.Name(), "testdata") && !strings.Contains(path, "/actions") && + strings.Contains(path, "/") { + types = append(types, path) + } + return nil + }) + + return types +} + func (vm VM) getPredefinedLuaScripts(objKey string, scriptFile string) (string, error) { data, err := resource_customizations.Embedded.ReadFile(filepath.Join(objKey, scriptFile)) if err != nil {