From 4104fea90dc870b6e4aa6eba66253f8a43f38666 Mon Sep 17 00:00:00 2001 From: dougbtv <dosmith@redhat.com> Date: Wed, 9 Apr 2025 14:33:41 -0400 Subject: [PATCH] Subdirectory CNI chain loading e2e tests Adds a test for plain subdirectory chaining and also using passthru CNI with auxiliaryCNIChainName --- .github/workflows/kind-e2e.yml | 10 ++ ...rectory-chain-passthru-configupdate.yml.j2 | 26 +++++ .../subdirectory-chaining-passthru.yml.j2 | 94 ++++++++++++++++++ .../subdirectory-chaining-pod.yml.j2 | 11 +++ e2e/templates/subdirectory-chaining.yml.j2 | 95 +++++++++++++++++++ e2e/test-subdirectory-chaining-passthru.sh | 81 ++++++++++++++++ e2e/test-subdirectory-chaining.sh | 37 ++++++++ 7 files changed, 354 insertions(+) create mode 100644 e2e/templates/subdirectory-chain-passthru-configupdate.yml.j2 create mode 100644 e2e/templates/subdirectory-chaining-passthru.yml.j2 create mode 100644 e2e/templates/subdirectory-chaining-pod.yml.j2 create mode 100644 e2e/templates/subdirectory-chaining.yml.j2 create mode 100755 e2e/test-subdirectory-chaining-passthru.sh create mode 100755 e2e/test-subdirectory-chaining.sh diff --git a/.github/workflows/kind-e2e.yml b/.github/workflows/kind-e2e.yml index 02424e463..56f7c1e0b 100644 --- a/.github/workflows/kind-e2e.yml +++ b/.github/workflows/kind-e2e.yml @@ -89,6 +89,16 @@ jobs: # working-directory: ./e2e # run: ./test-dra-integration.sh + - name: Test subdirectory CNI chaining + if: ${{ matrix.multus-manifest == 'multus-daemonset-thick.yml' }} + working-directory: ./e2e + run: ./test-subdirectory-chaining.sh + + - name: Test subdirectory CNI chaining with passthru CNI / auxiliaryCNIChainName + if: ${{ matrix.multus-manifest == 'multus-daemonset-thick.yml' }} + working-directory: ./e2e + run: ./test-subdirectory-chaining-passthru.sh + - name: Export kind logs if: always() run: | diff --git a/e2e/templates/subdirectory-chain-passthru-configupdate.yml.j2 b/e2e/templates/subdirectory-chain-passthru-configupdate.yml.j2 new file mode 100644 index 000000000..e9b5cbd77 --- /dev/null +++ b/e2e/templates/subdirectory-chain-passthru-configupdate.yml.j2 @@ -0,0 +1,26 @@ +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: multus-daemon-config + namespace: kube-system + labels: + tier: node + app: multus +data: + daemon-config.json: | + { + "confDir": "/host/etc/cni/net.d", + "logToStderr": true, + "logLevel": "debug", + "logFile": "/tmp/multus.log", + "binDir": "/host/opt/cni/bin", + "cniDir": "/var/lib/cni/multus", + "socketDir": "/host/run/multus", + "cniVersion": "{{ CNI_VERSION }}", + "cniConfigDir": "/host/etc/cni/net.d", + "multusConfigFile": "auto", + "forceCNIVersion": true, + "multusAutoconfigDir": "/host/etc/cni/net.d", + "auxiliaryCNIChainName": "vendor-cni-chain" + } \ No newline at end of file diff --git a/e2e/templates/subdirectory-chaining-passthru.yml.j2 b/e2e/templates/subdirectory-chaining-passthru.yml.j2 new file mode 100644 index 000000000..c6e73a71f --- /dev/null +++ b/e2e/templates/subdirectory-chaining-passthru.yml.j2 @@ -0,0 +1,94 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: cni-setup-script + namespace: default +data: + setup.sh: | + #!/bin/bash + set -euxo pipefail + + DEFAULT_NETWORK_CNI_NAME="vendor-cni-chain" + + cleanup() { + echo "Cleaning up..." + rm -f /host/etc/cni/net.d/${DEFAULT_NETWORK_CNI_NAME}/sysctltwiddle.conf + if [ $? -ne 0 ]; then + echo "Failed to remove sysctltwiddle.conf" >&2 + exit 1 + fi + echo "Cleanup completed successfully" + } + trap cleanup EXIT + + # Create the chained CNI directory if it doesn't exist + mkdir -p /host/etc/cni/net.d/${DEFAULT_NETWORK_CNI_NAME} + if [ $? -ne 0 ]; then + echo "Failed to create directory /host/etc/cni/net.d/${DEFAULT_NETWORK_CNI_NAME}" >&2 + exit 1 + fi + + # Write the chained tuning CNI config + cat <<EOF > /host/etc/cni/net.d/${DEFAULT_NETWORK_CNI_NAME}/sysctltwiddle.conf + { + "cniVersion": "{{ CNI_VERSION }}", + "name": "sysctltwiddle", + "type": "tuning", + "sysctl": { + "net.ipv4.conf.eth0.arp_filter": "1" + } + } + EOF + + if [ $? -ne 0 ]; then + echo "Failed to create chained CNI config" >&2 + exit 1 + fi + + echo "CNI chained setup completed successfully." + sleep infinity +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: cni-setup-daemonset + namespace: default + labels: + app: cni-setup +spec: + selector: + matchLabels: + app: cni-setup + template: + metadata: + labels: + app: cni-setup + spec: + tolerations: + - operator: Exists + effect: NoSchedule + - operator: Exists + effect: NoExecute + containers: + - name: setup + image: quay.io/fedora/fedora:40 + securityContext: + privileged: true + volumeMounts: + - name: cni-config + mountPath: /host/etc/cni/net.d + - name: script-volume + mountPath: /scripts + command: ["/bin/bash", "/scripts/setup.sh"] + volumes: + - name: cni-config + hostPath: + path: /etc/cni/net.d + type: Directory + - name: script-volume + configMap: + name: cni-setup-script + items: + - key: setup.sh + path: setup.sh diff --git a/e2e/templates/subdirectory-chaining-pod.yml.j2 b/e2e/templates/subdirectory-chaining-pod.yml.j2 new file mode 100644 index 000000000..44d2794c0 --- /dev/null +++ b/e2e/templates/subdirectory-chaining-pod.yml.j2 @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Pod +metadata: + name: sysctl-modified +spec: + containers: + - name: sysctl + image: quay.io/dosmith/fedora-procps + command: ["/bin/bash", "-c", "trap : TERM INT; sleep infinity & wait"] + securityContext: + privileged: true \ No newline at end of file diff --git a/e2e/templates/subdirectory-chaining.yml.j2 b/e2e/templates/subdirectory-chaining.yml.j2 new file mode 100644 index 000000000..d918e736e --- /dev/null +++ b/e2e/templates/subdirectory-chaining.yml.j2 @@ -0,0 +1,95 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: cni-setup-script + namespace: default +data: + setup.sh: | + #!/bin/bash + set -euxo pipefail + + DEFAULT_NETWORK_CNI_NAME="kindnet" + + cleanup() { + echo "Cleaning up..." + rm -f /host/etc/cni/net.d/${DEFAULT_NETWORK_CNI_NAME}/sysctltwiddle.conf + if [ $? -ne 0 ]; then + echo "Failed to remove sysctltwiddle.conf" >&2 + exit 1 + fi + echo "Cleanup completed successfully" + } + trap cleanup EXIT + + # Create the chained CNI directory if it doesn't exist + mkdir -p /host/etc/cni/net.d/${DEFAULT_NETWORK_CNI_NAME} + if [ $? -ne 0 ]; then + echo "Failed to create directory /host/etc/cni/net.d/${DEFAULT_NETWORK_CNI_NAME}" >&2 + exit 1 + fi + + # Write the chained tuning CNI config + cat <<EOF > /host/etc/cni/net.d/${DEFAULT_NETWORK_CNI_NAME}/sysctltwiddle.conf + { + "cniVersion": "{{ CNI_VERSION }}", + "name": "sysctltwiddle", + "type": "tuning", + "sysctl": { + "net.ipv4.conf.IFNAME.arp_filter": "1" + } + } + EOF + + if [ $? -ne 0 ]; then + echo "Failed to create chained CNI config" >&2 + exit 1 + fi + + echo "CNI chained setup completed successfully." + sleep infinity +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: cni-setup-daemonset + namespace: default + labels: + app: cni-setup +spec: + selector: + matchLabels: + app: cni-setup + template: + metadata: + labels: + app: cni-setup + spec: + hostNetwork: true + tolerations: + - operator: Exists + effect: NoSchedule + - operator: Exists + effect: NoExecute + containers: + - name: setup + image: quay.io/fedora/fedora:40 + securityContext: + privileged: true + volumeMounts: + - name: cni-config + mountPath: /host/etc/cni/net.d + - name: script-volume + mountPath: /scripts + command: ["/bin/bash", "/scripts/setup.sh"] + volumes: + - name: cni-config + hostPath: + path: /etc/cni/net.d + type: Directory + - name: script-volume + configMap: + name: cni-setup-script + items: + - key: setup.sh + path: setup.sh diff --git a/e2e/test-subdirectory-chaining-passthru.sh b/e2e/test-subdirectory-chaining-passthru.sh new file mode 100755 index 000000000..95584078f --- /dev/null +++ b/e2e/test-subdirectory-chaining-passthru.sh @@ -0,0 +1,81 @@ +#!/bin/bash +set -o errexit +set -o nounset +set -o pipefail + +export PATH=${PATH}:./bin + +TEST_POD_NAME="sysctl-modified" +EXPECTED_BINARIES="${EXPECTED_BINARIES:-/opt/cni/bin/ptp /opt/cni/bin/portmap /opt/cni/bin/tuning}" +EXPECTED_CNI_DIR="/etc/cni/net.d" + +# Reconfigure multus +echo "Applying subdirectory chain passthru config..." +kubectl apply -f yamls/subdirectory-chain-passthru-configupdate.yml + +# Restart the multus daemonset to pick up the new config +echo "Restarting Multus DaemonSet..." +kubectl rollout restart daemonset kube-multus-ds-amd64 -n kube-system +kubectl rollout status daemonset/kube-multus-ds-amd64 -n kube-system + +# Debug: show CNI configs and binaries inside each Kind node +echo "Checking CNI configs and binaries on nodes..." + +for node in $(kubectl get nodes --no-headers | awk '{print $1}'); do + container_name=$(docker ps --format '{{.Names}}' | grep "^${node}$") + + echo "------" + echo "Node: ${node} (container: ${container_name})" + echo "Listing /opt/cni/bin contents..." + docker exec "${container_name}" ls -l /opt/cni/bin || echo "WARNING: /opt/cni/bin missing!" + + echo "Checking expected binaries..." + for bin in $EXPECTED_BINARIES; do + echo "Checking for ${bin}..." + if docker exec "${container_name}" test -f "${bin}"; then + echo "SUCCESS: ${bin} found." + else + echo "FAIL: ${bin} NOT found!" + fi + done + + echo "Listing /etc/cni/net.d configs..." + docker exec "${container_name}" ls -l ${EXPECTED_CNI_DIR} || echo "WARNING: ${EXPECTED_CNI_DIR} missing!" +done +echo "------" + +# Deploy the daemonset that will lay down the chained CNI config +echo "Applying CNI setup DaemonSet..." +kubectl apply -f yamls/subdirectory-chaining-passthru.yml + +# Wait for the daemonset pods to be ready (make sure they set up CNI config) +echo "Waiting for CNI setup DaemonSet to be Ready..." +kubectl rollout status daemonset/cni-setup-daemonset --timeout=300s + +# Deploy a test pod that will get chained CNI applied +echo "Applying test pod..." +kubectl apply -f yamls/subdirectory-chaining-pod.yml + +# Wait for the pod to be Ready +echo "Waiting for test pod to be Ready..." +kubectl wait --for=condition=ready pod/${TEST_POD_NAME} --timeout=300s + +# Check that the sysctl got set +echo "Verifying sysctl arp_filter is set to 1 on eth0..." + +SYSCTL_VALUE=$(kubectl exec ${TEST_POD_NAME} -- sysctl -n net.ipv4.conf.eth0.arp_filter) + +if [ "$SYSCTL_VALUE" != "1" ]; then + echo "FAIL: net.ipv4.conf.eth0.arp_filter is not set to 1, got ${SYSCTL_VALUE}" >&2 + exit 1 +else + echo "SUCCESS: net.ipv4.conf.eth0.arp_filter is set correctly." +fi + +# Cleanup +echo "Cleaning up test resources..." +kubectl delete -f yamls/subdirectory-chaining-pod.yml +kubectl delete -f yamls/subdirectory-chaining-passthru.yml + +echo "Test completed successfully." +exit 0 diff --git a/e2e/test-subdirectory-chaining.sh b/e2e/test-subdirectory-chaining.sh new file mode 100755 index 000000000..16f531e02 --- /dev/null +++ b/e2e/test-subdirectory-chaining.sh @@ -0,0 +1,37 @@ +#!/bin/sh +set -o errexit + +export PATH=${PATH}:./bin + +TEST_POD_NAME="sysctl-modified" + +# Deploy the daemonset that will lay down the chained CNI config +kubectl apply -f yamls/subdirectory-chaining.yml + +# Wait for the daemonset pods to be ready (we need the config to be laid down) +kubectl rollout status daemonset/cni-setup-daemonset + +# Deploy a test pod that will get chained CNI applied +kubectl apply -f yamls/subdirectory-chaining-pod.yml + +# Wait for the pod to be Ready +kubectl wait --for=condition=ready pod/sysctl-modified --timeout=300s + +# Check that the sysctl got set properly inside the pod's eth0 interface +echo "Verifying sysctl arp_filter is set to 1 on eth0" + +SYSCTL_VALUE=$(kubectl exec sysctl-modified -- sysctl -n net.ipv4.conf.eth0.arp_filter) + +if [ "$SYSCTL_VALUE" != "1" ]; then + echo "FAIL: net.ipv4.conf.eth0.arp_filter is not set to 1, got ${SYSCTL_VALUE}" >&2 + exit 1 +else + echo "SUCCESS: net.ipv4.conf.eth0.arp_filter is set correctly." +fi + +# 6. Clean up +echo "Cleaning up test resources" +kubectl delete -f yamls/subdirectory-chaining-pod.yml +kubectl delete -f yamls/subdirectory-chaining.yml + +exit 0