mirror of
https://github.com/komodorio/helm-dashboard.git
synced 2026-03-24 11:48:04 +00:00
Extract repeated healthy/unhealthy logic into applyHealthFromCondition helper and convert if/else chain to switch statement (complexity 29 → 16). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
191 lines
4.7 KiB
Go
191 lines
4.7 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/joomcode/errorx"
|
|
"github.com/komodorio/helm-dashboard/v2/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"
|
|
)
|
|
|
|
const Unknown = "Unknown"
|
|
const Healthy = "Healthy"
|
|
const Unhealthy = "Unhealthy"
|
|
const Progressing = "Progressing"
|
|
|
|
type KubeHandler struct {
|
|
*Contexted
|
|
}
|
|
|
|
func (h *KubeHandler) GetContexts(c *gin.Context) {
|
|
app := h.GetApp(c)
|
|
if app == nil {
|
|
return // sets error inside
|
|
}
|
|
|
|
res, err := h.Data.ListContexts()
|
|
if err != nil {
|
|
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
c.IndentedJSON(http.StatusOK, res)
|
|
}
|
|
|
|
func (h *KubeHandler) GetResourceInfo(c *gin.Context) {
|
|
qp, err := utils.GetQueryProps(c)
|
|
if err != nil {
|
|
_ = c.AbortWithError(http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
app := h.GetApp(c)
|
|
if app == nil {
|
|
return // sets error inside
|
|
}
|
|
|
|
res, err := app.K8s.GetResourceInfo(c.Param("kind"), qp.Namespace, qp.Name)
|
|
if errors.IsNotFound(err) {
|
|
res = &v12.Carp{Status: v12.CarpStatus{Phase: "NotFound", Message: err.Error()}}
|
|
//_ = c.AbortWithError(http.StatusNotFound, err)
|
|
//return
|
|
} else if err != nil {
|
|
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
res.Status = *EnhanceStatus(res, nil)
|
|
|
|
c.IndentedJSON(http.StatusOK, res)
|
|
}
|
|
|
|
func EnhanceStatus(res *v12.Carp, err error) *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 err != nil {
|
|
c.Reason = "ErrorGettingStatus"
|
|
c.Message = err.Error()
|
|
} else if s.Phase == "Error" {
|
|
c.Status = Unhealthy
|
|
} else if slices.Contains([]string{"Available", "Active", "Established", "Bound", "Ready"}, string(s.Phase)) {
|
|
c.Status = Healthy
|
|
c.Reason = "Exists" //since there is no condition to check here, we can set reason as exists.
|
|
} else if s.Phase == "" && len(s.Conditions) > 0 {
|
|
applyCustomConditions(&s, &c)
|
|
} else if s.Phase == "Pending" || s.Phase == "Terminating" {
|
|
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 applyHealthFromCondition(c *v12.CarpCondition, cond v12.CarpCondition) {
|
|
if cond.Status == "False" {
|
|
c.Status = Unhealthy
|
|
} else {
|
|
c.Status = Healthy
|
|
}
|
|
c.Reason = cond.Reason
|
|
c.Message = cond.Message
|
|
}
|
|
|
|
func applyCustomConditions(s *v12.CarpStatus, c *v12.CarpCondition) {
|
|
for _, cond := range s.Conditions {
|
|
switch cond.Type {
|
|
case "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
|
|
}
|
|
case "Complete": // condition for Job
|
|
if c.Status == Unknown && cond.Status == "True" {
|
|
c.Status = Healthy
|
|
c.Reason = cond.Reason
|
|
c.Message = cond.Message
|
|
}
|
|
case "Failed": // condition for Job
|
|
if c.Status == Unknown && cond.Status == "True" {
|
|
c.Status = Unhealthy
|
|
c.Reason = cond.Reason
|
|
c.Message = cond.Message
|
|
}
|
|
case "Available", "DisruptionAllowed", "Ready", "AbleToScale", "ScalingActive":
|
|
if c.Status == Unknown {
|
|
applyHealthFromCondition(c, cond)
|
|
}
|
|
case "Established", "NamesAccepted": // condition for CRD
|
|
if c.Status == Unknown || c.Status == Healthy {
|
|
applyHealthFromCondition(c, cond)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (h *KubeHandler) Describe(c *gin.Context) {
|
|
qp, err := utils.GetQueryProps(c)
|
|
if err != nil {
|
|
_ = c.AbortWithError(http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
app := h.GetApp(c)
|
|
if app == nil {
|
|
return // sets error inside
|
|
}
|
|
|
|
res, err := app.K8s.DescribeResource(c.Param("kind"), qp.Namespace, qp.Name)
|
|
if err != nil {
|
|
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
c.String(http.StatusOK, res)
|
|
}
|
|
|
|
func (h *KubeHandler) GetNameSpaces(c *gin.Context) {
|
|
if c.Param("kind") != "namespaces" {
|
|
_ = c.AbortWithError(http.StatusBadRequest, errorx.AssertionFailed.New("Only 'namespaces' kind is allowed for listing"))
|
|
return
|
|
}
|
|
|
|
app := h.GetApp(c)
|
|
if app == nil {
|
|
return // sets error inside
|
|
}
|
|
|
|
res, err := app.K8s.GetNameSpaces()
|
|
if err != nil {
|
|
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
c.IndentedJSON(http.StatusOK, res)
|
|
}
|