Skip to content

Commit

Permalink
Add information about current disruption in budgets
Browse files Browse the repository at this point in the history
This helps owners of budget understand what disruptions
are impacting/might impact their budgets
  • Loading branch information
geobeau committed Nov 3, 2023
1 parent 2cfb540 commit 57bbc9e
Show file tree
Hide file tree
Showing 10 changed files with 269 additions and 34 deletions.
11 changes: 11 additions & 0 deletions api/v1alpha1/applicationdisruptionbudget_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 20 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 24 additions & 6 deletions chart/templates/applicationdisruptionbudget-crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,6 @@ spec:
description: ApplicationDisruptionBudgetSpec defines the desired state of
ApplicationDisruptionBudget
properties:
healthURL:
description: Health URL is deprecated and will be removed in next version,
please use healthHook instead. Health URL is an optional URL to
call to validate the state of the application. Maintenance will proceed
only if the endpoint responds 2XX.
type: string
healthHook:
description: Define a optional hook to call when validating a NodeDisruption.
It perform a POST http request containing the NodeDisruption that
Expand All @@ -71,6 +65,12 @@ spec:
form (`scheme://host:port/path`).
type: string
type: object
healthURL:
description: Health URL is deprecated and will be removed in next version,
please use healthHook instead. Health URL is an optional URL to call
to validate the state of the application. Maintenance will proceed
only if the endpoint responds 2XX.
type: string
maxDisruptions:
description: A NodeDisruption is allowed if at most "maxDisruptions"
nodes selected by selectors are unavailable after the disruption.
Expand Down Expand Up @@ -175,6 +175,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
Expand All @@ -188,6 +205,7 @@ spec:
type: array
required:
- currentDisruptions
- disruptions
- disruptionsAllowed
type: object
type: object
Expand Down
18 changes: 18 additions & 0 deletions chart/templates/nodedisruptionbudget-crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -128,6 +145,7 @@ spec:
type: array
required:
- currentDisruptions
- disruptions
- disruptionsAllowed
type: object
type: object
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -189,6 +206,7 @@ spec:
type: array
required:
- currentDisruptions
- disruptions
- disruptionsAllowed
type: object
type: object
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -128,6 +145,7 @@ spec:
type: array
required:
- currentDisruptions
- disruptions
- disruptionsAllowed
type: object
type: object
Expand Down
33 changes: 21 additions & 12 deletions internal/controller/applicationdisruptionbudget_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ func (r *ApplicationDisruptionBudgetReconciler) SetupWithManager(mgr ctrl.Manage
&corev1.PersistentVolumeClaim{},
handler.EnqueueRequestsFromMapFunc(r.MapFuncBuilder()),
builder.WithPredicates(predicate.ResourceVersionChangedPredicate{})).
Watches(
&nodedisruptionv1alpha1.NodeDisruption{},
handler.EnqueueRequestsFromMapFunc(r.MapFuncBuilder()),
builder.WithPredicates(predicate.ResourceVersionChangedPredicate{})).
Complete(r)
}

Expand All @@ -155,14 +159,15 @@ 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
}

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
}

Expand Down Expand Up @@ -259,34 +264,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
}
62 changes: 60 additions & 2 deletions internal/controller/applicationdisruptionbudget_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ var _ = Describe("ApplicationDisruptionBudget controller", func() {
clearAllNodeDisruptionRessources()
})

When("there are no budgets in the cluster", func() {
It("grants the node disruption", func() {
When("Pods or PVC changes", func() {
It("updates the budget status", func() {
By("creating a budget that accepts one disruption")
ndb := &nodedisruptionv1alpha1.ApplicationDisruptionBudget{
TypeMeta: metav1.TypeMeta{
Expand Down Expand Up @@ -184,6 +184,64 @@ var _ = Describe("ApplicationDisruptionBudget controller", func() {
})
})

When("Node disruption is created", func() {
It("updates the budget status", func() {
By("creating a budget that accepts one disruption")
ndb := &nodedisruptionv1alpha1.ApplicationDisruptionBudget{
TypeMeta: metav1.TypeMeta{
APIVersion: "nodedisruption.criteo.com/v1alpha1",
Kind: "ApplicationDisruptionBudget",
},
ObjectMeta: metav1.ObjectMeta{
Name: ADBname,
Namespace: ADBNamespace,
},
Spec: nodedisruptionv1alpha1.ApplicationDisruptionBudgetSpec{
PodSelector: metav1.LabelSelector{MatchLabels: podLabels},
PVCSelector: metav1.LabelSelector{MatchLabels: podLabels},
MaxDisruptions: 1,
},
}
Expect(k8sClient.Create(ctx, ndb)).Should(Succeed())

By("checking the ApplicationDisruptionBudget in synchronized")
ADBLookupKey := types.NamespacedName{Name: ADBname, Namespace: ADBNamespace}
createdADB := &nodedisruptionv1alpha1.ApplicationDisruptionBudget{}
Eventually(func() []string {
err := k8sClient.Get(ctx, ADBLookupKey, createdADB)
Expect(err).Should(Succeed())
return createdADB.Status.WatchedNodes
}, timeout, interval).Should(Equal([]string{"node1", "node2", "node3"}))

By("Adding Node Disruption")
disruption := &nodedisruptionv1alpha1.NodeDisruption{
TypeMeta: metav1.TypeMeta{
APIVersion: "nodedisruption.criteo.com/v1alpha1",
Kind: "NodeDisruption",
},
ObjectMeta: metav1.ObjectMeta{
Name: "test-adb-1",
Namespace: "",
},
Spec: nodedisruptionv1alpha1.NodeDisruptionSpec{
NodeSelector: metav1.LabelSelector{MatchLabels: nodeLabels1},
},
}
Expect(k8sClient.Create(ctx, disruption.DeepCopy())).Should(Succeed())

By("checking the ApplicationDisruptionBudget updated the status")
Eventually(func() []nodedisruptionv1alpha1.Disruption {
err := k8sClient.Get(ctx, ADBLookupKey, createdADB)
Expect(err).Should(Succeed())
return createdADB.Status.Disruptions
}, timeout, interval).Should(Equal([]nodedisruptionv1alpha1.Disruption{{
Name: "test-adb-1",
State: "granted",
}}))

})
})

})

})
Expand Down
Loading

0 comments on commit 57bbc9e

Please sign in to comment.