Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

interface: add APIs for eviction/disruption budget #911

Merged
merged 14 commits into from
Oct 22, 2024
166 changes: 166 additions & 0 deletions apis/placement/v1alpha1/disruptionbudget_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/*
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
Arvindthiru marked this conversation as resolved.
Show resolved Hide resolved
// +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.
//
michaelawyu marked this conversation as resolved.
Show resolved Hide resolved
// To apply a ClusterResourcePlacementDisruptionBudget to a ClusterResourcePlacement, use the
Arvindthiru marked this conversation as resolved.
Show resolved Hide resolved
// same name for the ClusterResourcePlacementDisruptionBudget object as the ClusterResourcePlacement
// object. This guarantees a 1:1 link between the two objects.
type ClusterResourcePlacementDisruptionBudget struct {
michaelawyu marked this conversation as resolved.
Show resolved Hide resolved
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

// Spec is the desired state of the ClusterResourcePlacementDisruptionBudget.
// +required
Spec ClusterResourcePlacementDisruptionBudgetSpec `json:"spec"`
Arvindthiru marked this conversation as resolved.
Show resolved Hide resolved

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

// ClusterResourcePlacementDisruptionBudgetSpec is the desired state of the
// ClusterResourcePlacementDisruptionBudget.
Arvindthiru marked this conversation as resolved.
Show resolved Hide resolved
type ClusterResourcePlacementDisruptionBudgetSpec struct {
// MaxUnavailable is the maximum number of placements 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
// from the linked ClusterResourcePlacement 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 ClusterResourcePlacement 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 ClusterResourcePlacement 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 ClusterResourcePlacement 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.
//
// Defaults to 25%.
// +kubebuilder:default="25%"
// +kubebuilder:validation:XIntOrString
// +kubebuilder:validation:Pattern="^((100|[0-9]{1,2})%|[0-9]+)$"
// +optional
MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty"`

// MinAvailable is the minimum number of placements 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 from the linked ClusterResourcePlacement 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 ClusterResourcePlacement 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 ClusterResourcePlacement 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 ClusterResourcePlacement 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:Pattern="^((100|[0-9]{1,2})%|[0-9]+)$"
// +optional
MinAvailable *intstr.IntOrString `json:"minAvailable,omitempty"`
}

// ClusterResourcePlacementDisruptionBudgetStatus is the observed state of the
// ClusterResourcePlacementDisruptionBudget.
type ClusterResourcePlacementDisruptionBudgetStatus struct {
// Number of placement disruptions that are currently allowed.
// +optional
DisruptionsAllowed int32 `json:"disruptionsAllowed"`

// Current number of available placements.
// +optional
CurrentAvailable int32 `json:"currentAvailable"`

// Minimum desired number of available placements.
// +optional
DesiredAvailable int32 `json:"desiredAvailable"`

// Total number of placements counted by this disruption budget.
// +optional
TotalPlacements int32 `json:"totalPlacements"`

// TODO: Add a map to track ongoing evictions, to protect against concurrent evictions.
Arvindthiru marked this conversation as resolved.
Show resolved Hide resolved

// Conditions is the list of currently observed conditions for the
// ClusterResourcePlacementDisruptionBudget object.
//
// Available condition types include:
// * Allowed: whether the disruption budget allows disruption for ClusterResourcePlacement.
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`
Arvindthiru marked this conversation as resolved.
Show resolved Hide resolved
}

// ClusterResourcePlacementDisruptionBudgetConditionType identifies a specific condition of the
// ClusterResourcePlacementDisruptionBudget.
type ClusterResourcePlacementDisruptionBudgetConditionType string

const (
// ClusterResourcePlacementDisruptionBudgetConditionTypeDisruptionAllowed indicates whether the disruption budget
// allows placements to be disrupted by voluntary disruptions.
//
// The following values are possible:
// * True: the disruption budget allows disruption for ClusterResourcePlacement.
// * False: the disruption budget does not allow any voluntary disruption for ClusterResourcePlacement.
ClusterResourcePlacementDisruptionBudgetConditionTypeDisruptionAllowed ClusterResourcePlacementDisruptionBudgetConditionType = "DisruptionAllowed"
)

// ClusterResourcePlacementDisruptionBudgetList contains a list of PlacementDisruptionBudget 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{})
}
123 changes: 123 additions & 0 deletions apis/placement/v1alpha1/eviction_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
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.,
Arvindthiru marked this conversation as resolved.
Show resolved Hide resolved
// 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 {
michaelawyu marked this conversation as resolved.
Show resolved Hide resolved
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 ClusterResourcePlacementEvictionSpec `json:"spec"`

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

// ClusterResourcePlacementEvictionSpec is the desired state of the ClusterResourcePlacementEviction.
type ClusterResourcePlacementEvictionSpec struct {
// PlacementReference is the name of the ClusterResourcePlacement object which
ryanzhang-oss marked this conversation as resolved.
Show resolved Hide resolved
Arvindthiru marked this conversation as resolved.
Show resolved Hide resolved
// the Eviction object targets.
// +kubebuilder:validation:Required
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="The PlacementReference field is immutable"
// +kubebuilder:validation:MaxLength=255
PlacementReference string `json:"placementReference"`
Arvindthiru marked this conversation as resolved.
Show resolved Hide resolved

// 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"`
}

// ClusterResourcePlacementEvictionStatus is the observed state of the ClusterResourcePlacementEviction.
type ClusterResourcePlacementEvictionStatus struct {
// Conditions is the list of currently observed conditions for the
// ClusterResourcePlacementEviction 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"`
}

// ClusterResourcePlacementEvictionConditionType identifies a specific condition of the
// ClusterResourcePlacementEviction.
type ClusterResourcePlacementEvictionConditionType string

const (
// ClusterResourcePlacementEvictionConditionTypeValid 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.
ClusterResourcePlacementEvictionConditionTypeValid ClusterResourcePlacementEvictionConditionType = "Valid"

// ClusterResourcePlacementEvictionConditionTypeExecuted 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.
ClusterResourcePlacementEvictionConditionTypeExecuted ClusterResourcePlacementEvictionConditionType = "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
Loading