at master 7.4 kB view raw
1import ../make-test-python.nix ( 2 { 3 pkgs, 4 lib, 5 rke2, 6 ... 7 }: 8 let 9 throwSystem = throw "RKE2: Unsupported system: ${pkgs.stdenv.hostPlatform.system}"; 10 coreImages = 11 { 12 aarch64-linux = rke2.images-core-linux-arm64-tar-zst; 13 x86_64-linux = rke2.images-core-linux-amd64-tar-zst; 14 } 15 .${pkgs.stdenv.hostPlatform.system} or throwSystem; 16 canalImages = 17 { 18 aarch64-linux = rke2.images-canal-linux-arm64-tar-zst; 19 x86_64-linux = rke2.images-canal-linux-amd64-tar-zst; 20 } 21 .${pkgs.stdenv.hostPlatform.system} or throwSystem; 22 helloImage = pkgs.dockerTools.buildImage { 23 name = "test.local/hello"; 24 tag = "local"; 25 compressor = "zstd"; 26 copyToRoot = pkgs.buildEnv { 27 name = "rke2-hello-image-env"; 28 paths = with pkgs; [ 29 coreutils 30 socat 31 ]; 32 }; 33 }; 34 # A daemonset that responds 'hello' on port 8000 35 networkTestDaemonset = pkgs.writeText "test.yml" '' 36 apiVersion: apps/v1 37 kind: DaemonSet 38 metadata: 39 name: test 40 labels: 41 name: test 42 spec: 43 selector: 44 matchLabels: 45 name: test 46 template: 47 metadata: 48 labels: 49 name: test 50 spec: 51 containers: 52 - name: test 53 image: test.local/hello:local 54 imagePullPolicy: Never 55 resources: 56 limits: 57 memory: 20Mi 58 command: ["socat", "TCP4-LISTEN:8000,fork", "EXEC:echo hello"] 59 ''; 60 tokenFile = pkgs.writeText "token" "p@s$w0rd"; 61 agentTokenFile = pkgs.writeText "agent-token" "agentP@s$w0rd"; 62 # Let flannel use eth1 to enable inter-node communication in tests 63 canalConfig = pkgs.writeText "rke2-canal-config.yaml" '' 64 apiVersion: helm.cattle.io/v1 65 kind: HelmChartConfig 66 metadata: 67 name: rke2-canal 68 namespace: kube-system 69 spec: 70 valuesContent: |- 71 flannel: 72 iface: "eth1" 73 ''; 74 in 75 { 76 name = "${rke2.name}-multi-node"; 77 meta.maintainers = rke2.meta.maintainers; 78 79 nodes = { 80 server = 81 { 82 config, 83 nodes, 84 pkgs, 85 ... 86 }: 87 { 88 # Setup image archives to be imported by rke2 89 systemd.tmpfiles.settings."10-rke2" = { 90 "/var/lib/rancher/rke2/agent/images/rke2-images-core.tar.zst" = { 91 "L+".argument = "${coreImages}"; 92 }; 93 "/var/lib/rancher/rke2/agent/images/rke2-images-canal.tar.zst" = { 94 "L+".argument = "${canalImages}"; 95 }; 96 "/var/lib/rancher/rke2/agent/images/hello.tar.zst" = { 97 "L+".argument = "${helloImage}"; 98 }; 99 # Copy the canal config so that rke2 can write the remaining default values to it 100 "/var/lib/rancher/rke2/server/manifests/rke2-canal-config.yaml" = { 101 "C".argument = "${canalConfig}"; 102 }; 103 }; 104 105 # Canal CNI with VXLAN 106 networking.firewall.allowedUDPPorts = [ 8472 ]; 107 networking.firewall.allowedTCPPorts = [ 108 # Kubernetes API 109 6443 110 # Canal CNI health checks 111 9099 112 # RKE2 supervisor API 113 9345 114 ]; 115 116 # RKE2 needs more resources than the default 117 virtualisation.cores = 4; 118 virtualisation.memorySize = 4096; 119 virtualisation.diskSize = 8092; 120 121 services.rke2 = { 122 enable = true; 123 role = "server"; 124 package = rke2; 125 inherit tokenFile; 126 inherit agentTokenFile; 127 # Without nodeIP the apiserver starts with the wrong service IP family 128 nodeIP = config.networking.primaryIPAddress; 129 disable = [ 130 "rke2-coredns" 131 "rke2-metrics-server" 132 "rke2-ingress-nginx" 133 "rke2-snapshot-controller" 134 "rke2-snapshot-controller-crd" 135 "rke2-snapshot-validation-webhook" 136 ]; 137 }; 138 }; 139 140 agent = 141 { 142 config, 143 nodes, 144 pkgs, 145 ... 146 }: 147 { 148 # Setup image archives to be imported by rke2 149 systemd.tmpfiles.settings."10-rke2" = { 150 "/var/lib/rancher/rke2/agent/images/rke2-images-core.linux-amd64.tar.zst" = { 151 "L+".argument = "${coreImages}"; 152 }; 153 "/var/lib/rancher/rke2/agent/images/rke2-images-canal.linux-amd64.tar.zst" = { 154 "L+".argument = "${canalImages}"; 155 }; 156 "/var/lib/rancher/rke2/agent/images/hello.tar.zst" = { 157 "L+".argument = "${helloImage}"; 158 }; 159 "/var/lib/rancher/rke2/server/manifests/rke2-canal-config.yaml" = { 160 "C".argument = "${canalConfig}"; 161 }; 162 }; 163 164 # Canal CNI health checks 165 networking.firewall.allowedTCPPorts = [ 9099 ]; 166 # Canal CNI with VXLAN 167 networking.firewall.allowedUDPPorts = [ 8472 ]; 168 169 # The agent node can work with less resources 170 virtualisation.memorySize = 2048; 171 virtualisation.diskSize = 8092; 172 173 services.rke2 = { 174 enable = true; 175 role = "agent"; 176 package = rke2; 177 tokenFile = agentTokenFile; 178 serverAddr = "https://${nodes.server.networking.primaryIPAddress}:9345"; 179 nodeIP = config.networking.primaryIPAddress; 180 }; 181 }; 182 }; 183 184 testScript = 185 let 186 kubectl = "${pkgs.kubectl}/bin/kubectl --kubeconfig=/etc/rancher/rke2/rke2.yaml"; 187 jq = "${pkgs.jq}/bin/jq"; 188 in 189 # python 190 '' 191 start_all() 192 193 server.wait_for_unit("rke2-server") 194 agent.wait_for_unit("rke2-agent") 195 196 # Wait for the agent to be ready 197 server.wait_until_succeeds(r"""${kubectl} wait --for='jsonpath={.status.conditions[?(@.type=="Ready")].status}=True' nodes/agent""") 198 199 server.succeed("${kubectl} cluster-info") 200 server.wait_until_succeeds("${kubectl} get serviceaccount default") 201 202 # Now create a pod on each node via a daemonset and verify they can talk to each other. 203 server.succeed("${kubectl} apply -f ${networkTestDaemonset}") 204 server.wait_until_succeeds( 205 f'[ "$(${kubectl} get ds test -o json | ${jq} .status.numberReady)" -eq {len(machines)} ]' 206 ) 207 208 # Get pod IPs 209 pods = server.succeed("${kubectl} get po -o json | ${jq} '.items[].metadata.name' -r").splitlines() 210 pod_ips = [ 211 server.succeed(f"${kubectl} get po {n} -o json | ${jq} '.status.podIP' -cr").strip() for n in pods 212 ] 213 214 # Verify each node can ping each pod ip 215 for pod_ip in pod_ips: 216 # The CNI sometimes needs a little time 217 server.wait_until_succeeds(f"ping -c 1 {pod_ip}", timeout=5) 218 agent.wait_until_succeeds(f"ping -c 1 {pod_ip}", timeout=5) 219 # Verify the server can exec into the pod 220 # for pod in pods: 221 # resp = server.succeed(f"${kubectl} exec {pod} -- socat TCP:{pod_ip}:8000 -") 222 # assert resp.strip() == "hello", f"Unexpected response from hello daemonset: {resp.strip()}" 223 ''; 224 } 225)