diff --git a/.github/workflows/envtest-kube.yaml b/.github/workflows/envtest-kube.yaml
index 0717d402fda4..eb6b3f15628e 100644
--- a/.github/workflows/envtest-kube.yaml
+++ b/.github/workflows/envtest-kube.yaml
@@ -41,7 +41,7 @@ jobs:
if [ "$EVENT_NAME" = "pull_request" ]; then
ref="$PR_DIFF_REF"
fi
- if git diff --name-only "$ref" | grep -qE '^(api/|test/envtest/|cmd/install/assets/crds/hypershift-operator/tests/|\.github/workflows/envtest-kube\.yaml$)'; then
+ if git diff --name-only "$ref" | grep -qE '^(api/|test/envtest/|cmd/install/assets/crds/hypershift-operator/tests/|karpenter-operator/controllers/karpenter/assets/tests/|\.github/workflows/envtest-kube\.yaml$)'; then
echo "should_run=true"
else
echo "should_run=false"
diff --git a/.github/workflows/envtest-ocp.yaml b/.github/workflows/envtest-ocp.yaml
index 28002e8291e5..45bf504d79d6 100644
--- a/.github/workflows/envtest-ocp.yaml
+++ b/.github/workflows/envtest-ocp.yaml
@@ -41,7 +41,7 @@ jobs:
if [ "$EVENT_NAME" = "pull_request" ]; then
ref="$PR_DIFF_REF"
fi
- if git diff --name-only "$ref" | grep -qE '^(api/|test/envtest/|cmd/install/assets/crds/hypershift-operator/tests/|\.github/workflows/envtest-ocp\.yaml$)'; then
+ if git diff --name-only "$ref" | grep -qE '^(api/|test/envtest/|cmd/install/assets/crds/hypershift-operator/tests/|karpenter-operator/controllers/karpenter/assets/tests/|\.github/workflows/envtest-ocp\.yaml$)'; then
echo "should_run=true"
else
echo "should_run=false"
diff --git a/Makefile b/Makefile
index 46bfbf6c80e7..3ed7879c16ea 100644
--- a/Makefile
+++ b/Makefile
@@ -199,6 +199,7 @@ karpenter-api: $(CONTROLLER_GEN) $(YQ)
karpenter-operator/hack/crds-sync.sh
karpenter-operator/hack/adjust-cel.sh
$(CONTROLLER_GEN) $(CRD_OPTIONS) paths="./api/karpenter/..." output:crd:artifacts:config=karpenter-operator/controllers/karpenter/assets
+ cp karpenter-operator/controllers/karpenter/assets/karpenter.hypershift.openshift.io_openshiftec2nodeclasses.yaml karpenter-operator/controllers/karpenter/assets/zz_generated.crd-manifests/openshiftec2nodeclasses.crd.yaml
.PHONY: control-plane-operator
control-plane-operator:
diff --git a/api/.golangci.yml b/api/.golangci.yml
index 80e2916f9de2..822c8a8d9b61 100644
--- a/api/.golangci.yml
+++ b/api/.golangci.yml
@@ -202,11 +202,6 @@ linters:
- kubeapilinter
path: hypershift/v1beta1/openstack.go
text: 'arrayofstruct: OpenStackPlatformSpec.Subnets is an array of structs, but the struct has no required fields. At least one field should be marked as required to prevent ambiguous YAML configurations'
- - linters:
- - kubeapilinter
- path: karpenter/v1beta1/karpenter_types.go
- text: 'arrayofstruct: OpenshiftEC2NodeClassSpec.CapacityReservationSelectorTerms is an array of structs, but the struct has no required fields. At least one field should be marked as required to prevent ambiguous YAML configurations'
-
# conditions (1 issue)
- linters:
- kubeapilinter
diff --git a/api/hypershift/v1beta1/featuregates/featureGate-Hypershift-Default.yaml b/api/hypershift/v1beta1/featuregates/featureGate-Hypershift-Default.yaml
index 9b77b0a53630..49e57fef36f1 100644
--- a/api/hypershift/v1beta1/featuregates/featureGate-Hypershift-Default.yaml
+++ b/api/hypershift/v1beta1/featuregates/featureGate-Hypershift-Default.yaml
@@ -15,9 +15,6 @@
"featureGates": [
{
"disabled": [
- {
- "name": "AutoNodeKarpenter",
- },
{
"name": "AROHCPManagedIdentities"
},
diff --git a/api/hypershift/v1beta1/featuregates/featureGate-Hypershift-TechPreviewNoUpgrade.yaml b/api/hypershift/v1beta1/featuregates/featureGate-Hypershift-TechPreviewNoUpgrade.yaml
index 6bf6115cb585..39a658ee9697 100644
--- a/api/hypershift/v1beta1/featuregates/featureGate-Hypershift-TechPreviewNoUpgrade.yaml
+++ b/api/hypershift/v1beta1/featuregates/featureGate-Hypershift-TechPreviewNoUpgrade.yaml
@@ -16,9 +16,6 @@
{
"disabled": [],
"enabled": [
- {
- "name": "AutoNodeKarpenter",
- },
{
"name": "ExternalOIDC"
},
diff --git a/api/hypershift/v1beta1/featuregates/featureGate-SelfManagedHA-Default.yaml b/api/hypershift/v1beta1/featuregates/featureGate-SelfManagedHA-Default.yaml
index e39f3861a86f..fd5c9fe89f9d 100644
--- a/api/hypershift/v1beta1/featuregates/featureGate-SelfManagedHA-Default.yaml
+++ b/api/hypershift/v1beta1/featuregates/featureGate-SelfManagedHA-Default.yaml
@@ -15,9 +15,6 @@
"featureGates": [
{
"disabled": [
- {
- "name": "AutoNodeKarpenter",
- },
{
"name": "ExternalOIDCWithUpstreamParity"
},
diff --git a/api/hypershift/v1beta1/featuregates/featureGate-SelfManagedHA-TechPreviewNoUpgrade.yaml b/api/hypershift/v1beta1/featuregates/featureGate-SelfManagedHA-TechPreviewNoUpgrade.yaml
index be4bd6ea1c9e..0de2b3b93ba2 100644
--- a/api/hypershift/v1beta1/featuregates/featureGate-SelfManagedHA-TechPreviewNoUpgrade.yaml
+++ b/api/hypershift/v1beta1/featuregates/featureGate-SelfManagedHA-TechPreviewNoUpgrade.yaml
@@ -16,9 +16,6 @@
{
"disabled": [],
"enabled": [
- {
- "name": "AutoNodeKarpenter",
- },
{
"name": "ExternalOIDC"
},
diff --git a/api/hypershift/v1beta1/hosted_controlplane.go b/api/hypershift/v1beta1/hosted_controlplane.go
index 585a3b3d4193..8486514f0a18 100644
--- a/api/hypershift/v1beta1/hosted_controlplane.go
+++ b/api/hypershift/v1beta1/hosted_controlplane.go
@@ -230,9 +230,8 @@ type HostedControlPlaneSpec struct {
// using the specified provisioner (e.g. Karpenter) instead of requiring
// manual NodePool management.
//
- // +openshift:enable:FeatureGate=AutoNodeKarpenter
// +optional
- AutoNode *AutoNode `json:"autoNode,omitempty"`
+ AutoNode AutoNode `json:"autoNode,omitzero"`
// nodeSelector when specified, must be true for the pods managed by the HostedCluster to be scheduled.
//
@@ -414,7 +413,6 @@ type HostedControlPlaneStatus struct {
NodeCount *int `json:"nodeCount,omitempty"`
// autoNode contains the observed state of the autoNode (Karpenter) provisioner.
- // +openshift:enable:FeatureGate=AutoNodeKarpenter
// +optional
AutoNode AutoNodeStatus `json:"autoNode,omitzero"`
diff --git a/api/hypershift/v1beta1/hostedcluster_types.go b/api/hypershift/v1beta1/hostedcluster_types.go
index d99f765f090f..8d4db6c2bb34 100644
--- a/api/hypershift/v1beta1/hostedcluster_types.go
+++ b/api/hypershift/v1beta1/hostedcluster_types.go
@@ -642,9 +642,8 @@ type HostedClusterSpec struct {
// autoNode specifies the configuration for automatic node provisioning and lifecycle management.
// When set, the provisioner(e.g. Karpenter) will be used to provision nodes for targeted workloads.
//
- // +openshift:enable:FeatureGate=AutoNodeKarpenter
// +optional
- AutoNode *AutoNode `json:"autoNode,omitempty"`
+ AutoNode AutoNode `json:"autoNode,omitzero"`
// etcd specifies configuration for the control plane etcd cluster. The
// default managementType is Managed. Once set, the managementType cannot be
@@ -1398,7 +1397,7 @@ type ProvisionerConfig struct {
//
// +optional
// +unionMember
- Karpenter *KarpenterConfig `json:"karpenter,omitempty"`
+ Karpenter KarpenterConfig `json:"karpenter,omitzero"`
}
// KarpenterConfig specifies the configuration for the Karpenter provisioner
@@ -1418,7 +1417,7 @@ type KarpenterConfig struct {
//
// +optional
// +unionMember
- AWS *KarpenterAWSConfig `json:"aws,omitempty"`
+ AWS KarpenterAWSConfig `json:"aws,omitzero"`
}
// KarpenterAWSConfig specifies AWS-specific configuration for the Karpenter provisioner.
@@ -2174,7 +2173,6 @@ type HostedClusterStatus struct {
Platform *PlatformStatus `json:"platform,omitempty"`
// autoNode contains the observed state of the autoNode (Karpenter) provisioner.
- // +openshift:enable:FeatureGate=AutoNodeKarpenter
// +optional
AutoNode AutoNodeStatus `json:"autoNode,omitzero"`
diff --git a/api/hypershift/v1beta1/zz_generated.deepcopy.go b/api/hypershift/v1beta1/zz_generated.deepcopy.go
index 558be0746f8f..0f69bb3bcdeb 100644
--- a/api/hypershift/v1beta1/zz_generated.deepcopy.go
+++ b/api/hypershift/v1beta1/zz_generated.deepcopy.go
@@ -558,7 +558,7 @@ func (in *AllocationPool) DeepCopy() *AllocationPool {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AutoNode) DeepCopyInto(out *AutoNode) {
*out = *in
- in.Provisioner.DeepCopyInto(&out.Provisioner)
+ out.Provisioner = in.Provisioner
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutoNode.
@@ -2305,11 +2305,7 @@ func (in *HostedClusterSpec) DeepCopyInto(out *HostedClusterSpec) {
in.DNS.DeepCopyInto(&out.DNS)
in.Networking.DeepCopyInto(&out.Networking)
in.Autoscaling.DeepCopyInto(&out.Autoscaling)
- if in.AutoNode != nil {
- in, out := &in.AutoNode, &out.AutoNode
- *out = new(AutoNode)
- (*in).DeepCopyInto(*out)
- }
+ out.AutoNode = in.AutoNode
in.Etcd.DeepCopyInto(&out.Etcd)
if in.Services != nil {
in, out := &in.Services, &out.Services
@@ -2583,11 +2579,7 @@ func (in *HostedControlPlaneSpec) DeepCopyInto(out *HostedControlPlaneSpec) {
**out = **in
}
in.Autoscaling.DeepCopyInto(&out.Autoscaling)
- if in.AutoNode != nil {
- in, out := &in.AutoNode, &out.AutoNode
- *out = new(AutoNode)
- (*in).DeepCopyInto(*out)
- }
+ out.AutoNode = in.AutoNode
if in.NodeSelector != nil {
in, out := &in.NodeSelector, &out.NodeSelector
*out = make(map[string]string, len(*in))
@@ -2910,11 +2902,7 @@ func (in *KarpenterAWSConfig) DeepCopy() *KarpenterAWSConfig {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KarpenterConfig) DeepCopyInto(out *KarpenterConfig) {
*out = *in
- if in.AWS != nil {
- in, out := &in.AWS, &out.AWS
- *out = new(KarpenterAWSConfig)
- **out = **in
- }
+ out.AWS = in.AWS
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KarpenterConfig.
@@ -4211,11 +4199,7 @@ func (in *PowerVSVPC) DeepCopy() *PowerVSVPC {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ProvisionerConfig) DeepCopyInto(out *ProvisionerConfig) {
*out = *in
- if in.Karpenter != nil {
- in, out := &in.Karpenter, &out.Karpenter
- *out = new(KarpenterConfig)
- (*in).DeepCopyInto(*out)
- }
+ out.Karpenter = in.Karpenter
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProvisionerConfig.
diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests.yaml
index e4be0c29179b..ae4043f97aa0 100644
--- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests.yaml
+++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests.yaml
@@ -204,7 +204,6 @@ hostedclusters.hypershift.openshift.io:
Capability: ""
Category: ""
FeatureGates:
- - AutoNodeKarpenter
- ClusterUpdateAcceptRisks
- ClusterVersionOperatorConfiguration
- ExternalOIDC
@@ -278,7 +277,6 @@ hostedcontrolplanes.hypershift.openshift.io:
Capability: ""
Category: cluster-api
FeatureGates:
- - AutoNodeKarpenter
- ClusterUpdateAcceptRisks
- ClusterVersionOperatorConfiguration
- ExternalOIDC
diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AAA_ungated.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AAA_ungated.yaml
index ee9f79007604..1abed4220a44 100644
--- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AAA_ungated.yaml
+++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AAA_ungated.yaml
@@ -124,6 +124,142 @@ spec:
type: string
type: object
x-kubernetes-map-type: atomic
+ autoNode:
+ description: |-
+ autoNode specifies the configuration for automatic node provisioning and lifecycle management.
+ When set, the provisioner(e.g. Karpenter) will be used to provision nodes for targeted workloads.
+ properties:
+ provisionerConfig:
+ description: provisionerConfig specifies the provisioner used
+ for automatic node management.
+ properties:
+ karpenter:
+ description: karpenter specifies the configuration for the
+ Karpenter provisioner.
+ properties:
+ aws:
+ description: aws specifies the AWS-specific configuration
+ for Karpenter.
+ properties:
+ roleARN:
+ description: "roleARN specifies the ARN of the IAM
+ role that Karpenter assumes to provision\nand manage
+ EC2 instances in the hosted cluster's AWS account.\n\nThe
+ referenced role must have a trust relationship that
+ allows it to be assumed\nby the karpenter service
+ account in the hosted cluster via OIDC.\nExample:\n{\n\t\"Version\":
+ \"2012-10-17\",\n\t\"Statement\": [\n\t\t{\n\t\t\t\"Effect\":
+ \"Allow\",\n\t\t\t\"Principal\": {\n\t\t\t\t\"Federated\":
+ \"\"\n\t\t\t},\n\t\t\t\"Action\":
+ \"sts:AssumeRoleWithWebIdentity\",\n\t\t\t\"Condition\":
+ {\n\t\t\t\t\"StringEquals\": {\n\t\t\t\t\t\":sub\":
+ \"system:serviceaccount:kube-system:karpenter\"\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t]\n}\n\nThe
+ following is an example of the policy document for
+ this role.\n\n{\n\t\"Version\": \"2012-10-17\",\n\t\"Statement\":
+ [\n\t\t{\n\t\t\t\"Sid\": \"AllowScopedEC2InstanceAccessActions\",\n\t\t\t\"Effect\":
+ \"Allow\",\n\t\t\t\"Resource\": [\n\t\t\t\t\"arn:*:ec2:*::image/*\",\n\t\t\t\t\"arn:*:ec2:*::snapshot/*\",\n\t\t\t\t\"arn:*:ec2:*:*:security-group/*\",\n\t\t\t\t\"arn:*:ec2:*:*:subnet/*\"\n\t\t\t],\n\t\t\t\"Action\":
+ [\n\t\t\t\t\"ec2:RunInstances\",\n\t\t\t\t\"ec2:CreateFleet\"\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
+ \"AllowScopedEC2LaunchTemplateAccessActions\",\n\t\t\t\"Effect\":
+ \"Allow\",\n\t\t\t\"Resource\": \"arn:*:ec2:*:*:launch-template/*\",\n\t\t\t\"Action\":
+ [\n\t\t\t\t\"ec2:RunInstances\",\n\t\t\t\t\"ec2:CreateFleet\"\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
+ \"AllowScopedEC2InstanceActionsWithTags\",\n\t\t\t\"Effect\":
+ \"Allow\",\n\t\t\t\"Resource\": [\n\t\t\t\t\"arn:*:ec2:*:*:fleet/*\",\n\t\t\t\t\"arn:*:ec2:*:*:instance/*\",\n\t\t\t\t\"arn:*:ec2:*:*:volume/*\",\n\t\t\t\t\"arn:*:ec2:*:*:network-interface/*\",\n\t\t\t\t\"arn:*:ec2:*:*:launch-template/*\",\n\t\t\t\t\"arn:*:ec2:*:*:spot-instances-request/*\"\n\t\t\t],\n\t\t\t\"Action\":
+ [\n\t\t\t\t\"ec2:RunInstances\",\n\t\t\t\t\"ec2:CreateFleet\",\n\t\t\t\t\"ec2:CreateLaunchTemplate\"\n\t\t\t],\n\t\t\t\"Condition\":
+ {\n\t\t\t\t\"StringLike\": {\n\t\t\t\t\t\"aws:RequestTag/karpenter.sh/nodepool\":
+ \"*\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
+ \"AllowScopedResourceCreationTagging\",\n\t\t\t\"Effect\":
+ \"Allow\",\n\t\t\t\"Resource\": [\n\t\t\t\t\"arn:*:ec2:*:*:fleet/*\",\n\t\t\t\t\"arn:*:ec2:*:*:instance/*\",\n\t\t\t\t\"arn:*:ec2:*:*:volume/*\",\n\t\t\t\t\"arn:*:ec2:*:*:network-interface/*\",\n\t\t\t\t\"arn:*:ec2:*:*:launch-template/*\",\n\t\t\t\t\"arn:*:ec2:*:*:spot-instances-request/*\"\n\t\t\t],\n\t\t\t\"Action\":
+ \"ec2:CreateTags\",\n\t\t\t\"Condition\": {\n\t\t\t\t\"StringEquals\":
+ {\n\t\t\t\t\t\"ec2:CreateAction\": [\n\t\t\t\t\t\t\"RunInstances\",\n\t\t\t\t\t\t\"CreateFleet\",\n\t\t\t\t\t\t\"CreateLaunchTemplate\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t\"StringLike\":
+ {\n\t\t\t\t\t\"aws:RequestTag/karpenter.sh/nodepool\":
+ \"*\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
+ \"AllowScopedResourceTagging\",\n\t\t\t\"Effect\":
+ \"Allow\",\n\t\t\t\"Resource\": \"arn:*:ec2:*:*:instance/*\",\n\t\t\t\"Action\":
+ \"ec2:CreateTags\",\n\t\t\t\"Condition\": {\n\t\t\t\t\"StringLike\":
+ {\n\t\t\t\t\t\"aws:ResourceTag/karpenter.sh/nodepool\":
+ \"*\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
+ \"AllowScopedDeletion\",\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Resource\":
+ [\n\t\t\t\t\"arn:*:ec2:*:*:instance/*\",\n\t\t\t\t\"arn:*:ec2:*:*:launch-template/*\"\n\t\t\t],\n\t\t\t\"Action\":
+ [\n\t\t\t\t\"ec2:TerminateInstances\",\n\t\t\t\t\"ec2:DeleteLaunchTemplate\"\n\t\t\t],\n\t\t\t\"Condition\":
+ {\n\t\t\t\t\"StringLike\": {\n\t\t\t\t\t\"aws:ResourceTag/karpenter.sh/nodepool\":
+ \"*\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
+ \"AllowRegionalReadActions\",\n\t\t\t\"Effect\":
+ \"Allow\",\n\t\t\t\"Resource\": \"*\",\n\t\t\t\"Action\":
+ [\n\t\t\t\t\"ec2:DescribeImages\",\n\t\t\t\t\"ec2:DescribeInstances\",\n\t\t\t\t\"ec2:DescribeInstanceTypeOfferings\",\n\t\t\t\t\"ec2:DescribeInstanceTypes\",\n\t\t\t\t\"ec2:DescribeLaunchTemplates\",\n\t\t\t\t\"ec2:DescribeSecurityGroups\",\n\t\t\t\t\"ec2:DescribeSpotPriceHistory\",\n\t\t\t\t\"ec2:DescribeSubnets\"\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
+ \"AllowSSMReadActions\",\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Resource\":
+ \"arn:*:ssm:*::parameter/aws/service/*\",\n\t\t\t\"Action\":
+ \"ssm:GetParameter\"\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
+ \"AllowPricingReadActions\",\n\t\t\t\"Effect\":
+ \"Allow\",\n\t\t\t\"Resource\": \"*\",\n\t\t\t\"Action\":
+ \"pricing:GetProducts\"\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
+ \"AllowInterruptionQueueActions\",\n\t\t\t\"Effect\":
+ \"Allow\",\n\t\t\t\"Resource\": \"*\",\n\t\t\t\"Action\":
+ [\n\t\t\t\t\"sqs:DeleteMessage\",\n\t\t\t\t\"sqs:GetQueueUrl\",\n\t\t\t\t\"sqs:ReceiveMessage\"\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
+ \"AllowPassingInstanceRole\",\n\t\t\t\"Effect\":
+ \"Allow\",\n\t\t\t\"Resource\": \"arn:*:iam::*:role/*\",\n\t\t\t\"Action\":
+ \"iam:PassRole\",\n\t\t\t\"Condition\": {\n\t\t\t\t\"StringEquals\":
+ {\n\t\t\t\t\t\"iam:PassedToService\": [\n\t\t\t\t\t\t\"ec2.amazonaws.com\",\n\t\t\t\t\t\t\"ec2.amazonaws.com.cn\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
+ \"AllowScopedInstanceProfileCreationActions\",\n\t\t\t\"Effect\":
+ \"Allow\",\n\t\t\t\"Resource\": \"arn:*:iam::*:instance-profile/*\",\n\t\t\t\"Action\":
+ [\n\t\t\t\t\"iam:CreateInstanceProfile\"\n\t\t\t],\n\t\t\t\"Condition\":
+ {\n\t\t\t\t\"StringLike\": {\n\t\t\t\t\t\"aws:RequestTag/karpenter.k8s.aws/ec2nodeclass\":
+ \"*\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
+ \"AllowScopedInstanceProfileTagActions\",\n\t\t\t\"Effect\":
+ \"Allow\",\n\t\t\t\"Resource\": \"arn:*:iam::*:instance-profile/*\",\n\t\t\t\"Action\":
+ [\n\t\t\t\t\"iam:TagInstanceProfile\"\n\t\t\t],\n\t\t\t\"Condition\":
+ {\n\t\t\t\t\"StringLike\": {\n\t\t\t\t\t\"aws:ResourceTag/karpenter.k8s.aws/ec2nodeclass\":
+ \"*\",\n\t\t\t\t\t\"aws:RequestTag/karpenter.k8s.aws/ec2nodeclass\":
+ \"*\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
+ \"AllowScopedInstanceProfileActions\",\n\t\t\t\"Effect\":
+ \"Allow\",\n\t\t\t\"Resource\": \"arn:*:iam::*:instance-profile/*\",\n\t\t\t\"Action\":
+ [\n\t\t\t\t\"iam:AddRoleToInstanceProfile\",\n\t\t\t\t\"iam:RemoveRoleFromInstanceProfile\",\n\t\t\t\t\"iam:DeleteInstanceProfile\"\n\t\t\t],\n\t\t\t\"Condition\":
+ {\n\t\t\t\t\"StringLike\": {\n\t\t\t\t\t\"aws:ResourceTag/karpenter.k8s.aws/ec2nodeclass\":
+ \"*\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
+ \"AllowInstanceProfileReadActions\",\n\t\t\t\"Effect\":
+ \"Allow\",\n\t\t\t\"Resource\": \"arn:*:iam::*:instance-profile/*\",\n\t\t\t\"Action\":
+ \"iam:GetInstanceProfile\"\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
+ \"AllowUnscopedInstanceProfileListAction\",\n\t\t\t\"Effect\":
+ \"Allow\",\n\t\t\t\"Resource\": \"*\",\n\t\t\t\"Action\":
+ \"iam:ListInstanceProfiles\"\n\t\t}\n\t]\n}"
+ maxLength: 2048
+ type: string
+ x-kubernetes-validations:
+ - message: roleARN must be a valid AWS IAM role ARN
+ (e.g. arn:aws:iam::123456789012:role/MyRole)
+ rule: self.matches('^arn:(aws|aws-cn|aws-us-gov):iam::[0-9]{12}:role/.+$')
+ required:
+ - roleARN
+ type: object
+ platform:
+ description: platform specifies the infrastructure platform
+ that Karpenter should provision nodes on.
+ enum:
+ - AWS
+ maxLength: 100
+ type: string
+ required:
+ - platform
+ type: object
+ x-kubernetes-validations:
+ - message: aws is required when platform is AWS, and forbidden
+ otherwise
+ rule: 'self.platform == ''AWS'' ? has(self.aws) : !has(self.aws)'
+ name:
+ description: name specifies the name of the provisioner to
+ use for automatic node management.
+ enum:
+ - Karpenter
+ type: string
+ required:
+ - name
+ type: object
+ x-kubernetes-validations:
+ - message: karpenter is required when name is Karpenter, and forbidden
+ otherwise
+ rule: 'self.name == ''Karpenter'' ? has(self.karpenter) : !has(self.karpenter)'
+ required:
+ - provisionerConfig
+ type: object
autoscaling:
description: |-
autoscaling specifies auto-scaling behavior that applies to all NodePools
@@ -6368,6 +6504,26 @@ spec:
status:
description: status is the latest observed status of the HostedCluster.
properties:
+ autoNode:
+ description: autoNode contains the observed state of the autoNode
+ (Karpenter) provisioner.
+ minProperties: 1
+ properties:
+ nodeClaimCount:
+ description: |-
+ nodeClaimCount is the total number of NodeClaims managed by Karpenter.
+ This represents what Karpenter intends to provision, whether or not the node object exists yet.
+ format: int32
+ minimum: 0
+ type: integer
+ nodeCount:
+ description: |-
+ nodeCount is the number of nodes fully provisioned by Karpenter.
+ These are node objects that exist in the cluster and carry the karpenter.sh/nodepool label.
+ format: int32
+ minimum: 0
+ type: integer
+ type: object
conditions:
description: |-
conditions represents the latest available observations of a control
diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AutoNodeKarpenter.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AutoNodeKarpenter.yaml
deleted file mode 100644
index 8cc9488a00ab..000000000000
--- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AutoNodeKarpenter.yaml
+++ /dev/null
@@ -1,7207 +0,0 @@
-apiVersion: apiextensions.k8s.io/v1
-kind: CustomResourceDefinition
-metadata:
- annotations:
- feature-gate.release.openshift.io/AutoNodeKarpenter: "true"
- name: hostedclusters.hypershift.openshift.io
-spec:
- group: hypershift.openshift.io
- names:
- kind: HostedCluster
- listKind: HostedClusterList
- plural: hostedclusters
- shortNames:
- - hc
- - hcs
- singular: hostedcluster
- scope: Namespaced
- versions:
- - additionalPrinterColumns:
- - description: Version
- jsonPath: .status.version.history[?(@.state=="Completed")].version
- name: Version
- type: string
- - description: Control Plane Version
- jsonPath: .status.controlPlaneVersion.history[?(@.state=="Completed")].version
- name: CP Version
- type: string
- - description: KubeConfig Secret
- jsonPath: .status.kubeconfig.name
- name: KubeConfig
- type: string
- - description: Progress
- jsonPath: .status.version.history[?(@.state!="")].state
- name: Progress
- type: string
- - description: Available
- jsonPath: .status.conditions[?(@.type=="Available")].status
- name: Available
- type: string
- - description: Progressing
- jsonPath: .status.conditions[?(@.type=="Progressing")].status
- name: Progressing
- type: string
- - description: Message
- jsonPath: .status.conditions[?(@.type=="Available")].message
- name: Message
- type: string
- - description: Control Plane Progress
- jsonPath: .status.controlPlaneVersion.history[0].state
- name: CP Progress
- priority: 1
- type: string
- - description: Data Plane Progress
- jsonPath: .status.version.history[0].state
- name: DP Progress
- priority: 1
- type: string
- name: v1beta1
- schema:
- openAPIV3Schema:
- description: |-
- HostedCluster is the primary representation of a HyperShift cluster and encapsulates
- the control plane and common data plane configuration. Creating a HostedCluster
- results in a fully functional OpenShift control plane with no attached nodes.
- To support workloads (e.g. pods), a HostedCluster may have one or more associated
- NodePool resources.
- properties:
- apiVersion:
- description: |-
- APIVersion defines the versioned schema of this representation of an object.
- Servers should convert recognized schemas to the latest internal value, and
- may reject unrecognized values.
- More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
- type: string
- kind:
- description: |-
- Kind is a string value representing the REST resource this object represents.
- Servers may infer this from the endpoint the client submits requests to.
- Cannot be updated.
- In CamelCase.
- More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
- type: string
- metadata:
- type: object
- spec:
- description: spec is the desired behavior of the HostedCluster.
- properties:
- additionalTrustBundle:
- description: |-
- additionalTrustBundle is a local reference to a ConfigMap that must have a "ca-bundle.crt" key
- whose content must be a PEM-encoded X.509 certificate bundle that will be added to the hosted controlplane and nodes
- If the reference is set but none of the above requirements are met, the HostedCluster will enter a degraded state.
- This will be part of every payload generated by the controllers for any NodePool of the HostedCluster.
- Changing this value will trigger a rollout for all existing NodePools in the cluster.
- properties:
- name:
- default: ""
- description: |-
- Name of the referent.
- This field is effectively required, but due to backwards compatibility is
- allowed to be empty. Instances of this type with an empty value here are
- almost certainly wrong.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- type: string
- type: object
- x-kubernetes-map-type: atomic
- auditWebhook:
- description: |-
- auditWebhook contains metadata for configuring an audit webhook endpoint
- for a cluster to process cluster audit events. It references a secret that
- contains the webhook information for the audit webhook endpoint. It is a
- secret because if the endpoint has mTLS the kubeconfig will contain client
- keys. The kubeconfig needs to be stored in the secret with a secret key
- name that corresponds to the constant AuditWebhookKubeconfigKey.
- properties:
- name:
- default: ""
- description: |-
- Name of the referent.
- This field is effectively required, but due to backwards compatibility is
- allowed to be empty. Instances of this type with an empty value here are
- almost certainly wrong.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- type: string
- type: object
- x-kubernetes-map-type: atomic
- autoNode:
- description: |-
- autoNode specifies the configuration for automatic node provisioning and lifecycle management.
- When set, the provisioner(e.g. Karpenter) will be used to provision nodes for targeted workloads.
- properties:
- provisionerConfig:
- description: provisionerConfig specifies the provisioner used
- for automatic node management.
- properties:
- karpenter:
- description: karpenter specifies the configuration for the
- Karpenter provisioner.
- properties:
- aws:
- description: aws specifies the AWS-specific configuration
- for Karpenter.
- properties:
- roleARN:
- description: "roleARN specifies the ARN of the IAM
- role that Karpenter assumes to provision\nand manage
- EC2 instances in the hosted cluster's AWS account.\n\nThe
- referenced role must have a trust relationship that
- allows it to be assumed\nby the karpenter service
- account in the hosted cluster via OIDC.\nExample:\n{\n\t\"Version\":
- \"2012-10-17\",\n\t\"Statement\": [\n\t\t{\n\t\t\t\"Effect\":
- \"Allow\",\n\t\t\t\"Principal\": {\n\t\t\t\t\"Federated\":
- \"\"\n\t\t\t},\n\t\t\t\"Action\":
- \"sts:AssumeRoleWithWebIdentity\",\n\t\t\t\"Condition\":
- {\n\t\t\t\t\"StringEquals\": {\n\t\t\t\t\t\":sub\":
- \"system:serviceaccount:kube-system:karpenter\"\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t]\n}\n\nThe
- following is an example of the policy document for
- this role.\n\n{\n\t\"Version\": \"2012-10-17\",\n\t\"Statement\":
- [\n\t\t{\n\t\t\t\"Sid\": \"AllowScopedEC2InstanceAccessActions\",\n\t\t\t\"Effect\":
- \"Allow\",\n\t\t\t\"Resource\": [\n\t\t\t\t\"arn:*:ec2:*::image/*\",\n\t\t\t\t\"arn:*:ec2:*::snapshot/*\",\n\t\t\t\t\"arn:*:ec2:*:*:security-group/*\",\n\t\t\t\t\"arn:*:ec2:*:*:subnet/*\"\n\t\t\t],\n\t\t\t\"Action\":
- [\n\t\t\t\t\"ec2:RunInstances\",\n\t\t\t\t\"ec2:CreateFleet\"\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
- \"AllowScopedEC2LaunchTemplateAccessActions\",\n\t\t\t\"Effect\":
- \"Allow\",\n\t\t\t\"Resource\": \"arn:*:ec2:*:*:launch-template/*\",\n\t\t\t\"Action\":
- [\n\t\t\t\t\"ec2:RunInstances\",\n\t\t\t\t\"ec2:CreateFleet\"\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
- \"AllowScopedEC2InstanceActionsWithTags\",\n\t\t\t\"Effect\":
- \"Allow\",\n\t\t\t\"Resource\": [\n\t\t\t\t\"arn:*:ec2:*:*:fleet/*\",\n\t\t\t\t\"arn:*:ec2:*:*:instance/*\",\n\t\t\t\t\"arn:*:ec2:*:*:volume/*\",\n\t\t\t\t\"arn:*:ec2:*:*:network-interface/*\",\n\t\t\t\t\"arn:*:ec2:*:*:launch-template/*\",\n\t\t\t\t\"arn:*:ec2:*:*:spot-instances-request/*\"\n\t\t\t],\n\t\t\t\"Action\":
- [\n\t\t\t\t\"ec2:RunInstances\",\n\t\t\t\t\"ec2:CreateFleet\",\n\t\t\t\t\"ec2:CreateLaunchTemplate\"\n\t\t\t],\n\t\t\t\"Condition\":
- {\n\t\t\t\t\"StringLike\": {\n\t\t\t\t\t\"aws:RequestTag/karpenter.sh/nodepool\":
- \"*\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
- \"AllowScopedResourceCreationTagging\",\n\t\t\t\"Effect\":
- \"Allow\",\n\t\t\t\"Resource\": [\n\t\t\t\t\"arn:*:ec2:*:*:fleet/*\",\n\t\t\t\t\"arn:*:ec2:*:*:instance/*\",\n\t\t\t\t\"arn:*:ec2:*:*:volume/*\",\n\t\t\t\t\"arn:*:ec2:*:*:network-interface/*\",\n\t\t\t\t\"arn:*:ec2:*:*:launch-template/*\",\n\t\t\t\t\"arn:*:ec2:*:*:spot-instances-request/*\"\n\t\t\t],\n\t\t\t\"Action\":
- \"ec2:CreateTags\",\n\t\t\t\"Condition\": {\n\t\t\t\t\"StringEquals\":
- {\n\t\t\t\t\t\"ec2:CreateAction\": [\n\t\t\t\t\t\t\"RunInstances\",\n\t\t\t\t\t\t\"CreateFleet\",\n\t\t\t\t\t\t\"CreateLaunchTemplate\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t\"StringLike\":
- {\n\t\t\t\t\t\"aws:RequestTag/karpenter.sh/nodepool\":
- \"*\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
- \"AllowScopedResourceTagging\",\n\t\t\t\"Effect\":
- \"Allow\",\n\t\t\t\"Resource\": \"arn:*:ec2:*:*:instance/*\",\n\t\t\t\"Action\":
- \"ec2:CreateTags\",\n\t\t\t\"Condition\": {\n\t\t\t\t\"StringLike\":
- {\n\t\t\t\t\t\"aws:ResourceTag/karpenter.sh/nodepool\":
- \"*\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
- \"AllowScopedDeletion\",\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Resource\":
- [\n\t\t\t\t\"arn:*:ec2:*:*:instance/*\",\n\t\t\t\t\"arn:*:ec2:*:*:launch-template/*\"\n\t\t\t],\n\t\t\t\"Action\":
- [\n\t\t\t\t\"ec2:TerminateInstances\",\n\t\t\t\t\"ec2:DeleteLaunchTemplate\"\n\t\t\t],\n\t\t\t\"Condition\":
- {\n\t\t\t\t\"StringLike\": {\n\t\t\t\t\t\"aws:ResourceTag/karpenter.sh/nodepool\":
- \"*\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
- \"AllowRegionalReadActions\",\n\t\t\t\"Effect\":
- \"Allow\",\n\t\t\t\"Resource\": \"*\",\n\t\t\t\"Action\":
- [\n\t\t\t\t\"ec2:DescribeImages\",\n\t\t\t\t\"ec2:DescribeInstances\",\n\t\t\t\t\"ec2:DescribeInstanceTypeOfferings\",\n\t\t\t\t\"ec2:DescribeInstanceTypes\",\n\t\t\t\t\"ec2:DescribeLaunchTemplates\",\n\t\t\t\t\"ec2:DescribeSecurityGroups\",\n\t\t\t\t\"ec2:DescribeSpotPriceHistory\",\n\t\t\t\t\"ec2:DescribeSubnets\"\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
- \"AllowSSMReadActions\",\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Resource\":
- \"arn:*:ssm:*::parameter/aws/service/*\",\n\t\t\t\"Action\":
- \"ssm:GetParameter\"\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
- \"AllowPricingReadActions\",\n\t\t\t\"Effect\":
- \"Allow\",\n\t\t\t\"Resource\": \"*\",\n\t\t\t\"Action\":
- \"pricing:GetProducts\"\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
- \"AllowInterruptionQueueActions\",\n\t\t\t\"Effect\":
- \"Allow\",\n\t\t\t\"Resource\": \"*\",\n\t\t\t\"Action\":
- [\n\t\t\t\t\"sqs:DeleteMessage\",\n\t\t\t\t\"sqs:GetQueueUrl\",\n\t\t\t\t\"sqs:ReceiveMessage\"\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
- \"AllowPassingInstanceRole\",\n\t\t\t\"Effect\":
- \"Allow\",\n\t\t\t\"Resource\": \"arn:*:iam::*:role/*\",\n\t\t\t\"Action\":
- \"iam:PassRole\",\n\t\t\t\"Condition\": {\n\t\t\t\t\"StringEquals\":
- {\n\t\t\t\t\t\"iam:PassedToService\": [\n\t\t\t\t\t\t\"ec2.amazonaws.com\",\n\t\t\t\t\t\t\"ec2.amazonaws.com.cn\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
- \"AllowScopedInstanceProfileCreationActions\",\n\t\t\t\"Effect\":
- \"Allow\",\n\t\t\t\"Resource\": \"arn:*:iam::*:instance-profile/*\",\n\t\t\t\"Action\":
- [\n\t\t\t\t\"iam:CreateInstanceProfile\"\n\t\t\t],\n\t\t\t\"Condition\":
- {\n\t\t\t\t\"StringLike\": {\n\t\t\t\t\t\"aws:RequestTag/karpenter.k8s.aws/ec2nodeclass\":
- \"*\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
- \"AllowScopedInstanceProfileTagActions\",\n\t\t\t\"Effect\":
- \"Allow\",\n\t\t\t\"Resource\": \"arn:*:iam::*:instance-profile/*\",\n\t\t\t\"Action\":
- [\n\t\t\t\t\"iam:TagInstanceProfile\"\n\t\t\t],\n\t\t\t\"Condition\":
- {\n\t\t\t\t\"StringLike\": {\n\t\t\t\t\t\"aws:ResourceTag/karpenter.k8s.aws/ec2nodeclass\":
- \"*\",\n\t\t\t\t\t\"aws:RequestTag/karpenter.k8s.aws/ec2nodeclass\":
- \"*\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
- \"AllowScopedInstanceProfileActions\",\n\t\t\t\"Effect\":
- \"Allow\",\n\t\t\t\"Resource\": \"arn:*:iam::*:instance-profile/*\",\n\t\t\t\"Action\":
- [\n\t\t\t\t\"iam:AddRoleToInstanceProfile\",\n\t\t\t\t\"iam:RemoveRoleFromInstanceProfile\",\n\t\t\t\t\"iam:DeleteInstanceProfile\"\n\t\t\t],\n\t\t\t\"Condition\":
- {\n\t\t\t\t\"StringLike\": {\n\t\t\t\t\t\"aws:ResourceTag/karpenter.k8s.aws/ec2nodeclass\":
- \"*\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
- \"AllowInstanceProfileReadActions\",\n\t\t\t\"Effect\":
- \"Allow\",\n\t\t\t\"Resource\": \"arn:*:iam::*:instance-profile/*\",\n\t\t\t\"Action\":
- \"iam:GetInstanceProfile\"\n\t\t},\n\t\t{\n\t\t\t\"Sid\":
- \"AllowUnscopedInstanceProfileListAction\",\n\t\t\t\"Effect\":
- \"Allow\",\n\t\t\t\"Resource\": \"*\",\n\t\t\t\"Action\":
- \"iam:ListInstanceProfiles\"\n\t\t}\n\t]\n}"
- maxLength: 2048
- type: string
- x-kubernetes-validations:
- - message: roleARN must be a valid AWS IAM role ARN
- (e.g. arn:aws:iam::123456789012:role/MyRole)
- rule: self.matches('^arn:(aws|aws-cn|aws-us-gov):iam::[0-9]{12}:role/.+$')
- required:
- - roleARN
- type: object
- platform:
- description: platform specifies the infrastructure platform
- that Karpenter should provision nodes on.
- enum:
- - AWS
- maxLength: 100
- type: string
- required:
- - platform
- type: object
- x-kubernetes-validations:
- - message: aws is required when platform is AWS, and forbidden
- otherwise
- rule: 'self.platform == ''AWS'' ? has(self.aws) : !has(self.aws)'
- name:
- description: name specifies the name of the provisioner to
- use for automatic node management.
- enum:
- - Karpenter
- type: string
- required:
- - name
- type: object
- x-kubernetes-validations:
- - message: karpenter is required when name is Karpenter, and forbidden
- otherwise
- rule: 'self.name == ''Karpenter'' ? has(self.karpenter) : !has(self.karpenter)'
- required:
- - provisionerConfig
- type: object
- autoscaling:
- description: |-
- autoscaling specifies auto-scaling behavior that applies to all NodePools
- associated with this HostedCluster.
- properties:
- balancingIgnoredLabels:
- description: |-
- balancingIgnoredLabels sets "--balancing-ignore-label
-autoNode
+autoNode,omitzero
AutoNode
@@ -40764,7 +40764,7 @@ associated with this HostedCluster.
|
-autoNode
+autoNode,omitzero
AutoNode
@@ -41762,7 +41762,7 @@ associated with the control plane.
|
-autoNode
+autoNode,omitzero
AutoNode
@@ -42959,7 +42959,7 @@ PlatformType
|
-aws
+aws,omitzero
KarpenterAWSConfig
@@ -47044,7 +47044,7 @@ Provisioner
|
-karpenter
+karpenter,omitzero
KarpenterConfig
diff --git a/docs/content/reference/api.md b/docs/content/reference/api.md
index d6a506f42a3b..e89303956e53 100644
--- a/docs/content/reference/api.md
+++ b/docs/content/reference/api.md
@@ -664,7 +664,7 @@ associated with this HostedCluster.
|
-autoNode
+autoNode,omitzero
AutoNode
@@ -8936,7 +8936,7 @@ associated with this HostedCluster.
|
-autoNode
+autoNode,omitzero
AutoNode
@@ -9934,7 +9934,7 @@ associated with the control plane.
|
-autoNode
+autoNode,omitzero
AutoNode
@@ -11131,7 +11131,7 @@ PlatformType
|
-aws
+aws,omitzero
KarpenterAWSConfig
@@ -15216,7 +15216,7 @@ Provisioner
|
-karpenter
+karpenter,omitzero
KarpenterConfig
diff --git a/hypershift-operator/controllers/hostedcluster/hostedcluster_controller.go b/hypershift-operator/controllers/hostedcluster/hostedcluster_controller.go
index 08b66c8b359b..130642cf19d9 100644
--- a/hypershift-operator/controllers/hostedcluster/hostedcluster_controller.go
+++ b/hypershift-operator/controllers/hostedcluster/hostedcluster_controller.go
@@ -30,7 +30,7 @@ import (
"time"
hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1"
- hyperkarpenterv1 "github.com/openshift/hypershift/api/karpenter/v1beta1"
+ hyperkarpenterv1 "github.com/openshift/hypershift/api/karpenter/v1"
"github.com/openshift/hypershift/api/util/configrefs"
"github.com/openshift/hypershift/cmd/util"
"github.com/openshift/hypershift/control-plane-operator/controllers/hostedcontrolplane/imageprovider"
diff --git a/hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go b/hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go
index 8922764adda8..52b4f990dd18 100644
--- a/hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go
+++ b/hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go
@@ -4667,12 +4667,12 @@ func TestReconcileComponents(t *testing.T) {
AWS: &hyperv1.AWSPlatformSpec{},
},
ReleaseImage: "quay.io/openshift-release-dev/ocp-release:4.16.10-x86_64",
- AutoNode: &hyperv1.AutoNode{
+ AutoNode: hyperv1.AutoNode{
Provisioner: hyperv1.ProvisionerConfig{
Name: "test-provisioner",
- Karpenter: &hyperv1.KarpenterConfig{
+ Karpenter: hyperv1.KarpenterConfig{
Platform: hyperv1.AWSPlatform,
- AWS: &hyperv1.KarpenterAWSConfig{
+ AWS: hyperv1.KarpenterAWSConfig{
RoleARN: "some-fake-arn",
},
},
diff --git a/hypershift-operator/controllers/hostedcluster/karpenter_test.go b/hypershift-operator/controllers/hostedcluster/karpenter_test.go
index 24ab777724d5..80140cadc0c7 100644
--- a/hypershift-operator/controllers/hostedcluster/karpenter_test.go
+++ b/hypershift-operator/controllers/hostedcluster/karpenter_test.go
@@ -23,10 +23,10 @@ import (
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
-var autoNode = &hyperv1.AutoNode{
+var autoNode = hyperv1.AutoNode{
Provisioner: hyperv1.ProvisionerConfig{
Name: hyperv1.ProvisionerKarpenter,
- Karpenter: &hyperv1.KarpenterConfig{
+ Karpenter: hyperv1.KarpenterConfig{
Platform: hyperv1.AWSPlatform,
},
},
@@ -252,10 +252,10 @@ func kasDeployment(namespace string, available bool) *appsv1.Deployment {
func TestReconcileAutoNodeEnabledCondition(t *testing.T) {
hcpNamespace := "clusters-test"
- karpenterEnabledAutoNode := &hyperv1.AutoNode{
+ karpenterEnabledAutoNode := hyperv1.AutoNode{
Provisioner: hyperv1.ProvisionerConfig{
Name: hyperv1.ProvisionerKarpenter,
- Karpenter: &hyperv1.KarpenterConfig{
+ Karpenter: hyperv1.KarpenterConfig{
Platform: hyperv1.AWSPlatform,
},
},
@@ -274,7 +274,7 @@ func TestReconcileAutoNodeEnabledCondition(t *testing.T) {
}
tests := map[string]struct {
- autoNode *hyperv1.AutoNode
+ autoNode hyperv1.AutoNode
components []hyperv1.ControlPlaneComponent
deployments []appsv1.Deployment
want metav1.Condition
@@ -339,7 +339,7 @@ func TestReconcileAutoNodeEnabledCondition(t *testing.T) {
},
},
"When karpenter is disabled and deployments are still present it should report progressing": {
- autoNode: nil,
+ autoNode: hyperv1.AutoNode{},
deployments: []appsv1.Deployment{
{ObjectMeta: metav1.ObjectMeta{Name: karpenterv2.ComponentName, Namespace: hcpNamespace}},
{ObjectMeta: metav1.ObjectMeta{Name: karpenteroperatorv2.ComponentName, Namespace: hcpNamespace}},
@@ -351,7 +351,7 @@ func TestReconcileAutoNodeEnabledCondition(t *testing.T) {
},
},
"When karpenter is disabled and only the karpenter deployment remains it should report progressing": {
- autoNode: nil,
+ autoNode: hyperv1.AutoNode{},
deployments: []appsv1.Deployment{
{ObjectMeta: metav1.ObjectMeta{Name: karpenterv2.ComponentName, Namespace: hcpNamespace}},
},
@@ -363,7 +363,7 @@ func TestReconcileAutoNodeEnabledCondition(t *testing.T) {
},
"When karpenter is disabled and CPC CRs remain but deployments are gone it should report not configured": {
// CPC CRs are deleted before pods terminate; once Deployments are gone teardown is complete.
- autoNode: nil,
+ autoNode: hyperv1.AutoNode{},
components: []hyperv1.ControlPlaneComponent{
{
ObjectMeta: metav1.ObjectMeta{Name: karpenteroperatorv2.ComponentName, Namespace: hcpNamespace},
@@ -378,7 +378,7 @@ func TestReconcileAutoNodeEnabledCondition(t *testing.T) {
},
},
"When karpenter is disabled and no deployments are present it should report not configured": {
- autoNode: nil,
+ autoNode: hyperv1.AutoNode{},
want: metav1.Condition{
Type: string(hyperv1.AutoNodeEnabled),
Status: metav1.ConditionFalse,
diff --git a/karpenter-operator/controllers/karpenter/assets/karpenter.hypershift.openshift.io_openshiftec2nodeclasses.yaml b/karpenter-operator/controllers/karpenter/assets/karpenter.hypershift.openshift.io_openshiftec2nodeclasses.yaml
index af31cd9bb70d..90ad3d615090 100644
--- a/karpenter-operator/controllers/karpenter/assets/karpenter.hypershift.openshift.io_openshiftec2nodeclasses.yaml
+++ b/karpenter-operator/controllers/karpenter/assets/karpenter.hypershift.openshift.io_openshiftec2nodeclasses.yaml
@@ -21,7 +21,7 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
- name: v1beta1
+ name: v1
schema:
openAPIV3Schema:
description: OpenshiftEC2NodeClass defines the desired state of OpenshiftEC2NodeClass.
@@ -535,6 +535,7 @@ spec:
type: array
x-kubernetes-list-type: atomic
conditions:
+ description: conditions contains signals for health and readiness.
items:
description: Condition contains details for one aspect of the current
state of this API Resource.
diff --git a/karpenter-operator/controllers/karpenter/assets/tests/openshiftec2nodeclasses.karpenter.hypershift.openshift.io/stable.openshiftec2nodeclasses.testsuite.yaml b/karpenter-operator/controllers/karpenter/assets/tests/openshiftec2nodeclasses.karpenter.hypershift.openshift.io/stable.openshiftec2nodeclasses.testsuite.yaml
new file mode 100644
index 000000000000..c55dee270256
--- /dev/null
+++ b/karpenter-operator/controllers/karpenter/assets/tests/openshiftec2nodeclasses.karpenter.hypershift.openshift.io/stable.openshiftec2nodeclasses.testsuite.yaml
@@ -0,0 +1,248 @@
+apiVersion: apiextensions.k8s.io/v1
+name: "OpenshiftEC2NodeClass validation"
+crdName: openshiftec2nodeclasses.karpenter.hypershift.openshift.io
+version: v1
+tests:
+ onCreate:
+ # --- SubnetSelectorTerms validation ---
+ - name: When subnetSelectorTerms is empty it should fail
+ initial: |
+ apiVersion: karpenter.hypershift.openshift.io/v1
+ kind: OpenshiftEC2NodeClass
+ spec:
+ subnetSelectorTerms: []
+ securityGroupSelectorTerms:
+ - id: sg-0123456789abcdef0
+ expectedError: "subnetSelectorTerms cannot be empty"
+
+ - name: When subnet term has neither tags nor id it should fail
+ initial: |
+ apiVersion: karpenter.hypershift.openshift.io/v1
+ kind: OpenshiftEC2NodeClass
+ spec:
+ subnetSelectorTerms:
+ - {}
+ securityGroupSelectorTerms:
+ - id: sg-0123456789abcdef0
+ expectedError: "expected at least one, got none, ['tags', 'id']"
+
+ - name: When subnet id has invalid format it should fail
+ initial: |
+ apiVersion: karpenter.hypershift.openshift.io/v1
+ kind: OpenshiftEC2NodeClass
+ spec:
+ subnetSelectorTerms:
+ - id: invalid-subnet
+ securityGroupSelectorTerms:
+ - id: sg-0123456789abcdef0
+ expectedError: "id must match the pattern subnet-"
+
+ - name: When subnet id and tags are both set it should fail
+ initial: |
+ apiVersion: karpenter.hypershift.openshift.io/v1
+ kind: OpenshiftEC2NodeClass
+ spec:
+ subnetSelectorTerms:
+ - id: subnet-0123456789abcdef0
+ tags:
+ env: test
+ securityGroupSelectorTerms:
+ - id: sg-0123456789abcdef0
+ expectedError: "'id' is mutually exclusive"
+
+ - name: When subnet has valid tags it should pass
+ initial: |
+ apiVersion: karpenter.hypershift.openshift.io/v1
+ kind: OpenshiftEC2NodeClass
+ spec:
+ subnetSelectorTerms:
+ - tags:
+ karpenter.sh/discovery: my-cluster
+ securityGroupSelectorTerms:
+ - id: sg-0123456789abcdef0
+
+ # --- SecurityGroupSelectorTerms validation ---
+ - name: When securityGroupSelectorTerms is empty it should fail
+ initial: |
+ apiVersion: karpenter.hypershift.openshift.io/v1
+ kind: OpenshiftEC2NodeClass
+ spec:
+ subnetSelectorTerms:
+ - tags:
+ env: test
+ securityGroupSelectorTerms: []
+ expectedError: "securityGroupSelectorTerms cannot be empty"
+
+ - name: When security group id has invalid format it should fail
+ initial: |
+ apiVersion: karpenter.hypershift.openshift.io/v1
+ kind: OpenshiftEC2NodeClass
+ spec:
+ subnetSelectorTerms:
+ - tags:
+ env: test
+ securityGroupSelectorTerms:
+ - id: invalid-sg
+ expectedError: "id must match the pattern sg-"
+
+ - name: When security group has valid id it should pass
+ initial: |
+ apiVersion: karpenter.hypershift.openshift.io/v1
+ kind: OpenshiftEC2NodeClass
+ spec:
+ subnetSelectorTerms:
+ - tags:
+ env: test
+ securityGroupSelectorTerms:
+ - id: sg-0123456789abcdef0
+
+ # --- Tags validation ---
+ - name: When tags contain restricted kubernetes.io/cluster/ key it should fail
+ initial: |
+ apiVersion: karpenter.hypershift.openshift.io/v1
+ kind: OpenshiftEC2NodeClass
+ spec:
+ subnetSelectorTerms:
+ - tags:
+ env: test
+ securityGroupSelectorTerms:
+ - id: sg-0123456789abcdef0
+ tags:
+ kubernetes.io/cluster/my-cluster: owned
+ expectedError: "tag contains a restricted tag matching kubernetes.io/cluster/"
+
+ - name: When tags contain restricted karpenter.sh/nodepool key it should fail
+ initial: |
+ apiVersion: karpenter.hypershift.openshift.io/v1
+ kind: OpenshiftEC2NodeClass
+ spec:
+ subnetSelectorTerms:
+ - tags:
+ env: test
+ securityGroupSelectorTerms:
+ - id: sg-0123456789abcdef0
+ tags:
+ karpenter.sh/nodepool: my-pool
+ expectedError: "tag contains a restricted tag matching karpenter.sh/nodepool"
+
+ - name: When tags have valid keys it should pass
+ initial: |
+ apiVersion: karpenter.hypershift.openshift.io/v1
+ kind: OpenshiftEC2NodeClass
+ spec:
+ subnetSelectorTerms:
+ - tags:
+ env: test
+ securityGroupSelectorTerms:
+ - id: sg-0123456789abcdef0
+ tags:
+ team: platform
+ environment: staging
+
+ # --- BlockDeviceMappings validation ---
+ - name: When multiple blockDeviceMappings have rootVolume it should fail
+ initial: |
+ apiVersion: karpenter.hypershift.openshift.io/v1
+ kind: OpenshiftEC2NodeClass
+ spec:
+ subnetSelectorTerms:
+ - tags:
+ env: test
+ securityGroupSelectorTerms:
+ - id: sg-0123456789abcdef0
+ blockDeviceMappings:
+ - deviceName: /dev/xvda
+ rootVolume: RootVolume
+ ebs:
+ volumeSizeGiB: 120
+ - deviceName: /dev/xvdb
+ rootVolume: RootVolume
+ ebs:
+ volumeSizeGiB: 50
+ expectedError: "must have only one blockDeviceMappings with rootVolume"
+
+ - name: When EBS has neither snapshotID nor volumeSizeGiB it should fail
+ initial: |
+ apiVersion: karpenter.hypershift.openshift.io/v1
+ kind: OpenshiftEC2NodeClass
+ spec:
+ subnetSelectorTerms:
+ - tags:
+ env: test
+ securityGroupSelectorTerms:
+ - id: sg-0123456789abcdef0
+ blockDeviceMappings:
+ - deviceName: /dev/xvda
+ ebs:
+ volumeType: GP3
+ expectedError: "snapshotID or volumeSizeGiB must be defined"
+
+ # --- Version validation ---
+ - name: When version is invalid format it should fail
+ initial: |
+ apiVersion: karpenter.hypershift.openshift.io/v1
+ kind: OpenshiftEC2NodeClass
+ spec:
+ subnetSelectorTerms:
+ - tags:
+ env: test
+ securityGroupSelectorTerms:
+ - id: sg-0123456789abcdef0
+ version: "invalid"
+ expectedError: "version must be a valid semantic version"
+
+ - name: When version is valid it should pass
+ initial: |
+ apiVersion: karpenter.hypershift.openshift.io/v1
+ kind: OpenshiftEC2NodeClass
+ spec:
+ subnetSelectorTerms:
+ - tags:
+ env: test
+ securityGroupSelectorTerms:
+ - id: sg-0123456789abcdef0
+ version: "4.20.1"
+
+ # --- CapacityReservationSelectorTerms validation ---
+ - name: When capacity reservation id has invalid format it should fail
+ initial: |
+ apiVersion: karpenter.hypershift.openshift.io/v1
+ kind: OpenshiftEC2NodeClass
+ spec:
+ subnetSelectorTerms:
+ - tags:
+ env: test
+ securityGroupSelectorTerms:
+ - id: sg-0123456789abcdef0
+ capacityReservationSelectorTerms:
+ - id: invalid-cr
+ expectedError: "id must match the pattern cr-"
+
+ - name: When capacity reservation ownerID is invalid it should fail
+ initial: |
+ apiVersion: karpenter.hypershift.openshift.io/v1
+ kind: OpenshiftEC2NodeClass
+ spec:
+ subnetSelectorTerms:
+ - tags:
+ env: test
+ securityGroupSelectorTerms:
+ - id: sg-0123456789abcdef0
+ capacityReservationSelectorTerms:
+ - ownerID: "abc"
+ expectedError: "ownerID must be a 12-digit AWS account ID"
+
+ # --- MetadataOptions validation ---
+ - name: When metadata httpPutResponseHopLimit exceeds max it should fail
+ initial: |
+ apiVersion: karpenter.hypershift.openshift.io/v1
+ kind: OpenshiftEC2NodeClass
+ spec:
+ subnetSelectorTerms:
+ - tags:
+ env: test
+ securityGroupSelectorTerms:
+ - id: sg-0123456789abcdef0
+ metadataOptions:
+ httpPutResponseHopLimit: 100
+ expectedError: "httpPutResponseHopLimit"
diff --git a/karpenter-operator/controllers/karpenter/assets/zz_generated.crd-manifests/openshiftec2nodeclasses.crd.yaml b/karpenter-operator/controllers/karpenter/assets/zz_generated.crd-manifests/openshiftec2nodeclasses.crd.yaml
new file mode 100644
index 000000000000..90ad3d615090
--- /dev/null
+++ b/karpenter-operator/controllers/karpenter/assets/zz_generated.crd-manifests/openshiftec2nodeclasses.crd.yaml
@@ -0,0 +1,676 @@
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.17.0
+ name: openshiftec2nodeclasses.karpenter.hypershift.openshift.io
+spec:
+ group: karpenter.hypershift.openshift.io
+ names:
+ kind: OpenshiftEC2NodeClass
+ listKind: OpenshiftEC2NodeClassList
+ plural: openshiftec2nodeclasses
+ shortNames:
+ - oec2nc
+ - oec2ncs
+ singular: openshiftec2nodeclass
+ scope: Cluster
+ versions:
+ - additionalPrinterColumns:
+ - jsonPath: .status.conditions[?(@.type=="Ready")].status
+ name: Ready
+ type: string
+ name: v1
+ schema:
+ openAPIV3Schema:
+ description: OpenshiftEC2NodeClass defines the desired state of OpenshiftEC2NodeClass.
+ properties:
+ apiVersion:
+ description: |-
+ APIVersion defines the versioned schema of this representation of an object.
+ Servers should convert recognized schemas to the latest internal value, and
+ may reject unrecognized values.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+ type: string
+ kind:
+ description: |-
+ Kind is a string value representing the REST resource this object represents.
+ Servers may infer this from the endpoint the client submits requests to.
+ Cannot be updated.
+ In CamelCase.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+ type: string
+ metadata:
+ type: object
+ spec:
+ description: spec defines the desired state of the OpenshiftEC2NodeClass.
+ minProperties: 1
+ properties:
+ blockDeviceMappings:
+ description: |-
+ blockDeviceMappings to be applied to provisioned nodes.
+ For OpenShift nodes, a default root volume size of 120Gi and type gp3 is set if no blockDeviceMapping overrides are specified.
+ items:
+ description: BlockDeviceMapping defines a block device mapping for
+ a node.
+ minProperties: 1
+ properties:
+ deviceName:
+ description: deviceName is the device name (for example, /dev/sdh
+ or xvdh).
+ maxLength: 256
+ minLength: 1
+ type: string
+ ebs:
+ description: ebs contains parameters used to automatically set
+ up EBS volumes when an instance is launched.
+ minProperties: 1
+ properties:
+ deleteOnTermination:
+ description: deleteOnTermination indicates whether the EBS
+ volume is deleted on instance termination.
+ enum:
+ - Delete
+ - Retain
+ type: string
+ encrypted:
+ description: |-
+ encrypted indicates whether the EBS volume is encrypted. Encrypted volumes can only
+ be attached to instances that support Amazon EBS encryption. If you are creating
+ a volume from a snapshot, you can't specify an encryption value.
+ enum:
+ - Encrypted
+ - Unencrypted
+ type: string
+ iops:
+ description: |-
+ iops is the number of I/O operations per second (IOPS). For gp3, io1, and io2 volumes,
+ this represents the number of IOPS that are provisioned for the volume. For
+ gp2 volumes, this represents the baseline performance of the volume and the
+ rate at which the volume accumulates I/O credits for bursting.
+
+ The following are the supported values for each volume type:
+
+ * gp3: 3,000-16,000 IOPS
+
+ * io1: 100-64,000 IOPS
+
+ * io2: 100-64,000 IOPS
+
+ For io1 and io2 volumes, we guarantee 64,000 IOPS only for Instances built
+ on the Nitro System (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html#ec2-nitro-instances).
+ Other instance families guarantee performance up to 32,000 IOPS.
+
+ This parameter is supported for io1, io2, and gp3 volumes only. This parameter
+ is not supported for gp2, st1, sc1, or standard volumes.
+ format: int64
+ type: integer
+ kmsKeyID:
+ description: kmsKeyID is the ARN of the symmetric Key Management
+ Service (KMS) CMK used for encryption.
+ maxLength: 2048
+ minLength: 1
+ type: string
+ snapshotID:
+ description: snapshotID is the ID of an EBS snapshot.
+ maxLength: 256
+ minLength: 1
+ type: string
+ throughput:
+ description: |-
+ throughput to provision for a gp3 volume, with a maximum of 1,000 MiB/s.
+ Valid Range: Minimum value of 125. Maximum value of 1000.
+ format: int64
+ type: integer
+ volumeSizeGiB:
+ description: |-
+ volumeSizeGiB is the size of the volume in GiB. You must specify either a snapshot ID or
+ a volume size. The following are the supported volume sizes for each volume type:
+
+ * gp2 and gp3: 1-16,384
+
+ * io1 and io2: 4-16,384
+
+ * st1 and sc1: 125-16,384
+
+ * standard: 1-1,024
+ format: int64
+ maximum: 65536
+ minimum: 1
+ type: integer
+ volumeType:
+ description: |-
+ volumeType is the type of the block device.
+ For more information, see Amazon EBS volume types (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html)
+ in the Amazon Elastic Compute Cloud User Guide.
+ enum:
+ - Standard
+ - IO1
+ - IO2
+ - GP2
+ - SC1
+ - ST1
+ - GP3
+ type: string
+ type: object
+ x-kubernetes-validations:
+ - message: snapshotID or volumeSizeGiB must be defined
+ rule: has(self.snapshotID) || has(self.volumeSizeGiB)
+ rootVolume:
+ description: |-
+ rootVolume indicates whether this device is mounted as kubelet root dir. You can
+ configure at most one root volume in BlockDeviceMappings.
+ enum:
+ - RootVolume
+ - NotRootVolume
+ type: string
+ type: object
+ maxItems: 50
+ minItems: 1
+ type: array
+ x-kubernetes-list-type: atomic
+ x-kubernetes-validations:
+ - message: must have only one blockDeviceMappings with rootVolume
+ rule: self.filter(x, has(x.rootVolume)?x.rootVolume=='RootVolume':false).size()
+ <= 1
+ capacityReservationSelectorTerms:
+ description: |-
+ capacityReservationSelectorTerms is a list of capacity reservation selector terms. The terms are ORed.
+ Each term must specify at least one of: tags, id, ownerID, or instanceMatchCriteria.
+ The id field is mutually exclusive and cannot be combined with tags, ownerID, or instanceMatchCriteria
+ within the same term.
+ items:
+ description: |-
+ CapacityReservationSelectorTerm defines selection logic for a capacity reservation used by Karpenter to launch nodes.
+ If multiple fields are used for selection, the requirements are ANDed.
+ minProperties: 1
+ properties:
+ id:
+ description: id is the capacity reservation id in EC2.
+ maxLength: 256
+ minLength: 1
+ type: string
+ x-kubernetes-validations:
+ - message: id must match the pattern cr-[0-9a-z]+
+ rule: self.matches('^cr-[0-9a-z]+$')
+ instanceMatchCriteria:
+ description: |-
+ instanceMatchCriteria filters capacity reservations by how they match instances.
+ When set to "Open", only selects reservations that any instance with matching
+ attributes (instance type, platform, Availability Zone) can use automatically.
+ When set to "Targeted", only selects reservations that require instances to
+ explicitly target them by ID.
+ When omitted, selects both Open and Targeted reservations (no filtering on this attribute).
+ enum:
+ - Open
+ - Targeted
+ type: string
+ ownerID:
+ description: ownerID is the owner account id for the capacity
+ reservation.
+ maxLength: 12
+ minLength: 1
+ type: string
+ x-kubernetes-validations:
+ - message: ownerID must be a 12-digit AWS account ID
+ rule: self.matches('^[0-9]{12}$')
+ tags:
+ additionalProperties:
+ type: string
+ description: |-
+ tags is a map of key/value tags used to select capacity reservations.
+ Specifying '*' for a value selects all values for a given tag key.
+ maxProperties: 20
+ minProperties: 1
+ type: object
+ x-kubernetes-validations:
+ - message: empty tag keys or values aren't supported
+ rule: self.all(k, k != '' && self[k] != '')
+ type: object
+ maxItems: 30
+ minItems: 1
+ type: array
+ x-kubernetes-list-type: atomic
+ x-kubernetes-validations:
+ - message: expected at least one, got none, ['tags', 'id', 'ownerID',
+ 'instanceMatchCriteria']
+ rule: self.all(x, has(x.tags) || has(x.id) || has(x.ownerID) ||
+ has(x.instanceMatchCriteria))
+ - message: '''id'' is mutually exclusive, cannot be set along with
+ other fields in a capacity reservation selector term'
+ rule: '!self.all(x, has(x.id) && (has(x.tags) || has(x.ownerID)
+ || has(x.instanceMatchCriteria)))'
+ instanceStorePolicy:
+ description: instanceStorePolicy specifies how to handle instance-store
+ disks.
+ enum:
+ - RAID0
+ type: string
+ ipAddressAssociation:
+ description: |-
+ ipAddressAssociation controls the IP address assignment for instances launched with the nodeclass.
+ Valid values are:
+ - "Public": assigns public IP addresses to instances, allowing direct internet access.
+ - "SubnetDefault": defers IP address assignment to the subnet configuration.
+ When unset, the cloud provider's subnet default behavior is used.
+ enum:
+ - Public
+ - SubnetDefault
+ type: string
+ metadataOptions:
+ description: |-
+ metadataOptions contains parameters for specifying the exposure of the
+ Instance Metadata Service to provisioned EC2 nodes.
+ When omitted, the platform preserves control over default behaviour
+ which is subject to change over time. Currently defaults to access
+ HTTPEndpoint, httpIPProtocol IPv4, httpPutResponseHopLimit of 1,
+ and httpTokens Required (IMDSv2).
+ minProperties: 1
+ properties:
+ access:
+ description: |-
+ access specifies how the instance metadata endpoint is accessed on
+ provisioned nodes. Valid values are "HTTPEndpoint" and "None".
+ When set to "HTTPEndpoint", the instance metadata service is accessible
+ via the HTTP endpoint. When set to "None", instance metadata is not
+ accessible on the node.
+ When omitted, the platform defaults to HTTP endpoint access.
+ enum:
+ - HTTPEndpoint
+ - None
+ type: string
+ httpIPProtocol:
+ description: |-
+ httpIPProtocol specifies the IP protocol version for the instance metadata
+ service endpoint on provisioned nodes. Valid values are "IPv4" and "IPv6".
+ When set to "IPv4", the metadata endpoint uses the IPv4 protocol.
+ When set to "IPv6", the metadata endpoint uses the IPv6 protocol.
+ When omitted, the platform defaults to IPv4.
+ enum:
+ - IPv4
+ - IPv6
+ type: string
+ httpPutResponseHopLimit:
+ description: |-
+ httpPutResponseHopLimit is the desired HTTP PUT response hop limit for
+ instance metadata requests. Possible values are integers from 1 to 64.
+ A value of 1 restricts IMDS access to the EC2 instance only, preventing
+ pods from reaching it. A value of 2 allows pods to access IMDS through
+ the container network bridge. Values above 2 are for advanced networking
+ scenarios such as nested virtualization.
+ When omitted, the platform defaults to a hop limit of 1.
+ format: int64
+ maximum: 64
+ minimum: 1
+ type: integer
+ httpTokens:
+ description: |-
+ httpTokens determines the state of token usage for instance metadata
+ requests. Valid values are "Required" and "Optional".
+ When set to "Required", a signed token header (IMDSv2) must be used for
+ all metadata requests. When set to "Optional", metadata can be retrieved
+ with or without a token (IMDSv1/v2).
+ When omitted, the platform defaults to requiring tokens (IMDSv2).
+ enum:
+ - Required
+ - Optional
+ type: string
+ type: object
+ monitoring:
+ description: |-
+ monitoring controls the monitoring level for instances that are launched.
+ For more information, see Basic monitoring and detailed monitoring
+ (https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch-metrics-basic-detailed.html).
+ Valid values are:
+ - "Detailed": enables detailed monitoring.
+ - "Basic": enables basic monitoring.
+ When unset, the cloud provider's default basic monitoring behavior is used.
+ enum:
+ - Detailed
+ - Basic
+ type: string
+ securityGroupSelectorTerms:
+ description: securityGroupSelectorTerms is a list of or security group
+ selector terms. The terms are ORed.
+ items:
+ description: |-
+ SecurityGroupSelectorTerm defines selection logic for a security group used by Karpenter to launch nodes.
+ If multiple fields are used for selection, the requirements are ANDed.
+ minProperties: 1
+ properties:
+ id:
+ description: |-
+ id is the security group id in EC2.
+ The expected format is "sg-" followed by alphanumeric characters, e.g. "sg-0a1b2c3d4e5f".
+ maxLength: 256
+ minLength: 1
+ type: string
+ x-kubernetes-validations:
+ - message: id must match the pattern sg-[0-9a-z]+
+ rule: self.matches('^sg-[0-9a-z]+$')
+ name:
+ description: |-
+ name is the security group name in EC2.
+ This value is the name field, which is different from the name tag.
+ maxLength: 256
+ minLength: 1
+ type: string
+ tags:
+ additionalProperties:
+ type: string
+ description: |-
+ tags is a map of key/value tags used to select security groups.
+ Specifying '*' for a value selects all values for a given tag key.
+ The expected format is {"key1": "value1", "key2": "*"}.
+ maxProperties: 20
+ minProperties: 1
+ type: object
+ x-kubernetes-validations:
+ - message: empty tag keys or values aren't supported
+ rule: self.all(k, k != '' && self[k] != '')
+ type: object
+ maxItems: 30
+ minItems: 1
+ type: array
+ x-kubernetes-list-type: atomic
+ x-kubernetes-validations:
+ - message: securityGroupSelectorTerms cannot be empty
+ rule: self.size() != 0
+ - message: expected at least one, got none, ['tags', 'id', 'name']
+ rule: self.all(x, has(x.tags) || has(x.id) || has(x.name))
+ - message: '''id'' is mutually exclusive, cannot be set with a combination
+ of other fields in securityGroupSelectorTerms'
+ rule: '!self.all(x, has(x.id) && (has(x.tags) || has(x.name)))'
+ - message: '''name'' is mutually exclusive, cannot be set with a combination
+ of other fields in securityGroupSelectorTerms'
+ rule: '!self.all(x, has(x.name) && (has(x.tags) || has(x.id)))'
+ subnetSelectorTerms:
+ description: subnetSelectorTerms is a list of or subnet selector terms.
+ The terms are ORed.
+ items:
+ description: |-
+ SubnetSelectorTerm defines selection logic for a subnet used by Karpenter to launch nodes.
+ If multiple fields are used for selection, the requirements are ANDed.
+ minProperties: 1
+ properties:
+ id:
+ description: |-
+ id is the subnet id in EC2.
+ The expected format is "subnet-" followed by alphanumeric characters, e.g. "subnet-0a1b2c3d4e5f".
+ maxLength: 256
+ minLength: 1
+ type: string
+ x-kubernetes-validations:
+ - message: id must match the pattern subnet-[0-9a-z]+
+ rule: self.matches('^subnet-[0-9a-z]+$')
+ tags:
+ additionalProperties:
+ type: string
+ description: |-
+ tags is a map of key/value tags used to select subnets.
+ Specifying '*' for a value selects all values for a given tag key.
+ The expected format is {"key1": "value1", "key2": "*"}.
+ maxProperties: 20
+ minProperties: 1
+ type: object
+ x-kubernetes-validations:
+ - message: empty tag keys or values aren't supported
+ rule: self.all(k, k != '' && self[k] != '')
+ type: object
+ maxItems: 30
+ minItems: 1
+ type: array
+ x-kubernetes-list-type: atomic
+ x-kubernetes-validations:
+ - message: subnetSelectorTerms cannot be empty
+ rule: self.size() != 0
+ - message: expected at least one, got none, ['tags', 'id']
+ rule: self.all(x, has(x.tags) || has(x.id))
+ - message: '''id'' is mutually exclusive, cannot be set with a combination
+ of other fields in subnetSelectorTerms'
+ rule: '!self.all(x, has(x.id) && has(x.tags))'
+ tags:
+ additionalProperties:
+ type: string
+ description: tags to be applied on ec2 resources like instances and
+ launch templates.
+ minProperties: 1
+ type: object
+ x-kubernetes-validations:
+ - message: empty tag keys aren't supported
+ rule: self.all(k, k != '')
+ - message: tag contains a restricted tag matching eks:eks-cluster-name
+ rule: self.all(k, k !='eks:eks-cluster-name')
+ - message: tag contains a restricted tag matching kubernetes.io/cluster/
+ rule: self.all(k, !k.startsWith('kubernetes.io/cluster') )
+ - message: tag contains a restricted tag matching karpenter.sh/nodepool
+ rule: self.all(k, k != 'karpenter.sh/nodepool')
+ - message: tag contains a restricted tag matching karpenter.sh/nodeclaim
+ rule: self.all(k, k !='karpenter.sh/nodeclaim')
+ - message: tag contains a restricted tag matching karpenter.k8s.aws/ec2nodeclass
+ rule: self.all(k, k !='karpenter.k8s.aws/ec2nodeclass')
+ version:
+ description: |-
+ version is an OpenShift version (e.g., "4.20.1") specifying the release version
+ for nodes managed by this NodeClass. When set, the controller resolves this to a
+ release image via the Cincinnati graph API. When not set, nodes use the control plane's
+ release image.
+ maxLength: 64
+ minLength: 5
+ type: string
+ x-kubernetes-validations:
+ - message: version must be a valid semantic version (e.g., 4.20.1)
+ rule: self.matches('^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$')
+ type: object
+ status:
+ description: status defines the observed state of the OpenshiftEC2NodeClass.
+ minProperties: 1
+ properties:
+ capacityReservations:
+ description: |-
+ capacityReservations contains the current Capacity Reservation values that are available to the
+ cluster under the capacityReservationSelectorTerms.
+ items:
+ description: CapacityReservation contains resolved CapacityReservation
+ selector values utilized for node launch
+ properties:
+ availabilityZone:
+ description: availabilityZone is the availability zone the capacity
+ reservation is available in.
+ maxLength: 64
+ minLength: 1
+ type: string
+ endTime:
+ description: |-
+ endTime is the time at which the capacity reservation expires. Once expired, the reserved capacity is released and Karpenter
+ will no longer be able to launch instances into that reservation.
+ format: date-time
+ type: string
+ id:
+ description: id is the id for the capacity reservation.
+ maxLength: 256
+ minLength: 1
+ type: string
+ instanceMatchCriteria:
+ description: instanceMatchCriteria indicates the type of instance
+ launches the capacity reservation accepts.
+ enum:
+ - Open
+ - Targeted
+ type: string
+ instanceType:
+ description: instanceType is the instance type for the capacity
+ reservation.
+ maxLength: 256
+ minLength: 1
+ type: string
+ ownerID:
+ description: ownerID is the ID of the AWS account that owns
+ the capacity reservation.
+ maxLength: 12
+ minLength: 1
+ type: string
+ reservationType:
+ description: reservationType is the type of capacity reservation.
+ enum:
+ - Default
+ - CapacityBlock
+ type: string
+ state:
+ description: state is the state of the capacity reservation.
+ enum:
+ - Active
+ - Expiring
+ type: string
+ required:
+ - availabilityZone
+ - id
+ - instanceMatchCriteria
+ - instanceType
+ - ownerID
+ type: object
+ maxItems: 100
+ minItems: 1
+ type: array
+ x-kubernetes-list-type: atomic
+ conditions:
+ description: conditions contains signals for health and readiness.
+ items:
+ description: Condition contains details for one aspect of the current
+ state of this API Resource.
+ properties:
+ lastTransitionTime:
+ description: |-
+ lastTransitionTime is the last time the condition transitioned from one status to another.
+ This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
+ format: date-time
+ type: string
+ message:
+ description: |-
+ message is a human readable message indicating details about the transition.
+ This may be an empty string.
+ maxLength: 32768
+ type: string
+ observedGeneration:
+ description: |-
+ observedGeneration represents the .metadata.generation that the condition was set based upon.
+ For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
+ with respect to the current state of the instance.
+ format: int64
+ minimum: 0
+ type: integer
+ reason:
+ description: |-
+ reason contains a programmatic identifier indicating the reason for the condition's last transition.
+ Producers of specific condition types may define expected values and meanings for this field,
+ and whether the values are considered a guaranteed API.
+ The value should be a CamelCase string.
+ This field may not be empty.
+ maxLength: 1024
+ minLength: 1
+ pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+ type: string
+ status:
+ description: status of the condition, one of True, False, Unknown.
+ enum:
+ - "True"
+ - "False"
+ - Unknown
+ type: string
+ type:
+ description: type of condition in CamelCase or in foo.example.com/CamelCase.
+ maxLength: 316
+ pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+ type: string
+ required:
+ - lastTransitionTime
+ - message
+ - reason
+ - status
+ - type
+ type: object
+ maxItems: 100
+ minItems: 1
+ type: array
+ x-kubernetes-list-map-keys:
+ - type
+ x-kubernetes-list-type: map
+ releaseImage:
+ description: |-
+ releaseImage is the fully qualified release image resolved either from spec.version, or inherited from
+ the HostedControlPlane's spec.ReleaseImage.
+ Of the format "quay.io/openshift-release-dev/ocp-release@sha256:".
+ maxLength: 512
+ minLength: 1
+ type: string
+ securityGroups:
+ description: |-
+ securityGroups contains the current Security Groups values that are available to the
+ cluster under the SecurityGroups selectors.
+ items:
+ description: SecurityGroup contains resolved SecurityGroup selector
+ values utilized for node launch
+ properties:
+ id:
+ description: id is the ID of the security group.
+ maxLength: 256
+ minLength: 1
+ type: string
+ name:
+ description: name is the name of the security group.
+ maxLength: 256
+ minLength: 1
+ type: string
+ required:
+ - id
+ type: object
+ maxItems: 100
+ minItems: 1
+ type: array
+ x-kubernetes-list-type: atomic
+ subnets:
+ description: |-
+ subnets contains the current Subnet values that are available to the
+ cluster under the subnet selectors.
+ items:
+ description: Subnet contains resolved Subnet selector values utilized
+ for node launch
+ properties:
+ id:
+ description: id is the ID of the subnet.
+ maxLength: 256
+ minLength: 1
+ type: string
+ zone:
+ description: zone is the associated availability zone.
+ maxLength: 256
+ minLength: 1
+ type: string
+ zoneID:
+ description: zoneID is the associated availability zone ID.
+ maxLength: 256
+ minLength: 1
+ type: string
+ required:
+ - id
+ - zone
+ type: object
+ maxItems: 100
+ minItems: 1
+ type: array
+ x-kubernetes-list-type: atomic
+ version:
+ description: version is the resolved OpenShift version corresponding
+ to the status.releaseImage.
+ maxLength: 64
+ minLength: 1
+ type: string
+ type: object
+ required:
+ - spec
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
diff --git a/karpenter-operator/controllers/karpenter/karpenter_controller.go b/karpenter-operator/controllers/karpenter/karpenter_controller.go
index 2733875aac5b..32c34a61d1ac 100644
--- a/karpenter-operator/controllers/karpenter/karpenter_controller.go
+++ b/karpenter-operator/controllers/karpenter/karpenter_controller.go
@@ -8,7 +8,7 @@ import (
"time"
hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1"
- hyperkarpenterv1 "github.com/openshift/hypershift/api/karpenter/v1beta1"
+ hyperkarpenterv1 "github.com/openshift/hypershift/api/karpenter/v1"
hypershiftv1beta1applyconfigurations "github.com/openshift/hypershift/client/applyconfiguration/hypershift/v1beta1"
hypershiftclient "github.com/openshift/hypershift/client/clientset/clientset"
"github.com/openshift/hypershift/control-plane-operator/controllers/hostedcontrolplane/common"
diff --git a/karpenter-operator/controllers/karpenterignition/karpenterignition_controller.go b/karpenter-operator/controllers/karpenterignition/karpenterignition_controller.go
index 282aa2327d71..318b720ea9a7 100644
--- a/karpenter-operator/controllers/karpenterignition/karpenterignition_controller.go
+++ b/karpenter-operator/controllers/karpenterignition/karpenterignition_controller.go
@@ -7,7 +7,7 @@ import (
"time"
hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1"
- hyperkarpenterv1 "github.com/openshift/hypershift/api/karpenter/v1beta1"
+ hyperkarpenterv1 "github.com/openshift/hypershift/api/karpenter/v1"
"github.com/openshift/hypershift/control-plane-operator/controllers/hostedcontrolplane/common"
"github.com/openshift/hypershift/hypershift-operator/controllers/nodepool"
haproxy "github.com/openshift/hypershift/hypershift-operator/controllers/nodepool/apiserver-haproxy"
diff --git a/karpenter-operator/controllers/karpenterignition/karpenterignition_controller_test.go b/karpenter-operator/controllers/karpenterignition/karpenterignition_controller_test.go
index 39333c1eecbd..fe5e6711d40f 100644
--- a/karpenter-operator/controllers/karpenterignition/karpenterignition_controller_test.go
+++ b/karpenter-operator/controllers/karpenterignition/karpenterignition_controller_test.go
@@ -10,7 +10,7 @@ import (
. "github.com/onsi/gomega"
hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1"
- hyperkarpenterv1 "github.com/openshift/hypershift/api/karpenter/v1beta1"
+ hyperkarpenterv1 "github.com/openshift/hypershift/api/karpenter/v1"
"github.com/openshift/hypershift/api/util/ipnet"
"github.com/openshift/hypershift/support/api"
karpenterutil "github.com/openshift/hypershift/support/karpenter"
@@ -99,10 +99,10 @@ func TestReconcile(t *testing.T) {
PullSecret: corev1.LocalObjectReference{
Name: "pull-secret",
},
- AutoNode: &hyperv1.AutoNode{
+ AutoNode: hyperv1.AutoNode{
Provisioner: hyperv1.ProvisionerConfig{
Name: hyperv1.ProvisionerKarpenter,
- Karpenter: &hyperv1.KarpenterConfig{
+ Karpenter: hyperv1.KarpenterConfig{
Platform: hyperv1.AWSPlatform,
},
},
@@ -414,10 +414,10 @@ func TestReconcileVersionResolution(t *testing.T) {
PullSecret: corev1.LocalObjectReference{
Name: "pull-secret",
},
- AutoNode: &hyperv1.AutoNode{
+ AutoNode: hyperv1.AutoNode{
Provisioner: hyperv1.ProvisionerConfig{
Name: hyperv1.ProvisionerKarpenter,
- Karpenter: &hyperv1.KarpenterConfig{
+ Karpenter: hyperv1.KarpenterConfig{
Platform: hyperv1.AWSPlatform,
},
},
diff --git a/karpenter-operator/controllers/nodeclass/ec2_nodeclass_controller.go b/karpenter-operator/controllers/nodeclass/ec2_nodeclass_controller.go
index 821ff3d11257..b1877572969e 100644
--- a/karpenter-operator/controllers/nodeclass/ec2_nodeclass_controller.go
+++ b/karpenter-operator/controllers/nodeclass/ec2_nodeclass_controller.go
@@ -10,7 +10,7 @@ import (
"time"
hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1"
- hyperkarpenterv1 "github.com/openshift/hypershift/api/karpenter/v1beta1"
+ hyperkarpenterv1 "github.com/openshift/hypershift/api/karpenter/v1"
"github.com/openshift/hypershift/hypershift-operator/controllers/nodepool"
"github.com/openshift/hypershift/karpenter-operator/controllers/karpenter/assets"
supportassets "github.com/openshift/hypershift/support/assets"
diff --git a/karpenter-operator/controllers/nodeclass/ec2_nodeclass_controller_test.go b/karpenter-operator/controllers/nodeclass/ec2_nodeclass_controller_test.go
index a0724455b3f5..793f2f9e97b4 100644
--- a/karpenter-operator/controllers/nodeclass/ec2_nodeclass_controller_test.go
+++ b/karpenter-operator/controllers/nodeclass/ec2_nodeclass_controller_test.go
@@ -11,7 +11,7 @@ import (
. "github.com/onsi/gomega"
hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1"
- hyperkarpenterv1 "github.com/openshift/hypershift/api/karpenter/v1beta1"
+ hyperkarpenterv1 "github.com/openshift/hypershift/api/karpenter/v1"
"github.com/openshift/hypershift/hypershift-operator/controllers/nodepool"
hyperapi "github.com/openshift/hypershift/support/api"
karpenterutil "github.com/openshift/hypershift/support/karpenter"
diff --git a/karpenter-operator/controllers/nodeclass/karpenter_util.go b/karpenter-operator/controllers/nodeclass/karpenter_util.go
index 1f150d035276..22824689233d 100644
--- a/karpenter-operator/controllers/nodeclass/karpenter_util.go
+++ b/karpenter-operator/controllers/nodeclass/karpenter_util.go
@@ -4,7 +4,7 @@ import (
"fmt"
"strings"
- hyperkarpenterv1 "github.com/openshift/hypershift/api/karpenter/v1beta1"
+ hyperkarpenterv1 "github.com/openshift/hypershift/api/karpenter/v1"
awskarpenterv1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1"
diff --git a/support/api/scheme.go b/support/api/scheme.go
index 0d19f8ecb539..0004a79e7366 100644
--- a/support/api/scheme.go
+++ b/support/api/scheme.go
@@ -10,7 +10,7 @@ import (
auditlogpersistencev1alpha1 "github.com/openshift/hypershift/api/auditlogpersistence/v1alpha1"
certificatesv1alpha1 "github.com/openshift/hypershift/api/certificates/v1alpha1"
hyperv1beta1 "github.com/openshift/hypershift/api/hypershift/v1beta1"
- hyperkarpenterv1 "github.com/openshift/hypershift/api/karpenter/v1beta1"
+ hyperkarpenterv1 "github.com/openshift/hypershift/api/karpenter/v1"
schedulingv1alpha1 "github.com/openshift/hypershift/api/scheduling/v1alpha1"
"github.com/openshift/hypershift/support/rhobsmonitoring"
diff --git a/support/controlplane-component/AGENTS.md b/support/controlplane-component/AGENTS.md
new file mode 100644
index 000000000000..3b2def4f989e
--- /dev/null
+++ b/support/controlplane-component/AGENTS.md
@@ -0,0 +1,40 @@
+# Control Plane Component Framework
+
+This library provides the reconciliation framework for managing individual control plane workloads (Deployments, StatefulSets, Jobs, CronJobs) and their associated manifests (ConfigMaps, Secrets, RBAC, Services, etc.) within HyperShift's hosted control plane.
+
+## Primary Consumer
+
+The **Control Plane Operator** (CPO) is the main consumer. Each control plane component (KAS, etcd, OAuth, KCM, scheduler, DNS, cloud controllers, etc.) is implemented as a `ControlPlaneComponent` in `control-plane-operator/controllers/hostedcontrolplane/v2/`. Manifests live alongside each component in `v2/assets/{component-name}/` as embedded YAML.
+
+## CR Representation
+
+Every component gets a `ControlPlaneComponent` CR (`hypershift.openshift.io/v1beta1`) in the same namespace as the `HostedControlPlane`. The CR tracks:
+
+- **Conditions**: `Available` (component is ready) and `RolloutComplete` (workload version matches desired)
+- **Version**: Current deployed version
+- **Resources**: List of resources managed by the component
+
+The framework reconciles these CRs automatically. Components declare dependencies on other components; reconciliation waits for dependencies to be `Available` and `RolloutComplete` at the correct version before proceeding.
+
+## Reuse Beyond CPO
+
+The framework is also consumed by:
+
+- **Karpenter Operator**: Manages the Karpenter component using the same `ControlPlaneComponent` and `ControlPlaneContext` abstractions
+- **HyperShift Operator**: Uses constants and context types from this package
+
+Ideally, other reconciliation loops like HCCO would also reuse these primitives rather than implementing parallel patterns for resource reconciliation, status tracking, and dependency management. The `ControlPlaneContext`, `WorkloadContext`, and builder API are designed to be general-purpose.
+
+## Key Files
+
+| File | Purpose |
+|------|---------|
+| `controlplane-component.go` | Core interfaces: `ControlPlaneComponent`, `ComponentOptions`, `ControlPlaneContext` |
+| `builder.go` | Fluent builder API for constructing components |
+| `deployment.go`, `statefulset.go`, `job.go`, `cronjob.go` | Workload-type providers |
+| `status.go` | Status reconciliation: dependency checks, Available/RolloutComplete conditions |
+| `defaults.go` | Default container configs: resource limits, security context, probes |
+| `generic-adapter.go` | Applies adapt functions and predicates to individual manifests |
+| `konnectivity-container.go` | Konnectivity proxy sidecar injection |
+| `token-minter-container.go` | Token-minter sidecar for cloud/KAS access |
+| `common.go` | Helpers: `AdaptPodDisruptionBudget`, `DisableIfAnnotationExist`, `EnableForPlatform` |
diff --git a/support/karpenter/karpenter.go b/support/karpenter/karpenter.go
index 8143a9cb5802..add76fc0969e 100644
--- a/support/karpenter/karpenter.go
+++ b/support/karpenter/karpenter.go
@@ -6,7 +6,7 @@ import (
"fmt"
hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1"
- hyperkarpenterv1 "github.com/openshift/hypershift/api/karpenter/v1beta1"
+ hyperkarpenterv1 "github.com/openshift/hypershift/api/karpenter/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
@@ -43,10 +43,8 @@ func SupportedArchitectures(platform hyperv1.PlatformType) ([]string, error) {
// IsKarpenterEnabled checks if Karpenter is enabled for the given AutoNode configuration.
// Note that we may eventually support other platforms, but for now we only support AWS.
-func IsKarpenterEnabled(autoNode *hyperv1.AutoNode) bool {
- return autoNode != nil &&
- autoNode.Provisioner.Name == hyperv1.ProvisionerKarpenter &&
- autoNode.Provisioner.Karpenter != nil &&
+func IsKarpenterEnabled(autoNode hyperv1.AutoNode) bool {
+ return autoNode.Provisioner.Name == hyperv1.ProvisionerKarpenter &&
autoNode.Provisioner.Karpenter.Platform == hyperv1.AWSPlatform
}
diff --git a/test/e2e/karpenter_test.go b/test/e2e/karpenter_test.go
index 38a346604119..31e847c2e7f9 100644
--- a/test/e2e/karpenter_test.go
+++ b/test/e2e/karpenter_test.go
@@ -22,7 +22,7 @@ import (
"github.com/blang/semver"
. "github.com/onsi/gomega"
hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1"
- hyperkarpenterv1 "github.com/openshift/hypershift/api/karpenter/v1beta1"
+ hyperkarpenterv1 "github.com/openshift/hypershift/api/karpenter/v1"
karpentercpov2 "github.com/openshift/hypershift/control-plane-operator/controllers/hostedcontrolplane/v2/karpenter"
karpenteroperatorcpov2 "github.com/openshift/hypershift/control-plane-operator/controllers/hostedcontrolplane/v2/karpenteroperator"
"github.com/openshift/hypershift/hypershift-operator/controllers/manifests"
@@ -49,9 +49,6 @@ import (
func TestKarpenter(t *testing.T) {
e2eutil.AtLeast(t, e2eutil.Version419)
- if os.Getenv("TECH_PREVIEW_NO_UPGRADE") != "true" {
- t.Skipf("Only tested when CI sets TECH_PREVIEW_NO_UPGRADE=true and the Hypershift Operator is installed with --tech-preview-no-upgrade")
- }
if globalOpts.Platform != hyperv1.AWSPlatform {
t.Skip("test only supported on platform AWS")
}
@@ -750,10 +747,8 @@ func testCapacityReservation(ctx context.Context, mgtClient, guestClient crclien
// AutoNode.Provisioner.Karpenter.AWS is required at the API level when karpenter
// is configured, so this should never happen/never be nil for a valid karpenter cluster.
- if hostedCluster.Spec.AutoNode == nil ||
- hostedCluster.Spec.AutoNode.Provisioner.Karpenter == nil ||
- hostedCluster.Spec.AutoNode.Provisioner.Karpenter.AWS == nil {
- t.Skip("HostedCluster does not have a Karpenter AWS role configured, skipping capacity reservation test")
+ if hostedCluster.Spec.AutoNode.Provisioner.Karpenter.Platform != hyperv1.AWSPlatform {
+ t.Skip("HostedCluster does not have a Karpenter AWS platform configured, skipping capacity reservation test")
}
// Determine an availability zone to use: pick the AZ from the first subnet in the cluster.
@@ -1167,7 +1162,7 @@ func testAutoNodeLifecycle(ctx context.Context, mgtClient crclient.Client, hoste
// Disable Karpenter.
t.Log("Disabling AutoNode (Karpenter) on HostedCluster")
err = e2eutil.UpdateObject(t, ctx, mgtClient, hostedCluster, func(obj *hyperv1.HostedCluster) {
- obj.Spec.AutoNode = nil
+ obj.Spec.AutoNode = hyperv1.AutoNode{}
})
g.Expect(err).NotTo(HaveOccurred(), "failed to disable AutoNode")
diff --git a/test/e2e/util/eventually.go b/test/e2e/util/eventually.go
index 5a825a4ecf9f..67a5f267cfc0 100644
--- a/test/e2e/util/eventually.go
+++ b/test/e2e/util/eventually.go
@@ -10,7 +10,7 @@ import (
certificatesv1alpha1 "github.com/openshift/hypershift/api/certificates/v1alpha1"
hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1"
- hyperkarpenterv1 "github.com/openshift/hypershift/api/karpenter/v1beta1"
+ hyperkarpenterv1 "github.com/openshift/hypershift/api/karpenter/v1"
certificatesv1 "k8s.io/api/certificates/v1"
corev1 "k8s.io/api/core/v1"
diff --git a/test/envtest/suite_test.go b/test/envtest/suite_test.go
index 00020ec18ad4..debdd55ba32b 100644
--- a/test/envtest/suite_test.go
+++ b/test/envtest/suite_test.go
@@ -38,9 +38,10 @@ func TestAPIs(t *testing.T) {
_, thisFile, _, _ := runtime.Caller(0)
testDir := filepath.Dir(thisFile)
assetsDir := filepath.Join(testDir, "..", "..", "cmd", "install", "assets", "crds", "hypershift-operator")
+ karpenterAssetsDir := filepath.Join(testDir, "..", "..", "karpenter-operator", "controllers", "karpenter", "assets")
var err error
- suites, err = LoadTestSuiteSpecs(assetsDir)
+ suites, err = LoadTestSuiteSpecs(assetsDir, karpenterAssetsDir)
g.Expect(err).ToNot(HaveOccurred())
RunSpecs(t, "HyperShift API Integration Suite")
diff --git a/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/hosted_controlplane.go b/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/hosted_controlplane.go
index 585a3b3d4193..8486514f0a18 100644
--- a/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/hosted_controlplane.go
+++ b/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/hosted_controlplane.go
@@ -230,9 +230,8 @@ type HostedControlPlaneSpec struct {
// using the specified provisioner (e.g. Karpenter) instead of requiring
// manual NodePool management.
//
- // +openshift:enable:FeatureGate=AutoNodeKarpenter
// +optional
- AutoNode *AutoNode `json:"autoNode,omitempty"`
+ AutoNode AutoNode `json:"autoNode,omitzero"`
// nodeSelector when specified, must be true for the pods managed by the HostedCluster to be scheduled.
//
@@ -414,7 +413,6 @@ type HostedControlPlaneStatus struct {
NodeCount *int `json:"nodeCount,omitempty"`
// autoNode contains the observed state of the autoNode (Karpenter) provisioner.
- // +openshift:enable:FeatureGate=AutoNodeKarpenter
// +optional
AutoNode AutoNodeStatus `json:"autoNode,omitzero"`
diff --git a/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/hostedcluster_types.go b/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/hostedcluster_types.go
index d99f765f090f..8d4db6c2bb34 100644
--- a/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/hostedcluster_types.go
+++ b/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/hostedcluster_types.go
@@ -642,9 +642,8 @@ type HostedClusterSpec struct {
// autoNode specifies the configuration for automatic node provisioning and lifecycle management.
// When set, the provisioner(e.g. Karpenter) will be used to provision nodes for targeted workloads.
//
- // +openshift:enable:FeatureGate=AutoNodeKarpenter
// +optional
- AutoNode *AutoNode `json:"autoNode,omitempty"`
+ AutoNode AutoNode `json:"autoNode,omitzero"`
// etcd specifies configuration for the control plane etcd cluster. The
// default managementType is Managed. Once set, the managementType cannot be
@@ -1398,7 +1397,7 @@ type ProvisionerConfig struct {
//
// +optional
// +unionMember
- Karpenter *KarpenterConfig `json:"karpenter,omitempty"`
+ Karpenter KarpenterConfig `json:"karpenter,omitzero"`
}
// KarpenterConfig specifies the configuration for the Karpenter provisioner
@@ -1418,7 +1417,7 @@ type KarpenterConfig struct {
//
// +optional
// +unionMember
- AWS *KarpenterAWSConfig `json:"aws,omitempty"`
+ AWS KarpenterAWSConfig `json:"aws,omitzero"`
}
// KarpenterAWSConfig specifies AWS-specific configuration for the Karpenter provisioner.
@@ -2174,7 +2173,6 @@ type HostedClusterStatus struct {
Platform *PlatformStatus `json:"platform,omitempty"`
// autoNode contains the observed state of the autoNode (Karpenter) provisioner.
- // +openshift:enable:FeatureGate=AutoNodeKarpenter
// +optional
AutoNode AutoNodeStatus `json:"autoNode,omitzero"`
diff --git a/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/zz_generated.deepcopy.go b/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/zz_generated.deepcopy.go
index 558be0746f8f..0f69bb3bcdeb 100644
--- a/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/zz_generated.deepcopy.go
+++ b/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/zz_generated.deepcopy.go
@@ -558,7 +558,7 @@ func (in *AllocationPool) DeepCopy() *AllocationPool {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AutoNode) DeepCopyInto(out *AutoNode) {
*out = *in
- in.Provisioner.DeepCopyInto(&out.Provisioner)
+ out.Provisioner = in.Provisioner
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutoNode.
@@ -2305,11 +2305,7 @@ func (in *HostedClusterSpec) DeepCopyInto(out *HostedClusterSpec) {
in.DNS.DeepCopyInto(&out.DNS)
in.Networking.DeepCopyInto(&out.Networking)
in.Autoscaling.DeepCopyInto(&out.Autoscaling)
- if in.AutoNode != nil {
- in, out := &in.AutoNode, &out.AutoNode
- *out = new(AutoNode)
- (*in).DeepCopyInto(*out)
- }
+ out.AutoNode = in.AutoNode
in.Etcd.DeepCopyInto(&out.Etcd)
if in.Services != nil {
in, out := &in.Services, &out.Services
@@ -2583,11 +2579,7 @@ func (in *HostedControlPlaneSpec) DeepCopyInto(out *HostedControlPlaneSpec) {
**out = **in
}
in.Autoscaling.DeepCopyInto(&out.Autoscaling)
- if in.AutoNode != nil {
- in, out := &in.AutoNode, &out.AutoNode
- *out = new(AutoNode)
- (*in).DeepCopyInto(*out)
- }
+ out.AutoNode = in.AutoNode
if in.NodeSelector != nil {
in, out := &in.NodeSelector, &out.NodeSelector
*out = make(map[string]string, len(*in))
@@ -2910,11 +2902,7 @@ func (in *KarpenterAWSConfig) DeepCopy() *KarpenterAWSConfig {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KarpenterConfig) DeepCopyInto(out *KarpenterConfig) {
*out = *in
- if in.AWS != nil {
- in, out := &in.AWS, &out.AWS
- *out = new(KarpenterAWSConfig)
- **out = **in
- }
+ out.AWS = in.AWS
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KarpenterConfig.
@@ -4211,11 +4199,7 @@ func (in *PowerVSVPC) DeepCopy() *PowerVSVPC {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ProvisionerConfig) DeepCopyInto(out *ProvisionerConfig) {
*out = *in
- if in.Karpenter != nil {
- in, out := &in.Karpenter, &out.Karpenter
- *out = new(KarpenterConfig)
- (*in).DeepCopyInto(*out)
- }
+ out.Karpenter = in.Karpenter
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProvisionerConfig.
diff --git a/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests.yaml b/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests.yaml
index e4be0c29179b..ae4043f97aa0 100644
--- a/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests.yaml
+++ b/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests.yaml
@@ -204,7 +204,6 @@ hostedclusters.hypershift.openshift.io:
Capability: ""
Category: ""
FeatureGates:
- - AutoNodeKarpenter
- ClusterUpdateAcceptRisks
- ClusterVersionOperatorConfiguration
- ExternalOIDC
@@ -278,7 +277,6 @@ hostedcontrolplanes.hypershift.openshift.io:
Capability: ""
Category: cluster-api
FeatureGates:
- - AutoNodeKarpenter
- ClusterUpdateAcceptRisks
- ClusterVersionOperatorConfiguration
- ExternalOIDC
diff --git a/api/karpenter/v1beta1/doc.go b/vendor/github.com/openshift/hypershift/api/karpenter/v1/doc.go
similarity index 87%
rename from api/karpenter/v1beta1/doc.go
rename to vendor/github.com/openshift/hypershift/api/karpenter/v1/doc.go
index e49f0456f04f..75fa4591f62a 100644
--- a/api/karpenter/v1beta1/doc.go
+++ b/vendor/github.com/openshift/hypershift/api/karpenter/v1/doc.go
@@ -1,4 +1,4 @@
// +k8s:deepcopy-gen=package,register
// +groupName=karpenter.hypershift.openshift.io
// +k8s:openapi-gen=true
-package v1beta1
+package v1
diff --git a/api/karpenter/v1beta1/karpenter_types.go b/vendor/github.com/openshift/hypershift/api/karpenter/v1/karpenter_types.go
similarity index 99%
rename from api/karpenter/v1beta1/karpenter_types.go
rename to vendor/github.com/openshift/hypershift/api/karpenter/v1/karpenter_types.go
index fe8638dff960..beba4471e100 100644
--- a/api/karpenter/v1beta1/karpenter_types.go
+++ b/vendor/github.com/openshift/hypershift/api/karpenter/v1/karpenter_types.go
@@ -1,4 +1,4 @@
-package v1beta1
+package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -609,8 +609,7 @@ const (
// OpenshiftEC2NodeClassStatus defines the observed state of OpenshiftEC2NodeClass.
// +kubebuilder:validation:MinProperties=1
type OpenshiftEC2NodeClassStatus struct {
- // conditions contain signals for health and readiness.
-
+ // conditions contains signals for health and readiness.
// +optional
// +listType=map
// +listMapKey=type
diff --git a/vendor/github.com/openshift/hypershift/api/karpenter/v1beta1/register.go b/vendor/github.com/openshift/hypershift/api/karpenter/v1/register.go
similarity index 94%
rename from vendor/github.com/openshift/hypershift/api/karpenter/v1beta1/register.go
rename to vendor/github.com/openshift/hypershift/api/karpenter/v1/register.go
index 88cc9e24c583..609c4ab8b3d9 100644
--- a/vendor/github.com/openshift/hypershift/api/karpenter/v1beta1/register.go
+++ b/vendor/github.com/openshift/hypershift/api/karpenter/v1/register.go
@@ -1,4 +1,4 @@
-package v1beta1
+package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -7,7 +7,7 @@ import (
)
// SchemeGroupVersion is group version used to register these objects.
-var SchemeGroupVersion = schema.GroupVersion{Group: "karpenter.hypershift.openshift.io", Version: "v1beta1"}
+var SchemeGroupVersion = schema.GroupVersion{Group: "karpenter.hypershift.openshift.io", Version: "v1"}
// Kind takes an unqualified kind and returns back a Group qualified GroupKind.
func Kind(kind string) schema.GroupKind {
diff --git a/vendor/github.com/openshift/hypershift/api/karpenter/v1beta1/zz_generated.deepcopy.go b/vendor/github.com/openshift/hypershift/api/karpenter/v1/zz_generated.deepcopy.go
similarity index 99%
rename from vendor/github.com/openshift/hypershift/api/karpenter/v1beta1/zz_generated.deepcopy.go
rename to vendor/github.com/openshift/hypershift/api/karpenter/v1/zz_generated.deepcopy.go
index b0f6605f8716..8c1af4f64387 100644
--- a/vendor/github.com/openshift/hypershift/api/karpenter/v1beta1/zz_generated.deepcopy.go
+++ b/vendor/github.com/openshift/hypershift/api/karpenter/v1/zz_generated.deepcopy.go
@@ -18,10 +18,10 @@ limitations under the License.
// Code generated by controller-gen. DO NOT EDIT.
-package v1beta1
+package v1
import (
- "k8s.io/apimachinery/pkg/apis/meta/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
@@ -234,7 +234,7 @@ func (in *OpenshiftEC2NodeClassStatus) DeepCopyInto(out *OpenshiftEC2NodeClassSt
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
- *out = make([]v1.Condition, len(*in))
+ *out = make([]metav1.Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index b9c09390fa1e..1a844c934518 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1132,7 +1132,7 @@ github.com/openshift/hypershift/api/certificates
github.com/openshift/hypershift/api/certificates/v1alpha1
github.com/openshift/hypershift/api/hypershift/v1beta1
github.com/openshift/hypershift/api/ibmcapi
-github.com/openshift/hypershift/api/karpenter/v1beta1
+github.com/openshift/hypershift/api/karpenter/v1
github.com/openshift/hypershift/api/scheduling
github.com/openshift/hypershift/api/scheduling/v1alpha1
github.com/openshift/hypershift/api/util/configrefs
|