diff --git a/staging/src/k8s.io/client-go/rest/transport_test.go b/staging/src/k8s.io/client-go/rest/transport_test.go new file mode 100644 index 00000000000..93b341030a3 --- /dev/null +++ b/staging/src/k8s.io/client-go/rest/transport_test.go @@ -0,0 +1,149 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package rest + +import ( + "context" + "net" + "strings" + "sync" + "testing" + + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" +) + +// TestTransportForThreadSafe is meant to be run with the race detector +// to detect that the Transport generation for client-go is safe to +// call from multiple goroutines. +func TestTransportForThreadSafe(t *testing.T) { + const ( + rootCACert = `-----BEGIN CERTIFICATE----- +MIIC4DCCAcqgAwIBAgIBATALBgkqhkiG9w0BAQswIzEhMB8GA1UEAwwYMTAuMTMu +MTI5LjEwNkAxNDIxMzU5MDU4MB4XDTE1MDExNTIxNTczN1oXDTE2MDExNTIxNTcz +OFowIzEhMB8GA1UEAwwYMTAuMTMuMTI5LjEwNkAxNDIxMzU5MDU4MIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAunDRXGwsiYWGFDlWH6kjGun+PshDGeZX +xtx9lUnL8pIRWH3wX6f13PO9sktaOWW0T0mlo6k2bMlSLlSZgG9H6og0W6gLS3vq +s4VavZ6DbXIwemZG2vbRwsvR+t4G6Nbwelm6F8RFnA1Fwt428pavmNQ/wgYzo+T1 +1eS+HiN4ACnSoDSx3QRWcgBkB1g6VReofVjx63i0J+w8Q/41L9GUuLqquFxu6ZnH +60vTB55lHgFiDLjA1FkEz2dGvGh/wtnFlRvjaPC54JH2K1mPYAUXTreoeJtLJKX0 +ycoiyB24+zGCniUmgIsmQWRPaOPircexCp1BOeze82BT1LCZNTVaxQIDAQABoyMw +ITAOBgNVHQ8BAf8EBAMCAKQwDwYDVR0TAQH/BAUwAwEB/zALBgkqhkiG9w0BAQsD +ggEBADMxsUuAFlsYDpF4fRCzXXwrhbtj4oQwcHpbu+rnOPHCZupiafzZpDu+rw4x +YGPnCb594bRTQn4pAu3Ac18NbLD5pV3uioAkv8oPkgr8aUhXqiv7KdDiaWm6sbAL +EHiXVBBAFvQws10HMqMoKtO8f1XDNAUkWduakR/U6yMgvOPwS7xl0eUTqyRB6zGb +K55q2dejiFWaFqB/y78txzvz6UlOZKE44g2JAVoJVM6kGaxh33q8/FmrL4kuN3ut +W+MmJCVDvd4eEqPwbp7146ZWTqpIJ8lvA6wuChtqV8lhAPka2hD/LMqY8iXNmfXD +uml0obOEy+ON91k+SWTJ3ggmF/U= +-----END CERTIFICATE-----` + authPluginName = "auth-plugin-tr" + ) + + err := RegisterAuthProviderPlugin(authPluginName, pluginAProvider) + // the plugins are stored in a global variable that fails if the test + // runs with the flag -count N, with N > 1 + if err != nil && !strings.Contains(err.Error(), "was registered twice") { + t.Errorf("Unexpected error: failed to register pluginA: %v", err) + } + + configurations := []struct { + name string + config *Config + }{ + { + name: "simple config", + config: &Config{ + Host: "localhost:8080", + Username: "gopher", + Password: "g0ph3r", + }, + }, + { + name: "not cacheable config", + config: &Config{ + Host: "localhost:8080", + Username: "gopher", + Password: "g0ph3r", + Dial: func(ctx context.Context, network, address string) (net.Conn, error) { + return nil, nil + }, + }, + }, + { + name: "with TLS config", + config: &Config{ + Host: "localhost:8080", + Username: "gopher", + Password: "g0ph3r", + TLSClientConfig: TLSClientConfig{ + CAData: []byte(rootCACert), + }, + }, + }, + { + name: "with auth provider", + config: &Config{ + Host: "localhost:8080", + Username: "gopher", + Password: "g0ph3r", + TLSClientConfig: TLSClientConfig{ + CAData: []byte(rootCACert), + }, + AuthProvider: &clientcmdapi.AuthProviderConfig{ + Name: authPluginName, + Config: map[string]string{"secret": "s3cr3t"}, + }, + }, + }, + { + name: "with exec provider", + config: &Config{ + Host: "localhost:8080", + Username: "gopher", + Password: "g0ph3r", + TLSClientConfig: TLSClientConfig{ + CAData: []byte(rootCACert), + }, + ExecProvider: &clientcmdapi.ExecConfig{ + Args: []string{"secret"}, + Env: []clientcmdapi.ExecEnvVar{{Name: "secret", Value: "s3cr3t"}}, + APIVersion: "client.authentication.k8s.io/v1beta1", + }, + }, + }, + } + var wg sync.WaitGroup + + for _, tt := range configurations { + tt := tt + wg.Add(1) + go func() { + defer wg.Done() + for i := 1; i <= 50; i++ { + wg.Add(1) + go func() { + defer wg.Done() + _, err := TransportFor(tt.config) + if err != nil { + t.Errorf("Config: %s TransportFor() error = %v", tt.name, err) + } + }() + } + + }() + } + wg.Wait() +}