Skip to content

Commit

Permalink
fix: update CEL validation for expireAfter and consolidateAfter field…
Browse files Browse the repository at this point in the history
…s to block invalid values like 1hr
  • Loading branch information
saurav-agarwalla committed Mar 6, 2025
1 parent af2b998 commit e0e2ece
Show file tree
Hide file tree
Showing 8 changed files with 64 additions and 30 deletions.
2 changes: 1 addition & 1 deletion kwok/charts/crds/karpenter.sh_nodeclaims.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ spec:
before terminating a node, measured from when the node is created. This
is useful to implement features like eventually consistent node upgrade,
memory leak protection, and disruption testing.
pattern: ^(([0-9]+(s|m|h))+)|(Never)$
pattern: ^(([0-9]+(s|m|h))+|Never)$
type: string
nodeClassRef:
description: NodeClassRef is a reference to an object that defines provider specific configuration
Expand Down
4 changes: 2 additions & 2 deletions kwok/charts/crds/karpenter.sh_nodepools.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ spec:
ConsolidateAfter is the duration the controller will wait
before attempting to terminate nodes that are underutilized.
Refer to ConsolidationPolicy for how underutilization is considered.
pattern: ^(([0-9]+(s|m|h))+)|(Never)$
pattern: ^(([0-9]+(s|m|h))+|Never)$
type: string
consolidationPolicy:
default: WhenEmptyOrUnderutilized
Expand Down Expand Up @@ -220,7 +220,7 @@ spec:
before terminating a node, measured from when the node is created. This
is useful to implement features like eventually consistent node upgrade,
memory leak protection, and disruption testing.
pattern: ^(([0-9]+(s|m|h))+)|(Never)$
pattern: ^(([0-9]+(s|m|h))+|Never)$
type: string
nodeClassRef:
description: NodeClassRef is a reference to an object that defines provider specific configuration
Expand Down
2 changes: 1 addition & 1 deletion pkg/apis/crds/karpenter.sh_nodeclaims.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ spec:
before terminating a node, measured from when the node is created. This
is useful to implement features like eventually consistent node upgrade,
memory leak protection, and disruption testing.
pattern: ^(([0-9]+(s|m|h))+)|(Never)$
pattern: ^(([0-9]+(s|m|h))+|Never)$
type: string
nodeClassRef:
description: NodeClassRef is a reference to an object that defines provider specific configuration
Expand Down
4 changes: 2 additions & 2 deletions pkg/apis/crds/karpenter.sh_nodepools.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ spec:
ConsolidateAfter is the duration the controller will wait
before attempting to terminate nodes that are underutilized.
Refer to ConsolidationPolicy for how underutilization is considered.
pattern: ^(([0-9]+(s|m|h))+)|(Never)$
pattern: ^(([0-9]+(s|m|h))+|Never)$
type: string
consolidationPolicy:
default: WhenEmptyOrUnderutilized
Expand Down Expand Up @@ -220,7 +220,7 @@ spec:
before terminating a node, measured from when the node is created. This
is useful to implement features like eventually consistent node upgrade,
memory leak protection, and disruption testing.
pattern: ^(([0-9]+(s|m|h))+)|(Never)$
pattern: ^(([0-9]+(s|m|h))+|Never)$
type: string
nodeClassRef:
description: NodeClassRef is a reference to an object that defines provider specific configuration
Expand Down
2 changes: 1 addition & 1 deletion pkg/apis/v1/nodeclaim.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ type NodeClaimSpec struct {
// is useful to implement features like eventually consistent node upgrade,
// memory leak protection, and disruption testing.
// +kubebuilder:default:="720h"
// +kubebuilder:validation:Pattern=`^(([0-9]+(s|m|h))+)|(Never)$`
// +kubebuilder:validation:Pattern=`^(([0-9]+(s|m|h))+|Never)$`
// +kubebuilder:validation:Type="string"
// +kubebuilder:validation:Schemaless
// +optional
Expand Down
35 changes: 28 additions & 7 deletions pkg/apis/v1/nodeclaim_validation_cel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,13 +244,34 @@ var _ = Describe("Validation", func() {
})
})
Context("TerminationGracePeriod", func() {
It("should succeed on a positive terminationGracePeriod duration", func() {
nodeClaim.Spec.TerminationGracePeriod = &metav1.Duration{Duration: time.Second * 300}
DescribeTable("should succeed on a valid terminationGracePeriod", func(value string) {
nodeClaim.Spec.TerminationGracePeriod = &metav1.Duration{Duration: lo.Must(time.ParseDuration(value))}
Expect(env.Client.Create(ctx, nodeClaim)).To(Succeed())
})
It("should fail on a negative terminationGracePeriod duration", func() {
nodeClaim.Spec.TerminationGracePeriod = &metav1.Duration{Duration: time.Second * -30}
Expect(env.Client.Create(ctx, nodeClaim)).ToNot(Succeed())
})
},
Entry("single unit", "30s"),
Entry("multiple units", "1h30m5s"),
)
DescribeTable("should fail on an invalid terminationGracePeriod", func(value string) {
nodeClaim.Spec.TerminationGracePeriod = &metav1.Duration{Duration: lo.Must(time.ParseDuration(value))}
Expect(env.Client.Create(ctx, nodeClaim)).To(Not(Succeed()))
},
Entry("negative", "-1s"),
)
})
Context("ExpireAfter", func() {
DescribeTable("should succeed on a valid expireAfter", func(value string) {
nodeClaim.Spec.ExpireAfter = MustParseNillableDuration(value)
Expect(env.Client.Create(ctx, nodeClaim)).To(Succeed())
},
Entry("single unit", "30s"),
Entry("multiple units", "1h30m5s"),
Entry("never", "Never"),
)
DescribeTable("should fail on an invalid expireAfter", func(value string) {
nodeClaim.Spec.ExpireAfter = MustParseNillableDuration(value)
Expect(env.Client.Create(ctx, nodeClaim)).To(Not(Succeed()))
},
Entry("negative", "-1s"),
)
})
})
4 changes: 2 additions & 2 deletions pkg/apis/v1/nodepool.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ type Disruption struct {
// ConsolidateAfter is the duration the controller will wait
// before attempting to terminate nodes that are underutilized.
// Refer to ConsolidationPolicy for how underutilization is considered.
// +kubebuilder:validation:Pattern=`^(([0-9]+(s|m|h))+)|(Never)$`
// +kubebuilder:validation:Pattern=`^(([0-9]+(s|m|h))+|Never)$`
// +kubebuilder:validation:Type="string"
// +kubebuilder:validation:Schemaless
// +required
Expand Down Expand Up @@ -206,7 +206,7 @@ type NodeClaimTemplateSpec struct {
// is useful to implement features like eventually consistent node upgrade,
// memory leak protection, and disruption testing.
// +kubebuilder:default:="720h"
// +kubebuilder:validation:Pattern=`^(([0-9]+(s|m|h))+)|(Never)$`
// +kubebuilder:validation:Pattern=`^(([0-9]+(s|m|h))+|Never)$`
// +kubebuilder:validation:Type="string"
// +kubebuilder:validation:Schemaless
// +optional
Expand Down
41 changes: 27 additions & 14 deletions pkg/apis/v1/nodepool_validation_cel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,31 +64,44 @@ var _ = Describe("CEL/Validation", func() {
}
})
Context("Disruption", func() {
It("should fail on negative expireAfter", func() {
nodePool.Spec.Template.Spec.ExpireAfter = MustParseNillableDuration("-1s")
Expect(env.Client.Create(ctx, nodePool)).ToNot(Succeed())
})
It("should succeed on a disabled expireAfter", func() {
nodePool.Spec.Template.Spec.ExpireAfter = MustParseNillableDuration("Never")
Expect(env.Client.Create(ctx, nodePool)).To(Succeed())
})
It("should succeed on a valid expireAfter", func() {
nodePool.Spec.Template.Spec.ExpireAfter = MustParseNillableDuration("30s")
DescribeTable("should succeed on a valid expireAfter", func(value string) {
nodePool.Spec.Template.Spec.ExpireAfter = MustParseNillableDuration(value)
Expect(env.Client.Create(ctx, nodePool)).To(Succeed())
})
It("should fail on negative consolidateAfter", func() {
nodePool.Spec.Disruption.ConsolidateAfter = MustParseNillableDuration("-1s")
Expect(env.Client.Create(ctx, nodePool)).ToNot(Succeed())
})
},
Entry("single unit", "30s"),
Entry("multiple units", "1h30m5s"),
Entry("never", "Never"),
)
DescribeTable("should fail on an invalid expireAfter", func(value string) {
nodePool.Spec.Template.Spec.ExpireAfter = MustParseNillableDuration(value)
Expect(env.Client.Create(ctx, nodePool)).To(Not(Succeed()))
},
Entry("negative", "-1s"),
)
It("should succeed on a disabled consolidateAfter", func() {
nodePool.Spec.Disruption.ConsolidateAfter = MustParseNillableDuration("Never")
Expect(env.Client.Create(ctx, nodePool)).To(Succeed())
})
It("should succeed on a valid consolidateAfter", func() {
nodePool.Spec.Disruption.ConsolidateAfter = MustParseNillableDuration("30s")
DescribeTable("should succeed on a valid consolidateAfter", func(value string) {
nodePool.Spec.Disruption.ConsolidateAfter = MustParseNillableDuration(value)
nodePool.Spec.Disruption.ConsolidationPolicy = ConsolidationPolicyWhenEmpty
Expect(env.Client.Create(ctx, nodePool)).To(Succeed())
})
},
Entry("single unit", "30s"),
Entry("multiple units", "1h30m5s"),
Entry("never", "Never"),
)
DescribeTable("should fail on an invalid expireAfter", func(value string) {
nodePool.Spec.Disruption.ConsolidateAfter = MustParseNillableDuration(value)
nodePool.Spec.Disruption.ConsolidationPolicy = ConsolidationPolicyWhenEmpty
Expect(env.Client.Create(ctx, nodePool)).To(Not(Succeed()))
},
Entry("negative", "-1s"),
)
It("should succeed when setting consolidateAfter with consolidationPolicy=WhenEmpty", func() {
nodePool.Spec.Disruption.ConsolidateAfter = MustParseNillableDuration("30s")
nodePool.Spec.Disruption.ConsolidationPolicy = ConsolidationPolicyWhenEmpty
Expand Down

0 comments on commit e0e2ece

Please sign in to comment.