Skip to content

Commit c71befd

Browse files
authored
Merge pull request #403 from mars1024/dev/reserve-after-terminated
only reserver ip instance after pod terminated
2 parents b5f6c0c + b826ab8 commit c71befd

File tree

3 files changed

+190
-0
lines changed

3 files changed

+190
-0
lines changed

pkg/controllers/networking/pod_controller.go

+10
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,11 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result
144144
}
145145

146146
if strategy.OwnByStatefulWorkload(ownedObj) {
147+
// Before pod terminated, should not reserve ip instance because of pre-stop
148+
if !utils.PodIsTerminated(pod) {
149+
return ctrl.Result{}, nil
150+
}
151+
147152
if err = r.reserve(ctx, pod); err != nil {
148153
return ctrl.Result{}, wrapError("unable to reserve pod", err)
149154
}
@@ -166,6 +171,11 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result
166171
return ctrl.Result{}, wrapError("unable to remove finalizer", r.removeFinalizer(ctx, pod))
167172
}
168173

174+
// Before pod terminated, should not reserve ip instance because of pre-stop
175+
if !utils.PodIsTerminated(pod) {
176+
return ctrl.Result{}, nil
177+
}
178+
169179
log.V(1).Info("reserve ip for VM pod")
170180
if err = r.reserve(ctx, pod, types.DropPodName(true)); err != nil {
171181
return ctrl.Result{}, wrapError("unable to reserve pod", err)

pkg/controllers/networking/pod_controller_test.go

+170
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,176 @@ var _ = Describe("Pod controller integration test suite", func() {
701701
Expect(k8sClient.Delete(context.Background(), pod, client.GracePeriodSeconds(0))).NotTo(HaveOccurred())
702702
})
703703

704+
It("Check ip reserved only after pod terminated", func() {
705+
By("create a stateful pod requiring IPv4 address")
706+
var ipInstanceName string
707+
pod := simplePodRender(podName, node1Name)
708+
pod.OwnerReferences = []metav1.OwnerReference{ownerReference}
709+
Expect(k8sClient.Create(context.Background(), pod)).Should(Succeed())
710+
711+
By("check the first allocated IPv4 address")
712+
Eventually(
713+
func(g Gomega) {
714+
ipInstances, err := utils.ListAllocatedIPInstancesOfPod(context.Background(), k8sClient, pod)
715+
g.Expect(err).NotTo(HaveOccurred())
716+
g.Expect(ipInstances).To(HaveLen(1))
717+
718+
ipInstance := ipInstances[0]
719+
ipInstanceName = ipInstance.Name
720+
g.Expect(ipInstance.Spec.Address.Version).To(Equal(networkingv1.IPv4))
721+
g.Expect(ipInstance.Spec.Binding.PodUID).To(Equal(pod.UID))
722+
g.Expect(ipInstance.Spec.Binding.PodName).To(Equal(pod.Name))
723+
g.Expect(ipInstance.Spec.Binding.NodeName).To(Equal(node1Name))
724+
g.Expect(ipInstance.Spec.Binding.ReferredObject).To(Equal(networkingv1.ObjectMeta{
725+
Kind: ownerReference.Kind,
726+
Name: ownerReference.Name,
727+
UID: ownerReference.UID,
728+
}))
729+
730+
g.Expect(ipInstance.Spec.Binding.Stateful).NotTo(BeNil())
731+
g.Expect(ipInstance.Spec.Binding.Stateful.Index).NotTo(BeNil())
732+
733+
idx := *ipInstance.Spec.Binding.Stateful.Index
734+
g.Expect(pod.Name).To(Equal(fmt.Sprintf("pod-%d", idx)))
735+
736+
g.Expect(ipInstance.Spec.Network).To(Equal(underlayNetworkName))
737+
g.Expect(ipInstance.Spec.Subnet).To(BeElementOf(underlaySubnetName))
738+
}).
739+
WithTimeout(30 * time.Second).
740+
WithPolling(time.Second).
741+
Should(Succeed())
742+
743+
By("update to make sure pod not terminated")
744+
patch := client.MergeFrom(pod.DeepCopy())
745+
pod.Status.ContainerStatuses = []corev1.ContainerStatus{
746+
{
747+
Name: "test",
748+
State: corev1.ContainerState{
749+
Terminated: nil,
750+
},
751+
},
752+
}
753+
Expect(k8sClient.Status().Patch(context.Background(), pod, patch)).NotTo(HaveOccurred())
754+
755+
By("try to delete stateful pod into terminating state")
756+
Expect(k8sClient.Delete(context.Background(), pod, client.GracePeriodSeconds(0))).NotTo(HaveOccurred())
757+
758+
By("check allocated IPv4 address still not reserved after 30s")
759+
Consistently(
760+
func(g Gomega) {
761+
ipInstances, err := utils.ListAllocatedIPInstancesOfPod(context.Background(), k8sClient, pod)
762+
g.Expect(err).NotTo(HaveOccurred())
763+
g.Expect(ipInstances).To(HaveLen(1))
764+
765+
ipInstance := ipInstances[0]
766+
g.Expect(ipInstance.Spec.Binding.NodeName).NotTo(BeEmpty())
767+
768+
}).
769+
WithTimeout(30 * time.Second).
770+
WithPolling(5 * time.Second).
771+
Should(Succeed())
772+
773+
By("update to make sure pod terminated")
774+
patch = client.MergeFrom(pod.DeepCopy())
775+
pod.Status.ContainerStatuses = []corev1.ContainerStatus{
776+
{
777+
Name: "test",
778+
State: corev1.ContainerState{
779+
Terminated: &corev1.ContainerStateTerminated{},
780+
},
781+
},
782+
}
783+
Expect(k8sClient.Status().Patch(context.Background(), pod, patch)).NotTo(HaveOccurred())
784+
785+
By("check the allocated IPv4 address is reserved")
786+
Eventually(
787+
func(g Gomega) {
788+
ipInstances, err := utils.ListAllocatedIPInstancesOfPod(context.Background(), k8sClient, pod)
789+
g.Expect(err).NotTo(HaveOccurred())
790+
g.Expect(ipInstances).To(HaveLen(1))
791+
792+
ipInstance := ipInstances[0]
793+
g.Expect(ipInstance.Name).To(Equal(ipInstanceName))
794+
g.Expect(ipInstance.Spec.Address.Version).To(Equal(networkingv1.IPv4))
795+
g.Expect(ipInstance.Spec.Binding.PodUID).To(BeEmpty())
796+
g.Expect(ipInstance.Spec.Binding.PodName).To(Equal(pod.Name))
797+
g.Expect(ipInstance.Spec.Binding.NodeName).To(BeEmpty())
798+
g.Expect(ipInstance.Spec.Binding.ReferredObject).To(Equal(networkingv1.ObjectMeta{
799+
Kind: ownerReference.Kind,
800+
Name: ownerReference.Name,
801+
UID: ownerReference.UID,
802+
}))
803+
804+
g.Expect(ipInstance.Spec.Binding.Stateful).NotTo(BeNil())
805+
g.Expect(ipInstance.Spec.Binding.Stateful.Index).NotTo(BeNil())
806+
807+
idx := *ipInstance.Spec.Binding.Stateful.Index
808+
g.Expect(pod.Name).To(Equal(fmt.Sprintf("pod-%d", idx)))
809+
810+
g.Expect(ipInstance.Spec.Network).To(Equal(underlayNetworkName))
811+
g.Expect(ipInstance.Spec.Subnet).To(BeElementOf(underlaySubnetName))
812+
}).
813+
WithTimeout(30 * time.Second).
814+
WithPolling(time.Second).
815+
Should(Succeed())
816+
817+
By("make sure pod deleted")
818+
Eventually(
819+
func(g Gomega) {
820+
err := k8sClient.Get(context.Background(),
821+
types.NamespacedName{
822+
Namespace: pod.Namespace,
823+
Name: podName,
824+
},
825+
&corev1.Pod{})
826+
g.Expect(err).NotTo(BeNil())
827+
g.Expect(errors.IsNotFound(err)).To(BeTrue())
828+
}).
829+
WithTimeout(30 * time.Second).
830+
WithPolling(time.Second).
831+
Should(Succeed())
832+
833+
By("recreate the stateful pod")
834+
pod = simplePodRender(podName, node1Name)
835+
pod.OwnerReferences = []metav1.OwnerReference{ownerReference}
836+
Expect(k8sClient.Create(context.Background(), pod)).NotTo(HaveOccurred())
837+
838+
By("check the allocated IPv4 address is retained and reused")
839+
Eventually(
840+
func(g Gomega) {
841+
ipInstances, err := utils.ListAllocatedIPInstancesOfPod(context.Background(), k8sClient, pod)
842+
g.Expect(err).NotTo(HaveOccurred())
843+
g.Expect(ipInstances).To(HaveLen(1))
844+
845+
ipInstance := ipInstances[0]
846+
g.Expect(ipInstance.Name).To(Equal(ipInstanceName))
847+
g.Expect(ipInstance.Spec.Address.Version).To(Equal(networkingv1.IPv4))
848+
g.Expect(ipInstance.Spec.Binding.PodUID).To(Equal(pod.UID))
849+
g.Expect(ipInstance.Spec.Binding.PodName).To(Equal(pod.Name))
850+
g.Expect(ipInstance.Spec.Binding.NodeName).To(Equal(node1Name))
851+
g.Expect(ipInstance.Spec.Binding.ReferredObject).To(Equal(networkingv1.ObjectMeta{
852+
Kind: ownerReference.Kind,
853+
Name: ownerReference.Name,
854+
UID: ownerReference.UID,
855+
}))
856+
857+
g.Expect(ipInstance.Spec.Binding.Stateful).NotTo(BeNil())
858+
g.Expect(ipInstance.Spec.Binding.Stateful.Index).NotTo(BeNil())
859+
860+
idx := *ipInstance.Spec.Binding.Stateful.Index
861+
g.Expect(pod.Name).To(Equal(fmt.Sprintf("pod-%d", idx)))
862+
863+
g.Expect(ipInstance.Spec.Network).To(Equal(underlayNetworkName))
864+
g.Expect(ipInstance.Spec.Subnet).To(BeElementOf(underlaySubnetName))
865+
}).
866+
WithTimeout(30 * time.Second).
867+
WithPolling(time.Second).
868+
Should(Succeed())
869+
870+
By("remove the test pod")
871+
Expect(k8sClient.Delete(context.Background(), pod, client.GracePeriodSeconds(0))).NotTo(HaveOccurred())
872+
})
873+
704874
AfterEach(func() {
705875
By("make sure test ip instances cleaned up")
706876
Expect(k8sClient.DeleteAllOf(

pkg/controllers/utils/pod.go

+10
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,14 @@ func PodIsScheduled(pod *v1.Pod) bool {
5151
return len(pod.Spec.NodeName) > 0
5252
}
5353

54+
func PodIsTerminated(pod *v1.Pod) bool {
55+
for i := range pod.Status.ContainerStatuses {
56+
if pod.Status.ContainerStatuses[i].State.Terminated == nil {
57+
return false
58+
}
59+
}
60+
61+
return true
62+
}
63+
5464
var ParseNetworkConfigOfPodByPriority = utils.ParseNetworkConfigOfPodByPriority

0 commit comments

Comments
 (0)