at master 6.2 kB view raw
1# A test that runs a multi-node k3s cluster and verify pod networking works across nodes 2import ../make-test-python.nix ( 3 { 4 pkgs, 5 lib, 6 k3s, 7 ... 8 }: 9 let 10 imageEnv = pkgs.buildEnv { 11 name = "k3s-pause-image-env"; 12 paths = with pkgs; [ 13 tini 14 bashInteractive 15 coreutils 16 socat 17 ]; 18 }; 19 pauseImage = pkgs.dockerTools.buildImage { 20 name = "test.local/pause"; 21 tag = "local"; 22 copyToRoot = imageEnv; 23 config.Entrypoint = [ 24 "/bin/tini" 25 "--" 26 "/bin/sleep" 27 "inf" 28 ]; 29 }; 30 # A daemonset that responds 'server' on port 8000 31 networkTestDaemonset = pkgs.writeText "test.yml" '' 32 apiVersion: apps/v1 33 kind: DaemonSet 34 metadata: 35 name: test 36 labels: 37 name: test 38 spec: 39 selector: 40 matchLabels: 41 name: test 42 template: 43 metadata: 44 labels: 45 name: test 46 spec: 47 containers: 48 - name: test 49 image: test.local/pause:local 50 imagePullPolicy: Never 51 resources: 52 limits: 53 memory: 20Mi 54 command: ["socat", "TCP4-LISTEN:8000,fork", "EXEC:echo server"] 55 ''; 56 tokenFile = pkgs.writeText "token" "p@s$w0rd"; 57 in 58 { 59 name = "${k3s.name}-multi-node"; 60 61 nodes = { 62 server = 63 { nodes, pkgs, ... }: 64 { 65 environment.systemPackages = with pkgs; [ 66 gzip 67 jq 68 ]; 69 # k3s uses enough resources the default vm fails. 70 virtualisation.memorySize = 1536; 71 virtualisation.diskSize = 4096; 72 73 services.k3s = { 74 inherit tokenFile; 75 enable = true; 76 role = "server"; 77 package = k3s; 78 images = [ pauseImage ]; 79 clusterInit = true; 80 extraFlags = [ 81 "--disable coredns" 82 "--disable local-storage" 83 "--disable metrics-server" 84 "--disable servicelb" 85 "--disable traefik" 86 "--pause-image test.local/pause:local" 87 "--node-ip ${nodes.server.networking.primaryIPAddress}" 88 # The interface selection logic of flannel would normally use eth0, as the nixos 89 # testing driver sets a default route via dev eth0. However, in test setups we 90 # have to use eth1 for inter-node communication. 91 "--flannel-iface eth1" 92 ]; 93 }; 94 networking.firewall.allowedTCPPorts = [ 95 2379 96 2380 97 6443 98 ]; 99 networking.firewall.allowedUDPPorts = [ 8472 ]; 100 }; 101 102 server2 = 103 { nodes, pkgs, ... }: 104 { 105 environment.systemPackages = with pkgs; [ 106 gzip 107 jq 108 ]; 109 virtualisation.memorySize = 1536; 110 virtualisation.diskSize = 4096; 111 112 services.k3s = { 113 inherit tokenFile; 114 enable = true; 115 package = k3s; 116 images = [ pauseImage ]; 117 serverAddr = "https://${nodes.server.networking.primaryIPAddress}:6443"; 118 clusterInit = false; 119 extraFlags = [ 120 "--disable coredns" 121 "--disable local-storage" 122 "--disable metrics-server" 123 "--disable servicelb" 124 "--disable traefik" 125 "--pause-image test.local/pause:local" 126 "--node-ip ${nodes.server2.networking.primaryIPAddress}" 127 "--flannel-iface eth1" 128 ]; 129 }; 130 networking.firewall.allowedTCPPorts = [ 131 2379 132 2380 133 6443 134 ]; 135 networking.firewall.allowedUDPPorts = [ 8472 ]; 136 }; 137 138 agent = 139 { nodes, pkgs, ... }: 140 { 141 virtualisation.memorySize = 1024; 142 virtualisation.diskSize = 2048; 143 services.k3s = { 144 inherit tokenFile; 145 enable = true; 146 role = "agent"; 147 package = k3s; 148 images = [ pauseImage ]; 149 serverAddr = "https://${nodes.server2.networking.primaryIPAddress}:6443"; 150 extraFlags = [ 151 "--pause-image test.local/pause:local" 152 "--node-ip ${nodes.agent.networking.primaryIPAddress}" 153 "--flannel-iface eth1" 154 ]; 155 }; 156 networking.firewall.allowedTCPPorts = [ 6443 ]; 157 networking.firewall.allowedUDPPorts = [ 8472 ]; 158 }; 159 }; 160 161 testScript = # python 162 '' 163 start_all() 164 165 machines = [server, server2, agent] 166 for m in machines: 167 m.wait_for_unit("k3s") 168 169 # wait for the agent to show up 170 server.wait_until_succeeds("k3s kubectl get node agent") 171 172 for m in machines: 173 m.succeed("k3s check-config") 174 175 server.succeed("k3s kubectl cluster-info") 176 # Also wait for our service account to show up; it takes a sec 177 server.wait_until_succeeds("k3s kubectl get serviceaccount default") 178 179 # Now create a pod on each node via a daemonset and verify they can talk to each other. 180 server.succeed("k3s kubectl apply -f ${networkTestDaemonset}") 181 server.wait_until_succeeds(f'[ "$(k3s kubectl get ds test -o json | jq .status.numberReady)" -eq {len(machines)} ]') 182 183 # Get pod IPs 184 pods = server.succeed("k3s kubectl get po -o json | jq '.items[].metadata.name' -r").splitlines() 185 pod_ips = [server.succeed(f"k3s kubectl get po {name} -o json | jq '.status.podIP' -cr").strip() for name in pods] 186 187 # Verify each server can ping each pod ip 188 for pod_ip in pod_ips: 189 server.succeed(f"ping -c 1 {pod_ip}") 190 server2.succeed(f"ping -c 1 {pod_ip}") 191 agent.succeed(f"ping -c 1 {pod_ip}") 192 # Verify the pods can talk to each other 193 for pod in pods: 194 resp = server.succeed(f"k3s kubectl exec {pod} -- socat TCP:{pod_ip}:8000 -") 195 t.assertEqual(resp.strip(), "server") 196 ''; 197 198 meta.maintainers = lib.teams.k3s.members; 199 } 200)