Skip to content

Commit

Permalink
Add XDP integration test (#1078)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jake-Shadle authored Jan 28, 2025
1 parent 61cc0d2 commit dbd6b98
Show file tree
Hide file tree
Showing 9 changed files with 272 additions and 0 deletions.
15 changes: 15 additions & 0 deletions .ci/xdp/cluster-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
# Proxy
- role: worker
# Client
- role: worker
# Server
- role: worker
# https://github.com/helm/kind-action?tab=readme-ov-file#configuring-local-registry
containerdConfigPatches:
- |-
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "/etc/containerd/certs.d"
82 changes: 82 additions & 0 deletions .ci/xdp/integration-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/bin/bash
set -eu

source="${BASH_SOURCE[0]}"

proxy_image="localhost:$REGISTRY_PORT/quilkin:ci"

echo "::notice file=$source,line=$LINENO::Building quilkin proxy"
cargo build -p quilkin --bin quilkin
# strip the binary to reduce copy times into the dockerfile
strip ./target/debug/quilkin

echo "::notice file=$source,line=$LINENO::Building quilkin image"
docker build -f .ci/xdp/proxy.dockerfile -t "${proxy_image}" .
echo "::notice file=$source,line=$LINENO::Pushing quilkin image"
docker push "${proxy_image}"

echo "::notice file=$source,line=$LINENO::Starting UDP echo server"
kubectl apply --context "$CLUSTER" -f .ci/xdp/server.yaml
kubectl wait --context "$CLUSTER" --for=condition=ready pod/echo-server

server_ip=$(kubectl get --context "$CLUSTER" pod echo-server --template '{{.status.podIP}}')

echo "::notice file=$source,line=$LINENO::Starting quilkin proxy"
kubectl apply --context "$CLUSTER" -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
name: proxy
spec:
containers:
- name: proxy
image: "$proxy_image"
ports:
- containerPort: 7777
hostPort: 7777
args: ["proxy", "--to", "${server_ip}:8078", "--service.udp.xdp"]
securityContext:
capabilities:
add:
- CAP_BPF # We load an eBPF program
- CAP_NET_RAW # We create SOCK_RAW (XDP) sockets
EOF

kubectl wait --context "$CLUSTER" --for=condition=ready pod/proxy

proxy_ip=$(kubectl get --context "$CLUSTER" pod proxy --template '{{.status.podIP}}')

echo "::notice file=$source,line=$LINENO::Running UDP client"
kubectl apply --context "$CLUSTER" -f - <<EOF
apiVersion: v1
kind: Job
metadata:
name: client
spec:
containers:
- name: client
image: fortio/fortio
args: ["load", "-n", "100", "udp://${proxy_ip}:7777"]
EOF

logs=$(kubectl logs -f --context "$CLUSTER" job/client)

echo "::notice file=$source,line=$LINENO::Finished sending client requests"

# Total Bytes sent: 30, received: 0
regex="Total Bytes sent: (\d+), received: (\d+)"

if [[ $logs =~ $regex ]]; then
send=${BASH_REMATCH[1]}
recv=${BASH_REMATCH[2]}
if [[ $send -eq $recv ]]; then
echo "::notice file=$source,line=$LINENO::Successfully sent and received {$recv}b"
exit 0
fi

echo "::error file=$source,line=$LINENO::sent ${send}b but only received ${recv}b"
exit 1
fi

echo "::error file=$source,line=$LINENO::Failed to find expected log line from UDP client"
exit 2
3 changes: 3 additions & 0 deletions .ci/xdp/proxy.dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM debian:bookworm-slim
COPY ./target/debug/quilkin /usr/local/bin
ENTRYPOINT quilkin
2 changes: 2 additions & 0 deletions .ci/xdp/proxy.dockerfile.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# This file exists purely to ignore the root .dockerignore otherwise it will ignore
# target/debug/quilkin when copying it into the image
13 changes: 13 additions & 0 deletions .ci/xdp/server.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: v1
kind: Pod
metadata:
name: echo-server
spec:
containers:
- name: echo-server
image: fortio/fortio
ports:
- containerPort: 8078
hostPort: 8078
args: ["udp-echo"]

47 changes: 47 additions & 0 deletions .ci/xdp/spinup-cluster-with-registry.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/bin/bash
set -eu

# 1. Create registry container unless it already exists
if [ "$(docker inspect -f '{{.State.Running}}' "${CLUSTER_DESIRED}" 2>/dev/null || true)" != 'true' ]; then
docker run \
-d --restart=always -p "127.0.0.1:${REGISTRY_PORT}:5000" --network bridge --name "${REGISTRY_NAME}" \
registry:2
fi

kind create cluster --name "${CLUSTER_DESIRED}" --config=.ci/xdp/cluster-config.yaml

# 3. Add the registry config to the nodes
#
# This is necessary because localhost resolves to loopback addresses that are
# network-namespace local.
# In other words: localhost in the container is not localhost on the host.
#
# We want a consistent name that works from both ends, so we tell containerd to
# alias localhost:${reg_port} to the registry container when pulling images
REGISTRY_DIR="/etc/containerd/certs.d/localhost:${REGISTRY_PORT}"
for node in $(kind get nodes); do
docker exec "${node}" mkdir -p "${REGISTRY_DIR}"
cat <<EOF | docker exec -i "${node}" cp /dev/stdin "${REGISTRY_DIR}/hosts.toml"
[host."http://${REGISTRY_NAME}:5000"]
EOF
done

# 4. Connect the registry to the cluster network if not already connected
# This allows kind to bootstrap the network but ensures they're on the same network
if [ "$(docker inspect -f='{{json .NetworkSettings.Networks.kind}}' "${REGISTRY_NAME}")" = 'null' ]; then
docker network connect "kind" "${REGISTRY_NAME}"
fi

# 5. Document the local registry
# https://github.com/kubernetes/enhancements/tree/master/keps/sig-cluster-lifecycle/generic/1755-communicating-a-local-registry
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: local-registry-hosting
namespace: kube-public
data:
localRegistryHosting.v1: |
host: "localhost:${REGISTRY_PORT}"
help: "https://kind.sigs.k8s.io/docs/user/local-registry/"
EOF
63 changes: 63 additions & 0 deletions .ci/xdp/veth-integ-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/bin/bash
set -e

source="${BASH_SOURCE[0]}"

cleanup() {
echo "Cleaning up"
ip netns del cs || true
ip netns del proxy || true

pkill fortio || true
pkill quilkin || true
}

trap cleanup EXIT

ip netns del cs || true
ip netns del proxy || true

echo "::notice file=$source,line=$LINENO::Creating network namespaces"

Check notice on line 20 in .ci/xdp/veth-integ-test.sh

View workflow job for this annotation

GitHub Actions / xdp-integration

Creating network namespaces
ip netns add cs
ip netns add proxy

echo "::notice file=$source,line=$LINENO::Adding client <-> proxy <-> server links"

Check notice on line 24 in .ci/xdp/veth-integ-test.sh

View workflow job for this annotation

GitHub Actions / xdp-integration

Adding client <-> proxy <-> server links
ip link add veth-cs type veth peer name veth-proxy

ip link set veth-cs netns cs
ip link set veth-proxy netns proxy

echo "::notice file=$source,line=$LINENO::Adding IPs"

Check notice on line 30 in .ci/xdp/veth-integ-test.sh

View workflow job for this annotation

GitHub Actions / xdp-integration

Adding IPs
ip -n cs addr add 10.0.0.1/24 dev veth-cs
ip -n proxy addr add 10.0.0.2/24 dev veth-proxy

echo "::notice file=$source,line=$LINENO::Creating network namespaces"

Check notice on line 34 in .ci/xdp/veth-integ-test.sh

View workflow job for this annotation

GitHub Actions / xdp-integration

Creating network namespaces
ip -n cs link set veth-cs up
ip -n proxy link set veth-proxy up

ip netns exec cs fortio udp-echo&
ip netns exec proxy ./target/debug/quilkin proxy --to 10.0.0.1:8078 --publish.udp.xdp&

echo "::notice file=$source,line=$LINENO::Launching client"

Check notice on line 41 in .ci/xdp/veth-integ-test.sh

View workflow job for this annotation

GitHub Actions / xdp-integration

Launching client
ip netns exec cs fortio load -n 10 udp://10.0.0.2:7777 2> ./target/logs.txt
logs=$(cat ./target/logs.txt)
echo "$logs"

regex="Total Bytes sent: ([0-9]+), received: ([0-9]+)"

if [[ $logs =~ $regex ]]; then
send=${BASH_REMATCH[1]}
recv=${BASH_REMATCH[2]}
# We could be more strict here and require they are exactly equal, but I can't
# even consistently get that on my local machine so I doubt CI will fair better
if [[ $recv -ne "0" ]]; then
echo "::notice file=$source,line=$LINENO::Successfully sent ${send}B and received ${recv}B"

Check notice on line 54 in .ci/xdp/veth-integ-test.sh

View workflow job for this annotation

GitHub Actions / xdp-integration

Successfully sent 240B and received 240B
exit 0
fi

echo "::error file=$source,line=$LINENO::sent ${send}B but only received ${recv}B"
exit 1
fi

echo "::error file=$source,line=$LINENO::Failed to find expected log line from UDP client"
exit 2
46 changes: 46 additions & 0 deletions .github/workflows/xdp-integration-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: xdp-integration-test

on:
push:
branches:
- "main"
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always

jobs:
xdp-integration:
runs-on: ubuntu-24.04
# env:
# CLUSTER_DESIRED: xdp-integ
# CLUSTER: kind-xdp-integ
# SERVER_PORT: "8078"
# REGISTRY_PORT: "5001"
# REGISTRY_NAME: registry
steps:
- uses: actions/checkout@v4
# - name: Create cluster
# id: kind
# uses: helm/kind-action@v1
# with:
# config: .ci/xdp/cluster-config.yaml
# cluster_name: ${{ env.CLUSTER_DESIRED }}
# registry: true
# registry_name: ${{ env.REGISTRY_NAME }}
# registry_port: ${{ env.REGISTRY_PORT }}
- uses: dtolnay/rust-toolchain@stable
- name: Fetch quilkin
run: cargo fetch --target x86_64-unknown-linux-gnu
- name: Build quilkin
run: cargo build -p quilkin --bin quilkin
- name: Install fortio
run: |
curl -L -o fortio.deb https://github.com/fortio/fortio/releases/download/v1.68.0/fortio_1.68.0_amd64.deb
sudo dpkg -i fortio.deb
- name: Run XDP integration test
run: sudo .ci/xdp/veth-integ-test.sh
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
!.gcloudignore
!.dockerignore
!.github
!.ci
!xds/ci/.golangci.yaml

*.iml
Expand Down

0 comments on commit dbd6b98

Please sign in to comment.