From d01ae2e8203e75113f7a982a87dde46c2028e932 Mon Sep 17 00:00:00 2001 From: Enrico Candino Date: Mon, 15 Jun 2026 16:03:28 +0200 Subject: [PATCH] adding e2e tests --- pkg/controller/cluster/hcp.go | 23 ++++------ tests/e2e/cluster_create_test.go | 79 ++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 14 deletions(-) diff --git a/pkg/controller/cluster/hcp.go b/pkg/controller/cluster/hcp.go index 127c5e0c..c6d5f299 100644 --- a/pkg/controller/cluster/hcp.go +++ b/pkg/controller/cluster/hcp.go @@ -151,15 +151,9 @@ func (c *ClusterReconciler) ensureHCPKubernetesEndpointSlice(ctx context.Context endpointSlice.Labels[discoveryv1.LabelServiceName] = "kubernetes" endpointSlice.AddressType = addressType - endpoint := discoveryv1.Endpoint{ - Addresses: []string{addr.IP}, + endpointSlice.Endpoints = []discoveryv1.Endpoint{ + {Addresses: []string{addr.IP}}, } - - if addr.Hostname != "" { - endpoint.Hostname = &addr.Hostname - } - - endpointSlice.Endpoints = []discoveryv1.Endpoint{endpoint} portName := "https" endpointSlice.Ports = []discoveryv1.EndpointPort{ @@ -177,7 +171,7 @@ func (c *ClusterReconciler) ensureHCPKubernetesEndpointSlice(ctx context.Context } log.V(1).Info("HCP kubernetes endpointslice reconciled", - "address", addr.IP, "hostname", addr.Hostname, "port", port) + "address", addr.IP, "host", host, "port", port) return nil } @@ -248,7 +242,7 @@ func (c *ClusterReconciler) ensureHCPKubernetesEndpoints(ctx context.Context, cl return fmt.Errorf("upserting default/kubernetes endpoints in virtual cluster: %w", err) } - log.V(1).Info("HCP kubernetes endpoints reconciled", "address", addr.IP, "hostname", addr.Hostname, "port", port) + log.V(1).Info("HCP kubernetes endpoints reconciled", "address", addr.IP, "host", host, "port", port) return nil } @@ -288,8 +282,9 @@ func parseHCPHostPort(rawURL string) (string, int32, error) { // hcpEndpointAddress builds a corev1.EndpointAddress from the externally // reachable host. Endpoints require an IP; if the host is a DNS name we -// resolve it and keep the original name as Hostname so logs/events remain -// human-readable. +// resolve it. The Hostname field is intentionally left unset: the +// kubernetes API validates it as a DNS-1123 label (no dots), so an FQDN +// like "host.example.com" would be rejected. func hcpEndpointAddress(host string) (corev1.EndpointAddress, error) { if ip := net.ParseIP(host); ip != nil { if ip.IsLoopback() { @@ -317,8 +312,8 @@ func hcpEndpointAddress(host string) (corev1.EndpointAddress, error) { } if v4 := filteredIPs[0].To4(); v4 != nil { - return corev1.EndpointAddress{IP: v4.String(), Hostname: host}, nil + return corev1.EndpointAddress{IP: v4.String()}, nil } - return corev1.EndpointAddress{IP: filteredIPs[0].String(), Hostname: host}, nil + return corev1.EndpointAddress{IP: filteredIPs[0].String()}, nil } diff --git a/tests/e2e/cluster_create_test.go b/tests/e2e/cluster_create_test.go index 9e2d2d5a..700096a3 100644 --- a/tests/e2e/cluster_create_test.go +++ b/tests/e2e/cluster_create_test.go @@ -5,8 +5,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/rancher/k3k/pkg/apis/k3k.io/v1beta1" + k3kcluster "github.com/rancher/k3k/pkg/controller/cluster" fwk3k "github.com/rancher/k3k/tests/framework/k3k" . "github.com/onsi/ginkgo/v2" @@ -61,3 +64,79 @@ var _ = When("creating a shared mode cluster", Label(e2eTestLabel), Label(slowTe Should(Succeed()) }) }) + +var _ = When("creating an HCP mode cluster", Label(e2eTestLabel), Label(slowTestsLabel), func() { + var virtualCluster *VirtualCluster + + BeforeEach(func() { + namespace := fwk3k.CreateNamespace(k8s) + + DeferCleanup(func() { + fwk3k.DeleteNamespaces(k8s, namespace.Name) + }) + + cluster := NewCluster(namespace.Name) + cluster.Spec.Mode = v1beta1.HCPClusterMode + + CreateCluster(cluster) + client, restConfig := NewVirtualK8sClientAndConfig(cluster) + + virtualCluster = &VirtualCluster{ + Cluster: cluster, + RestConfig: restConfig, + Client: client, + } + }) + + It("is up and running", func() { + Eventually(func(g Gomega) { + ctx := GinkgoT().Context() + key := client.ObjectKeyFromObject(virtualCluster.Cluster) + g.Expect(k8sClient.Get(ctx, key, virtualCluster.Cluster)).To(Succeed()) + g.Expect(virtualCluster.Cluster.Status.Phase).To(BeEquivalentTo(v1beta1.ClusterReady)) + }). + WithTimeout(time.Minute). + WithPolling(time.Second). + Should(Succeed()) + }) + + It("creates a populated token secret", func() { + ctx := GinkgoT().Context() + + var tokenSecret corev1.Secret + err := k8sClient.Get(ctx, client.ObjectKey{ + Name: k3kcluster.TokenSecretName(virtualCluster.Cluster.Name), + Namespace: virtualCluster.Cluster.Namespace, + }, &tokenSecret) + Expect(err).NotTo(HaveOccurred()) + Expect(tokenSecret.Data["token"]).NotTo(BeEmpty()) + }) + + It("reconciles default/kubernetes Endpoints and EndpointSlice", func() { + ctx := GinkgoT().Context() + + Eventually(func(g Gomega) { + endpoints, err := virtualCluster.Client.CoreV1().Endpoints("default").Get(ctx, "kubernetes", metav1.GetOptions{}) + g.Expect(err).To(Not(HaveOccurred())) + g.Expect(endpoints.Subsets).To(HaveLen(1)) + g.Expect(endpoints.Subsets[0].Addresses).To(HaveLen(1)) + g.Expect(endpoints.Subsets[0].Addresses[0].IP).To(Equal(hostIP)) + }). + WithTimeout(time.Minute). + WithPolling(time.Second). + Should(Succeed()) + + Eventually(func(g Gomega) { + slices, err := virtualCluster.Client.DiscoveryV1().EndpointSlices("default").List(ctx, metav1.ListOptions{ + LabelSelector: "kubernetes.io/service-name=kubernetes", + }) + g.Expect(err).To(Not(HaveOccurred())) + g.Expect(slices.Items).To(HaveLen(1)) + g.Expect(slices.Items[0].Endpoints).To(HaveLen(1)) + g.Expect(slices.Items[0].Endpoints[0].Addresses).To(ContainElement(hostIP)) + }). + WithTimeout(time.Minute). + WithPolling(time.Second). + Should(Succeed()) + }) +})