Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions api/v1alpha1/applicationdisruptionbudget_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ type ApplicationDisruptionBudgetSpec struct {
// A POST http request containing a Disruption that is being reconciled is sent ot each of the hooks.
// +kubebuilder:validation:Optional
HookV2BasePath HookSpec `json:"hookV2BasePath,omitempty"`

// SupportedNodeDisruptionTypes is the list of node disruption types that this budget supports.
// When set, this budget will only be considered during reconciliation of NodeDisruptions whose type
// is in this list. When empty, the controller's default node disruption types are used.
// +kubebuilder:validation:Optional
SupportedNodeDisruptionTypes []string `json:"supportedNodeDisruptionTypes,omitempty"`
}

type HookSpec struct {
Expand Down
5 changes: 5 additions & 0 deletions api/v1alpha1/nodedisruptionbudget_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ type NodeDisruptionBudgetSpec struct {
MinUndisruptedNodes int `json:"minUndisruptedNodes"`
// NodeSelector query over pods whose nodes are managed by the disruption budget.
NodeSelector metav1.LabelSelector `json:"nodeSelector,omitempty"`
// SupportedNodeDisruptionTypes is the list of node disruption types that this budget supports.
// When set, this budget will only be considered during reconciliation of NodeDisruptions whose type
// is in this list. When empty, the controller's default node disruption types are used.
// +kubebuilder:validation:Optional
SupportedNodeDisruptionTypes []string `json:"supportedNodeDisruptionTypes,omitempty"`
}

//+kubebuilder:object:root=true
Expand Down
10 changes: 10 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.

23 changes: 23 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package main

import (
"flag"
"fmt"
"os"
"strings"
"time"
Expand Down Expand Up @@ -61,6 +62,7 @@ func main() {
var rejectOverlappingDisruption bool
var healthHookTimeout time.Duration
var nodeDisruptionTypesRaw string
var defaultNodeDisruptionTypesRaw string
flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
Expand All @@ -71,6 +73,7 @@ func main() {
flag.BoolVar(&rejectOverlappingDisruption, "reject-overlapping-disruption", false, "Automatically reject any overlapping NodeDisruption (based on node selector), preserving the oldest one")
flag.DurationVar(&healthHookTimeout, "healthhook-timeout", controller.DefaultHealthHookTimeout, "HTTP client timeout for calling HealthHook resolved from ADB")
flag.StringVar(&nodeDisruptionTypesRaw, "node-disruption-types", "", "The list of types allowed for a node disruption separated by a comma.")
flag.StringVar(&defaultNodeDisruptionTypesRaw, "default-node-disruption-types", "", "The default list of node disruption types for ADBs that don't specify supportedNodeDisruptionTypes. Must be a subset of --node-disruption-types.")

opts := zap.Options{
Development: true,
Expand Down Expand Up @@ -105,6 +108,20 @@ func main() {
}

nodeDisruptionTypes := strings.FieldsFunc(nodeDisruptionTypesRaw, func(c rune) bool { return c == ',' })
defaultNodeDisruptionTypes := strings.FieldsFunc(defaultNodeDisruptionTypesRaw, func(c rune) bool { return c == ',' })

if len(nodeDisruptionTypes) > 0 {
nodeDisruptionTypesSet := make(map[string]struct{}, len(nodeDisruptionTypes))
for _, t := range nodeDisruptionTypes {
nodeDisruptionTypesSet[t] = struct{}{}
}
for _, t := range defaultNodeDisruptionTypes {
if _, ok := nodeDisruptionTypesSet[t]; !ok {
setupLog.Error(fmt.Errorf("default-node-disruption-types contains type %q which is not in node-disruption-types", t), "invalid configuration")
os.Exit(1)
}
}
}

if err = (&controller.NodeDisruptionReconciler{
Client: mgr.GetClient(),
Expand All @@ -123,13 +140,19 @@ func main() {
if err = (&controller.ApplicationDisruptionBudgetReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Config: controller.ApplicationDisruptionBudgetConfig{
DefaultNodeDisruptionTypes: defaultNodeDisruptionTypes,
},
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "ApplicationDisruptionBudget")
os.Exit(1)
}
if err = (&controller.NodeDisruptionBudgetReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Config: controller.NodeDisruptionBudgetConfig{
DefaultNodeDisruptionTypes: defaultNodeDisruptionTypes,
},
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "NodeDisruptionBudget")
os.Exit(1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,14 @@ spec:
type: object
type: object
x-kubernetes-map-type: atomic
supportedNodeDisruptionTypes:
description: |-
SupportedNodeDisruptionTypes is the list of node disruption types that this budget supports.
When set, this budget will only be considered during reconciliation of NodeDisruptions whose type
is in this list. When empty, the controller's default node disruption types are used.
items:
type: string
type: array
required:
- maxDisruptions
type: object
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,14 @@ spec:
type: object
type: object
x-kubernetes-map-type: atomic
supportedNodeDisruptionTypes:
description: |-
SupportedNodeDisruptionTypes is the list of node disruption types that this budget supports.
When set, this budget will only be considered during reconciliation of NodeDisruptions whose type
is in this list. When empty, the controller's default node disruption types are used.
items:
type: string
type: array
required:
- maxDisruptedNodes
- minUndisruptedNodes
Expand Down
16 changes: 16 additions & 0 deletions internal/controller/applicationdisruptionbudget_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,16 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

type ApplicationDisruptionBudgetConfig struct {
DefaultNodeDisruptionTypes []string
}

// ApplicationDisruptionBudgetReconciler reconciles a ApplicationDisruptionBudget object
type ApplicationDisruptionBudgetReconciler struct {
client.Client
Scheme *runtime.Scheme
HTTPCli *http.Client
Config ApplicationDisruptionBudgetConfig
}

//+kubebuilder:rbac:groups=nodedisruption.criteo.com,resources=applicationdisruptionbudgets,verbs=get;list;watch;create;update;patch;delete
Expand Down Expand Up @@ -92,6 +97,13 @@ func (r *ApplicationDisruptionBudgetReconciler) Reconcile(ctx context.Context, r
return ctrl.Result{}, err
}

if len(adb.Spec.SupportedNodeDisruptionTypes) == 0 && len(r.Config.DefaultNodeDisruptionTypes) > 0 {
adb.Spec.SupportedNodeDisruptionTypes = r.Config.DefaultNodeDisruptionTypes
if err := r.Update(ctx, adb); err != nil {
return ctrl.Result{}, err
}
}

UpdateADBMetrics(ref, adb)
logger.Info("Start reconcile of adb", "version", adb.ResourceVersion)

Expand Down Expand Up @@ -225,6 +237,10 @@ func (r *ApplicationDisruptionBudgetResolver) GetNamespacedName() nodedisruption
}
}

func (r *ApplicationDisruptionBudgetResolver) GetSupportedDisruptionTypes() []string {
return r.ApplicationDisruptionBudget.Spec.SupportedNodeDisruptionTypes
}

func (r *ApplicationDisruptionBudgetResolver) V2HooksReady() bool {
return r.ApplicationDisruptionBudget.Spec.HookV2BasePath.URL != ""
}
Expand Down
31 changes: 16 additions & 15 deletions internal/controller/applicationdisruptionbudget_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ var _ = Describe("ApplicationDisruptionBudget controller", func() {
},
Spec: nodedisruptionv1alpha1.NodeDisruptionSpec{
NodeSelector: metav1.LabelSelector{MatchLabels: nodeLabels1},
Type: "maintenance",
},
}
Expect(k8sClient.Create(ctx, disruption.DeepCopy())).Should(Succeed())
Expand Down Expand Up @@ -329,21 +330,21 @@ var _ = Describe("ApplicationDisruptionBudget controller", func() {
}
Expect(k8sClient.Create(ctx, adb)).Should(Succeed())

By("checking the ApplicationDisruptionBudget watches no nodes")
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(BeEmpty())

By("verifying disruptions are still allowed")
Eventually(func() int {
err := k8sClient.Get(ctx, ADBLookupKey, createdADB)
Expect(err).Should(Succeed())
return createdADB.Status.DisruptionsAllowed
}, timeout, interval).Should(Equal(1))
By("checking the ApplicationDisruptionBudget watches no nodes")
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(BeEmpty())

By("verifying disruptions are still allowed")
Eventually(func() int {
err := k8sClient.Get(ctx, ADBLookupKey, createdADB)
Expect(err).Should(Succeed())
return createdADB.Status.DisruptionsAllowed
}, timeout, interval).Should(Equal(1))
})
})

Expand Down
2 changes: 2 additions & 0 deletions internal/controller/budget.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ type Budget interface {
UpdateStatus(context.Context) error
// Get the name, namespace and kind of budget
GetNamespacedName() nodedisruptionv1alpha1.NamespacedName
// Get the list of supported node disruption types for this budget
GetSupportedDisruptionTypes() []string
}

// PruneBudgetMetrics remove metrics for a Disruption Budget that doesn't exist anymore
Expand Down
Loading
Loading