Skip to content

Commit

Permalink
Merge pull request alibaba#403 from mars1024/dev/reserve-after-termin…
Browse files Browse the repository at this point in the history
…ated

only reserver ip instance after pod terminated
  • Loading branch information
hhyasdf committed Sep 21, 2023
1 parent 00f48b6 commit 090558e
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 0 deletions.
10 changes: 10 additions & 0 deletions pkg/controllers/networking/pod_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result
}

if strategy.OwnByStatefulWorkload(ownedObj) {
// Before pod terminated, should not reserve ip instance because of pre-stop
if !utils.PodIsTerminated(pod) {
return ctrl.Result{}, nil
}

if err = r.reserve(ctx, pod); err != nil {
return ctrl.Result{}, wrapError("unable to reserve pod", err)
}
Expand All @@ -166,6 +171,11 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result
return ctrl.Result{}, wrapError("unable to remove finalizer", r.removeFinalizer(ctx, pod))
}

// Before pod terminated, should not reserve ip instance because of pre-stop
if !utils.PodIsTerminated(pod) {
return ctrl.Result{}, nil
}

log.V(1).Info("reserve ip for VM pod")
if err = r.reserve(ctx, pod, types.DropPodName(true)); err != nil {
return ctrl.Result{}, wrapError("unable to reserve pod", err)
Expand Down
170 changes: 170 additions & 0 deletions pkg/controllers/networking/pod_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,176 @@ var _ = Describe("Pod controller integration test suite", func() {
Expect(k8sClient.Delete(context.Background(), pod, client.GracePeriodSeconds(0))).NotTo(HaveOccurred())
})

It("Check ip reserved only after pod terminated", func() {
By("create a stateful pod requiring IPv4 address")
var ipInstanceName string
pod := simplePodRender(podName, node1Name)
pod.OwnerReferences = []metav1.OwnerReference{ownerReference}
Expect(k8sClient.Create(context.Background(), pod)).Should(Succeed())

By("check the first allocated IPv4 address")
Eventually(
func(g Gomega) {
ipInstances, err := utils.ListAllocatedIPInstancesOfPod(context.Background(), k8sClient, pod)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(ipInstances).To(HaveLen(1))

ipInstance := ipInstances[0]
ipInstanceName = ipInstance.Name
g.Expect(ipInstance.Spec.Address.Version).To(Equal(networkingv1.IPv4))
g.Expect(ipInstance.Spec.Binding.PodUID).To(Equal(pod.UID))
g.Expect(ipInstance.Spec.Binding.PodName).To(Equal(pod.Name))
g.Expect(ipInstance.Spec.Binding.NodeName).To(Equal(node1Name))
g.Expect(ipInstance.Spec.Binding.ReferredObject).To(Equal(networkingv1.ObjectMeta{
Kind: ownerReference.Kind,
Name: ownerReference.Name,
UID: ownerReference.UID,
}))

g.Expect(ipInstance.Spec.Binding.Stateful).NotTo(BeNil())
g.Expect(ipInstance.Spec.Binding.Stateful.Index).NotTo(BeNil())

idx := *ipInstance.Spec.Binding.Stateful.Index
g.Expect(pod.Name).To(Equal(fmt.Sprintf("pod-%d", idx)))

g.Expect(ipInstance.Spec.Network).To(Equal(underlayNetworkName))
g.Expect(ipInstance.Spec.Subnet).To(BeElementOf(underlaySubnetName))
}).
WithTimeout(30 * time.Second).
WithPolling(time.Second).
Should(Succeed())

By("update to make sure pod not terminated")
patch := client.MergeFrom(pod.DeepCopy())
pod.Status.ContainerStatuses = []corev1.ContainerStatus{
{
Name: "test",
State: corev1.ContainerState{
Terminated: nil,
},
},
}
Expect(k8sClient.Status().Patch(context.Background(), pod, patch)).NotTo(HaveOccurred())

By("try to delete stateful pod into terminating state")
Expect(k8sClient.Delete(context.Background(), pod, client.GracePeriodSeconds(0))).NotTo(HaveOccurred())

By("check allocated IPv4 address still not reserved after 30s")
Consistently(
func(g Gomega) {
ipInstances, err := utils.ListAllocatedIPInstancesOfPod(context.Background(), k8sClient, pod)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(ipInstances).To(HaveLen(1))

ipInstance := ipInstances[0]
g.Expect(ipInstance.Spec.Binding.NodeName).NotTo(BeEmpty())

}).
WithTimeout(30 * time.Second).
WithPolling(5 * time.Second).
Should(Succeed())

By("update to make sure pod terminated")
patch = client.MergeFrom(pod.DeepCopy())
pod.Status.ContainerStatuses = []corev1.ContainerStatus{
{
Name: "test",
State: corev1.ContainerState{
Terminated: &corev1.ContainerStateTerminated{},
},
},
}
Expect(k8sClient.Status().Patch(context.Background(), pod, patch)).NotTo(HaveOccurred())

By("check the allocated IPv4 address is reserved")
Eventually(
func(g Gomega) {
ipInstances, err := utils.ListAllocatedIPInstancesOfPod(context.Background(), k8sClient, pod)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(ipInstances).To(HaveLen(1))

ipInstance := ipInstances[0]
g.Expect(ipInstance.Name).To(Equal(ipInstanceName))
g.Expect(ipInstance.Spec.Address.Version).To(Equal(networkingv1.IPv4))
g.Expect(ipInstance.Spec.Binding.PodUID).To(BeEmpty())
g.Expect(ipInstance.Spec.Binding.PodName).To(Equal(pod.Name))
g.Expect(ipInstance.Spec.Binding.NodeName).To(BeEmpty())
g.Expect(ipInstance.Spec.Binding.ReferredObject).To(Equal(networkingv1.ObjectMeta{
Kind: ownerReference.Kind,
Name: ownerReference.Name,
UID: ownerReference.UID,
}))

g.Expect(ipInstance.Spec.Binding.Stateful).NotTo(BeNil())
g.Expect(ipInstance.Spec.Binding.Stateful.Index).NotTo(BeNil())

idx := *ipInstance.Spec.Binding.Stateful.Index
g.Expect(pod.Name).To(Equal(fmt.Sprintf("pod-%d", idx)))

g.Expect(ipInstance.Spec.Network).To(Equal(underlayNetworkName))
g.Expect(ipInstance.Spec.Subnet).To(BeElementOf(underlaySubnetName))
}).
WithTimeout(30 * time.Second).
WithPolling(time.Second).
Should(Succeed())

By("make sure pod deleted")
Eventually(
func(g Gomega) {
err := k8sClient.Get(context.Background(),
types.NamespacedName{
Namespace: pod.Namespace,
Name: podName,
},
&corev1.Pod{})
g.Expect(err).NotTo(BeNil())
g.Expect(errors.IsNotFound(err)).To(BeTrue())
}).
WithTimeout(30 * time.Second).
WithPolling(time.Second).
Should(Succeed())

By("recreate the stateful pod")
pod = simplePodRender(podName, node1Name)
pod.OwnerReferences = []metav1.OwnerReference{ownerReference}
Expect(k8sClient.Create(context.Background(), pod)).NotTo(HaveOccurred())

By("check the allocated IPv4 address is retained and reused")
Eventually(
func(g Gomega) {
ipInstances, err := utils.ListAllocatedIPInstancesOfPod(context.Background(), k8sClient, pod)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(ipInstances).To(HaveLen(1))

ipInstance := ipInstances[0]
g.Expect(ipInstance.Name).To(Equal(ipInstanceName))
g.Expect(ipInstance.Spec.Address.Version).To(Equal(networkingv1.IPv4))
g.Expect(ipInstance.Spec.Binding.PodUID).To(Equal(pod.UID))
g.Expect(ipInstance.Spec.Binding.PodName).To(Equal(pod.Name))
g.Expect(ipInstance.Spec.Binding.NodeName).To(Equal(node1Name))
g.Expect(ipInstance.Spec.Binding.ReferredObject).To(Equal(networkingv1.ObjectMeta{
Kind: ownerReference.Kind,
Name: ownerReference.Name,
UID: ownerReference.UID,
}))

g.Expect(ipInstance.Spec.Binding.Stateful).NotTo(BeNil())
g.Expect(ipInstance.Spec.Binding.Stateful.Index).NotTo(BeNil())

idx := *ipInstance.Spec.Binding.Stateful.Index
g.Expect(pod.Name).To(Equal(fmt.Sprintf("pod-%d", idx)))

g.Expect(ipInstance.Spec.Network).To(Equal(underlayNetworkName))
g.Expect(ipInstance.Spec.Subnet).To(BeElementOf(underlaySubnetName))
}).
WithTimeout(30 * time.Second).
WithPolling(time.Second).
Should(Succeed())

By("remove the test pod")
Expect(k8sClient.Delete(context.Background(), pod, client.GracePeriodSeconds(0))).NotTo(HaveOccurred())
})

AfterEach(func() {
By("make sure test ip instances cleaned up")
Expect(k8sClient.DeleteAllOf(
Expand Down
10 changes: 10 additions & 0 deletions pkg/controllers/utils/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,14 @@ func PodIsScheduled(pod *v1.Pod) bool {
return len(pod.Spec.NodeName) > 0
}

func PodIsTerminated(pod *v1.Pod) bool {
for i := range pod.Status.ContainerStatuses {
if pod.Status.ContainerStatuses[i].State.Terminated == nil {
return false
}
}

return true
}

var ParseNetworkConfigOfPodByPriority = utils.ParseNetworkConfigOfPodByPriority

0 comments on commit 090558e

Please sign in to comment.