2
0

lib.sh 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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" 1>&2
  19. if [ "$c" != "$COUNT" ]; then
  20. printf "retrying in %d seconds...\n" "$SLEEP" | color "$YELLOW" 1>&2
  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. # shellcheck disable=SC2120
  46. build_kind_config() {
  47. local WORKER_COUNT="${1:-0}"
  48. export API_SERVER_PORT="${2:-6443}"
  49. export POD_SUBNET="${3:-10.42.0.0/16}"
  50. export SERVICE_SUBNET="${4:-10.43.0.0/16}"
  51. export WORKERS=""
  52. local i=0
  53. while [ "$i" -lt "$WORKER_COUNT" ]; do
  54. WORKERS="$(printf "%s\n- role: worker" "$WORKERS")"
  55. ((i++))
  56. done
  57. envsubst < ./kind-config.yaml
  58. unset API_SERVER_PORT POD_SUBNET SERVICE_SUBNET WORKERS
  59. }
  60. create_interface() {
  61. 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 masipcat/wireguard-go:0.0.20230223 wireguard-go --foreground "$1"
  62. }
  63. delete_interface() {
  64. docker rm --force "$1"
  65. }
  66. create_peer() {
  67. cat <<EOF | _kubectl apply -f -
  68. apiVersion: kilo.squat.ai/v1alpha1
  69. kind: Peer
  70. metadata:
  71. name: $1
  72. spec:
  73. allowedIPs:
  74. - $2
  75. persistentKeepalive: $3
  76. publicKey: $4
  77. EOF
  78. }
  79. delete_peer() {
  80. _kubectl delete peer "$1"
  81. }
  82. is_ready() {
  83. for pod in $(_kubectl -n "$1" get pods -o name -l "$2"); do
  84. if ! _kubectl -n "$1" get "$pod" | tail -n 1 | grep -q Running; then
  85. return 1;
  86. fi
  87. done
  88. return 0
  89. }
  90. # Returns non zero if one pod of the given name in the given namespace is not ready.
  91. block_until_ready_by_name() {
  92. block_until_ready "$1" "app.kubernetes.io/name=$2"
  93. }
  94. # Blocks until all pods of a deployment are ready.
  95. block_until_ready() {
  96. retry 30 5 "some $2 pods are not ready yet" is_ready "$1" "$2"
  97. }
  98. # create_cluster launches a kind cluster and deploys Kilo, Adjacency, and a helper with curl.
  99. create_cluster() {
  100. # shellcheck disable=SC2119
  101. local CONFIG="${1:-$(build_kind_config)}"
  102. _kind delete clusters $KIND_CLUSTER > /dev/null
  103. # Create the kind cluster.
  104. _kind create cluster --name $KIND_CLUSTER --config <(echo "$CONFIG")
  105. # Load the Kilo image into kind.
  106. docker tag "$KILO_IMAGE" squat/kilo:test
  107. # This command does not accept the --kubeconfig flag, so call the command directly.
  108. $KIND_BINARY load docker-image squat/kilo:test --name $KIND_CLUSTER
  109. # Create the kubeconfig secret.
  110. _kubectl create secret generic kubeconfig --from-file=kubeconfig="$KUBECONFIG" -n kube-system
  111. # Apply Kilo the the cluster.
  112. _kubectl apply -f ../manifests/crds.yaml
  113. _kubectl apply -f kilo-kind-userspace.yaml
  114. if ! block_until_ready_by_name kube-system kilo-userspace; then return 1; fi
  115. _kubectl wait nodes --all --for=condition=Ready
  116. # Wait for CoreDNS.
  117. block_until_ready kube_system k8s-app=kube-dns
  118. # Ensure the curl helper is not scheduled on a control-plane node.
  119. _kubectl apply -f helper-curl.yaml
  120. block_until_ready_by_name default curl || return 1
  121. _kubectl taint node $KIND_CLUSTER-control-plane node-role.kubernetes.io/control-plane:NoSchedule-
  122. _kubectl apply -f https://raw.githubusercontent.com/kilo-io/adjacency/main/example.yaml
  123. block_until_ready_by_name default adjacency || return 1
  124. # Install node-local-dns
  125. # shellcheck disable=SC2155
  126. local kubedns=$(_kubectl get svc kube-dns -n kube-system -o jsonpath='{.spec.clusterIP}')
  127. local localdns=169.254.20.10
  128. local domain=cluster.local
  129. curl -L https://raw.githubusercontent.com/kubernetes/kubernetes/82a636f1f3c27f511221642d13d91dd09d111fb3/cluster/addons/dns/nodelocaldns/nodelocaldns.yaml | sed "s/__PILLAR__LOCAL__DNS__/$localdns/g; s/__PILLAR__DNS__DOMAIN__/$domain/g; s/__PILLAR__DNS__SERVER__/$kubedns/g" | _kubectl apply -f -
  130. block_until_ready kube_system k8s-app=node-local-dns
  131. }
  132. delete_cluster () {
  133. _kind delete clusters $KIND_CLUSTER
  134. }
  135. curl_pod() {
  136. _kubectl get pods -l app.kubernetes.io/name=curl -o name | xargs -I{} "$KUBECTL_BINARY" --kubeconfig="$KUBECONFIG" exec {} -- /usr/bin/curl "$@"
  137. }
  138. check_ping() {
  139. local LOCAL
  140. while [ $# -gt 0 ]; do
  141. case $1 in
  142. --local)
  143. LOCAL=true
  144. ;;
  145. esac
  146. shift
  147. done
  148. for ip in $(_kubectl get pods -l app.kubernetes.io/name=adjacency -o jsonpath='{.items[*].status.podIP}'); do
  149. if [ -n "$LOCAL" ]; then
  150. ping=$(curl -m 1 -s http://"$ip":8080/ping)
  151. else
  152. ping=$(curl_pod -m 1 -s http://"$ip":8080/ping)
  153. fi
  154. if [ "$ping" = "pong" ]; then
  155. echo "successfully pinged $ip"
  156. else
  157. printf 'failed to ping %s; expected "pong" but got "%s"\n' "$ip" "$ping"
  158. return 1
  159. fi
  160. done
  161. return 0
  162. }
  163. check_adjacent() {
  164. curl_pod adjacency:8080/?format=fancy
  165. [ "$(curl_pod -m 1 -s adjacency:8080/?format=json | jq '.[].latencies[].ok' | grep -c true)" -eq $(($1*$1)) ]
  166. }
  167. check_peer() {
  168. local INTERFACE=$1
  169. local PEER=$2
  170. local ALLOWED_IP=$3
  171. local GRANULARITY=$4
  172. create_interface "$INTERFACE"
  173. docker run --rm leonnicolas/wg-tools wg genkey > "$INTERFACE"
  174. assert "create_peer $PEER $ALLOWED_IP 10 $(docker run --rm --entrypoint=/bin/sh -v "$PWD/$INTERFACE":/key leonnicolas/wg-tools -c 'cat /key | wg pubkey')" "should be able to create Peer"
  175. assert "_kgctl showconf peer $PEER --mesh-granularity=$GRANULARITY > $PEER.ini" "should be able to get Peer configuration"
  176. 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 leonnicolas/wg-tools setconf $INTERFACE /peer.ini" "should be able to apply configuration from kgctl"
  177. docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/usr/bin/wg -v /var/run/wireguard:/var/run/wireguard -v "$PWD/$INTERFACE":/key leonnicolas/wg-tools set "$INTERFACE" private-key /key
  178. docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/sbin/ip leonnicolas/wg-tools address add "$ALLOWED_IP" dev "$INTERFACE"
  179. docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/sbin/ip leonnicolas/wg-tools link set "$INTERFACE" up
  180. docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/sbin/ip leonnicolas/wg-tools route add 10.42/16 dev "$INTERFACE"
  181. assert "retry 10 5 '' check_ping --local" "should be able to ping Pods from host"
  182. assert_equals "$(_kgctl showconf peer "$PEER")" "$(_kgctl showconf peer "$PEER" --mesh-granularity="$GRANULARITY")" "kgctl should be able to auto detect the mesh granularity"
  183. rm "$INTERFACE" "$PEER".ini
  184. delete_peer "$PEER"
  185. delete_interface "$INTERFACE"
  186. }