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)