diff --git a/charts/k3k/templates/crds/k3k.io_clusters.yaml b/charts/k3k/templates/crds/k3k.io_clusters.yaml
index 9eee4c0e..ca528890 100644
--- a/charts/k3k/templates/crds/k3k.io_clusters.yaml
+++ b/charts/k3k/templates/crds/k3k.io_clusters.yaml
@@ -3173,13 +3173,6 @@ spec:
- type
type: object
type: array
- hcpRegistration:
- description: |-
- HCPRegistration is a copy-pasteable K3s installer command that external
- (BYO) nodes can run to register against an HCP-mode cluster.
- Only populated when Mode is "hcp" and an externally-routable endpoint
- (NodePort, LoadBalancer or Ingress) is configured.
- type: string
hostVersion:
description: HostVersion is the Kubernetes version of the host node.
type: string
diff --git a/docs/cli/k3kcli.adoc b/docs/cli/k3kcli.adoc
index b6fbebf5..e31ed8c7 100644
--- a/docs/cli/k3kcli.adoc
+++ b/docs/cli/k3kcli.adoc
@@ -54,7 +54,7 @@ k3kcli cluster create [command options] NAME
--kubeconfig-server string override the kubeconfig server host
--labels stringArray Labels to add to the cluster object (e.g. key=value)
--mirror-host-nodes Mirror Host Cluster Nodes
- --mode string k3k mode type (shared, virtual) (default "shared")
+ --mode string k3k mode type (shared, virtual, hcp) (default "shared")
-n, --namespace string namespace of the k3k cluster
--persistence-type string persistence mode for the nodes (dynamic, ephemeral) (default "dynamic")
--policy string The policy to create the cluster in
diff --git a/docs/cli/k3kcli_cluster_create.md b/docs/cli/k3kcli_cluster_create.md
index 7afccb21..dc487f24 100644
--- a/docs/cli/k3kcli_cluster_create.md
+++ b/docs/cli/k3kcli_cluster_create.md
@@ -25,7 +25,7 @@ k3kcli cluster create [command options] NAME
--kubeconfig-server string override the kubeconfig server host
--labels stringArray Labels to add to the cluster object (e.g. key=value)
--mirror-host-nodes Mirror Host Cluster Nodes
- --mode string k3k mode type (shared, virtual) (default "shared")
+ --mode string k3k mode type (shared, virtual, hcp) (default "shared")
-n, --namespace string namespace of the k3k cluster
--persistence-type string persistence mode for the nodes (dynamic, ephemeral) (default "dynamic")
--policy string The policy to create the cluster in
diff --git a/docs/crds/crds.adoc b/docs/crds/crds.adoc
index 6d01db49..cd57d5d8 100644
--- a/docs/crds/crds.adoc
+++ b/docs/crds/crds.adoc
@@ -134,7 +134,7 @@ _Underlying type:_ _string_
ClusterMode is the possible provisioning mode of a Cluster.
_Validation:_
-- Enum: [shared virtual]
+- Enum: [shared virtual hcp]
_Appears In:_
@@ -177,8 +177,8 @@ _Appears In:_
| *`version`* __string__ | Version is the K3s version to use for the virtual nodes. +
It should follow the K3s versioning convention (e.g., v1.28.2-k3s1). +
If not specified, the Kubernetes version of the host node will be used. + | |
-| *`mode`* __xref:{anchor_prefix}-github-com-rancher-k3k-pkg-apis-k3k-io-v1beta1-clustermode[$$ClusterMode$$]__ | Mode specifies the cluster provisioning mode: "shared" or "virtual". +
-Defaults to "shared". This field is immutable. + | shared | Enum: [shared virtual] +
+| *`mode`* __xref:{anchor_prefix}-github-com-rancher-k3k-pkg-apis-k3k-io-v1beta1-clustermode[$$ClusterMode$$]__ | Mode specifies the cluster provisioning mode: "shared", "virtual" or "hcp". +
+Defaults to "shared". This field is immutable. + | shared | Enum: [shared virtual hcp] +
| *`servers`* __integer__ | Servers specifies the number of K3s pods to run in server (control plane) mode. +
Must be at least 1. Defaults to 1. + | 1 |
@@ -790,7 +790,7 @@ to set defaults and constraints (min/max) + | |
This includes both node affinity and pod affinity/anti-affinity rules. + | |
| *`defaultAgentAffinity`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#affinity-v1-core[$$Affinity$$]__ | DefaultAgentAffinity specifies the affinity rules applied to agent pods of all clusters in the target Namespace. +
This includes both node affinity and pod affinity/anti-affinity rules. + | |
-| *`allowedMode`* __xref:{anchor_prefix}-github-com-rancher-k3k-pkg-apis-k3k-io-v1beta1-clustermode[$$ClusterMode$$]__ | AllowedMode specifies the allowed cluster provisioning mode. Defaults to "shared". + | shared | Enum: [shared virtual] +
+| *`allowedMode`* __xref:{anchor_prefix}-github-com-rancher-k3k-pkg-apis-k3k-io-v1beta1-clustermode[$$ClusterMode$$]__ | AllowedMode specifies the allowed cluster provisioning mode. Defaults to "shared". + | shared | Enum: [shared virtual hcp] +
| *`disableNetworkPolicy`* __boolean__ | DisableNetworkPolicy indicates whether to disable the creation of a default network policy for cluster isolation. + | |
| *`podSecurityAdmissionLevel`* __xref:{anchor_prefix}-github-com-rancher-k3k-pkg-apis-k3k-io-v1beta1-podsecurityadmissionlevel[$$PodSecurityAdmissionLevel$$]__ | PodSecurityAdmissionLevel specifies the pod security admission level applied to the pods in the namespace. + | | Enum: [privileged baseline restricted] +
diff --git a/docs/crds/crds.md b/docs/crds/crds.md
index 35bd7a27..a43bd48b 100644
--- a/docs/crds/crds.md
+++ b/docs/crds/crds.md
@@ -103,7 +103,7 @@ _Underlying type:_ _string_
ClusterMode is the possible provisioning mode of a Cluster.
_Validation:_
-- Enum: [shared virtual]
+- Enum: [shared virtual hcp]
_Appears in:_
- [ClusterSpec](#clusterspec)
@@ -138,7 +138,7 @@ _Appears in:_
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `version` _string_ | Version is the K3s version to use for the virtual nodes.
It should follow the K3s versioning convention (e.g., v1.28.2-k3s1).
If not specified, the Kubernetes version of the host node will be used. | | |
-| `mode` _[ClusterMode](#clustermode)_ | Mode specifies the cluster provisioning mode: "shared" or "virtual".
Defaults to "shared". This field is immutable. | shared | Enum: [shared virtual]
|
+| `mode` _[ClusterMode](#clustermode)_ | Mode specifies the cluster provisioning mode: "shared", "virtual" or "hcp".
Defaults to "shared". This field is immutable. | shared | Enum: [shared virtual hcp]
|
| `servers` _integer_ | Servers specifies the number of K3s pods to run in server (control plane) mode.
Must be at least 1. Defaults to 1. | 1 | |
| `agents` _integer_ | Agents specifies the number of K3s pods to run in agent (worker) mode.
Must be 0 or greater. Defaults to 0.
This field is ignored in "shared" mode. | 0 | |
| `clusterCIDR` _string_ | ClusterCIDR is the CIDR range for pod IPs.
Defaults to 10.42.0.0/16 in shared mode and 10.52.0.0/16 in virtual mode.
This field is immutable. | | |
@@ -590,7 +590,7 @@ _Appears in:_
| `defaultPriorityClass` _string_ | DefaultPriorityClass specifies the priorityClassName applied to all pods of all clusters in the target Namespace. | | |
| `defaultServerAffinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#affinity-v1-core)_ | DefaultServerAffinity specifies the affinity rules applied to server pods of all clusters in the target Namespace.
This includes both node affinity and pod affinity/anti-affinity rules. | | |
| `defaultAgentAffinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#affinity-v1-core)_ | DefaultAgentAffinity specifies the affinity rules applied to agent pods of all clusters in the target Namespace.
This includes both node affinity and pod affinity/anti-affinity rules. | | |
-| `allowedMode` _[ClusterMode](#clustermode)_ | AllowedMode specifies the allowed cluster provisioning mode. Defaults to "shared". | shared | Enum: [shared virtual]
|
+| `allowedMode` _[ClusterMode](#clustermode)_ | AllowedMode specifies the allowed cluster provisioning mode. Defaults to "shared". | shared | Enum: [shared virtual hcp]
|
| `disableNetworkPolicy` _boolean_ | DisableNetworkPolicy indicates whether to disable the creation of a default network policy for cluster isolation. | | |
| `podSecurityAdmissionLevel` _[PodSecurityAdmissionLevel](#podsecurityadmissionlevel)_ | PodSecurityAdmissionLevel specifies the pod security admission level applied to the pods in the namespace. | | Enum: [privileged baseline restricted]
|
| `sync` _[SyncConfig](#syncconfig)_ | Sync specifies the resources types that will be synced from virtual cluster to host cluster. | \{ \} | |
diff --git a/pkg/apis/k3k.io/v1beta1/types.go b/pkg/apis/k3k.io/v1beta1/types.go
index a3758fb1..59f47198 100644
--- a/pkg/apis/k3k.io/v1beta1/types.go
+++ b/pkg/apis/k3k.io/v1beta1/types.go
@@ -624,14 +624,6 @@ type ClusterStatus struct {
// +optional
KubeletPort int `json:"kubeletPort,omitempty"`
- // HCPRegistration is a copy-pasteable K3s installer command that external
- // (BYO) nodes can run to register against an HCP-mode cluster.
- // Only populated when Mode is "hcp" and an externally-routable endpoint
- // (NodePort, LoadBalancer or Ingress) is configured.
- //
- // +optional
- HCPRegistration string `json:"hcpRegistration,omitempty"`
-
// Conditions are the individual conditions for the cluster set.
//
// +optional
diff --git a/pkg/controller/cluster/hcp.go b/pkg/controller/cluster/hcp.go
index dff7fab8..0c5ce04b 100644
--- a/pkg/controller/cluster/hcp.go
+++ b/pkg/controller/cluster/hcp.go
@@ -29,7 +29,7 @@ import (
func (c *ClusterReconciler) ensureHCPRegistration(ctx context.Context, cluster *v1beta1.Cluster, token string) error {
log := ctrl.LoggerFrom(ctx)
- url, external, err := server.ServerURL(ctx, c.Client, cluster, selectNonLoopbackSAN(cluster), 0)
+ _, external, err := server.ServerURL(ctx, c.Client, cluster, selectNonLoopbackSAN(cluster), 0)
if err != nil {
return err
}
@@ -44,33 +44,11 @@ func (c *ClusterReconciler) ensureHCPRegistration(ctx context.Context, cluster *
Reason: ReasonHCPNoExternalEndpoint,
Message: "HCP cluster has no external endpoint; set spec.expose.nodePort, spec.expose.loadBalancer or spec.expose.ingress so external nodes can reach the API server",
})
-
- cluster.Status.HCPRegistration = ""
-
- return nil
}
- version := cluster.Spec.Version
- if version == "" {
- version = cluster.Status.HostVersion
- }
-
- cluster.Status.HCPRegistration = hcpRegistrationCommand(version, url, token)
-
return nil
}
-// hcpRegistrationCommand returns the standard K3s installer one-liner an
-// end-user can copy onto an external host to join an HCP cluster.
-func hcpRegistrationCommand(version, serverURL, token string) string {
- if version == "" {
- return fmt.Sprintf("curl -sfL https://get.k3s.io | K3S_URL=%s K3S_TOKEN=%s sh -", serverURL, token)
- }
-
- return fmt.Sprintf("curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=%s K3S_URL=%s K3S_TOKEN=%s sh -",
- version, serverURL, token)
-}
-
// selectNonLoopbackSAN returns the first non-loopback address from the
// cluster's TLS SANs, preferring spec.TLSSANs then falling back to status.TLSSANs.
// Returns empty string if no non-loopback address is found.
diff --git a/pkg/controller/cluster/hcp_test.go b/pkg/controller/cluster/hcp_test.go
index 6587b0aa..da1b5f57 100644
--- a/pkg/controller/cluster/hcp_test.go
+++ b/pkg/controller/cluster/hcp_test.go
@@ -2,7 +2,6 @@ package cluster
import (
"context"
- "strings"
"testing"
"github.com/stretchr/testify/assert"
@@ -19,38 +18,6 @@ import (
"github.com/rancher/k3k/pkg/controller/cluster/server"
)
-func Test_hcpRegistrationCommand(t *testing.T) {
- tests := []struct {
- name string
- version string
- serverURL string
- token string
- want string
- }{
- {
- name: "with version",
- version: "v1.33.1-k3s1",
- serverURL: "https://1.2.3.4:30443",
- token: "abcd1234",
- want: "curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=v1.33.1-k3s1 K3S_URL=https://1.2.3.4:30443 K3S_TOKEN=abcd1234 sh -",
- },
- {
- name: "without version",
- version: "",
- serverURL: "https://hcp.example.com",
- token: "tok",
- want: "curl -sfL https://get.k3s.io | K3S_URL=https://hcp.example.com K3S_TOKEN=tok sh -",
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got := hcpRegistrationCommand(tt.version, tt.serverURL, tt.token)
- assert.Equal(t, tt.want, got)
- })
- }
-}
-
func Test_ensureHCPRegistration(t *testing.T) {
scheme := runtime.NewScheme()
require.NoError(t, corev1.AddToScheme(scheme))
@@ -68,33 +35,6 @@ func Test_ensureHCPRegistration(t *testing.T) {
},
}
- t.Run("nodeport service produces ready-to-copy command", func(t *testing.T) {
- svc := &corev1.Service{
- ObjectMeta: metav1.ObjectMeta{
- Name: server.ServiceName(cluster.Name),
- Namespace: cluster.Namespace,
- },
- Spec: corev1.ServiceSpec{
- Type: corev1.ServiceTypeNodePort,
- ClusterIP: "10.43.0.50",
- Ports: []corev1.ServicePort{
- {Name: "k3s-server-port", Port: 443, NodePort: 31001},
- },
- },
- }
-
- fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(svc).Build()
- r := &ClusterReconciler{Client: fakeClient}
-
- c := cluster.DeepCopy()
- require.NoError(t, r.ensureHCPRegistration(context.Background(), c, "join-token-xyz"))
-
- assert.Contains(t, c.Status.HCPRegistration, "K3S_URL=https://hcp.example.com:31001")
- assert.Contains(t, c.Status.HCPRegistration, "K3S_TOKEN=join-token-xyz")
- assert.Contains(t, c.Status.HCPRegistration, "INSTALL_K3S_VERSION=v1.33.1-k3s1")
- assert.True(t, strings.HasPrefix(c.Status.HCPRegistration, "curl -sfL https://get.k3s.io"))
- })
-
t.Run("clusterip-only service sets degraded condition and clears registration", func(t *testing.T) {
svc := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
@@ -114,11 +54,8 @@ func Test_ensureHCPRegistration(t *testing.T) {
r := &ClusterReconciler{Client: fakeClient}
c := cluster.DeepCopy()
- c.Status.HCPRegistration = "stale-value"
require.NoError(t, r.ensureHCPRegistration(context.Background(), c, "ignored"))
- assert.Empty(t, c.Status.HCPRegistration)
-
cond := meta.FindStatusCondition(c.Status.Conditions, ConditionReady)
require.NotNil(t, cond)
assert.Equal(t, metav1.ConditionFalse, cond.Status)