Skip to content

Commit 8a5bd81

Browse files
Create karpenter custom resources in workload clusters (#268)
* wip * wip2 * wip3 * Use envtest k8s client * Only use security groups and subnets if defined * Use map for subnet and security group selectors * Take types from karpenter project * Add managed-by label to resources created by controller * Check if NodePool field is nil * Avoid setting defaults * Run goimports in api files (including generated files) * Refactor tests * Fix user data s3 key * Ignore 'no matches for kind' errors when deleting karpenter CRs * Mimic upstream ec2nodeclass * Add CAPA additional tags to karpenter resources * Refactor * Use conditions properly * Fix condition message formatting * Go mod tidy * Use different copies for the different patch operations * Use update instead of patch for conditions * Rename policy skew condition * Fix condition message formatting again * Update conditions immediately * Use patchHelper * Improve condition handling * Reconcile AWS metadata endpoint config * Bring back status.ready field * Reconcile taints and startuptaints * Extract version skew to its own package * Requeue instead of returning error when version skew policy does not allow workers upgrade * Add comment explaining goimports on generated files * linting fixes * revert makegen * revert makegen * file origins * not needed * patch KMP * Limit rbac permissions * remove hold from build --------- Co-authored-by: Pau Rosello <[email protected]>
1 parent a778924 commit 8a5bd81

34 files changed

+7188
-434
lines changed

.circleci/config.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,6 @@ workflows:
7474
only: /^v.*/
7575

7676
- architect/go-build:
77-
requires:
78-
- hold
7977
context: architect
8078
name: go-build
8179
binary: aws-resolver-rules-operator

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Changed
11+
12+
- Create karpenter custom resources in workload clusters.
13+
1014
## [0.20.0] - 2025-06-23
1115

1216
### Changed

Makefile.custom.mk

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,8 @@ crds: controller-gen ## Generate CustomResourceDefinition.
2626
generate: controller-gen crds ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
2727
go generate ./...
2828
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
29-
30-
.PHONY: fmt
31-
fmt: ## Run go fmt against code.
32-
go fmt ./...
33-
34-
.PHONY: vet
35-
vet: ## Run go vet against code.
36-
go vet ./...
29+
# We need to run goimports after controller-gen to avoid CI complains about goimports in the generated files
30+
@go run golang.org/x/tools/cmd/goimports -w ./api/v1alpha1
3731

3832
.PHONY: create-acceptance-cluster
3933
create-acceptance-cluster: kind
@@ -127,7 +121,7 @@ coverage-html: test-unit
127121
CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
128122
.PHONY: controller-gen
129123
controller-gen: ## Download controller-gen locally if necessary.
130-
$(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.16.5)
124+
$(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.18.0)
131125

132126
ENVTEST = $(shell pwd)/bin/setup-envtest
133127
.PHONY: envtest

Makefile.gen.go.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ MODULE := $(shell go list -m)
1212
OS := $(shell go env GOOS)
1313
SOURCES := $(shell find . -name '*.go')
1414
VERSION := $(shell architect project version)
15+
1516
ifeq ($(OS), linux)
1617
EXTLDFLAGS := -static
1718
endif

api/v1alpha1/duration.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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+
// Decode the JSON bytes to get the actual string value
66+
var str string
67+
if err := json.Unmarshal(d.Raw, &str); err == nil {
68+
return str
69+
}
70+
// Fallback to string conversion if unmarshal fails
71+
if d.Duration != nil {
72+
return (*d.Duration).String()
73+
}
74+
return Never
75+
}
76+
if d.Duration != nil {
77+
return (*d.Duration).String()
78+
}
79+
return Never
80+
}

api/v1alpha1/ec2nodeclass.go

Lines changed: 431 additions & 0 deletions
Large diffs are not rendered by default.

api/v1alpha1/karpentermachinepool_types.go

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,19 @@ package v1alpha1
1818

1919
import (
2020
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21+
capi "sigs.k8s.io/cluster-api/api/v1beta1"
2122
)
2223

2324
// KarpenterMachinePoolSpec defines the desired state of KarpenterMachinePool.
2425
type KarpenterMachinePoolSpec struct {
25-
// The name or the Amazon Resource Name (ARN) of the instance profile associated
26-
// with the IAM role for the instance. The instance profile contains the IAM
27-
// role.
28-
IamInstanceProfile string `json:"iamInstanceProfile,omitempty"`
26+
// NodePool specifies the configuration for the Karpenter NodePool
27+
// +optional
28+
NodePool *NodePoolSpec `json:"nodePool,omitempty"`
29+
30+
// EC2NodeClass specifies the configuration for the Karpenter EC2NodeClass
31+
// +optional
32+
EC2NodeClass *EC2NodeClassSpec `json:"ec2NodeClass,omitempty"`
33+
2934
// ProviderIDList are the identification IDs of machine instances provided by the provider.
3035
// This field must match the provider IDs as seen on the node objects corresponding to a machine pool's machine instances.
3136
// +optional
@@ -34,13 +39,17 @@ type KarpenterMachinePoolSpec struct {
3439

3540
// KarpenterMachinePoolStatus defines the observed state of KarpenterMachinePool.
3641
type KarpenterMachinePoolStatus struct {
37-
// Ready is true when the provider resource is ready.
42+
// Ready denotes that the KarpenterMachinePool is ready and fulfilling the infrastructure contract.
3843
// +optional
3944
Ready bool `json:"ready"`
4045

4146
// Replicas is the most recently observed number of replicas
4247
// +optional
4348
Replicas int32 `json:"replicas"`
49+
50+
// Conditions defines current service state of the KarpenterMachinePool.
51+
// +optional
52+
Conditions capi.Conditions `json:"conditions,omitempty"`
4453
}
4554

4655
// +kubebuilder:object:root=true
@@ -72,3 +81,11 @@ type KarpenterMachinePoolList struct {
7281
func init() {
7382
SchemeBuilder.Register(&KarpenterMachinePool{}, &KarpenterMachinePoolList{})
7483
}
84+
85+
func (in *KarpenterMachinePool) GetConditions() capi.Conditions {
86+
return in.Status.Conditions
87+
}
88+
89+
func (in *KarpenterMachinePool) SetConditions(conditions capi.Conditions) {
90+
in.Status.Conditions = conditions
91+
}

0 commit comments

Comments
 (0)