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  }