lib.sh 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. #!/usr/bin/env bash
  2. KUBECONFIG="kind.yaml"
  3. KIND_CLUSTER="kind-cluster-kilo"
  4. KIND_BINARY="${KIND_BINARY:-kind}"
  5. KUBECTL_BINARY="${KUBECTL_BINARY:-kubectl}"
  6. KGCTL_BINARY="${KGCTL_BINARY:-kgctl}"
  7. KILO_IMAGE="${KILO_IMAGE:-squat/kilo}"
  8. retry() {
  9. local COUNT="${1:-10}"
  10. local SLEEP="${2:-5}"
  11. local ERROR=$3
  12. [ -n "$ERROR" ] && ERROR="$ERROR "
  13. shift 3
  14. for c in $(seq 1 "$COUNT"); do
  15. if "$@"; then
  16. return 0
  17. else
  18. printf "%s(attempt %d/%d)\n" "$ERROR" "$c" "$COUNT" | color "$YELLOW"
  19. if [ "$c" != "$COUNT" ]; then
  20. printf "retrying in %d seconds...\n" "$SLEEP" | color "$YELLOW"
  21. sleep "$SLEEP"
  22. fi
  23. fi
  24. done
  25. return 1
  26. }
  27. _not() {
  28. if "$@"; then
  29. return 1
  30. fi
  31. return 0
  32. }
  33. # _kubectl is a helper that calls kubectl with the --kubeconfig flag.
  34. _kubectl() {
  35. $KUBECTL_BINARY --kubeconfig="$KUBECONFIG" "$@"
  36. }
  37. # _kgctl is a helper that calls kgctl with the --kubeconfig flag.
  38. _kgctl() {
  39. $KGCTL_BINARY --kubeconfig="$KUBECONFIG" "$@"
  40. }
  41. # _kind is a helper that calls kind with the --kubeconfig flag.
  42. _kind() {
  43. $KIND_BINARY --kubeconfig="$KUBECONFIG" "$@"
  44. }
  45. create_interface() {
  46. docker run -d --name="$1" --rm --network=host --cap-add=NET_ADMIN --device=/dev/net/tun -v /var/run/wireguard:/var/run/wireguard -e WG_LOG_LEVEL=debug leonnicolas/boringtun --foreground --disable-drop-privileges true "$1"
  47. }
  48. delete_interface() {
  49. docker rm --force "$1"
  50. }
  51. create_peer() {
  52. cat <<EOF | _kubectl apply -f -
  53. apiVersion: kilo.squat.ai/v1alpha1
  54. kind: Peer
  55. metadata:
  56. name: $1
  57. spec:
  58. allowedIPs:
  59. - $2
  60. persistentKeepalive: $3
  61. publicKey: $4
  62. EOF
  63. }
  64. delete_peer() {
  65. _kubectl delete peer "$1"
  66. }
  67. is_ready() {
  68. for pod in $(_kubectl -n "$1" get pods -o name -l "$2"); do
  69. if ! _kubectl -n "$1" get "$pod" | tail -n 1 | grep -q Running; then
  70. return 1;
  71. fi
  72. done
  73. return 0
  74. }
  75. # Returns non zero if one pod of the given name in the given namespace is not ready.
  76. block_until_ready_by_name() {
  77. block_until_ready "$1" "app.kubernetes.io/name=$2"
  78. }
  79. # Blocks until all pods of a deployment are ready.
  80. block_until_ready() {
  81. retry 30 5 "some $2 pods are not ready yet" is_ready "$1" "$2"
  82. }
  83. # create_cluster launches a kind cluster and deploys Kilo, Adjacency, and a helper with curl.
  84. create_cluster() {
  85. _kind delete clusters $KIND_CLUSTER > /dev/null
  86. # Create the kind cluster.
  87. _kind create cluster --name $KIND_CLUSTER --config ./kind-config.yaml
  88. # Load the Kilo image into kind.
  89. docker tag "$KILO_IMAGE" squat/kilo:test
  90. # This command does not accept the --kubeconfig flag, so call the command directly.
  91. $KIND_BINARY load docker-image squat/kilo:test --name $KIND_CLUSTER
  92. # Create the kubeconfig secret.
  93. _kubectl create secret generic kubeconfig --from-file=kubeconfig="$KUBECONFIG" -n kube-system
  94. # Apply Kilo the the cluster.
  95. _kubectl apply -f ../manifests/crds.yaml
  96. _kubectl apply -f kilo-kind-userspace.yaml
  97. block_until_ready_by_name kube-system kilo-userspace
  98. _kubectl wait nodes --all --for=condition=Ready
  99. # Wait for CoreDNS.
  100. block_until_ready kube_system k8s-app=kube-dns
  101. # Ensure the curl helper is not scheduled on a control-plane node.
  102. _kubectl apply -f helper-curl.yaml
  103. block_until_ready_by_name default curl
  104. _kubectl taint node $KIND_CLUSTER-control-plane node-role.kubernetes.io/master:NoSchedule-
  105. _kubectl apply -f https://raw.githubusercontent.com/heptoprint/adjacency/master/example.yaml
  106. block_until_ready_by_name adjacency adjacency
  107. }
  108. delete_cluster () {
  109. _kind delete clusters $KIND_CLUSTER
  110. }
  111. curl_pod() {
  112. _kubectl get pods -l app.kubernetes.io/name=curl -o name | xargs -I{} "$KUBECTL_BINARY" --kubeconfig="$KUBECONFIG" exec {} -- /bin/sh -c "curl $*"
  113. }
  114. check_ping() {
  115. local LOCAL
  116. while [ $# -gt 0 ]; do
  117. case $1 in
  118. --local)
  119. LOCAL=true
  120. ;;
  121. esac
  122. shift
  123. done
  124. for ip in $(_kubectl get pods -l app.kubernetes.io/name=adjacency -o jsonpath='{.items[*].status.podIP}'); do
  125. if [ -n "$LOCAL" ]; then
  126. ping=$(curl -m 1 -s http://"$ip":8080/ping)
  127. else
  128. ping=$(curl_pod -m 1 -s http://"$ip":8080/ping)
  129. fi
  130. if [ "$ping" = "pong" ]; then
  131. echo "successfully pinged $ip"
  132. else
  133. printf 'failed to ping %s; expected "pong" but got "%s"\n' "$ip" "$ping"
  134. return 1
  135. fi
  136. done
  137. return 0
  138. }
  139. check_adjacent() {
  140. _kubectl get pods -l app.kubernetes.io/name=curl -o name | xargs -I{} "$KUBECTL_BINARY" --kubeconfig="$KUBECONFIG" exec {} -- /bin/sh -c 'curl -m 1 -s adjacency:8080/?format=fancy'
  141. [ "$(curl_pod -m 1 -s adjacency:8080/?format=json | jq '.[].latencies[].ok' | grep -c true)" -eq $(($1*$1)) ]
  142. }
  143. check_peer() {
  144. local INTERFACE=$1
  145. local PEER=$2
  146. local ALLOWED_IP=$3
  147. local GRANULARITY=$4
  148. create_interface "$INTERFACE"
  149. docker run --rm --entrypoint=/usr/bin/wg "$KILO_IMAGE" genkey > "$INTERFACE"
  150. assert "create_peer $PEER $ALLOWED_IP 10 $(docker run --rm --entrypoint=/bin/sh -v "$PWD/$INTERFACE":/key "$KILO_IMAGE" -c 'cat /key | wg pubkey')" "should be able to create Peer"
  151. assert "_kgctl showconf peer $PEER --mesh-granularity=$GRANULARITY > $PEER.ini" "should be able to get Peer configuration"
  152. assert "docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/usr/bin/wg -v /var/run/wireguard:/var/run/wireguard -v $PWD/$PEER.ini:/peer.ini $KILO_IMAGE setconf $INTERFACE /peer.ini" "should be able to apply configuration from kgctl"
  153. docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/usr/bin/wg -v /var/run/wireguard:/var/run/wireguard -v "$PWD/$INTERFACE":/key "$KILO_IMAGE" set "$INTERFACE" private-key /key
  154. docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/sbin/ip "$KILO_IMAGE" address add "$ALLOWED_IP" dev "$INTERFACE"
  155. docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/sbin/ip "$KILO_IMAGE" link set "$INTERFACE" up
  156. docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/sbin/ip "$KILO_IMAGE" route add 10.42/16 dev "$INTERFACE"
  157. assert "retry 10 5 '' check_ping --local" "should be able to ping Pods from host"
  158. assert_equals "$(_kgctl showconf peer "$PEER")" "$(_kgctl showconf peer "$PEER" --mesh-granularity="$GRANULARITY")" "kgctl should be able to auto detect the mesh granularity"
  159. rm "$INTERFACE" "$PEER".ini
  160. delete_peer "$PEER"
  161. delete_interface "$INTERFACE"
  162. }