istio.io/istio@v0.0.0-20240520182934-d79c90f27776/cni/pkg/nodeagent/cni-watcher_test.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package nodeagent 16 17 import ( 18 "context" 19 "encoding/json" 20 "fmt" 21 "net" 22 "net/netip" 23 "testing" 24 25 corev1 "k8s.io/api/core/v1" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/types" 28 29 "istio.io/istio/cni/pkg/util" 30 "istio.io/istio/pkg/config/constants" 31 "istio.io/istio/pkg/kube" 32 "istio.io/istio/pkg/test/util/assert" 33 ) 34 35 func TestProcessAddEventGoodPayload(t *testing.T) { 36 valid := CNIPluginAddEvent{ 37 Netns: "/var/netns/foo", 38 PodName: "pod-bingo", 39 PodNamespace: "funkyns", 40 } 41 42 payload, _ := json.Marshal(valid) 43 44 addEvent, err := processAddEvent(payload) 45 46 assert.NoError(t, err) 47 assert.Equal(t, valid, addEvent) 48 } 49 50 func TestProcessAddEventBadPayload(t *testing.T) { 51 valid := CNIPluginAddEvent{ 52 Netns: "/var/netns/foo", 53 PodName: "pod-bingo", 54 PodNamespace: "funkyns", 55 } 56 57 payload, _ := json.Marshal(valid) 58 59 invalid := string(payload) + "funkyjunk" 60 61 _, err := processAddEvent([]byte(invalid)) 62 63 assert.Error(t, err) 64 } 65 66 func TestCNIPluginServer(t *testing.T) { 67 fakePodIP := "11.1.1.12" 68 _, addr, _ := net.ParseCIDR(fakePodIP + "/32") 69 valid := CNIPluginAddEvent{ 70 Netns: "/var/netns/foo", 71 PodName: "pod-bingo", 72 PodNamespace: "funkyns", 73 IPs: []IPConfig{{ 74 Address: *addr, 75 }}, 76 } 77 78 setupLogging() 79 NodeName = "testnode" 80 ctx, cancel := context.WithCancel(context.Background()) 81 defer cancel() 82 pod := &corev1.Pod{ 83 ObjectMeta: metav1.ObjectMeta{ 84 Name: "pod-bingo", 85 Namespace: "funkyns", 86 }, 87 Spec: corev1.PodSpec{ 88 NodeName: NodeName, 89 }, 90 Status: corev1.PodStatus{ 91 PodIP: fakePodIP, 92 }, 93 } 94 ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "funkyns"}} 95 96 client := kube.NewFakeClient(ns, pod) 97 98 // We are expecting at most 1 calls to the mock, wait for them 99 wg, waitForMockCalls := NewWaitForNCalls(t, 1) 100 fs := &fakeServer{testWG: wg} 101 102 fs.On("AddPodToMesh", 103 ctx, 104 pod, 105 util.GetPodIPsIfPresent(pod), 106 valid.Netns, 107 ).Return(nil) 108 109 dpServer := &meshDataplane{ 110 kubeClient: client.Kube(), 111 netServer: fs, 112 } 113 114 handlers := setupHandlers(ctx, client, dpServer, "istio-system") 115 116 // We are not going to start the server, so the sockpath is irrelevant 117 pluginServer := startCniPluginServer(ctx, "/tmp/test.sock", handlers, dpServer) 118 119 // label the namespace 120 labelsPatch := []byte(fmt.Sprintf(`{"metadata":{"labels":{"%s":"%s"}}}`, 121 constants.DataplaneModeLabel, constants.DataplaneModeAmbient)) 122 _, err := client.Kube().CoreV1().Namespaces().Patch(ctx, ns.Name, 123 types.MergePatchType, labelsPatch, metav1.PatchOptions{}) 124 assert.NoError(t, err) 125 126 client.RunAndWait(ctx.Done()) 127 128 payload, _ := json.Marshal(valid) 129 130 // serialize our fake plugin event 131 addEvent, err := processAddEvent(payload) 132 assert.Equal(t, err, nil) 133 134 // Push it thru the handler 135 pluginServer.ReconcileCNIAddEvent(ctx, addEvent) 136 137 waitForMockCalls() 138 139 assertPodAnnotated(t, client, pod) 140 // Assert expected calls actually made 141 fs.AssertExpectations(t) 142 } 143 144 func TestCNIPluginServerPrefersCNIProvidedPodIP(t *testing.T) { 145 fakePodIP := "11.1.1.12" 146 _, addr, _ := net.ParseCIDR(fakePodIP + "/32") 147 valid := CNIPluginAddEvent{ 148 Netns: "/var/netns/foo", 149 PodName: "pod-bingo", 150 PodNamespace: "funkyns", 151 IPs: []IPConfig{{ 152 Address: *addr, 153 }}, 154 } 155 156 setupLogging() 157 NodeName = "testnode" 158 ctx, cancel := context.WithCancel(context.Background()) 159 defer cancel() 160 pod := &corev1.Pod{ 161 ObjectMeta: metav1.ObjectMeta{ 162 Name: "pod-bingo", 163 Namespace: "funkyns", 164 }, 165 Spec: corev1.PodSpec{ 166 NodeName: NodeName, 167 }, 168 } 169 ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "funkyns"}} 170 171 client := kube.NewFakeClient(ns, pod) 172 173 // We are expecting at most 1 calls to the mock, wait for them 174 wg, waitForMockCalls := NewWaitForNCalls(t, 1) 175 fs := &fakeServer{testWG: wg} 176 177 // This pod should be enmeshed with the CNI ip, even tho the pod status had no ip 178 fs.On("AddPodToMesh", 179 ctx, 180 pod, 181 []netip.Addr{netip.MustParseAddr(fakePodIP)}, 182 valid.Netns, 183 ).Return(nil) 184 185 dpServer := &meshDataplane{ 186 kubeClient: client.Kube(), 187 netServer: fs, 188 } 189 190 handlers := setupHandlers(ctx, client, dpServer, "istio-system") 191 192 // We are not going to start the server, so the sockpath is irrelevant 193 pluginServer := startCniPluginServer(ctx, "/tmp/test.sock", handlers, dpServer) 194 195 // label the namespace 196 labelsPatch := []byte(fmt.Sprintf(`{"metadata":{"labels":{"%s":"%s"}}}`, 197 constants.DataplaneModeLabel, constants.DataplaneModeAmbient)) 198 _, err := client.Kube().CoreV1().Namespaces().Patch(ctx, ns.Name, 199 types.MergePatchType, labelsPatch, metav1.PatchOptions{}) 200 assert.NoError(t, err) 201 202 client.RunAndWait(ctx.Done()) 203 204 payload, _ := json.Marshal(valid) 205 206 // serialize our fake plugin event 207 addEvent, err := processAddEvent(payload) 208 assert.Equal(t, err, nil) 209 210 // Push it thru the handler 211 pluginServer.ReconcileCNIAddEvent(ctx, addEvent) 212 213 waitForMockCalls() 214 215 assertPodAnnotated(t, client, pod) 216 // Assert expected calls actually made 217 fs.AssertExpectations(t) 218 }