From 47ba33586d82d50a33d774809f58ff8ff2e9c7ec Mon Sep 17 00:00:00 2001 From: ColdsteelRail <574252631@qq.com> Date: Sat, 16 May 2026 11:34:07 +0800 Subject: [PATCH] feat: auto-set pod hostname when using PersistentSequence naming When NamingStrategy.PodNamingSuffixPolicy is PersistentSequence, pod names are deterministic before creation, so automatically set pod.spec.hostname to the pod name. This removes the need for a separate HostnamePolicy field and makes the behavior implicit. - Add GetPostCreateFunc to XSetOperation for hostname injection - Add integration test for auto hostname with PersistentSequence - Update kube-api to v0.7.5-0.20260512114711-9570d38337c2 - Update kube-xset to v0.0.2-0.20260515171226-5fb80db19c40 - Add GOLANGCI_VERSION to Makefile Co-Authored-By: Claude Opus 4.6 --- Makefile | 1 + go.mod | 4 +- go.sum | 8 +- .../collaset/collaset_controller.go | 15 ++++ .../collaset/collaset_controller_test.go | 81 +++++++++++++++++++ 5 files changed, 103 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 6f80c50a..6f070ee9 100644 --- a/Makefile +++ b/Makefile @@ -172,6 +172,7 @@ GINKGO ?= $(LOCALBIN)/ginkgo ## Tool Versions KUSTOMIZE_VERSION ?= v4.5.5 CONTROLLER_TOOLS_VERSION ?= v0.10.0 +GOLANGCI_VERSION ?= v2.0.2 GINKGO_VERSION ?= 1.16.5 KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" diff --git a/go.mod b/go.mod index bbc0d796..6b416f2b 100644 --- a/go.mod +++ b/go.mod @@ -22,9 +22,9 @@ require ( k8s.io/klog/v2 v2.130.1 k8s.io/kubernetes v0.0.0-00010101000000-000000000000 k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 - kusionstack.io/kube-api v0.7.5-0.20260127130112-9424ce325e09 + kusionstack.io/kube-api v0.7.5-0.20260512114711-9570d38337c2 kusionstack.io/kube-utils v0.2.1-0.20251120063041-6043805ee00d - kusionstack.io/kube-xset v0.0.2-0.20260127130229-a7a010eba7e0 + kusionstack.io/kube-xset v0.0.2-0.20260515171226-5fb80db19c40 kusionstack.io/resourceconsist v0.0.1 sigs.k8s.io/controller-runtime v0.17.3 ) diff --git a/go.sum b/go.sum index fcfdc7c4..a9fa4c2e 100644 --- a/go.sum +++ b/go.sum @@ -1077,12 +1077,12 @@ k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/ k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -kusionstack.io/kube-api v0.7.5-0.20260127130112-9424ce325e09 h1:Kgc1N61F9KoBi1sHCrwoN8ax0j+0f1n6dQDQe2Luy9M= -kusionstack.io/kube-api v0.7.5-0.20260127130112-9424ce325e09/go.mod h1:e1jtrQH2LK5fD2nTyfIXG6nYrYbU8VXShRxTRwVPaLk= +kusionstack.io/kube-api v0.7.5-0.20260512114711-9570d38337c2 h1:jrUVO6a6/fuwdfF8NnehVXGbiYV57AfIJvqnsYRB3sI= +kusionstack.io/kube-api v0.7.5-0.20260512114711-9570d38337c2/go.mod h1:e1jtrQH2LK5fD2nTyfIXG6nYrYbU8VXShRxTRwVPaLk= kusionstack.io/kube-utils v0.2.1-0.20251120063041-6043805ee00d h1:iQtnK03ia/MN4K/6O75EMI91ep7jpcIG0pWyeREBqtg= kusionstack.io/kube-utils v0.2.1-0.20251120063041-6043805ee00d/go.mod h1:KEHTfo1Y8SWMODnckF6daO2cSIW0FJ8fkk8PBA5O2GU= -kusionstack.io/kube-xset v0.0.2-0.20260127130229-a7a010eba7e0 h1:mU1Jjdfgihju0xiYMmW/jSTGhovca/WEID7QzJrwkmw= -kusionstack.io/kube-xset v0.0.2-0.20260127130229-a7a010eba7e0/go.mod h1:FceKgqapMHhwiyIqCziTQRW27fsSXpPS611AApnyiYI= +kusionstack.io/kube-xset v0.0.2-0.20260515171226-5fb80db19c40 h1:GE29FQNibwf6icahu+8JJBQBC5rrZXykUZt60bk65BM= +kusionstack.io/kube-xset v0.0.2-0.20260515171226-5fb80db19c40/go.mod h1:FceKgqapMHhwiyIqCziTQRW27fsSXpPS611AApnyiYI= kusionstack.io/resourceconsist v0.0.1 h1:+k/jriq5Ld7fQUYfWSMGynz/FesHtl3Rk2fmQPjBe0g= kusionstack.io/resourceconsist v0.0.1/go.mod h1:816xS/fY6EOUbPFjXIWW/TGs8/YE46qP4ElKeIiwFdU= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= diff --git a/pkg/controllers/collaset/collaset_controller.go b/pkg/controllers/collaset/collaset_controller.go index c466b5c8..bb12af22 100644 --- a/pkg/controllers/collaset/collaset_controller.go +++ b/pkg/controllers/collaset/collaset_controller.go @@ -143,6 +143,21 @@ func (s *XSetOperation) GetXSetSpec(xset xsetapi.XSetObject) *xsetapi.XSetSpec { return &xSetSpec } +func (s *XSetOperation) GetPostCreateFunc(xset xsetapi.XSetObject) func(client.Object) error { + set := xset.(*appsv1alpha1.CollaSet) + if set.Spec.NamingStrategy == nil || set.Spec.NamingStrategy.PodNamingSuffixPolicy != appsv1alpha1.PodNamingSuffixPolicyPersistentSequence { + return nil + } + return func(object client.Object) error { + pod, ok := object.(*corev1.Pod) + if !ok { + return nil + } + pod.Spec.Hostname = pod.Name + return nil + } +} + func (s *XSetOperation) GetXSetPatch(object metav1.Object) ([]byte, error) { set := object.(*appsv1alpha1.CollaSet) dsBytes, err := json.Marshal(set) diff --git a/pkg/controllers/collaset/collaset_controller_test.go b/pkg/controllers/collaset/collaset_controller_test.go index cf43ea68..24429359 100644 --- a/pkg/controllers/collaset/collaset_controller_test.go +++ b/pkg/controllers/collaset/collaset_controller_test.go @@ -4977,6 +4977,87 @@ var _ = Describe("collaset controller", func() { }, 5*time.Second, 1*time.Second).Should(BeTrue()) Expect(c.Get(context.TODO(), types.NamespacedName{Namespace: cs.Namespace, Name: cs.Name}, cs)).Should(BeNil()) }) + + It("[hostnamePolicy] auto hostname for fixed pod names", func() { + testcase := "test-hostname-policy" + Expect(createNamespace(c, testcase)).Should(BeNil()) + + podList := &corev1.PodList{} + + // Scenario 1: no hostname set when NamingStrategy is not PersistentSequence + csRandom := &appsv1alpha1.CollaSet{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: testcase, + Name: "random", + }, + Spec: appsv1alpha1.CollaSetSpec{ + Replicas: int32Pointer(1), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "random"}, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": "random"}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{Name: "foo", Image: "nginx:v1"}}, + }, + }, + }, + } + Expect(c.Create(context.TODO(), csRandom)).Should(BeNil()) + Eventually(func() bool { + Expect(c.List(context.TODO(), podList, client.InNamespace(csRandom.Namespace), client.MatchingLabels{"app": "random"})).Should(BeNil()) + return len(podList.Items) == 1 + }, 5*time.Second, 1*time.Second).Should(BeTrue()) + Expect(podList.Items[0].Spec.Hostname).Should(BeEmpty(), "pod should not have hostname when namingStrategy is not PersistentSequence") + + // Scenario 2: hostname is automatically set to pod name when using PersistentSequence + csFixed := &appsv1alpha1.CollaSet{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: testcase, + Name: "fixed", + }, + Spec: appsv1alpha1.CollaSetSpec{ + Replicas: int32Pointer(3), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "fixed"}, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": "fixed"}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{Name: "foo", Image: "nginx:v1"}}, + }, + }, + NamingStrategy: &appsv1alpha1.NamingStrategy{ + PodNamingSuffixPolicy: appsv1alpha1.PodNamingSuffixPolicyPersistentSequence, + }, + }, + } + Expect(c.Create(context.TODO(), csFixed)).Should(BeNil()) + Eventually(func() bool { + Expect(c.List(context.TODO(), podList, client.InNamespace(csFixed.Namespace), client.MatchingLabels{"app": "fixed"})).Should(BeNil()) + return len(podList.Items) == 3 + }, 5*time.Second, 1*time.Second).Should(BeTrue()) + for _, pod := range podList.Items { + Expect(pod.Spec.Hostname).Should(Equal(pod.Name), "pod %s should have hostname equal to its name with PersistentSequence", pod.Name) + } + + // Scenario 3: in-place update keeps hostname (hostname is not part of version comparison) + Expect(updateCollaSetWithRetry(c, csFixed.Namespace, csFixed.Name, func(cls *appsv1alpha1.CollaSet) bool { + cls.Spec.Template.Spec.Containers[0].Image = "nginx:v2" + return true + })).Should(BeNil()) + Eventually(func() error { + return expectedStatusReplicas(c, csFixed, 0, 0, 0, 3, 3, 0, 0, 0) + }, 10*time.Second, 1*time.Second).Should(BeNil()) + Expect(c.List(context.TODO(), podList, client.InNamespace(csFixed.Namespace), client.MatchingLabels{"app": "fixed"})).Should(BeNil()) + for _, pod := range podList.Items { + Expect(pod.Spec.Hostname).Should(Equal(pod.Name), "pod %s hostname should remain after in-place update", pod.Name) + } + }) }) func expectedStatusReplicas(c client.Client, cls *appsv1alpha1.CollaSet, scheduledReplicas, readyReplicas, availableReplicas, replicas, updatedReplicas, operatingReplicas,