blob: 3b31c5b3ad7d606c474c90c2448241cfc6430f2f [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
2// SPDX-License-Identifier: Apache-2.0
3
Serge Bazanski9104e382023-04-04 20:08:21 +02004package main
5
6import (
7 "context"
8 "crypto/tls"
9 "crypto/x509"
10 "encoding/json"
11 "fmt"
12 "log"
13 "net/http"
14 "os"
15 "time"
16)
17
18// test1InClusterKubernetes exercises connectivity to the cluster-local
19// Kubernetes API server. It expects to be able to connect to the APIserver using
20// the ServiceAccount and cluster CA injected by the Kubelet.
21//
22// The entire functionality is reimplemented without relying on Kubernetes
23// client code to make the expected behaviour clear.
24func test1InClusterKubernetes(ctx context.Context) error {
25 token, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token")
26 if err != nil {
27 return fmt.Errorf("failed to read serviceaccount token: %w", err)
28 }
29
30 cacert, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/ca.crt")
31 if err != nil {
32 return fmt.Errorf("failed to read cluster CA certificate: %w", err)
33 }
34 pool := x509.NewCertPool()
35 pool.AppendCertsFromPEM(cacert)
36
37 client := &http.Client{
38 Transport: &http.Transport{
39 TLSClientConfig: &tls.Config{
40 RootCAs: pool,
41 },
42 },
43 }
44
45 req, err := http.NewRequestWithContext(ctx, "GET", "https://kubernetes.default.svc.cluster.local/api", nil)
46 if err != nil {
47 return fmt.Errorf("creating request failed: %w", err)
48 }
49 req.Header.Set("Authorization", "Bearer "+string(token))
50
51 res, err := client.Do(req)
52 if err != nil {
53 return fmt.Errorf("request failed: %w", err)
54 }
55 defer res.Body.Close()
56
57 j := struct {
58 Kind string `json:"kind"`
59 Message string `json:"message"`
60 }{}
61 if err := json.NewDecoder(res.Body).Decode(&j); err != nil {
62 return fmt.Errorf("json parse error: %w", err)
63 }
64
65 if j.Kind == "Status" {
66 return fmt.Errorf("API server responded with error: %q", j.Message)
67 }
68 if j.Kind != "APIVersions" {
69 return fmt.Errorf("unexpected response from server (kind: %q)", j.Kind)
70 }
71
72 return nil
73}
74
75func main() {
76 log.Printf("Metropolis Kubernetes self-test starting...")
77 ctx, ctxC := context.WithTimeout(context.Background(), 10*time.Second)
78 defer ctxC()
79
80 log.Printf("1. In-cluster Kubernetes client...")
81 if err := test1InClusterKubernetes(ctx); err != nil {
82 fmt.Println(err.Error())
83 os.Exit(1)
84 }
85
86 log.Printf("All tests passed.")
87}