diff --git a/pkg/controller/ippool/common.go b/pkg/controller/ippool/common.go index 67c93b0..c3ecece 100644 --- a/pkg/controller/ippool/common.go +++ b/pkg/controller/ippool/common.go @@ -191,6 +191,14 @@ func NewIPPoolBuilder(namespace, name string) *IPPoolBuilder { } } +func (b *IPPoolBuilder) Annotation(key, value string) *IPPoolBuilder { + if b.ipPool.Annotations == nil { + b.ipPool.Annotations = make(map[string]string) + } + b.ipPool.Annotations[key] = value + return b +} + func (b *IPPoolBuilder) NetworkName(networkName string) *IPPoolBuilder { b.ipPool.Spec.NetworkName = networkName return b diff --git a/pkg/controller/ippool/controller.go b/pkg/controller/ippool/controller.go index aa2c3ce..fc9fabb 100644 --- a/pkg/controller/ippool/controller.go +++ b/pkg/controller/ippool/controller.go @@ -29,7 +29,8 @@ import ( const ( controllerName = "vm-dhcp-ippool-controller" - multusNetworksAnnotationKey = "k8s.v1.cni.cncf.io/networks" + multusNetworksAnnotationKey = "k8s.v1.cni.cncf.io/networks" + holdIPPoolAgentUpgradeAnnotationKey = "network.harvesterhci.io/hold-ippool-agent-upgrade" ipPoolNamespaceLabelKey = network.GroupName + "/ippool-namespace" ipPoolNameLabelKey = network.GroupName + "/ippool-name" @@ -281,8 +282,7 @@ func (h *Handler) DeployAgent(ipPool *networkv1.IPPool, status networkv1.IPPoolS } if ipPool.Status.AgentPodRef != nil { - status.AgentPodRef.Image = h.agentImage.String() - + status.AgentPodRef.Image = h.getAgentImage(ipPool) pod, err := h.podCache.Get(ipPool.Status.AgentPodRef.Namespace, ipPool.Status.AgentPodRef.Name) if err != nil { if !apierrors.IsNotFound(err) { @@ -434,6 +434,14 @@ func isPodReady(pod *corev1.Pod) bool { return false } +func (h *Handler) getAgentImage(ipPool *networkv1.IPPool) string { + _, ok := ipPool.Annotations[holdIPPoolAgentUpgradeAnnotationKey] + if ok { + return ipPool.Status.AgentPodRef.Image + } + return h.agentImage.String() +} + func (h *Handler) cleanup(ipPool *networkv1.IPPool) error { if ipPool.Status.AgentPodRef == nil { return nil diff --git a/pkg/controller/ippool/controller_test.go b/pkg/controller/ippool/controller_test.go index 67bd871..aed77c9 100644 --- a/pkg/controller/ippool/controller_test.go +++ b/pkg/controller/ippool/controller_test.go @@ -555,6 +555,64 @@ func TestHandler_DeployAgent(t *testing.T) { assert.Equal(t, expectedStatus, status) }) + t.Run("agent pod upgrade held back", func(t *testing.T) { + givenIPPool := newTestIPPoolBuilder(). + Annotation(holdIPPoolAgentUpgradeAnnotationKey, "true"). + ServerIP(testServerIP). + CIDR(testCIDR). + NetworkName(testNetworkName). + AgentPodRef(testPodNamespace, testPodName, testImage, "").Build() + givenNAD := newTestNetworkAttachmentDefinitionBuilder(). + Label(clusterNetworkLabelKey, testClusterNetwork).Build() + givenPod, _ := prepareAgentPod( + NewIPPoolBuilder(testIPPoolNamespace, testIPPoolName). + ServerIP(testServerIP). + CIDR(testCIDR). + NetworkName(testNetworkName).Build(), + false, + testPodNamespace, + testClusterNetwork, + testServiceAccountName, + &config.Image{ + Repository: testImageRepository, + Tag: testImageTag, + }, + ) + + expectedStatus := newTestIPPoolStatusBuilder(). + AgentPodRef(testPodNamespace, testPodName, testImage, "").Build() + + nadGVR := schema.GroupVersionResource{ + Group: "k8s.cni.cncf.io", + Version: "v1", + Resource: "network-attachment-definitions", + } + + clientset := fake.NewSimpleClientset() + err := clientset.Tracker().Create(nadGVR, givenNAD, givenNAD.Namespace) + assert.Nil(t, err, "mock resource should add into fake controller tracker") + + k8sclientset := k8sfake.NewSimpleClientset() + err = k8sclientset.Tracker().Add(givenPod) + assert.Nil(t, err, "mock resource should add into fake controller tracker") + + handler := Handler{ + agentNamespace: testPodNamespace, + agentImage: &config.Image{ + Repository: testImageRepository, + Tag: testImageTagNew, + }, + agentServiceAccountName: testServiceAccountName, + nadCache: fakeclient.NetworkAttachmentDefinitionCache(clientset.K8sCniCncfIoV1().NetworkAttachmentDefinitions), + podClient: fakeclient.PodClient(k8sclientset.CoreV1().Pods), + podCache: fakeclient.PodCache(k8sclientset.CoreV1().Pods), + } + + status, err := handler.DeployAgent(givenIPPool, givenIPPool.Status) + assert.Nil(t, err) + assert.Equal(t, expectedStatus, status) + }) + t.Run("existing agent pod uid mismatch", func(t *testing.T) { givenIPPool := newTestIPPoolBuilder(). ServerIP(testServerIP).