Skip to content

Commit 810a7b3

Browse files
committed
Take types from karpenter project
1 parent ff48046 commit 810a7b3

9 files changed

+1239
-334
lines changed

api/v1alpha1/duration.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package v1alpha1
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"slices"
7+
"time"
8+
9+
"github.com/samber/lo"
10+
)
11+
12+
const Never = "Never"
13+
14+
// NillableDuration is a wrapper around time.Duration which supports correct
15+
// marshaling to YAML and JSON. It uses the value "Never" to signify
16+
// that the duration is disabled and sets the inner duration as nil
17+
type NillableDuration struct {
18+
*time.Duration
19+
20+
// Raw is used to ensure we remarshal the NillableDuration in the same format it was specified.
21+
// This ensures tools like Flux and ArgoCD don't mistakenly detect drift due to our conversion webhooks.
22+
Raw []byte `hash:"ignore"`
23+
}
24+
25+
func MustParseNillableDuration(val string) NillableDuration {
26+
nd := NillableDuration{}
27+
// Use %q instead of %s to ensure that we unmarshal the value as a string and not an int
28+
lo.Must0(json.Unmarshal([]byte(fmt.Sprintf("%q", val)), &nd))
29+
return nd
30+
}
31+
32+
// UnmarshalJSON implements the json.Unmarshaller interface.
33+
func (d *NillableDuration) UnmarshalJSON(b []byte) error {
34+
var str string
35+
err := json.Unmarshal(b, &str)
36+
if err != nil {
37+
return err
38+
}
39+
if str == Never {
40+
return nil
41+
}
42+
pd, err := time.ParseDuration(str)
43+
if err != nil {
44+
return err
45+
}
46+
d.Raw = slices.Clone(b)
47+
d.Duration = &pd
48+
return nil
49+
}
50+
51+
// MarshalJSON implements the json.Marshaler interface.
52+
func (d NillableDuration) MarshalJSON() ([]byte, error) {
53+
if d.Raw != nil {
54+
return d.Raw, nil
55+
}
56+
if d.Duration != nil {
57+
return json.Marshal(d.Duration.String())
58+
}
59+
return json.Marshal(Never)
60+
}
61+
62+
// ToUnstructured implements the value.UnstructuredConverter interface.
63+
func (d NillableDuration) ToUnstructured() interface{} {
64+
if d.Raw != nil {
65+
return d.Raw
66+
}
67+
if d.Duration != nil {
68+
return d.Duration.String()
69+
}
70+
return Never
71+
}

api/v1alpha1/karpentermachinepool_types.go

Lines changed: 181 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,51 +17,211 @@ limitations under the License.
1717
package v1alpha1
1818

1919
import (
20+
v1 "k8s.io/api/core/v1"
2021
"k8s.io/apimachinery/pkg/api/resource"
2122
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2223
capi "sigs.k8s.io/cluster-api/api/v1beta1"
2324
)
2425

2526
// NodePoolSpec defines the configuration for a Karpenter NodePool
2627
type NodePoolSpec struct {
27-
// Disruption specifies the disruption behavior for the node pool
28-
// +optional
29-
Disruption *DisruptionSpec `json:"disruption,omitempty"`
28+
// Template contains the template of possibilities for the provisioning logic to launch a NodeClaim with.
29+
// NodeClaims launched from this NodePool will often be further constrained than the template specifies.
30+
// +required
31+
Template NodeClaimTemplate `json:"template"`
3032

31-
// Limits specifies the limits for the node pool
33+
// Disruption contains the parameters that relate to Karpenter's disruption logic
34+
// +kubebuilder:default:={consolidateAfter: "0s"}
3235
// +optional
33-
Limits *LimitsSpec `json:"limits,omitempty"`
36+
Disruption Disruption `json:"disruption"`
3437

35-
// Requirements specifies the requirements for the node pool
38+
// Limits define a set of bounds for provisioning capacity.
3639
// +optional
37-
Requirements []RequirementSpec `json:"requirements,omitempty"`
38-
39-
// Taints specifies the taints to apply to nodes in this pool
40+
Limits Limits `json:"limits,omitempty"`
41+
42+
// Weight is the priority given to the nodepool during scheduling. A higher
43+
// numerical weight indicates that this nodepool will be ordered
44+
// ahead of other nodepools with lower weights. A nodepool with no weight
45+
// will be treated as if it is a nodepool with a weight of 0.
46+
// +kubebuilder:validation:Minimum:=1
47+
// +kubebuilder:validation:Maximum:=100
4048
// +optional
41-
Taints []TaintSpec `json:"taints,omitempty"`
49+
Weight *int32 `json:"weight,omitempty"`
50+
}
4251

43-
// Labels specifies the labels to apply to nodes in this pool
52+
type NodeClaimTemplate struct {
53+
ObjectMeta `json:"metadata,omitempty"`
54+
// +required
55+
Spec NodeClaimTemplateSpec `json:"spec"`
56+
}
57+
58+
type ObjectMeta struct {
59+
// Map of string keys and values that can be used to organize and categorize
60+
// (scope and select) objects. May match selectors of replication controllers
61+
// and services.
62+
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels
4463
// +optional
4564
Labels map[string]string `json:"labels,omitempty"`
4665

47-
// Weight specifies the weight of this node pool
66+
// Annotations is an unstructured key value map stored with a resource that may be
67+
// set by external tools to store and retrieve arbitrary metadata. They are not
68+
// queryable and should be preserved when modifying objects.
69+
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations
4870
// +optional
49-
Weight *int32 `json:"weight,omitempty"`
71+
Annotations map[string]string `json:"annotations,omitempty"`
5072
}
5173

52-
// DisruptionSpec defines the disruption behavior for a NodePool
53-
type DisruptionSpec struct {
54-
// ConsolidateAfter specifies when to consolidate nodes
74+
// NodeClaimTemplateSpec describes the desired state of the NodeClaim in the Nodepool
75+
// NodeClaimTemplateSpec is used in the NodePool's NodeClaimTemplate, with the resource requests omitted since
76+
// users are not able to set resource requests in the NodePool.
77+
type NodeClaimTemplateSpec struct {
78+
// Taints will be applied to the NodeClaim's node.
5579
// +optional
56-
ConsolidateAfter *metav1.Duration `json:"consolidateAfter,omitempty"`
80+
Taints []v1.Taint `json:"taints,omitempty"`
81+
// StartupTaints are taints that are applied to nodes upon startup which are expected to be removed automatically
82+
// within a short period of time, typically by a DaemonSet that tolerates the taint. These are commonly used by
83+
// daemonsets to allow initialization and enforce startup ordering. StartupTaints are ignored for provisioning
84+
// purposes in that pods are not required to tolerate a StartupTaint in order to have nodes provisioned for them.
85+
// +optional
86+
StartupTaints []v1.Taint `json:"startupTaints,omitempty"`
87+
// Requirements are layered with GetLabels and applied to every node.
88+
// +kubebuilder:validation:XValidation:message="requirements with operator 'In' must have a value defined",rule="self.all(x, x.operator == 'In' ? x.values.size() != 0 : true)"
89+
// +kubebuilder:validation:XValidation:message="requirements operator 'Gt' or 'Lt' must have a single positive integer value",rule="self.all(x, (x.operator == 'Gt' || x.operator == 'Lt') ? (x.values.size() == 1 && int(x.values[0]) >= 0) : true)"
90+
// +kubebuilder:validation:XValidation:message="requirements with 'minValues' must have at least that many values specified in the 'values' field",rule="self.all(x, (x.operator == 'In' && has(x.minValues)) ? x.values.size() >= x.minValues : true)"
91+
// +kubebuilder:validation:MaxItems:=100
92+
// +required
93+
Requirements []NodeSelectorRequirementWithMinValues `json:"requirements" hash:"ignore"`
94+
// TerminationGracePeriod is the maximum duration the controller will wait before forcefully deleting the pods on a node, measured from when deletion is first initiated.
95+
//
96+
// Warning: this feature takes precedence over a Pod's terminationGracePeriodSeconds value, and bypasses any blocked PDBs or the karpenter.sh/do-not-disrupt annotation.
97+
//
98+
// This field is intended to be used by cluster administrators to enforce that nodes can be cycled within a given time period.
99+
// When set, drifted nodes will begin draining even if there are pods blocking eviction. Draining will respect PDBs and the do-not-disrupt annotation until the TGP is reached.
100+
//
101+
// Karpenter will preemptively delete pods so their terminationGracePeriodSeconds align with the node's terminationGracePeriod.
102+
// If a pod would be terminated without being granted its full terminationGracePeriodSeconds prior to the node timeout,
103+
// that pod will be deleted at T = node timeout - pod terminationGracePeriodSeconds.
104+
//
105+
// The feature can also be used to allow maximum time limits for long-running jobs which can delay node termination with preStop hooks.
106+
// If left undefined, the controller will wait indefinitely for pods to be drained.
107+
// +kubebuilder:validation:Pattern=`^([0-9]+(s|m|h))+$`
108+
// +kubebuilder:validation:Type="string"
109+
// +optional
110+
TerminationGracePeriod *metav1.Duration `json:"terminationGracePeriod,omitempty"`
111+
// ExpireAfter is the duration the controller will wait
112+
// before terminating a node, measured from when the node is created. This
113+
// is useful to implement features like eventually consistent node upgrade,
114+
// memory leak protection, and disruption testing.
115+
// +kubebuilder:default:="720h"
116+
// +kubebuilder:validation:Pattern=`^(([0-9]+(s|m|h))+|Never)$`
117+
// +kubebuilder:validation:Type="string"
118+
// +kubebuilder:validation:Schemaless
119+
// +optional
120+
ExpireAfter NillableDuration `json:"expireAfter,omitempty"`
121+
}
57122

58-
// ConsolidationPolicy specifies the consolidation policy
123+
// A node selector requirement with min values is a selector that contains values, a key, an operator that relates the key and values
124+
// and minValues that represent the requirement to have at least that many values.
125+
type NodeSelectorRequirementWithMinValues struct {
126+
v1.NodeSelectorRequirement `json:",inline"`
127+
// This field is ALPHA and can be dropped or replaced at any time
128+
// MinValues is the minimum number of unique values required to define the flexibility of the specific requirement.
129+
// +kubebuilder:validation:Minimum:=1
130+
// +kubebuilder:validation:Maximum:=50
59131
// +optional
60-
ConsolidationPolicy *string `json:"consolidationPolicy,omitempty"`
132+
MinValues *int `json:"minValues,omitempty"`
133+
}
134+
135+
type NodeClassReference struct {
136+
// Kind of the referent; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds"
137+
// +kubebuilder:validation:XValidation:rule="self != ''",message="kind may not be empty"
138+
// +required
139+
Kind string `json:"kind"`
140+
// Name of the referent; More info: http://kubernetes.io/docs/user-guide/identifiers#names
141+
// +kubebuilder:validation:XValidation:rule="self != ''",message="name may not be empty"
142+
// +required
143+
Name string `json:"name"`
144+
// API version of the referent
145+
// +kubebuilder:validation:XValidation:rule="self != ''",message="group may not be empty"
146+
// +kubebuilder:validation:Pattern=`^[^/]*$`
147+
// +required
148+
Group string `json:"group"`
149+
}
61150

62-
// ConsolidateUnder specifies when to consolidate under
151+
type Limits v1.ResourceList
152+
153+
type ConsolidationPolicy string
154+
155+
const (
156+
ConsolidationPolicyWhenEmpty ConsolidationPolicy = "WhenEmpty"
157+
ConsolidationPolicyWhenEmptyOrUnderutilized ConsolidationPolicy = "WhenEmptyOrUnderutilized"
158+
)
159+
160+
// DisruptionReason defines valid reasons for disruption budgets.
161+
// +kubebuilder:validation:Enum={Underutilized,Empty,Drifted}
162+
type DisruptionReason string
163+
164+
// Budget defines when Karpenter will restrict the
165+
// number of Node Claims that can be terminating simultaneously.
166+
type Budget struct {
167+
// Reasons is a list of disruption methods that this budget applies to. If Reasons is not set, this budget applies to all methods.
168+
// Otherwise, this will apply to each reason defined.
169+
// allowed reasons are Underutilized, Empty, and Drifted.
170+
// +optional
171+
Reasons []DisruptionReason `json:"reasons,omitempty"`
172+
// Nodes dictates the maximum number of NodeClaims owned by this NodePool
173+
// that can be terminating at once. This is calculated by counting nodes that
174+
// have a deletion timestamp set, or are actively being deleted by Karpenter.
175+
// This field is required when specifying a budget.
176+
// This cannot be of type intstr.IntOrString since kubebuilder doesn't support pattern
177+
// checking for int nodes for IntOrString nodes.
178+
// Ref: https://github.com/kubernetes-sigs/controller-tools/blob/55efe4be40394a288216dab63156b0a64fb82929/pkg/crd/markers/validation.go#L379-L388
179+
// +kubebuilder:validation:Pattern:="^((100|[0-9]{1,2})%|[0-9]+)$"
180+
// +kubebuilder:default:="10%"
181+
Nodes string `json:"nodes" hash:"ignore"`
182+
// Schedule specifies when a budget begins being active, following
183+
// the upstream cronjob syntax. If omitted, the budget is always active.
184+
// Timezones are not supported.
185+
// This field is required if Duration is set.
186+
// +kubebuilder:validation:Pattern:=`^(@(annually|yearly|monthly|weekly|daily|midnight|hourly))|((.+)\s(.+)\s(.+)\s(.+)\s(.+))$`
187+
// +optional
188+
Schedule *string `json:"schedule,omitempty" hash:"ignore"`
189+
// Duration determines how long a Budget is active since each Schedule hit.
190+
// Only minutes and hours are accepted, as cron does not work in seconds.
191+
// If omitted, the budget is always active.
192+
// This is required if Schedule is set.
193+
// This regex has an optional 0s at the end since the duration.String() always adds
194+
// a 0s at the end.
195+
// +kubebuilder:validation:Pattern=`^((([0-9]+(h|m))|([0-9]+h[0-9]+m))(0s)?)$`
196+
// +kubebuilder:validation:Type="string"
63197
// +optional
64-
ConsolidateUnder *ConsolidateUnderSpec `json:"consolidateUnder,omitempty"`
198+
Duration *metav1.Duration `json:"duration,omitempty" hash:"ignore"`
199+
}
200+
201+
type Disruption struct {
202+
// ConsolidateAfter is the duration the controller will wait
203+
// before attempting to terminate nodes that are underutilized.
204+
// Refer to ConsolidationPolicy for how underutilization is considered.
205+
// +kubebuilder:validation:Pattern=`^(([0-9]+(s|m|h))+|Never)$`
206+
// +kubebuilder:validation:Type="string"
207+
// +kubebuilder:validation:Schemaless
208+
// +required
209+
ConsolidateAfter NillableDuration `json:"consolidateAfter"`
210+
// ConsolidationPolicy describes which nodes Karpenter can disrupt through its consolidation
211+
// algorithm. This policy defaults to "WhenEmptyOrUnderutilized" if not specified
212+
// +kubebuilder:default:="WhenEmptyOrUnderutilized"
213+
// +kubebuilder:validation:Enum:={WhenEmpty,WhenEmptyOrUnderutilized}
214+
// +optional
215+
ConsolidationPolicy ConsolidationPolicy `json:"consolidationPolicy,omitempty"`
216+
// Budgets is a list of Budgets.
217+
// If there are multiple active budgets, Karpenter uses
218+
// the most restrictive value. If left undefined,
219+
// this will default to one budget with a value to 10%.
220+
// +kubebuilder:validation:XValidation:message="'schedule' must be set with 'duration'",rule="self.all(x, has(x.schedule) == has(x.duration))"
221+
// +kubebuilder:default:={{nodes: "10%"}}
222+
// +kubebuilder:validation:MaxItems=50
223+
// +optional
224+
Budgets []Budget `json:"budgets,omitempty" hash:"ignore"`
65225
}
66226

67227
// ConsolidateUnderSpec defines when to consolidate under
@@ -128,10 +288,6 @@ type EC2NodeClassSpec struct {
128288
// Subnets specifies the subnets to use
129289
// +optional
130290
Subnets map[string]string `json:"subnets,omitempty"`
131-
132-
// Tags specifies the tags to apply to EC2 instances
133-
// +optional
134-
Tags map[string]string `json:"tags,omitempty"`
135291
}
136292

137293
// KarpenterMachinePoolSpec defines the desired state of KarpenterMachinePool.

0 commit comments

Comments
 (0)