Skip to content

Commit fbfd0c0

Browse files
kvapsclaude
andcommitted
test(e2e): add Cilium-CNI e2e suite
Adds a new e2e harness that brings up a kind cluster with Cilium as the CNI (VXLAN overlay, default kube-proxy) and runs Kilo on top with --cni=false --compatibility=cilium. This validates the cross granularity + Cilium combination, which is the configuration shipped by downstream platforms but not exercised by the existing bridge-CNI suite. Files: - e2e/kilo-kind-cilium.yaml: Kilo DaemonSet for the Cilium-CNI cluster (no kilo CNI ConfigMap, no install-cni init container, --cni=false, --compatibility=cilium) - e2e/lib.sh: install_cilium() helper (Helm, Cilium 1.16.5, VXLAN tunnelProtocol, IPAM kubernetes, host firewall off) and create_cilium_cluster() - e2e/cilium-setup.sh, cilium-cross-mesh.sh, cilium-teardown.sh: three-stage suite mirroring the existing setup/...mesh/teardown pattern - Makefile: new `e2e-cilium` target, kept separate from `e2e` because the Cilium cluster is incompatible with the Kilo bridge CNI used by the default suite Kube-proxy replacement is intentionally left at the default (off) for this baseline; KPR coverage can be added in a follow-up. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
1 parent 10fe19d commit fbfd0c0

6 files changed

Lines changed: 256 additions & 0 deletions

File tree

Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,13 @@ test: lint unit e2e
8989
e2e:
9090
KILO_IMAGE=squat/kilo:test bash_unit $(BASH_UNIT_FLAGS) ./e2e/setup.sh ./e2e/full-mesh.sh ./e2e/location-mesh.sh ./e2e/cross-mesh.sh ./e2e/multi-cluster.sh ./e2e/handlers.sh ./e2e/kgctl.sh ./e2e/teardown.sh
9191

92+
# e2e-cilium runs the Kilo --compatibility=cilium e2e suite against a
93+
# kind cluster where Cilium provides the CNI. It is a separate target
94+
# from `e2e` because the Cilium cluster is incompatible with the Kilo
95+
# bridge CNI used by the default suite.
96+
e2e-cilium:
97+
KILO_IMAGE=squat/kilo:test bash_unit $(BASH_UNIT_FLAGS) ./e2e/cilium-setup.sh ./e2e/cilium-cross-mesh.sh ./e2e/cilium-teardown.sh
98+
9299
docs/kg.md:
93100
go run ./cmd/kg/... --help | head -n -2 > help.txt
94101
go tool embedmd -w docs/kg.md

e2e/cilium-cross-mesh.sh

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/usr/bin/env bash
2+
# shellcheck disable=SC1091
3+
. lib.sh
4+
5+
# Cilium-CNI counterpart of e2e/cross-mesh.sh. The Kilo DaemonSet is the
6+
# one applied by create_cilium_cluster (kilo-kind-cilium.yaml), which
7+
# already runs Kilo with --cni=false --compatibility=cilium. This suite
8+
# only annotates locations and switches granularity to "cross".
9+
setup_suite() {
10+
_kubectl annotate node "$KIND_CLUSTER-control-plane" kilo.squat.ai/location=loc-a --overwrite
11+
_kubectl annotate node "$KIND_CLUSTER-worker" kilo.squat.ai/location=loc-a --overwrite
12+
_kubectl annotate node "$KIND_CLUSTER-worker2" kilo.squat.ai/location=loc-b --overwrite
13+
# shellcheck disable=SC2016
14+
_kubectl patch ds -n kube-system kilo -p '{"spec":{"template":{"spec":{"containers":[{"name":"kilo","args":["--hostname=$(NODE_NAME)","--create-interface=false","--cni=false","--compatibility=cilium","--mesh-granularity=cross","--kubeconfig=/etc/kubernetes/kubeconfig","--internal-cidr=$(NODE_IP)/32"]}]}}}}'
15+
block_until_ready_by_name kube-system kilo-userspace
16+
}
17+
18+
test_cilium_cross_mesh_connectivity() {
19+
assert "retry 30 5 '' check_ping" "should be able to ping all Pods over Cilium VXLAN + Kilo cross mesh"
20+
assert "retry 10 5 'the adjacency matrix is not complete yet' check_adjacent 3" "adjacency should return the right number of successful pings"
21+
echo "sleep for 30s (one reconciliation period) and try again..."
22+
sleep 30
23+
assert "retry 10 5 'the adjacency matrix is not complete yet' check_adjacent 3" "adjacency should return the right number of successful pings after reconciling"
24+
}
25+
26+
test_cilium_cross_peer_topology() {
27+
local CP_PEERS WORKER_PEERS WORKER2_PEERS
28+
CP_PEERS=$(_kgctl showconf node "$KIND_CLUSTER-control-plane" | grep -c '^\[Peer\]')
29+
WORKER_PEERS=$(_kgctl showconf node "$KIND_CLUSTER-worker" | grep -c '^\[Peer\]')
30+
WORKER2_PEERS=$(_kgctl showconf node "$KIND_CLUSTER-worker2" | grep -c '^\[Peer\]')
31+
assert_equals "1" "$CP_PEERS" "control-plane (loc-a) should have 1 peer (the loc-b node)"
32+
assert_equals "1" "$WORKER_PEERS" "worker (loc-a) should have 1 peer (the loc-b node)"
33+
assert_equals "2" "$WORKER2_PEERS" "worker2 (loc-b) should have 2 peers (both loc-a nodes)"
34+
}

e2e/cilium-setup.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/env bash
2+
# shellcheck disable=SC1091
3+
. lib.sh
4+
5+
# Bring up a kind cluster with Cilium as the CNI for the Cilium-mode e2e
6+
# suite. Counterpart of e2e/setup.sh, which provisions a cluster that
7+
# uses the Kilo bridge CNI.
8+
setup_suite() {
9+
create_cilium_cluster "$(build_kind_config 2)"
10+
}

e2e/cilium-teardown.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/env bash
2+
# shellcheck disable=SC1091
3+
. lib.sh
4+
5+
teardown_suite () {
6+
if [ -n "$E2E_SKIP_TEARDOWN_ON_FAILURE" ]; then
7+
return
8+
fi
9+
delete_cluster
10+
}

e2e/kilo-kind-cilium.yaml

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
apiVersion: v1
2+
kind: ServiceAccount
3+
metadata:
4+
name: kilo
5+
namespace: kube-system
6+
---
7+
apiVersion: rbac.authorization.k8s.io/v1
8+
kind: ClusterRole
9+
metadata:
10+
name: kilo
11+
rules:
12+
- apiGroups:
13+
- ""
14+
resources:
15+
- nodes
16+
verbs:
17+
- list
18+
- patch
19+
- watch
20+
- apiGroups:
21+
- kilo.squat.ai
22+
resources:
23+
- peers
24+
verbs:
25+
- list
26+
- watch
27+
- apiGroups:
28+
- apiextensions.k8s.io
29+
resources:
30+
- customresourcedefinitions
31+
verbs:
32+
- get
33+
---
34+
apiVersion: rbac.authorization.k8s.io/v1
35+
kind: ClusterRoleBinding
36+
metadata:
37+
name: kilo
38+
roleRef:
39+
apiGroup: rbac.authorization.k8s.io
40+
kind: ClusterRole
41+
name: kilo
42+
subjects:
43+
- kind: ServiceAccount
44+
name: kilo
45+
namespace: kube-system
46+
---
47+
# Kilo DaemonSet for the Cilium e2e suite. The CNI is provided by Cilium
48+
# (no kilo CNI ConfigMap and no install-cni init container), so Kilo runs
49+
# in --cni=false / --compatibility=cilium mode and only manages the WG mesh
50+
# on top of Cilium's overlay.
51+
apiVersion: apps/v1
52+
kind: DaemonSet
53+
metadata:
54+
name: kilo
55+
namespace: kube-system
56+
labels:
57+
app.kubernetes.io/name: kilo-userspace
58+
app.kubernetes.io/part-of: kilo
59+
spec:
60+
selector:
61+
matchLabels:
62+
app.kubernetes.io/name: kilo-userspace
63+
app.kubernetes.io/part-of: kilo
64+
template:
65+
metadata:
66+
labels:
67+
app.kubernetes.io/name: kilo-userspace
68+
app.kubernetes.io/part-of: kilo
69+
spec:
70+
serviceAccountName: kilo
71+
hostNetwork: true
72+
containers:
73+
- name: kilo
74+
image: squat/kilo:test
75+
imagePullPolicy: Never
76+
args:
77+
- --hostname=$(NODE_NAME)
78+
- --create-interface=false
79+
- --cni=false
80+
- --compatibility=cilium
81+
- --mesh-granularity=full
82+
- --kubeconfig=/etc/kubernetes/kubeconfig
83+
- --internal-cidr=$(NODE_IP)/32
84+
env:
85+
- name: NODE_NAME
86+
valueFrom:
87+
fieldRef:
88+
fieldPath: spec.nodeName
89+
- name: NODE_IP
90+
valueFrom:
91+
fieldRef:
92+
fieldPath: status.hostIP
93+
ports:
94+
- containerPort: 1107
95+
name: metrics
96+
securityContext:
97+
privileged: true
98+
volumeMounts:
99+
- name: kilo-dir
100+
mountPath: /var/lib/kilo
101+
- name: lib-modules
102+
mountPath: /lib/modules
103+
readOnly: true
104+
- name: xtables-lock
105+
mountPath: /run/xtables.lock
106+
readOnly: false
107+
- name: wireguard
108+
mountPath: /var/run/wireguard
109+
readOnly: false
110+
- name: kubeconfig
111+
mountPath: /etc/kubernetes
112+
readOnly: true
113+
- name: wireguard
114+
image: ghcr.io/masipcat/wireguard-go-docker:0.0.20230223
115+
args:
116+
- wireguard-go
117+
- --foreground
118+
- kilo0
119+
securityContext:
120+
privileged: true
121+
volumeMounts:
122+
- name: wireguard
123+
mountPath: /var/run/wireguard
124+
readOnly: false
125+
tolerations:
126+
- effect: NoSchedule
127+
operator: Exists
128+
- effect: NoExecute
129+
operator: Exists
130+
volumes:
131+
- name: kilo-dir
132+
hostPath:
133+
path: /var/lib/kilo
134+
- name: lib-modules
135+
hostPath:
136+
path: /lib/modules
137+
- name: xtables-lock
138+
hostPath:
139+
path: /run/xtables.lock
140+
type: FileOrCreate
141+
- name: wireguard
142+
hostPath:
143+
path: /var/run/wireguard
144+
- name: kubeconfig
145+
secret:
146+
secretName: kubeconfig

e2e/lib.sh

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,55 @@ delete_cluster () {
142142
_kind delete clusters $KIND_CLUSTER
143143
}
144144

145+
# install_cilium installs Cilium via Helm into the current kind cluster
146+
# using a minimal config: VXLAN overlay, Kubernetes IPAM, host firewall off.
147+
# Kube-proxy replacement is intentionally left at the default (off) to
148+
# keep the e2e harness focused on Kilo's --compatibility=cilium path
149+
# rather than Cilium's eBPF service LB; KPR coverage can be added in a
150+
# follow-up.
151+
install_cilium() {
152+
local CILIUM_VERSION="${CILIUM_VERSION:-1.16.5}"
153+
helm repo add cilium https://helm.cilium.io/ >/dev/null 2>&1 || true
154+
helm repo update cilium >/dev/null 2>&1 || true
155+
helm --kubeconfig="$KUBECONFIG" install cilium cilium/cilium \
156+
--namespace kube-system \
157+
--version "$CILIUM_VERSION" \
158+
--set ipam.mode=kubernetes \
159+
--set tunnelProtocol=vxlan \
160+
--set hostFirewall.enabled=false \
161+
--set image.pullPolicy=IfNotPresent \
162+
--set rollOutCiliumPods=true \
163+
--wait
164+
}
165+
166+
# create_cilium_cluster launches a kind cluster, installs Cilium as the CNI,
167+
# deploys Kilo in --compatibility=cilium mode, and brings up Adjacency +
168+
# the curl helper, mirroring create_cluster.
169+
create_cilium_cluster() {
170+
# shellcheck disable=SC2119
171+
local CONFIG="${1:-$(build_kind_config)}"
172+
_kind delete clusters $KIND_CLUSTER > /dev/null
173+
_kind create cluster --name $KIND_CLUSTER --config <(echo "$CONFIG")
174+
# Cilium needs to be installed before any pod that requires CNI networking
175+
# can become Ready, so install it first.
176+
install_cilium
177+
block_until_ready kube-system k8s-app=cilium
178+
_kubectl wait nodes --all --for=condition=Ready --timeout=120s
179+
block_until_ready kube_system k8s-app=kube-dns
180+
# Load the Kilo image into kind and apply the Cilium-mode manifest.
181+
docker tag "$KILO_IMAGE" squat/kilo:test
182+
$KIND_BINARY load docker-image squat/kilo:test --name $KIND_CLUSTER
183+
_kubectl create secret generic kubeconfig --from-file=kubeconfig="$KUBECONFIG" -n kube-system
184+
_kubectl apply -f ../manifests/crds.yaml
185+
_kubectl apply -f kilo-kind-cilium.yaml
186+
if ! block_until_ready_by_name kube-system kilo-userspace; then return 1; fi
187+
_kubectl apply -f helper-curl.yaml
188+
block_until_ready_by_name default curl || return 1
189+
_kubectl taint node $KIND_CLUSTER-control-plane node-role.kubernetes.io/control-plane:NoSchedule-
190+
_kubectl apply -f https://raw.githubusercontent.com/kilo-io/adjacency/main/example.yaml
191+
block_until_ready_by_name default adjacency
192+
}
193+
145194
curl_pod() {
146195
_kubectl get pods -l app.kubernetes.io/name=curl -o name | xargs -I{} "$KUBECTL_BINARY" --kubeconfig="$KUBECONFIG" exec {} -- /usr/bin/curl "$@"
147196
}

0 commit comments

Comments
 (0)