Skip to content

Commit 70a6dcf

Browse files
authored
Merge pull request #404 from hhyasdf/release/v0.8.7
[CHERRY PICK] release for v0.8.7
2 parents df30173 + 090558e commit 70a6dcf

14 files changed

+230
-58
lines changed

charts/hybridnet/crds/multicluster.alibaba.com_remoteclusters.yaml

+8-7
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,14 @@ spec:
8383
description: "Condition contains details for one aspect of the current
8484
state of this API Resource. --- This struct is intended for direct
8585
use as an array at the field path .status.conditions. For example,
86-
type FooStatus struct{ // Represents the observations of a
87-
foo's current state. // Known .status.conditions.type are:
88-
\"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type
89-
\ // +patchStrategy=merge // +listType=map // +listMapKey=type
90-
\ Conditions []metav1.Condition `json:\"conditions,omitempty\"
91-
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
92-
\n // other fields }"
86+
\n \ttype FooStatus struct{ \t // Represents the observations
87+
of a foo's current state. \t // Known .status.conditions.type
88+
are: \"Available\", \"Progressing\", and \"Degraded\" \t //
89+
+patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map
90+
\t // +listMapKey=type \t Conditions []metav1.Condition
91+
`json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
92+
protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields
93+
\t}"
9394
properties:
9495
lastTransitionTime:
9596
description: lastTransitionTime is the last time the condition

charts/hybridnet/crds/multicluster.alibaba.com_remoteendpointslice.yaml

+6-5
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,13 @@ spec:
6767
a service.
6868
properties:
6969
addresses:
70-
description: addresses of this endpoint. The contents of this
70+
description: 'addresses of this endpoint. The contents of this
7171
field are interpreted according to the corresponding EndpointSlice
7272
addressType field. Consumers must handle different types of
7373
addresses in the context of their own capabilities. This must
74-
contain at least one address but no more than 100.
74+
contain at least one address but no more than 100. These are
75+
all assumed to be fungible and clients may choose to only
76+
use the first element. Refer to: https://issue.k8s.io/106267'
7577
items:
7678
type: string
7779
type: array
@@ -136,8 +138,7 @@ spec:
136138
nodeName:
137139
description: nodeName represents the name of the Node hosting
138140
this endpoint. This can be used to determine endpoints local
139-
to a Node. This field can be enabled with the EndpointSliceNodeName
140-
feature gate.
141+
to a Node.
141142
type: string
142143
targetRef:
143144
description: targetRef is a reference to a Kubernetes object
@@ -212,7 +213,7 @@ spec:
212213
description: The application protocol for this port. This field
213214
follows standard Kubernetes label syntax. Un-prefixed names
214215
are reserved for IANA standard service names (as per RFC-6335
215-
and http://www.iana.org/assignments/service-names). Non-standard
216+
and https://www.iana.org/assignments/service-names). Non-standard
216217
protocols should use prefixed names such as mycompany.com/my-custom-protocol.
217218
type: string
218219
name:

charts/hybridnet/crds/networking.alibaba.com_ipinstances.yaml

-3
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,6 @@ spec:
125125
properties:
126126
nodeName:
127127
type: string
128-
phase:
129-
description: DEPRECATED. Planned to remove in v0.6
130-
type: string
131128
podName:
132129
type: string
133130
podNamespace:

charts/hybridnet/templates/daemonsets.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ spec:
8282
- --check-pod-connectivity-from-host={{ .Values.daemon.checkPodConnectivityFromHost }}
8383
- --enable-vlan-arp-enhancement={{ .Values.daemon.enableVlanARPEnhancement }}
8484
- --feature-gates=MultiCluster={{ .Values.multiCluster }}
85+
- --update-ipinstance-status={{ .Values.daemon.updateIPInstanceStatus }}
8586
securityContext:
8687
runAsUser: 0
8788
privileged: true

charts/hybridnet/values.yaml

+4-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ daemon:
9090

9191
## If it is empty, daemon on each node will take one of the valid address of the vxlan interface's parent
9292
## (try ipv4 addresses first and then ipv6 addresses if no valid ipv4 address exists) as node's VTEP address
93-
## randomly. If is is not empty, the first result matches any of the CIDRs will be chose as VTEP address.
93+
## randomly. If it is not empty, the first result matches any of the CIDRs will be chosen as VTEP address.
9494
vtepAddressCIDRs: "0.0.0.0/0,::/0"
9595

9696
# -- The community CNI plugins needed to be copied by hybridnet from inside container to the /opt/cni/bin/ directory of host
@@ -102,6 +102,9 @@ daemon:
102102
# -- Whether will daemon check the connectivity of local pod before staring it
103103
checkPodConnectivityFromHost: true
104104

105+
# -- Whether will daemon update the status of IPInstance while create pod sandbox
106+
updateIPInstanceStatus: true
107+
105108
# -- Specifies the resources for the cni-daemon containers
106109
resources: {}
107110
# limits:

pkg/apis/networking/v1/ipinstance_types.go

-3
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,6 @@ type StatefulInfo struct {
7070
type IPInstanceStatus struct {
7171
// +kubebuilder:validation:Optional
7272
NodeName string `json:"nodeName,omitempty"`
73-
// DEPRECATED. Planned to remove in v0.6
74-
// +kubebuilder:validation:Optional
75-
Phase IPPhase `json:"phase,omitempty"`
7673
// +kubebuilder:validation:Optional
7774
PodName string `json:"podName,omitempty"`
7875
// +kubebuilder:validation:Optional

pkg/apis/networking/v1/types.go

-2
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,6 @@ type VTEPInfo struct {
124124
LocalIPs []string `json:"localIPs,omitempty"`
125125
}
126126

127-
type IPPhase string
128-
129127
// The conversion process from IPInstance v1.1 to v1.2 has been removed after hybridnet v0.6.0.
130128
const (
131129
IPInstanceV12 = "v1.2"

pkg/controllers/multicluster/remotevtep_controller.go

-3
Original file line numberDiff line numberDiff line change
@@ -285,9 +285,6 @@ func (r *RemoteVtepReconciler) SetupWithManager(mgr ctrl.Manager) (err error) {
285285
constants.LabelNode,
286286
},
287287
},
288-
// TODO: phase change means nothing in new IPInstance model,
289-
// to be removed in next major version
290-
&utils.IPInstancePhaseChangePredicate{},
291288
),
292289
),
293290
).

pkg/controllers/networking/pod_controller.go

+11-1
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)
@@ -692,7 +702,7 @@ func (r *PodReconciler) checkMACAddressCollision(pod *corev1.Pod, networkName st
692702
if !ipInstance.DeletionTimestamp.IsZero() {
693703
continue
694704
}
695-
if ipInstance.Status.PodNamespace != pod.GetNamespace() || ipInstance.Status.PodName != pod.GetName() {
705+
if ipInstance.Namespace != pod.GetNamespace() || ipInstance.Spec.Binding.PodName != pod.GetName() {
696706
return fmt.Errorf("specified mac address %s is in conflict with existing ip instance %s/%s", macAddr, ipInstance.Namespace, ipInstance.Name)
697707
}
698708
}

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

pkg/controllers/utils/predicates.go

-18
Original file line numberDiff line numberDiff line change
@@ -186,24 +186,6 @@ func (s SpecifiedLabelChangedPredicate) Update(e event.UpdateEvent) bool {
186186
return false
187187
}
188188

189-
type IPInstancePhaseChangePredicate struct {
190-
predicate.Funcs
191-
}
192-
193-
// Update implements default UpdateEvent filter for checking whether IPInstance phase change
194-
func (IPInstancePhaseChangePredicate) Update(e event.UpdateEvent) bool {
195-
oldIPInstance, ok := e.ObjectOld.(*networkingv1.IPInstance)
196-
if !ok {
197-
return false
198-
}
199-
newIPInstance, ok := e.ObjectNew.(*networkingv1.IPInstance)
200-
if !ok {
201-
return false
202-
}
203-
204-
return oldIPInstance.Status.Phase != newIPInstance.Status.Phase
205-
}
206-
207189
type RemoteClusterUUIDChangePredicate struct {
208190
predicate.Funcs
209191
}

pkg/daemon/config/config.go

+3
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ type Configuration struct {
103103
EnableVlanArpEnhancement bool
104104
PatchCalicoPodIPsAnnotation bool
105105
CheckPodConnectivityFromHost bool
106+
UpdateIPInstanceStatus bool
106107
}
107108

108109
// ParseFlags will parse cmd args then init kubeClient and configuration
@@ -134,6 +135,7 @@ func ParseFlags() (*Configuration, error) {
134135
argIPv6RouteCacheGCThresh = pflag.Int("ipv6-route-cache-gc-thresh", DefaultIPv6RouteCacheGCThresh, "Value to set net.ipv6.route.gc_thresh")
135136
argPatchCalicoPodIPsAnnotation = pflag.Bool("patch-calico-pod-ips-annotation", true, "Patch \"cni.projectcalico.org/podIPs\" annotations to pod")
136137
argCheckPodConnectivityFromHost = pflag.Bool("check-pod-connectivity-from-host", true, "Check pod's connectivity from host before start it")
138+
argUpdateIPInstanceStatus = pflag.Bool("update-ipinstance-status", true, "Update ipinstance status while creating pod sandbox")
137139
)
138140

139141
// mute info log for ipset lib
@@ -172,6 +174,7 @@ func ParseFlags() (*Configuration, error) {
172174
IPv6RouteCacheGCThresh: *argIPv6RouteCacheGCThresh,
173175
PatchCalicoPodIPsAnnotation: *argPatchCalicoPodIPsAnnotation,
174176
CheckPodConnectivityFromHost: *argCheckPodConnectivityFromHost,
177+
UpdateIPInstanceStatus: *argUpdateIPInstanceStatus,
175178
}
176179

177180
if *argPreferVlanInterfaces == "" {

0 commit comments

Comments
 (0)