From be523f25aec809ef25dc00d6f019f51463fa4005 Mon Sep 17 00:00:00 2001 From: D074096 Date: Thu, 11 Jan 2024 19:23:13 +0100 Subject: [PATCH] Present check plugin info on webpage --- plugin/check.go | 8 ++++++++ plugin/impl/affinity.go | 6 +++--- plugin/impl/annotation.go | 4 +++- plugin/impl/condition.go | 4 +++- plugin/impl/kubernikus.go | 4 ++-- plugin/impl/label.go | 4 +++- plugin/impl/maxmaintenance.go | 5 +++-- plugin/impl/nodecount.go | 4 +++- plugin/impl/prometheus.go | 1 + plugin/impl/semver.go | 3 ++- plugin/impl/stagger.go | 7 +++++-- plugin/impl/timewindow.go | 4 ++-- plugin/impl/wait.go | 4 ++-- static/index.html | 2 ++ 14 files changed, 42 insertions(+), 18 deletions(-) diff --git a/plugin/check.go b/plugin/check.go index 4ad0dac..2abd4d7 100644 --- a/plugin/check.go +++ b/plugin/check.go @@ -37,10 +37,18 @@ func Passed(info map[string]any) CheckResult { return CheckResult{Passed: true, Info: info} } +func PassedWithReason(reason string) CheckResult { + return CheckResult{Passed: true, Info: map[string]any{"reason": reason}} +} + func Failed(info map[string]any) CheckResult { return CheckResult{Passed: false, Info: info} } +func FailedWithReason(reason string) CheckResult { + return CheckResult{Passed: false, Info: map[string]any{"reason": reason}} +} + // Checker is the interface that check plugins need to implement. // Check plugins have to be idempotent, as they are invoked multiple times. // A zero-initialized check plugin should not actually work as it is used diff --git a/plugin/impl/affinity.go b/plugin/impl/affinity.go index 36138cd..0d4c7f3 100644 --- a/plugin/impl/affinity.go +++ b/plugin/impl/affinity.go @@ -72,7 +72,7 @@ func (a *Affinity) Check(params plugin.Parameters) (plugin.CheckResult, error) { return plugin.Failed(nil), err } if operationalCount >= a.MinOperational { - return plugin.Passed(map[string]any{"reason": "minOperational exceeded"}), nil + return plugin.PassedWithReason("minOperational exceeded"), nil } } currentAffinity, err := hasAffinityPod(params.Node.Name, ¶ms) @@ -81,7 +81,7 @@ func (a *Affinity) Check(params plugin.Parameters) (plugin.CheckResult, error) { } // current node does not have any relevant pods, so pass if !currentAffinity { - return plugin.Passed(nil), nil + return plugin.PassedWithReason("no pods with affinity"), nil } return checkOther(¶ms, nodeStates) } @@ -137,7 +137,7 @@ func checkOther(params *plugin.Parameters, nodeStates nodeStateMap) (plugin.Chec return plugin.Failed(nil), fmt.Errorf("failed to check if node %v has affinity pods: %w", params.Node.Name, err) } if !nodeAffinity { - return plugin.Failed(nil), nil + return plugin.FailedWithReason("pods without affinity on: " + node.Name), nil } } // all other nodes have relevant pods as well, so pass diff --git a/plugin/impl/annotation.go b/plugin/impl/annotation.go index 210c3a8..d6ae679 100644 --- a/plugin/impl/annotation.go +++ b/plugin/impl/annotation.go @@ -20,6 +20,8 @@ package impl import ( + "fmt" + "github.com/sapcc/maintenance-controller/plugin" "github.com/sapcc/ucfgwrap" ) @@ -51,7 +53,7 @@ func (h *HasAnnotation) ID() string { func (h *HasAnnotation) Check(params plugin.Parameters) (plugin.CheckResult, error) { val, ok := params.Node.Annotations[h.Key] if !ok { - return plugin.Failed(nil), nil + return plugin.FailedWithReason(fmt.Sprintf("annotation %s not present", h.Key)), nil } if h.Value == "" { return plugin.Passed(nil), nil diff --git a/plugin/impl/condition.go b/plugin/impl/condition.go index 8df86bc..cfb9ad6 100644 --- a/plugin/impl/condition.go +++ b/plugin/impl/condition.go @@ -20,6 +20,8 @@ package impl import ( + "fmt" + "github.com/sapcc/maintenance-controller/plugin" "github.com/sapcc/ucfgwrap" v1 "k8s.io/api/core/v1" @@ -55,7 +57,7 @@ func (c *Condition) Check(params plugin.Parameters) (plugin.CheckResult, error) return plugin.CheckResult{Passed: condition.Status == v1.ConditionStatus(c.Status)}, nil } } - return plugin.Failed(nil), nil + return plugin.FailedWithReason(fmt.Sprintf("condition %s not present", c.Type)), nil } func (c *Condition) OnTransition(params plugin.Parameters) error { diff --git a/plugin/impl/kubernikus.go b/plugin/impl/kubernikus.go index b4e9c66..d48f6ef 100644 --- a/plugin/impl/kubernikus.go +++ b/plugin/impl/kubernikus.go @@ -76,9 +76,9 @@ func (kc *KubernikusCount) Check(params plugin.Parameters) (plugin.CheckResult, specCount += nodePool.Size } if len(nodeList.Items) >= specCount { - return plugin.Passed(nil), nil + return plugin.PassedWithReason("found equal or more nodes than specified by nodepool"), nil } - return plugin.Failed(nil), nil + return plugin.FailedWithReason("found less nodes than specified by nodepool"), nil } func (kc *KubernikusCount) fetchKluster(params *plugin.Parameters) (kluster, error) { diff --git a/plugin/impl/label.go b/plugin/impl/label.go index 8e2be1e..396997d 100644 --- a/plugin/impl/label.go +++ b/plugin/impl/label.go @@ -20,6 +20,8 @@ package impl import ( + "fmt" + "github.com/sapcc/maintenance-controller/plugin" "github.com/sapcc/ucfgwrap" v1 "k8s.io/api/core/v1" @@ -51,7 +53,7 @@ func (h *HasLabel) ID() string { func (h *HasLabel) Check(params plugin.Parameters) (plugin.CheckResult, error) { val, ok := params.Node.Labels[h.Key] if !ok { - return plugin.Failed(nil), nil + return plugin.FailedWithReason(fmt.Sprintf("label %s not present", h.Key)), nil } if h.Value == "" { return plugin.Passed(nil), nil diff --git a/plugin/impl/maxmaintenance.go b/plugin/impl/maxmaintenance.go index ae2af01..dfac0d4 100644 --- a/plugin/impl/maxmaintenance.go +++ b/plugin/impl/maxmaintenance.go @@ -95,10 +95,11 @@ func (m *MaxMaintenance) checkInternal(params plugin.Parameters, nodes []corev1. } nodes = filtered } + info := map[string]any{"maintained": len(nodes), "max": m.MaxNodes} if len(nodes) >= m.MaxNodes { - return plugin.Failed(nil), nil + return plugin.Failed(info), nil } - return plugin.Passed(nil), nil + return plugin.Passed(info), nil } func (m *MaxMaintenance) filterProfileName(nodes []corev1.Node) []corev1.Node { diff --git a/plugin/impl/nodecount.go b/plugin/impl/nodecount.go index 03e94b0..c9a1c38 100644 --- a/plugin/impl/nodecount.go +++ b/plugin/impl/nodecount.go @@ -51,7 +51,9 @@ func (n *NodeCount) Check(params plugin.Parameters) (plugin.CheckResult, error) if err != nil { return plugin.Failed(nil), err } - return plugin.CheckResult{Passed: len(nodeList.Items) >= n.Count}, nil + current := len(nodeList.Items) + info := map[string]any{"current": current, "expected": n.Count} + return plugin.CheckResult{Passed: current >= n.Count, Info: info}, nil } func (n *NodeCount) OnTransition(params plugin.Parameters) error { diff --git a/plugin/impl/prometheus.go b/plugin/impl/prometheus.go index 7a2abf9..31fc2cb 100644 --- a/plugin/impl/prometheus.go +++ b/plugin/impl/prometheus.go @@ -82,6 +82,7 @@ func (pi *PrometheusInstant) Check(params plugin.Parameters) (plugin.CheckResult return plugin.Failed(info), fmt.Errorf("result does not contain exactly one element") } value := float64(vector[0].Value) + info["value"] = value evaluable, err := gval.Full().NewEvaluable(pi.Expr) if err != nil { return plugin.Failed(info), err diff --git a/plugin/impl/semver.go b/plugin/impl/semver.go index 2b661a3..a6dd154 100644 --- a/plugin/impl/semver.go +++ b/plugin/impl/semver.go @@ -87,7 +87,8 @@ func (cs *ClusterSemver) Check(params plugin.Parameters) (plugin.CheckResult, er maxVersion = version } } - return plugin.CheckResult{Passed: ownVersion.LT(maxVersion)}, nil + info := map[string]any{"own": ownVersion.String(), "max": maxVersion.String()} + return plugin.CheckResult{Passed: ownVersion.LT(maxVersion), Info: info}, nil } func filterByProfile(nodes []v1.Node, profile string) []v1.Node { diff --git a/plugin/impl/stagger.go b/plugin/impl/stagger.go index 71362a3..a38520c 100644 --- a/plugin/impl/stagger.go +++ b/plugin/impl/stagger.go @@ -71,17 +71,20 @@ func (s *Stagger) ID() string { // Check asserts that since the last successful check is a certain time has passed. func (s *Stagger) Check(params plugin.Parameters) (plugin.CheckResult, error) { s.grabIndex = noGrab + availableIn := make([]time.Duration, 0) for i := 0; i < s.Parallel; i++ { lease, err := s.getOrCreateLease(i, ¶ms) if err != nil { return plugin.Failed(nil), err } - if time.Since(lease.Spec.RenewTime.Time) > time.Duration(*lease.Spec.LeaseDurationSeconds)*time.Second { + leaseDuration := time.Duration(*lease.Spec.LeaseDurationSeconds) * time.Second + if time.Since(lease.Spec.RenewTime.Time) > leaseDuration { s.grabIndex = i return plugin.Passed(nil), nil } + availableIn = append(availableIn, leaseDuration-time.Since(lease.Spec.RenewTime.Time)) } - return plugin.Failed(nil), nil + return plugin.Failed(map[string]any{"availableIn": availableIn}), nil } func (s *Stagger) getOrCreateLease(idx int, params *plugin.Parameters) (coordinationv1.Lease, error) { diff --git a/plugin/impl/timewindow.go b/plugin/impl/timewindow.go index bd17541..0ba3a77 100644 --- a/plugin/impl/timewindow.go +++ b/plugin/impl/timewindow.go @@ -105,7 +105,7 @@ func (tw *TimeWindow) checkInternal(current time.Time) plugin.CheckResult { } } if !containsWeekday { - return plugin.Failed(nil) + return plugin.FailedWithReason("current day of week forbidden") } isExcluded := false for _, exclude := range tw.Exclude { @@ -115,7 +115,7 @@ func (tw *TimeWindow) checkInternal(current time.Time) plugin.CheckResult { } } if isExcluded { - return plugin.Failed(nil) + return plugin.FailedWithReason("day/month forbidden") } // It is required to set the date to the configured values only keeping the time compare := time.Date(tw.Start.Year(), tw.Start.Month(), tw.Start.Day(), current.Hour(), diff --git a/plugin/impl/wait.go b/plugin/impl/wait.go index b07ff79..8f975cf 100644 --- a/plugin/impl/wait.go +++ b/plugin/impl/wait.go @@ -55,7 +55,7 @@ func (w *Wait) Check(params plugin.Parameters) (plugin.CheckResult, error) { if time.Since(params.LastTransition) > w.Duration { return plugin.Passed(nil), nil } - return plugin.Failed(nil), nil + return plugin.Failed(map[string]any{"available_in": w.Duration - time.Since(params.LastTransition)}), nil } func (w *Wait) OnTransition(params plugin.Parameters) error { @@ -137,7 +137,7 @@ func (we *WaitExclude) checkInternal(params *plugin.Parameters, now time.Time) p if since > we.Duration { return plugin.Passed(nil) } - return plugin.Failed(nil) + return plugin.Failed(map[string]any{"available_in": we.Duration - since}) } func (we *WaitExclude) isExcluded(weekday time.Weekday) bool { diff --git a/static/index.html b/static/index.html index 9c3967e..ac2ba49 100644 --- a/static/index.html +++ b/static/index.html @@ -133,6 +133,7 @@

Instance Type Passed + Notes @@ -141,6 +142,7 @@

+