Display resource health aggregate icons on list of releases (#235)

* Display aggregate resource health status

* Reuse old API request, show icons

* Take progress indication from deployment conditions

* Improve status

* Cleanup

* Fixups

* Squares approach
This commit is contained in:
Andrey Pokhilko
2023-03-13 12:56:31 +00:00
committed by GitHub
parent 47dae4d35a
commit 57d4d073e9
7 changed files with 175 additions and 51 deletions

View File

@@ -136,6 +136,26 @@ func (h *HelmHandler) Resources(c *gin.Context) {
return
}
if c.Query("health") != "" { // we need to query k8s for health status
app := h.GetApp(c)
if app == nil {
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
}
for _, obj := range res {
ns := obj.Namespace
if ns == "" {
ns = c.Param("ns")
}
info, err := app.K8s.GetResourceInfo(obj.Kind, ns, obj.Name)
if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
}
obj.Status = *EnhanceStatus(info)
}
}
c.IndentedJSON(http.StatusOK, res)
}
@@ -587,23 +607,24 @@ func (h *HelmHandler) repoFromArtifactHub(name string) (string, error) {
return true
}
// more popular
if ri.Stars != rj.Stars {
return ri.Stars > rj.Stars
}
// or from verified publishers
if ri.Repository.VerifiedPublisher && !rj.Repository.VerifiedPublisher {
return true
}
// or just more popular
if ri.Stars > rj.Stars {
return true
}
// or with more recent app version
if semver.Compare("v"+ri.AppVersion, "v"+rj.AppVersion) > 0 {
return true
c := semver.Compare("v"+ri.AppVersion, "v"+rj.AppVersion)
if c != 0 {
return c > 0
}
return false
// shorter repo name is usually closer to officials
return len(ri.Repository.Name) < len(rj.Repository.Name)
})
r := results[0]

View File

@@ -1,15 +1,21 @@
package handlers
import (
"github.com/joomcode/errorx"
"k8s.io/apimachinery/pkg/api/errors"
"net/http"
"github.com/gin-gonic/gin"
"github.com/joomcode/errorx"
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
log "github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/api/errors"
v12 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
"k8s.io/utils/strings/slices"
"net/http"
)
const Unknown = "Unknown"
const Healthy = "Healthy"
const Unhealthy = "Unhealthy"
const Progressing = "Progressing"
type KubeHandler struct {
*Contexted
}
@@ -55,20 +61,59 @@ func (h *KubeHandler) GetResourceInfo(c *gin.Context) {
c.IndentedJSON(http.StatusOK, res)
}
func EnhanceStatus(res *v12.Carp) {
// custom logic to provide most meaningful status for the resource
if res.Status.Phase == "Active" || res.Status.Phase == "Error" {
_ = res.Name + ""
} else if res.Status.Phase == "" && len(res.Status.Conditions) > 0 {
res.Status.Phase = v12.CarpPhase(res.Status.Conditions[len(res.Status.Conditions)-1].Type)
res.Status.Message = res.Status.Conditions[len(res.Status.Conditions)-1].Message
res.Status.Reason = res.Status.Conditions[len(res.Status.Conditions)-1].Reason
if res.Status.Conditions[len(res.Status.Conditions)-1].Status == "False" {
res.Status.Phase = "Not" + res.Status.Phase
}
} else if res.Status.Phase == "" {
res.Status.Phase = "Exists"
func EnhanceStatus(res *v12.Carp) *v12.CarpStatus {
s := res.Status
if s.Conditions == nil {
s.Conditions = []v12.CarpCondition{}
}
c := v12.CarpCondition{
Type: "hdHealth",
Status: Unknown,
Reason: s.Reason,
Message: s.Message,
}
// custom logic to provide most meaningful status for the resource
if s.Phase == "Error" {
c.Status = Unhealthy
} else if slices.Contains([]string{"Available", "Active", "Established", "Bound", "Ready"}, string(s.Phase)) {
c.Status = Healthy
} else if s.Phase == "" && len(s.Conditions) > 0 {
for _, cond := range s.Conditions {
if cond.Type == "Progressing" { // https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
if cond.Status == "False" {
c.Status = Unhealthy
c.Reason = cond.Reason
c.Message = cond.Message
} else if cond.Reason != "NewReplicaSetAvailable" {
c.Status = Progressing
c.Reason = cond.Reason
c.Message = cond.Message
}
} else if cond.Type == "Available" && c.Status == Unknown {
if cond.Status == "False" {
c.Status = Unhealthy
} else {
c.Status = Healthy
}
c.Reason = cond.Reason
c.Message = cond.Message
}
}
} else if s.Phase == "Pending" {
c.Status = Progressing
c.Reason = string(s.Phase)
} else if s.Phase == "" {
c.Status = Healthy
c.Reason = "Exists"
} else {
log.Warnf("Unhandled status: %v", s)
c.Reason = string(s.Phase)
}
s.Conditions = append(s.Conditions, c)
return &s
}
func (h *KubeHandler) Describe(c *gin.Context) {