istio.io/istio@v0.0.0-20240520182934-d79c90f27776/cni/pkg/nodeagent/ztunnelserver_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 "fmt" 20 "net" 21 "os" 22 "sync/atomic" 23 "testing" 24 "time" 25 26 "golang.org/x/sys/unix" 27 "google.golang.org/protobuf/proto" 28 v1 "k8s.io/api/core/v1" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/types" 31 32 "istio.io/istio/pkg/monitoring/monitortest" 33 "istio.io/istio/pkg/test/util/assert" 34 "istio.io/istio/pkg/zdsapi" 35 ) 36 37 var ztunnelTestCounter atomic.Uint32 38 39 func TestZtunnelSendsPodSnapshot(t *testing.T) { 40 ztunnelKeepAliveCheckInterval = time.Second / 10 41 mt := monitortest.New(t) 42 setupLogging() 43 ctx, cancel := context.WithCancel(context.Background()) 44 defer cancel() 45 46 fixture := connect(ctx) 47 ztunClient := fixture.ztunClient 48 uid := fixture.uid 49 50 m, fds := readRequest(t, ztunClient) 51 // we got am essage from ztun, so it should have observed us being connected 52 mt.Assert(ztunnelConnected.Name(), nil, monitortest.Exactly(1)) 53 54 // we should get the fd to dev null. note that we can't assert the fd number 55 // as the kernel may have given us a different number that refers to the same file. 56 assert.Equal(t, len(fds), 1) 57 // in theory we should close the fd, but it's just a test.. 58 59 assert.Equal(t, m.Payload.(*zdsapi.WorkloadRequest_Add).Add.Uid, uid) 60 // send ack so the server doesn't wait for us. 61 sendAck(ztunClient) 62 63 // second message should be the snap sent message 64 m, fds = readRequest(t, ztunClient) 65 assert.Equal(t, len(fds), 0) 66 67 sent := m.Payload.(*zdsapi.WorkloadRequest_SnapshotSent).SnapshotSent 68 if sent == nil { 69 panic("expected snapshot sent") 70 } 71 sendAck(ztunClient) 72 ztunClient.Close() 73 // this will retry for a bit, so shouldn't flake 74 mt.Assert(ztunnelConnected.Name(), nil, monitortest.Exactly(0)) 75 } 76 77 func TestZtunnelRemovePod(t *testing.T) { 78 ztunnelKeepAliveCheckInterval = time.Second / 10 79 mt := monitortest.New(t) 80 setupLogging() 81 ctx, cancel := context.WithCancel(context.Background()) 82 defer cancel() 83 84 fixture := connect(ctx) 85 ztunClient := fixture.ztunClient 86 uid := fixture.uid 87 // read initial pod add 88 readRequest(t, ztunClient) 89 sendAck(ztunClient) 90 // read snapshot sent 91 m, fds := readRequest(t, ztunClient) 92 assert.Equal(t, len(fds), 0) 93 sent := m.Payload.(*zdsapi.WorkloadRequest_SnapshotSent).SnapshotSent 94 if sent == nil { 95 panic("expected snapshot sent") 96 } 97 sendAck(ztunClient) 98 99 // now remove the pod 100 ztunnelServer := fixture.ztunServer 101 errChan := make(chan error) 102 go func() { 103 errChan <- ztunnelServer.PodDeleted(ctx, uid) 104 }() 105 // read the msg to delete from ztunnel 106 m, fds = readRequest(t, ztunClient) 107 assert.Equal(t, len(fds), 0) 108 assert.Equal(t, m.Payload.(*zdsapi.WorkloadRequest_Del).Del.Uid, uid) 109 sendAck(ztunClient) 110 111 assert.NoError(t, <-errChan) 112 113 ztunClient.Close() 114 // this will retry for a bit, so shouldn't flake 115 mt.Assert(ztunnelConnected.Name(), nil, monitortest.Exactly(0)) 116 } 117 118 func TestZtunnelPodAdded(t *testing.T) { 119 ztunnelKeepAliveCheckInterval = time.Second / 10 120 mt := monitortest.New(t) 121 setupLogging() 122 ctx, cancel := context.WithCancel(context.Background()) 123 defer cancel() 124 125 fixture := connect(ctx) 126 ztunClient := fixture.ztunClient 127 // read initial pod add 128 readRequest(t, ztunClient) 129 sendAck(ztunClient) 130 // read snapshot sent 131 m, fds := readRequest(t, ztunClient) 132 assert.Equal(t, len(fds), 0) 133 sent := m.Payload.(*zdsapi.WorkloadRequest_SnapshotSent).SnapshotSent 134 if sent == nil { 135 panic("expected snapshot sent") 136 } 137 sendAck(ztunClient) 138 139 // now remove the pod 140 ztunnelServer := fixture.ztunServer 141 errChan := make(chan error) 142 pod2, ns2 := podAndNetns() 143 go func() { 144 errChan <- ztunnelServer.PodAdded(ctx, pod2, ns2) 145 }() 146 // read the msg to delete from ztunnel 147 m, fds = readRequest(t, ztunClient) 148 assert.Equal(t, len(fds), 1) 149 assert.Equal(t, m.Payload.(*zdsapi.WorkloadRequest_Add).Add.Uid, string(pod2.UID)) 150 sendAck(ztunClient) 151 152 assert.NoError(t, <-errChan) 153 154 ztunClient.Close() 155 // this will retry for a bit, so shouldn't flake 156 mt.Assert(ztunnelConnected.Name(), nil, monitortest.Exactly(0)) 157 } 158 159 func TestZtunnelPodKept(t *testing.T) { 160 ztunnelKeepAliveCheckInterval = time.Second / 10 161 mt := monitortest.New(t) 162 setupLogging() 163 ctx, cancel := context.WithCancel(context.Background()) 164 defer cancel() 165 166 pods := &fakePodCache{} 167 168 pod, f := podAndNetns() 169 f.Close() 170 pods.pods = map[string]WorkloadInfo{ 171 string(pod.UID): {}, // simulate unknown netns 172 } 173 174 fixture := connectWithPods(ctx, pods) 175 ztunClient := fixture.ztunClient 176 // read initial pod add 177 keep, fds := readRequest(t, ztunClient) 178 assert.Equal(t, len(fds), 0) 179 kept := keep.Payload.(*zdsapi.WorkloadRequest_Keep).Keep 180 if kept.Uid != string(pod.UID) { 181 panic("expected keep received") 182 } 183 184 sendAck(ztunClient) 185 // read snapshot sent 186 m, fds := readRequest(t, ztunClient) 187 assert.Equal(t, len(fds), 0) 188 sent := m.Payload.(*zdsapi.WorkloadRequest_SnapshotSent).SnapshotSent 189 if sent == nil { 190 panic("expected snapshot sent") 191 } 192 sendAck(ztunClient) 193 194 ztunClient.Close() 195 // this will retry for a bit, so shouldn't flake 196 mt.Assert(ztunnelConnected.Name(), nil, monitortest.Exactly(0)) 197 } 198 199 func podAndNetns() (*v1.Pod, *fakeNs) { 200 devNull, err := os.Open(os.DevNull) 201 if err != nil { 202 panic(err) 203 } 204 // we can't close this now, because we need to pass it from the ztunnel server to the client 205 // it would leak, but this is a test, so we don't care 206 // defer devNull.Close() 207 208 id := ztunnelTestCounter.Add(1) 209 pod := &v1.Pod{ 210 ObjectMeta: metav1.ObjectMeta{ 211 Name: fmt.Sprintf("name-%d", id), 212 UID: types.UID(fmt.Sprintf("uid-%d", id)), 213 }, 214 Spec: v1.PodSpec{}, 215 Status: v1.PodStatus{}, 216 } 217 return pod, newFakeNs(devNull.Fd()) 218 } 219 220 func connect(ctx context.Context) struct { 221 ztunClient *net.UnixConn 222 ztunServer *ztunnelServer 223 uid string 224 } { 225 pods := &fakePodCache{} 226 227 pod, ns := podAndNetns() 228 workload := WorkloadInfo{ 229 Workload: podToWorkload(pod), 230 Netns: ns, 231 } 232 pods.pods = map[string]WorkloadInfo{ 233 string(pod.UID): workload, 234 } 235 ret := connectWithPods(ctx, pods) 236 237 return struct { 238 ztunClient *net.UnixConn 239 ztunServer *ztunnelServer 240 uid string 241 }{ztunClient: ret.ztunClient, ztunServer: ret.ztunServer, uid: string(pod.UID)} 242 } 243 244 func connectWithPods(ctx context.Context, pods PodNetnsCache) struct { 245 ztunClient *net.UnixConn 246 ztunServer *ztunnelServer 247 } { 248 // go uses @ instead of \0 for abstract unix sockets 249 addr := fmt.Sprintf("@testaddr%d", ztunnelTestCounter.Add(1)) 250 ztun, err := newZtunnelServer(addr, pods) 251 if err != nil { 252 panic(err) 253 } 254 go ztun.Run(ctx) 255 256 // now as a client connect confirm we and get snapshot 257 resolvedAddr, err := net.ResolveUnixAddr("unixpacket", addr) 258 if err != nil { 259 panic(err) 260 } 261 ztunClient, err := net.DialUnix("unixpacket", nil, resolvedAddr) 262 if err != nil { 263 panic(err) 264 } 265 266 // send hello 267 sendHello(ztunClient) 268 269 return struct { 270 ztunClient *net.UnixConn 271 ztunServer *ztunnelServer 272 }{ztunClient: ztunClient, ztunServer: ztun} 273 } 274 275 func readRequest(t *testing.T, c *net.UnixConn) (*zdsapi.WorkloadRequest, []int) { 276 var oob [1024]byte 277 m, oobn, err := readProto[zdsapi.WorkloadRequest](c, time.Second, oob[:]) 278 if err != nil { 279 panic(err) 280 } 281 282 receivedoob := oob[:oobn] 283 msgs, err := unix.ParseSocketControlMessage(receivedoob) 284 if err != nil { 285 panic(err) 286 } 287 288 // we should get 0 or 1 oob messages 289 if len(msgs) != 0 { 290 assert.Equal(t, len(msgs), 1) 291 } 292 293 var fdss []int 294 for _, msg := range msgs { 295 fds, err := unix.ParseUnixRights(&msg) 296 if err != nil { 297 panic(err) 298 } 299 fdss = append(fdss, fds...) 300 } 301 return m, fdss 302 } 303 304 func sendAck(c *net.UnixConn) { 305 ack := &zdsapi.WorkloadResponse{ 306 Payload: &zdsapi.WorkloadResponse_Ack{ 307 Ack: &zdsapi.Ack{}, 308 }, 309 } 310 data, err := proto.Marshal(ack) 311 if err != nil { 312 panic(err) 313 } 314 err = c.SetWriteDeadline(time.Now().Add(time.Second)) 315 if err != nil { 316 panic(err) 317 } 318 c.Write(data) 319 } 320 321 func sendHello(c *net.UnixConn) { 322 ack := &zdsapi.ZdsHello{ 323 Version: zdsapi.Version_V1, 324 } 325 data, err := proto.Marshal(ack) 326 if err != nil { 327 panic(err) 328 } 329 err = c.SetWriteDeadline(time.Now().Add(time.Second)) 330 if err != nil { 331 panic(err) 332 } 333 c.Write(data) 334 } 335 336 type fakePodCache struct { 337 pods map[string]WorkloadInfo 338 } 339 340 func (f fakePodCache) ReadCurrentPodSnapshot() map[string]WorkloadInfo { 341 return f.pods 342 }