Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Controller: handle add/remove in the same reconciliation #158

Merged
merged 1 commit into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions pkg/annotations/dynamic-network-status.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package annotations
import (
"encoding/json"
"fmt"
"strings"

corev1 "k8s.io/api/core/v1"

nettypes "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
v1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
nadutils "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/utils"
)

Expand Down Expand Up @@ -64,6 +66,31 @@ func PodDynamicNetworkStatus(currentPod *corev1.Pod) ([]nettypes.NetworkStatus,
return currentIfaceStatus, nil
}

// SetNetworkStatus updates the Pod status
func SetNetworkStatus(pod *corev1.Pod, statuses []v1.NetworkStatus) error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, maybe I'm missing something, but why are you implementing this function instead of re-using the one provided in the network attachment definition client ?

We're already importing those utils.

If there's a reason for that, please also refer it in the commit message.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function in the nad-client will keep the pod object passed in parameter intact. It fetches the pod via the name from the Kubernetes API, updates its annotation and updates it via the Kubenretes API.
https://github.com/k8snetworkplumbingwg/network-attachment-definition-client/blob/v1.4.0/pkg/utils/net-attach-def.go#L87

The function I created is different. It updates the annotation of the pod object passed as parameter without updating it in Kubernetes.
So, when the add happens, the pod object is modified with the new annotations (not yet updated in Kubernetes). The del will then happen with the pod object modified with the added annotation (here is the difference with the previous behavior), and then the del will modify the annotations and then the Kubernetes API is called to update the pod itself.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks for the explanation.

if pod == nil {
return fmt.Errorf("no pod set")
}

var networkStatus []string

for _, status := range statuses {
data, err := json.MarshalIndent(status, "", " ")
if err != nil {
return fmt.Errorf("SetNetworkStatus: error with Marshal Indent: %v", err)
}
networkStatus = append(networkStatus, string(data))
}

if len(pod.Annotations) == 0 {
pod.Annotations = make(map[string]string)
}

pod.Annotations[v1.NetworkStatusAnnot] = fmt.Sprintf("[%s]", strings.Join(networkStatus, ","))

return nil
}

func podNameAndNs(currentPod *corev1.Pod) string {
return fmt.Sprintf("%s/%s", currentPod.GetNamespace(), currentPod.GetName())
}
Expand Down
25 changes: 21 additions & 4 deletions pkg/controller/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,20 @@ func (pnc *PodNetworksController) processNextWorkItem() bool {
}
}

// handleDynamicInterfaceRequest modifies the pod annotation with the latest status
// so the updated status annotation can be obtained from there.
newIfaceStatus, err := annotations.PodDynamicNetworkStatus(pod)
if err != nil {
klog.Errorf("failed while getting network status annotation: %v", err)
return true
}

// apply the network status using the Kubernetes API.
if err := nadutils.SetNetworkStatus(pnc.k8sClientSet, pod, newIfaceStatus); err != nil {
klog.Errorf("failed while setting network status annotation: %v", err)
return true
}
Comment on lines +259 to +263
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commit message says:

The controller is now setting the pod network status annotation only
once in the reconcile loop to avoid conflicts when a network attachment
is added and another one removed at the same time.

But I don't think that's true; IIUC, you're setting it 3 times - once in addNetworks, once in remove networks, and a final one (this one) at the end of processNextWorkItem .

Right ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add and remove is only updating the pod object, it doesn't update the pod in Kubernetes

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks for explaining.


return true
}

Expand Down Expand Up @@ -346,8 +360,9 @@ func (pnc *PodNetworksController) addNetworks(dynamicAttachmentRequest *DynamicA
return fmt.Errorf("failed to compute the updated network status: %v", err)
}

if err := nadutils.SetNetworkStatus(pnc.k8sClientSet, pod, newIfaceStatus); err != nil {
return err
err = annotations.SetNetworkStatus(pod, newIfaceStatus)
if err != nil {
Comment on lines +363 to +364
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the purpose of this change ?

Please keep the old format. It's OK to update the error msg.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This updates the annotation in the pod object passed in parameter. The update of the pod via the Kubernetes API is no longer done here

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, I had missed you had changed the package !!! My bad.

I do think the function's name is misleading though. And this is not as explicit as I would like it to be.

return fmt.Errorf("failed to set the network status annotation: %v", err)
}

return nil
Expand Down Expand Up @@ -404,8 +419,10 @@ func (pnc *PodNetworksController) removeNetworks(dynamicAttachmentRequest *Dynam
err,
)
}
if err := nadutils.SetNetworkStatus(pnc.k8sClientSet, pod, newIfaceStatus); err != nil {
return err

err = annotations.SetNetworkStatus(pod, newIfaceStatus)
if err != nil {
return fmt.Errorf("failed to set the network status annotation: %v", err)
Comment on lines +422 to +425
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

}

return nil
Expand Down
52 changes: 52 additions & 0 deletions pkg/controller/pod_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,58 @@ var _ = Describe("Dynamic Attachment controller", func() {
})
})

When("an attachment is removed and another is added from the pod's network annotations", func() {
maiqueb marked this conversation as resolved.
Show resolved Hide resolved
JustBeforeEach(func() {
pod = updatePodSpec(pod)
netSelectionElements := []nad.NetworkSelectionElement{
{
Name: networkToAdd,
Namespace: namespace,
InterfaceRequest: "net1",
},
}
serelizedNetSelectionElements, _ := json.Marshal(netSelectionElements)
pod.Annotations[nad.NetworkAttachmentAnnot] = string(serelizedNetSelectionElements)
var err error
_, err = k8sClient.CoreV1().Pods(namespace).UpdateStatus(
context.TODO(),
pod,
metav1.UpdateOptions{})
Expect(err).NotTo(HaveOccurred())
})

It("an `AddedInterface` event and then and `RemovedInterface` event are seen in the event recorded", func() {
expectedEventPayload := fmt.Sprintf(
"Normal AddedInterface pod [%s]: added interface %s to network: %s",
annotations.NamespacedName(namespace, podName),
"net1",
networkToAdd,
)
Eventually(<-eventRecorder.Events).Should(Equal(expectedEventPayload))
expectedEventPayload = fmt.Sprintf(
"Normal RemovedInterface pod [%s]: removed interface %s from network: %s",
annotations.NamespacedName(namespace, podName),
"net0",
networkName,
)
Eventually(<-eventRecorder.Events).Should(Equal(expectedEventPayload))
})

It("the pod network-status is updated with the new network attachment and without the other one", func() {
Eventually(func() ([]nad.NetworkStatus, error) {
updatedPod, err := k8sClient.CoreV1().Pods(namespace).Get(context.TODO(), podName, metav1.GetOptions{})
if err != nil {
return nil, err
}
status, err := annotations.PodDynamicNetworkStatus(updatedPod)
if err != nil {
return nil, err
}
return status, nil
}).Should(ConsistOf(ifaceStatusForDefaultNamespace(networkToAdd, "net1", macAddr)))
})
})

})
})
})
Expand Down
Loading