Last active
June 20, 2025 20:33
-
-
Save ahmetb/1c38cf5e3fe6c36bc257cdc2286ac3cb to your computer and use it in GitHub Desktop.
how many requests does it take to GOAWAY on k8s apiserver
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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