diff --git a/cmd/options/options.go b/cmd/options/options.go index fa4fb65a5..2a654bce3 100644 --- a/cmd/options/options.go +++ b/cmd/options/options.go @@ -88,6 +88,9 @@ type NodeProblemDetectorOptions struct { MonitorConfigPaths types.ProblemDaemonConfigPathMap // application options + // DeleteDeprecatedConditions is the flag determining whether to delete deprecated Conditions and Events from the node status. + DeleteDeprecatedConditions bool + DeprecatedConditionTypes []string // NodeName is the node name used to communicate with Kubernetes ApiServer. NodeName string @@ -131,6 +134,8 @@ func (npdo *NodeProblemDetectorOptions) AddFlags(fs *pflag.FlagSet) { "127.0.0.1", "The address to bind the Prometheus scrape endpoint.") fs.Float32Var(&npdo.QPS, "kube-api-qps", 500, "Maximum QPS to use while talking with Kubernetes API") fs.IntVar(&npdo.Burst, "kube-api-burst", 500, "Maximum burst for throttle while talking with Kubernetes API") + fs.BoolVar(&npdo.DeleteDeprecatedConditions, "delete-deprecated-conditions", false, "Whether to delete deprecated Conditions and Events from the node status") + fs.StringSliceVar(&npdo.DeprecatedConditionTypes, "deprecated-condition-types", []string{}, "List of deprecated condition types to delete. This is ignored if --delete-deprecated-conditions is false.") for _, exporterName := range exporters.GetExporterNames() { exporterHandler := exporters.GetExporterHandlerOrDie(exporterName) exporterHandler.Options.SetFlags(fs) diff --git a/pkg/exporters/k8sexporter/k8s_exporter.go b/pkg/exporters/k8sexporter/k8s_exporter.go index 31ec9c793..8d44f305d 100644 --- a/pkg/exporters/k8sexporter/k8s_exporter.go +++ b/pkg/exporters/k8sexporter/k8s_exporter.go @@ -63,6 +63,12 @@ func NewExporterOrDie(ctx context.Context, npdo *options.NodeProblemDetectorOpti } ke.startHTTPReporting(npdo) + if npdo.DeleteDeprecatedConditions { + err := ke.client.DeleteDeprecatedConditions(ctx, npdo.DeprecatedConditionTypes) + if err != nil { + klog.Errorf("Failed to delete deprecated conditions: %v", err) + } + } ke.conditionManager.Start(ctx) return &ke diff --git a/pkg/exporters/k8sexporter/problemclient/fake_problem_client.go b/pkg/exporters/k8sexporter/problemclient/fake_problem_client.go index c3ccf0248..b803d9156 100644 --- a/pkg/exporters/k8sexporter/problemclient/fake_problem_client.go +++ b/pkg/exporters/k8sexporter/problemclient/fake_problem_client.go @@ -60,6 +60,18 @@ func (f *FakeProblemClient) AssertConditions(expected []v1.NodeCondition) error return nil } +// DeleteDeprecatedConditions is a fake mimic of DeleteDeprecatedConditions, it only delete the internal condition cache. +func (f *FakeProblemClient) DeleteDeprecatedConditions(ctx context.Context, types []string) error { + if err, ok := f.errors["DeleteDeprecatedConditions"]; ok { + return err + } + realTypes := generateConditionTypes(types) + for _, t := range realTypes { + delete(f.conditions, t) + } + return nil +} + // SetConditions is a fake mimic of SetConditions, it only update the internal condition cache. func (f *FakeProblemClient) SetConditions(ctx context.Context, conditions []v1.NodeCondition) error { f.Lock() diff --git a/pkg/exporters/k8sexporter/problemclient/problem_client.go b/pkg/exporters/k8sexporter/problemclient/problem_client.go index 03f6bf37a..b968ee5a2 100644 --- a/pkg/exporters/k8sexporter/problemclient/problem_client.go +++ b/pkg/exporters/k8sexporter/problemclient/problem_client.go @@ -23,6 +23,7 @@ import ( "net/url" "os" "path/filepath" + "slices" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -41,6 +42,8 @@ import ( // Client is the interface of problem client type Client interface { + // DeleteDeprecatedConditions deletes the deprecated conditions of the node. + DeleteDeprecatedConditions(ctx context.Context, conditionTypeStrings []string) error // GetConditions get all specific conditions of current node. GetConditions(ctx context.Context, conditionTypes []v1.NodeConditionType) ([]*v1.NodeCondition, error) // SetConditions set or update conditions of current node. @@ -120,6 +123,44 @@ func (c *nodeProblemClient) SetConditions(ctx context.Context, newConditions []v ) } +func (c *nodeProblemClient) DeleteDeprecatedConditions(ctx context.Context, conditionTypeStrings []string) error { + // get node object + klog.Infof("Deleting deprecated conditions %v (if present)...", conditionTypeStrings) + node, err := c.GetNode(ctx) + if err != nil { + return err + } + + conditionTypes := generateConditionTypes(conditionTypeStrings) + // create a slice of the conditions we want to keep + newConditions := []v1.NodeCondition{} + for _, condition := range node.Status.Conditions { + if !slices.Contains(conditionTypes, condition.Type) { + newConditions = append(newConditions, condition) + } else { + klog.Infof("Deleting deprecated condition %s", condition.Type) + } + } + + // update the node status if we need to, + // we only need to if the number of conditions has changed + if len(newConditions) < len(node.Status.Conditions) { + node.Status.Conditions = newConditions + return retry.OnError(retry.DefaultRetry, + func(error) bool { + return true + }, + func() error { + _, err = c.client.Nodes().UpdateStatus(ctx, node, metav1.UpdateOptions{}) + return err + }, + ) + } else { + klog.Infof("No deprecated conditions to delete") + } + return nil +} + func (c *nodeProblemClient) Eventf(eventType, source, reason, messageFmt string, args ...interface{}) { recorder, found := c.recorders[source] if !found { @@ -163,3 +204,12 @@ func getNodeRef(namespace, nodeName string) *v1.ObjectReference { Namespace: namespace, } } + +func generateConditionTypes(conditionTypeStrings []string) []v1.NodeConditionType { + // convert the condition type strings to NodeConditionType + conditionTypes := []v1.NodeConditionType{} + for _, conditionTypeString := range conditionTypeStrings { + conditionTypes = append(conditionTypes, v1.NodeConditionType(conditionTypeString)) + } + return conditionTypes +}