mirror of
https://github.com/kubereboot/kured.git
synced 2026-05-04 15:37:14 +00:00
Extract blockers
This will make it easier to manipulate main in the future. Signed-off-by: Jean-Philippe Evrard <open-source@a.spamming.party>
This commit is contained in:
102
pkg/blockers/blockers.go
Normal file
102
pkg/blockers/blockers.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package blockers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/kubereboot/kured/pkg/alerts"
|
||||
log "github.com/sirupsen/logrus"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// RebootBlocked checks that a single block Checker
|
||||
// will block the reboot or not.
|
||||
func RebootBlocked(blockers ...RebootBlocker) bool {
|
||||
for _, blocker := range blockers {
|
||||
if blocker.IsBlocked() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RebootBlocker interface should be implemented by types
|
||||
// to know if their instantiations should block a reboot
|
||||
type RebootBlocker interface {
|
||||
IsBlocked() bool
|
||||
}
|
||||
|
||||
// PrometheusBlockingChecker contains info for connecting
|
||||
// to prometheus, and can give info about whether a reboot should be blocked
|
||||
type PrometheusBlockingChecker struct {
|
||||
// prometheusClient to make prometheus-go-client and api config available
|
||||
// into the PrometheusBlockingChecker struct
|
||||
PromClient *alerts.PromClient
|
||||
// regexp used to get alerts
|
||||
Filter *regexp.Regexp
|
||||
// bool to indicate if only firing alerts should be considered
|
||||
FiringOnly bool
|
||||
// bool to indicate that we're only blocking on alerts which match the filter
|
||||
FilterMatchOnly bool
|
||||
}
|
||||
|
||||
// KubernetesBlockingChecker contains info for connecting
|
||||
// to k8s, and can give info about whether a reboot should be blocked
|
||||
type KubernetesBlockingChecker struct {
|
||||
// client used to contact kubernetes API
|
||||
Client *kubernetes.Clientset
|
||||
Nodename string
|
||||
// lised used to filter pods (podSelector)
|
||||
Filter []string
|
||||
}
|
||||
|
||||
// IsBlocked for the prometheus will check if there are active alerts matching
|
||||
// the arguments given into promclient which would actively block the reboot.
|
||||
// As of today, no blocker information is shared as a return of the method,
|
||||
// and the information is simply logged.
|
||||
func (pb PrometheusBlockingChecker) IsBlocked() bool {
|
||||
alertNames, err := pb.PromClient.ActiveAlerts(pb.Filter, pb.FiringOnly, pb.FilterMatchOnly)
|
||||
if err != nil {
|
||||
log.Warnf("Reboot blocked: prometheus query error: %v", err)
|
||||
return true
|
||||
}
|
||||
count := len(alertNames)
|
||||
if count > 10 {
|
||||
alertNames = append(alertNames[:10], "...")
|
||||
}
|
||||
if count > 0 {
|
||||
log.Warnf("Reboot blocked: %d active alerts: %v", count, alertNames)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsBlocked for the KubernetesBlockingChecker will check if a pod, for the node, is preventing
|
||||
// the reboot. It will warn in the logs about blocking, but does not return an error.
|
||||
func (kb KubernetesBlockingChecker) IsBlocked() bool {
|
||||
fieldSelector := fmt.Sprintf("spec.nodeName=%s,status.phase!=Succeeded,status.phase!=Failed,status.phase!=Unknown", kb.Nodename)
|
||||
for _, labelSelector := range kb.Filter {
|
||||
podList, err := kb.Client.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{
|
||||
LabelSelector: labelSelector,
|
||||
FieldSelector: fieldSelector,
|
||||
Limit: 10})
|
||||
if err != nil {
|
||||
log.Warnf("Reboot blocked: pod query error: %v", err)
|
||||
return true
|
||||
}
|
||||
|
||||
if len(podList.Items) > 0 {
|
||||
podNames := make([]string, 0, len(podList.Items))
|
||||
for _, pod := range podList.Items {
|
||||
podNames = append(podNames, pod.Name)
|
||||
}
|
||||
if len(podList.Continue) > 0 {
|
||||
podNames = append(podNames, "...")
|
||||
}
|
||||
log.Warnf("Reboot blocked: matching pods: %v", podNames)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
67
pkg/blockers/blockers_test.go
Normal file
67
pkg/blockers/blockers_test.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package blockers
|
||||
|
||||
import (
|
||||
"github.com/kubereboot/kured/pkg/alerts"
|
||||
papi "github.com/prometheus/client_golang/api"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type BlockingChecker struct {
|
||||
blocking bool
|
||||
}
|
||||
|
||||
func (fbc BlockingChecker) IsBlocked() bool {
|
||||
return fbc.blocking
|
||||
}
|
||||
|
||||
func Test_rebootBlocked(t *testing.T) {
|
||||
noCheckers := []RebootBlocker{}
|
||||
nonblockingChecker := BlockingChecker{blocking: false}
|
||||
blockingChecker := BlockingChecker{blocking: true}
|
||||
|
||||
// Instantiate a prometheusClient with a broken_url
|
||||
promClient, _ := alerts.NewPromClient(papi.Config{Address: "broken_url"})
|
||||
brokenPrometheusClient := PrometheusBlockingChecker{PromClient: promClient, Filter: nil, FiringOnly: false}
|
||||
|
||||
type args struct {
|
||||
blockers []RebootBlocker
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "Do not block on no blocker defined",
|
||||
args: args{blockers: noCheckers},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "Ensure a blocker blocks",
|
||||
args: args{blockers: []RebootBlocker{blockingChecker}},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Ensure a non-blocker doesn't block",
|
||||
args: args{blockers: []RebootBlocker{nonblockingChecker}},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "Ensure one blocker is enough to block",
|
||||
args: args{blockers: []RebootBlocker{nonblockingChecker, blockingChecker}},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Do block on error contacting prometheus API",
|
||||
args: args{blockers: []RebootBlocker{brokenPrometheusClient}},
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := RebootBlocked(tt.args.blockers...); got != tt.want {
|
||||
t.Errorf("rebootBlocked() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user