Skip to content

Commit

Permalink
test: Add GH Action for Cilium L4LB XDP tests
Browse files Browse the repository at this point in the history
This commit introduces a new GH action called "Cilium L4LB XDP" which is
responsible for running the standalone LB tests.

The action starts a Fedora VM with vagrant. We do that because we need
to run Kind on cgroupv2-only machine (otherwise, bpf_sock which is
required by the LB health check is not guaranteed to work).
Unfortunately, GH Action does not support any runner with cgroupv2-only.
So instead we run Fedora 34 which has cgroupv1 disabled on the MacOS
runner which supports nested virtualisation.

For now the test issues 10 requests to LB VIP from the Fedora VM. See
test.sh for more details.

Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Martynas Pumputis <m@lambda.lt>
  • Loading branch information
brb committed Jun 4, 2021
1 parent b4616b9 commit 8c25baf
Show file tree
Hide file tree
Showing 7 changed files with 768 additions and 0 deletions.
75 changes: 75 additions & 0 deletions .github/workflows/tests-l4lb.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Cilium L4LB XDP

# Any change in triggers needs to be reflected in the concurrency group.
on:
issue_comment:
types:
- created
# Run every 6 hours
schedule:
- cron: '0 5/6 * * *'
### FOR TESTING PURPOSES
# pull_request:
# types:
# - "labeled"
###

concurrency:
# In case of PR comment, we can't simply append the comment to the group name
# as that is too long and results in an error. Instead we check if it's a
# trigger phrase and append a simple 'trigger-phrase'.
group: |
"${{ github.workflow }} ${{ github.event.issue.pull_request.url || 'scheduled' }}
${{ (startsWith(github.event.comment.body, 'ci-l4lb') ||
startsWith(github.event.comment.body, 'test-me-please')) && 'trigger-phrase' }}"
cancel-in-progress: true

jobs:
setup-and-test:
name: Setup & Test
if: |
(github.event.issue.pull_request && (
startsWith(github.event.comment.body, 'ci-l4lb') ||
(startsWith(github.event.comment.body, 'test-me-please'))
)) ||
(github.event_name == 'schedule' && github.repository == 'cilium/cilium') ||
github.event.label.name == 'ci-run/l4lb'
# We need nested virtualisation which is supported only by MacOS runner
runs-on: macos-10.15
timeout-minutes: 30
steps:
- uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
with:
persist-credentials: false

- name: Boot Fedora
run: |
ln -sf ./test/l4lb/Vagrantfile ./Vagrantfile
# Retry if it fails (download.fedoraproject.org returns 404 sometimes)
# Spend up to 10 seconds on this
for i in {1..4}; do
if vagrant up; then
break
fi
sleep $i
done
vagrant ssh-config >> ~/.ssh/config
- name: Set image tag
id: vars
run: |
if [ ${{ github.event.pull_request.head.sha }} != "" ]; then
echo ::set-output name=tag::${{ github.event.pull_request.head.sha }}
else
echo ::set-output name=tag::${{ github.sha }}
fi
- name: Wait for image to be available
timeout-minutes: 10
shell: bash
run: |
until curl --silent -f -lSL "https://quay.io/api/v1/repository/${{ github.repository_owner }}/cilium-ci/tag/${{ steps.vars.outputs.tag }}/images" &> /dev/null; do sleep 45s; done
- name: Run tests
run: |
ssh default "sudo /bin/sh -c 'cd /vagrant/test/l4lb && ./test.sh ${{ github.repository_owner}} ${{ steps.vars.outputs.tag }}'"
2 changes: 2 additions & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,8 @@ Jenkinsfile.nightly @cilium/ci-structure
/test/runtime/fqdn.go @cilium/proxy @cilium/ci-structure
/test/runtime/kafka.go @cilium/proxy @cilium/ci-structure
/test/runtime/memcache.go @cilium/proxy @cilium/ci-structure
# Standalone L4LB tests
/test/l4lb @cilium/loadbalancer
# Misc. tests
/test/k8sT/cli.go @cilium/cli @cilium/ci-structure
/test/k8sT/Health.go @cilium/health @cilium/ci-structure
Expand Down
39 changes: 39 additions & 0 deletions test/l4lb/Vagrantfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Vagrant box for testing kind with cgroup v2
# Adopted from https://github.com/kubernetes-sigs/kind/blob/main/hack/ci/Vagrantfile
#
# Currently, there is no Github Action runner which would run cgroupv2-only.
# The latter is required to test the LB health checks on Kind implemented by
# bpf_sock. So for now, let's rely on Vagrant to create a Fedora VM which runs
# with cgroupv2-only.
Vagrant.configure("2") do |config|
config.vm.box = "fedora/34-cloud-base"
memory = 2048
cpus = 2
config.vm.provider :virtualbox do |v|
v.memory = memory
v.cpus = cpus
end
config.vm.provider :libvirt do |v|
v.memory = memory
v.cpus = cpus
end
config.vm.provision "install-packages", type: "shell", run: "once" do |sh|
sh.inline = <<~SHELL
set -eux -o pipefail
dnf install -y make kubernetes-client clang llvm glibc-devel.i686 libbpf-devel ethtool openssl
# cgroupv2 requires >= Docker 20.10 from the upstream.
curl -fsSL https://get.docker.com | sh
systemctl enable --now docker
# cgroupv2 requires >= Kind 0.11.0
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.11.0/kind-linux-amd64
chmod +x ./kind
mv ./kind /usr/local/bin
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3
chmod +x ./get_helm.sh
./get_helm.sh
SHELL
end
end
14 changes: 14 additions & 0 deletions test/l4lb/bpf_xdp_veth_host.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#include <linux/bpf.h>

#ifndef __section
# define __section(NAME) \
__attribute__((section(NAME), used))
#endif

__section("prog")
int xdp_foo(struct xdp_md *ctx)
{
return XDP_PASS;
}

char __license[] __section("license") = "GPL";
8 changes: 8 additions & 0 deletions test/l4lb/kind-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane # L4LB (kind-control-plane)
- role: worker # nginx backend (kind-worker)
networking:
disableDefaultCNI: true
kubeProxyMode: none
87 changes: 87 additions & 0 deletions test/l4lb/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#!/bin/bash

set -eux

IMG_OWNER=${1:-cilium}
IMG_TAG=${2:-latest}

###########
# SETUP #
###########

# bpf_xdp_veth_host is a dummy XDP program which is going to be attached to LB
# node's veth pair end in the host netns. When bpf_xdp, which is attached in
# the container netns, forwards a LB request with XDP_TX, the request needs to
# be picked in the host netns by a NAPI handler. To register the handler, we
# attach the dummy program.
clang -O2 -Wall -target bpf -c bpf_xdp_veth_host.c -o bpf_xdp_veth_host.o

# The worker (aka backend node) will receive IPIP packets from the LB node.
# To decapsulate the packets instead of creating an ipip dev which would
# complicate network setup, we will attach the following program which
# terminates the tunnel.
# The program is taken from the Linux kernel selftests.
clang -O2 -Wall -target bpf -c test_tc_tunnel.c -o test_tc_tunnel.o

# With Kind we create two nodes cluster:
#
# * "kind-control-plane" runs cilium in the LB-only mode.
# * "kind-worker" runs the nginx server.
#
# The LB cilium does not connect to the kube-apiserver. For now we use Kind
# just to create Docker-in-Docker containers.
kind create cluster --config kind-config.yaml
helm install cilium ../../install/kubernetes/cilium \
--wait \
--namespace kube-system \
--set image.repository=quay.io/${IMG_OWNER}/cilium-ci \
--set image.tag=${IMG_TAG} \
--set image.pullPolicy=IfNotPresent \
--set operator.enabled=false \
--set loadBalancer.standalone=true \
--set loadBalancer.algorithm=maglev \
--set loadBalancer.mode=dsr \
--set loadBalancer.acceleration=native \
--set loadBalancer.dsrDispatch=ipip \
--set devices=eth0 \
--set ipv6.enabled=false \
--set affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key="kubernetes.io/hostname" \
--set affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].operator=In \
--set affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].values[0]=kind-control-plane

IFIDX=$(docker exec -i kind-control-plane \
/bin/sh -c 'echo $(( $(ip -o l show eth0 | awk "{print $1}" | cut -d: -f1) ))')
LB_VETH_HOST=$(ip -o l | grep "if$IFIDX" | awk '{print $2}' | cut -d@ -f1)
ip l set dev $LB_VETH_HOST xdp obj bpf_xdp_veth_host.o

# Disable TX and RX csum offloading, as veth does not support it. Otherwise,
# the forwarded packets by the LB to the worker node will have invalid csums.
ethtool -K $LB_VETH_HOST rx off tx off

docker exec kind-worker /bin/sh -c 'apt-get update && apt-get install -y nginx && systemctl start nginx'
WORKER_IP=$(docker exec kind-worker ip -o -4 a s eth0 | awk '{print $4}' | cut -d/ -f1)
nsenter -t $(docker inspect kind-worker -f '{{ .State.Pid }}') -n /bin/sh -c \
'tc qdisc add dev eth0 clsact && tc filter add dev eth0 ingress bpf direct-action object-file ./test_tc_tunnel.o section decap'

CILIUM_POD_NAME=$(kubectl -n kube-system get pod -l k8s-app=cilium -o=jsonpath='{.items[0].metadata.name}')
kubectl -n kube-system wait --for=condition=Ready pod "$CILIUM_POD_NAME" --timeout=5m

##########
# TEST #
##########

LB_VIP="2.2.2.2"

nsenter -t $(docker inspect kind-worker -f '{{ .State.Pid }}') -n /bin/sh -c \
"ip a a dev eth0 ${LB_VIP}/32"

kubectl -n kube-system exec $CILIUM_POD_NAME -- \
cilium service update --id 1 --frontend "${LB_VIP}:80" --backends "${WORKER_IP}:80" --k8s-node-port

LB_NODE_IP=$(docker exec kind-control-plane ip -o -4 a s eth0 | awk '{print $4}' | cut -d/ -f1)
ip r a "${LB_VIP}/32" via "$LB_NODE_IP"

# Issue 10 requests to LB
for i in $(seq 1 10); do
curl -o /dev/null "${LB_VIP}:80"
done

0 comments on commit 8c25baf

Please sign in to comment.