Skip to content

Commit

Permalink
interface: add APIs for eviction/disruption budget (#911)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelawyu authored Oct 22, 2024
1 parent 899c925 commit 1d28a09
Show file tree
Hide file tree
Showing 8 changed files with 1,064 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ GOLANGCI_LINT_BIN := golangci-lint
GOLANGCI_LINT := $(abspath $(TOOLS_BIN_DIR)/$(GOLANGCI_LINT_BIN)-$(GOLANGCI_LINT_VER))

# ENVTEST_K8S_VERSION refers to the version of k8s binary assets to be downloaded by envtest binary.
ENVTEST_K8S_VERSION = 1.28.0
ENVTEST_K8S_VERSION = 1.30.0
# ENVTEST_VER is the version of the ENVTEST binary
ENVTEST_VER = v0.0.0-20240317073005-bd9ea79e8d18
ENVTEST_BIN := setup-envtest
Expand Down
116 changes: 116 additions & 0 deletions apis/placement/v1alpha1/disruptionbudget_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
Copyright (c) Microsoft Corporation.
Licensed under the MIT license.
*/

package v1alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
)

// +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Cluster,categories={fleet,fleet-placement},shortName=crpdb
// +kubebuilder:subresource:status
// +kubebuilder:storageversion

// ClusterResourcePlacementDisruptionBudget is the policy applied to a ClusterResourcePlacement
// object that specifies its disruption budget, i.e., how many placements (clusters) can be
// down at the same time due to voluntary disruptions (e.g., evictions). Involuntary
// disruptions are not subject to this budget, but will still count against it.
//
// To apply a ClusterResourcePlacementDisruptionBudget to a ClusterResourcePlacement, use the
// same name for the ClusterResourcePlacementDisruptionBudget object as the ClusterResourcePlacement
// object. This guarantees a 1:1 link between the two objects.
type ClusterResourcePlacementDisruptionBudget struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

// Spec is the desired state of the ClusterResourcePlacementDisruptionBudget.
// +kubebuilder:validation:XValidation:rule="!(has(self.maxUnavailable) && has(self.minAvailable))",message="Both MaxUnavailable and MinAvailable cannot be specified"
// +required
Spec PlacementDisruptionBudgetSpec `json:"spec"`
}

// PlacementDisruptionBudgetSpec is the desired state of the PlacementDisruptionBudget.
type PlacementDisruptionBudgetSpec struct {
// MaxUnavailable is the maximum number of placements (clusters) that can be down at the
// same time due to voluntary disruptions. For example, a setting of 1 would imply that
// a voluntary disruption (e.g., an eviction) can only happen if all placements (clusters)
// from the linked Placement object are applied and available.
//
// This can be either an absolute value (e.g., 1) or a percentage (e.g., 10%).
//
// If a percentage is specified, Fleet will calculate the corresponding absolute values
// as follows:
// * if the linked Placement object is of the PickFixed placement type,
// the percentage is against the number of clusters specified in the placement (i.e., the
// length of ClusterNames field in the placement policy);
// * if the linked Placement object is of the PickAll placement type,
// the percentage is against the total number of clusters being selected by the scheduler
// at the time of the evaluation of the disruption budget;
// * if the linked Placement object is of the PickN placement type,
// the percentage is against the number of clusters specified in the placement (i.e., the
// value of the NumberOfClusters fields in the placement policy).
// The end result will be rounded up to the nearest integer if applicable.
//
// One may use a value of 0 for this field; in this case, no voluntary disruption would be
// allowed.
//
// This field is mutually exclusive with the MinAvailable field in the spec; exactly one
// of them can be set at a time.
//
// +kubebuilder:validation:XIntOrString
// +kubebuilder:validation:XValidation:rule="type(self) == string ? self.matches('^(100|[0-9]{1,2}%)$') : self >= 0",message="If supplied value is String should match regex '^(100|[0-9]{1,2}%)$' or If supplied value is Integer must be greater than or equal to 0"
// +optional
MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty"`

// MinAvailable is the minimum number of placements (clusters) that must be available at any
// time despite voluntary disruptions. For example, a setting of 10 would imply that
// a voluntary disruption (e.g., an eviction) can only happen if there are at least 11
// placements (clusters) from the linked Placement object are applied and available.
//
// This can be either an absolute value (e.g., 1) or a percentage (e.g., 10%).
//
// If a percentage is specified, Fleet will calculate the corresponding absolute values
// as follows:
// * if the linked Placement object is of the PickFixed placement type,
// the percentage is against the number of clusters specified in the placement (i.e., the
// length of ClusterNames field in the placement policy);
// * if the linked Placement object is of the PickAll placement type,
// the percentage is against the total number of clusters being selected by the scheduler
// at the time of the evaluation of the disruption budget;
// * if the linked Placement object is of the PickN placement type,
// the percentage is against the number of clusters specified in the placement (i.e., the
// value of the NumberOfClusters fields in the placement policy).
// The end result will be rounded up to the nearest integer if applicable.
//
// One may use a value of 0 for this field; in this case, voluntary disruption would be
// allowed at any time.
//
// This field is mutually exclusive with the MaxUnavailable field in the spec; exactly one
// of them can be set at a time.
//
// +kubebuilder:validation:XIntOrString
// +kubebuilder:validation:XValidation:rule="type(self) == string ? self.matches('^(100|[0-9]{1,2}%)$') : self >= 0",message="If supplied value is String should match regex '^(100|[0-9]{1,2}%)$' or If supplied value is Integer must be greater than or equal to 0"
// +optional
MinAvailable *intstr.IntOrString `json:"minAvailable,omitempty"`
}

// ClusterResourcePlacementDisruptionBudgetList contains a list of ClusterResourcePlacementDisruptionBudget objects.
// +kubebuilder:resource:scope=Cluster
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type ClusterResourcePlacementDisruptionBudgetList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`

// Items is the list of PlacementDisruptionBudget objects.
Items []ClusterResourcePlacementDisruptionBudget `json:"items"`
}

func init() {
SchemeBuilder.Register(
&ClusterResourcePlacementDisruptionBudget{},
&ClusterResourcePlacementDisruptionBudgetList{})
}
125 changes: 125 additions & 0 deletions apis/placement/v1alpha1/eviction_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
Copyright (c) Microsoft Corporation.
Licensed under the MIT license.
*/

package v1alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Cluster,categories={fleet,fleet-placement},shortName=crpe
// +kubebuilder:subresource:status
// +kubebuilder:storageversion

// ClusterResourcePlacementEviction is an eviction attempt on a specific placement from
// a ClusterResourcePlacement object; one may use this API to force the removal of specific
// resources from a cluster.
//
// An eviction is a voluntary disruption; its execution is subject to the disruption budget
// linked with the target ClusterResourcePlacement object (if present).
//
// Beware that an eviction alone does not guarantee that a placement will not re-appear; i.e.,
// after an eviction, the Fleet scheduler might still pick the previous target cluster for
// placement. To prevent this, considering adding proper taints to the target cluster before running
// an eviction that will exclude it from future placements; this is especially true in scenarios
// where one would like to perform a cluster replacement.
//
// For safety reasons, Fleet will only execute an eviction once; the spec in this object is immutable,
// and once executed, the object will be ignored after. To trigger another eviction attempt on the
// same placement from the same ClusterResourcePlacement object, one must re-create (delete and
// create) the same Eviction object. Note also that an Eviction object will be
// ignored once it is deemed invalid (e.g., such an object might be targeting a CRP object or
// a placement that does not exist yet), even if it does become valid later
// (e.g., the CRP object or the placement appears later). To fix the situation, re-create the
// Eviction object.
//
// Executed evictions might be kept around for a while for auditing purposes; the Fleet controllers might
// have a TTL set up for such objects and will garbage collect them automatically. For further
// information, see the Fleet documentation.
type ClusterResourcePlacementEviction struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

// Spec is the desired state of the ClusterResourcePlacementEviction.
//
// Note that all fields in the spec are immutable.
// +required
Spec PlacementEvictionSpec `json:"spec"`

// Status is the observed state of the ClusterResourcePlacementEviction.
// +optional
Status PlacementEvictionStatus `json:"status,omitempty"`
}

// PlacementEvictionSpec is the desired state of the parent PlacementEviction.
type PlacementEvictionSpec struct {
// PlacementName is the name of the Placement object which
// the Eviction object targets.
// +kubebuilder:validation:Required
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="The PlacementName field is immutable"
// +kubebuilder:validation:MaxLength=255
PlacementName string `json:"placementName"`

// ClusterName is the name of the cluster that the Eviction object targets.
// +kubebuilder:validation:Required
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="The ClusterName field is immutable"
// +kubebuilder:validation:MaxLength=255
ClusterName string `json:"clusterName"`
}

// PlacementEvictionStatus is the observed state of the parent PlacementEviction.
type PlacementEvictionStatus struct {
// Conditions is the list of currently observed conditions for the
// PlacementEviction object.
//
// Available condition types include:
// * Valid: whether the Eviction object is valid, i.e., it targets at a valid placement.
// * Executed: whether the Eviction object has been executed.
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`
}

// PlacementEvictionConditionType identifies a specific condition of the
// PlacementEviction.
type PlacementEvictionConditionType string

const (
// PlacementEvictionConditionTypeValid indicates whether the Eviction object is valid.
//
// The following values are possible:
// * True: the Eviction object is valid.
// * False: the Eviction object is invalid; it might be targeting a CRP object or a placement
// that does not exist yet.
// Note that this is a terminal state; once an Eviction object is deemed invalid, it will
// not be evaluated again, even if the target appears later.
PlacementEvictionConditionTypeValid PlacementEvictionConditionType = "Valid"

// PlacementEvictionConditionTypeExecuted indicates whether the Eviction object has been executed.
//
// The following values are possible:
// * True: the Eviction object has been executed.
// Note that this is a terminal state; once an Eviction object is executed, it will not be
// executed again.
// * False: the Eviction object has not been executed yet.
PlacementEvictionConditionTypeExecuted PlacementEvictionConditionType = "Executed"
)

// ClusterResourcePlacementEvictionList contains a list of ClusterResourcePlacementEviction objects.
// +kubebuilder:resource:scope=Cluster
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type ClusterResourcePlacementEvictionList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`

// Items is the list of ClusterResourcePlacementEviction objects.
Items []ClusterResourcePlacementEviction `json:"items"`
}

func init() {
SchemeBuilder.Register(
&ClusterResourcePlacementEviction{},
&ClusterResourcePlacementEvictionList{})
}
Loading

0 comments on commit 1d28a09

Please sign in to comment.