Skip to content

Instantly share code, notes, and snippets.

@ahmetb
Last active June 20, 2025 20:33
Show Gist options
  • Save ahmetb/1c38cf5e3fe6c36bc257cdc2286ac3cb to your computer and use it in GitHub Desktop.
Save ahmetb/1c38cf5e3fe6c36bc257cdc2286ac3cb to your computer and use it in GitHub Desktop.
how many requests does it take to GOAWAY on k8s apiserver
package main
import (
"context"
"flag"
"fmt"
"log"
"net/http"
"sort"
"strings"
"sync"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
)
// sanCapturingTransport is a transport wrapper that inspects the server's
// certificate and prints its Subject Alternative Names (SANs).
type sanCapturingTransport struct {
mu sync.Mutex
roundTripper http.RoundTripper
lastServer string
reqCount int
}
func (t *sanCapturingTransport) RoundTrip(req *http.Request) (*http.Response, error) {
resp, err := t.roundTripper.RoundTrip(req)
// After the request, inspect the TLS connection state.
if err == nil && resp.TLS != nil && len(resp.TLS.PeerCertificates) > 0 {
cert := resp.TLS.PeerCertificates[0]
sort.Strings(cert.DNSNames)
dnsNames := strings.Join(cert.DNSNames, ", ")
// exclude .atd. and .itd. and .ard. record
t.mu.Lock()
lastServer := t.lastServer
if t.reqCount%1000 == 0 && t.reqCount != 0 {
log.Printf("total requests made: %d", t.reqCount)
}
if lastServer != dnsNames {
if t.reqCount != 0 {
log.Printf("Connected to a new server after %d requests: %s", t.reqCount, dnsNames)
}
t.reqCount = 0
t.lastServer = dnsNames
}
t.reqCount++
t.mu.Unlock()
}
return resp, err
}
func main() {
var (
host = flag.String("host", "localhost", "Kubernetes API server host")
port = flag.String("port", "8080", "Kubernetes API server port")
)
flag.Parse()
// Create the server URL - always insecure as requested
serverURL := fmt.Sprintf("https://%s:%s", *host, *port)
// Create REST config for an insecure connection
config := &rest.Config{
Host: serverURL,
TLSClientConfig: rest.TLSClientConfig{
Insecure: true,
},
QPS: -1,
Burst: -1,
}
// Set the WrapTransport to inject our SAN capturing logic.
config.WrapTransport = func(rt http.RoundTripper) http.RoundTripper {
return &sanCapturingTransport{roundTripper: rt}
}
// To connect to a root API endpoint like /version, we need to configure the REST client
// to hit the root API path with an empty GroupVersion.
config.APIPath = "/"
config.GroupVersion = &schema.GroupVersion{}
config.NegotiatedSerializer = scheme.Codecs
// Create a raw REST client
restClient, err := rest.RESTClientFor(config)
if err != nil {
log.Fatalf("Failed to create REST client: %v", err)
}
ctx := context.Background()
for i := 0; i < 1000000; i++ {
// Make raw request to /version endpoint and discard the result
// This is just to verify the connection is successful
result := restClient.Get().AbsPath("/version").Do(ctx)
if err := result.Error(); err != nil {
log.Printf("Failed to connect to /version endpoint: %v", err)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment