From 50316944a841c6904263d4428ac845540655c8e8 Mon Sep 17 00:00:00 2001 From: Geoffrey Beausire Date: Fri, 27 Oct 2023 11:44:54 +0200 Subject: [PATCH] Add information about current disruption in budgets This helps owners of budget understand what disruptions are impacting/might impact their budgets --- .../applicationdisruptionbudget_types.go | 11 +++++++ api/v1alpha1/zz_generated.deepcopy.go | 20 +++++++++++++ ...iteo.com_applicationdisruptionbudgets.yaml | 18 +++++++++++ ...tion.criteo.com_nodedisruptionbudgets.yaml | 18 +++++++++++ .../applicationdisruptionbudget_controller.go | 29 ++++++++++-------- .../nodedisruptionbudget_controller.go | 30 +++++++++++-------- 6 files changed, 101 insertions(+), 25 deletions(-) diff --git a/api/v1alpha1/applicationdisruptionbudget_types.go b/api/v1alpha1/applicationdisruptionbudget_types.go index 4b6f199..9a81670 100644 --- a/api/v1alpha1/applicationdisruptionbudget_types.go +++ b/api/v1alpha1/applicationdisruptionbudget_types.go @@ -74,6 +74,17 @@ type DisruptionBudgetStatus struct { // Number of disruption currently seen on the cluster // +kubebuilder:default=0 CurrentDisruptions int `json:"currentDisruptions"` + + // Disruptions contains a list of disruptions that are related to the budget + Disruptions []Disruption `json:"disruptions"` +} + +// Basic information about disruptions +type Disruption struct { + // Name of the disruption + Name string `json:"name"` + // State of the disruption + State string `json:"state"` } //+kubebuilder:object:root=true diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 3e874cf..f35b9c1 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -123,6 +123,21 @@ func (in *DisruptedBudgetStatus) DeepCopy() *DisruptedBudgetStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Disruption) DeepCopyInto(out *Disruption) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Disruption. +func (in *Disruption) DeepCopy() *Disruption { + if in == nil { + return nil + } + out := new(Disruption) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DisruptionBudgetStatus) DeepCopyInto(out *DisruptionBudgetStatus) { *out = *in @@ -131,6 +146,11 @@ func (in *DisruptionBudgetStatus) DeepCopyInto(out *DisruptionBudgetStatus) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.Disruptions != nil { + in, out := &in.Disruptions, &out.Disruptions + *out = make([]Disruption, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DisruptionBudgetStatus. diff --git a/config/crd/bases/nodedisruption.criteo.com_applicationdisruptionbudgets.yaml b/config/crd/bases/nodedisruption.criteo.com_applicationdisruptionbudgets.yaml index a5fa77e..eca7174 100644 --- a/config/crd/bases/nodedisruption.criteo.com_applicationdisruptionbudgets.yaml +++ b/config/crd/bases/nodedisruption.criteo.com_applicationdisruptionbudgets.yaml @@ -176,6 +176,23 @@ spec: default: 0 description: Number of disruption currently seen on the cluster type: integer + disruptions: + description: Disruptions contains a list of disruptions that are related + to the budget + items: + description: Basic information about disruptions + properties: + name: + description: Name of the disruption + type: string + state: + description: State of the disruption + type: string + required: + - name + - state + type: object + type: array disruptionsAllowed: default: 0 description: Number of disruption allowed on the nodes of this @@ -189,6 +206,7 @@ spec: type: array required: - currentDisruptions + - disruptions - disruptionsAllowed type: object type: object diff --git a/config/crd/bases/nodedisruption.criteo.com_nodedisruptionbudgets.yaml b/config/crd/bases/nodedisruption.criteo.com_nodedisruptionbudgets.yaml index b5f2ab8..f37ecef 100644 --- a/config/crd/bases/nodedisruption.criteo.com_nodedisruptionbudgets.yaml +++ b/config/crd/bases/nodedisruption.criteo.com_nodedisruptionbudgets.yaml @@ -115,6 +115,23 @@ spec: default: 0 description: Number of disruption currently seen on the cluster type: integer + disruptions: + description: Disruptions contains a list of disruptions that are related + to the budget + items: + description: Basic information about disruptions + properties: + name: + description: Name of the disruption + type: string + state: + description: State of the disruption + type: string + required: + - name + - state + type: object + type: array disruptionsAllowed: default: 0 description: Number of disruption allowed on the nodes of this @@ -128,6 +145,7 @@ spec: type: array required: - currentDisruptions + - disruptions - disruptionsAllowed type: object type: object diff --git a/internal/controller/applicationdisruptionbudget_controller.go b/internal/controller/applicationdisruptionbudget_controller.go index cbce1f1..9032cf0 100644 --- a/internal/controller/applicationdisruptionbudget_controller.go +++ b/internal/controller/applicationdisruptionbudget_controller.go @@ -155,7 +155,7 @@ func (r *ApplicationDisruptionBudgetResolver) Sync(ctx context.Context) error { nodes := resolver.NodeSetToStringList(nodeNames) - disruptionCount, err := r.ResolveDisruption(ctx) + disruptionCount, disruptions, err := r.ResolveDisruption(ctx) if err != nil { return err } @@ -163,6 +163,7 @@ func (r *ApplicationDisruptionBudgetResolver) Sync(ctx context.Context) error { r.ApplicationDisruptionBudget.Status.WatchedNodes = nodes r.ApplicationDisruptionBudget.Status.CurrentDisruptions = disruptionCount r.ApplicationDisruptionBudget.Status.DisruptionsAllowed = r.ApplicationDisruptionBudget.Spec.MaxDisruptions - disruptionCount + r.ApplicationDisruptionBudget.Status.Disruptions = disruptions return nil } @@ -259,34 +260,38 @@ func (r *ApplicationDisruptionBudgetResolver) GetSelectedNodes(ctx context.Conte return nodesFromPods.Union(nodesFromPVCs), nil } -func (r *ApplicationDisruptionBudgetResolver) ResolveDisruption(ctx context.Context) (int, error) { +func (r *ApplicationDisruptionBudgetResolver) ResolveDisruption(ctx context.Context) (int, []nodedisruptionv1alpha1.Disruption, error) { + disruptions := []nodedisruptionv1alpha1.Disruption{} selectedNodes, err := r.GetSelectedNodes(ctx) if err != nil { - return 0, err + return 0, disruptions, err } - disruptions := 0 + disruptionCount := 0 opts := []client.ListOption{} nodeDisruptions := &nodedisruptionv1alpha1.NodeDisruptionList{} err = r.Client.List(ctx, nodeDisruptions, opts...) if err != nil { - return 0, err + return 0, disruptions, err } for _, nd := range nodeDisruptions.Items { - if nd.Status.State != nodedisruptionv1alpha1.Granted { - continue - } - impactedNodes, err := r.Resolver.GetNodeFromNodeSelector(ctx, nd.Spec.NodeSelector) if err != nil { - return 0, err + return 0, disruptions, err } + if selectedNodes.Intersection(impactedNodes).Len() > 0 { - disruptions++ + if nd.Status.State == nodedisruptionv1alpha1.Granted { + disruptionCount++ + } + disruptions = append(disruptions, nodedisruptionv1alpha1.Disruption{ + Name: nd.Name, + State: string(nd.Status.State), + }) } } - return disruptions, nil + return disruptionCount, disruptions, nil } diff --git a/internal/controller/nodedisruptionbudget_controller.go b/internal/controller/nodedisruptionbudget_controller.go index 7358325..935b7cd 100644 --- a/internal/controller/nodedisruptionbudget_controller.go +++ b/internal/controller/nodedisruptionbudget_controller.go @@ -140,7 +140,7 @@ func (r *NodeDisruptionBudgetResolver) Sync(ctx context.Context) error { nodes := resolver.NodeSetToStringList(nodeNames) - disruptionCount, err := r.ResolveDisruption(ctx) + disruptionCount, disruptions, err := r.ResolveDisruption(ctx) if err != nil { return err } @@ -150,7 +150,7 @@ func (r *NodeDisruptionBudgetResolver) Sync(ctx context.Context) error { disruptionsForMax := r.NodeDisruptionBudget.Spec.MaxDisruptedNodes - disruptionCount disruptionsForMin := (len(nodes) - disruptionCount) - r.NodeDisruptionBudget.Spec.MinUndisruptedNodes r.NodeDisruptionBudget.Status.DisruptionsAllowed = int(math.Min(float64(disruptionsForMax), float64(disruptionsForMin))) - disruptionCount - + r.NodeDisruptionBudget.Status.Disruptions = disruptions return nil } @@ -198,34 +198,38 @@ func (r *NodeDisruptionBudgetResolver) GetSelectedNodes(ctx context.Context) (re return nodesFromPods, nil } -func (r *NodeDisruptionBudgetResolver) ResolveDisruption(ctx context.Context) (int, error) { +func (r *NodeDisruptionBudgetResolver) ResolveDisruption(ctx context.Context) (int, []nodedisruptionv1alpha1.Disruption, error) { + disruptions := []nodedisruptionv1alpha1.Disruption{} selectedNodes, err := r.GetSelectedNodes(ctx) if err != nil { - return 0, err + return 0, disruptions, err } - disruptions := 0 + disruptionCount := 0 opts := []client.ListOption{} nodeDisruptions := &nodedisruptionv1alpha1.NodeDisruptionList{} err = r.Client.List(ctx, nodeDisruptions, opts...) if err != nil { - return 0, err + return 0, disruptions, err } for _, nd := range nodeDisruptions.Items { - if nd.Status.State != nodedisruptionv1alpha1.Granted { - continue - } - impactedNodes, err := r.Resolver.GetNodeFromNodeSelector(ctx, nd.Spec.NodeSelector) if err != nil { - return 0, err + return 0, disruptions, err } + if selectedNodes.Intersection(impactedNodes).Len() > 0 { - disruptions++ + if nd.Status.State == nodedisruptionv1alpha1.Granted { + disruptionCount++ + } + disruptions = append(disruptions, nodedisruptionv1alpha1.Disruption{ + Name: nd.Name, + State: string(nd.Status.State), + }) } } - return disruptions, nil + return disruptionCount, disruptions, err }