From 04cacabc16696ef069adac1576fc23bd8b366ed3 Mon Sep 17 00:00:00 2001 From: Matt Moyer Date: Wed, 29 Jul 2020 16:24:16 -0500 Subject: [PATCH] Convert pkg/client to depend only on stdlib. Signed-off-by: Matt Moyer --- Dockerfile | 2 + cmd/placeholder-name/main.go | 22 +++- cmd/placeholder-name/main_test.go | 34 +++--- go.mod | 3 + pkg/client/client.go | 195 ++++++++++++++++++++---------- pkg/client/client_test.go | 113 ++++++++++++----- pkg/client/go.mod | 5 + pkg/client/go.sum | 11 ++ test/integration/client_test.go | 2 +- 9 files changed, 271 insertions(+), 116 deletions(-) create mode 100644 pkg/client/go.mod create mode 100644 pkg/client/go.sum diff --git a/Dockerfile b/Dockerfile index 0017c62a..3822a57d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,6 +24,8 @@ WORKDIR /work # Get dependencies first so they can be cached as a layer COPY go.mod . COPY go.sum . +COPY pkg/client/go.mod ./pkg/client/go.mod +COPY pkg/client/go.sum ./pkg/client/go.sum RUN go mod download # Copy only the production source code to avoid cache misses when editing other files COPY cmd ./cmd diff --git a/cmd/placeholder-name/main.go b/cmd/placeholder-name/main.go index 3930fc91..ba343dc9 100644 --- a/cmd/placeholder-name/main.go +++ b/cmd/placeholder-name/main.go @@ -13,6 +13,7 @@ import ( "os" "time" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1" "github.com/suzerain-io/placeholder-name/internal/constable" @@ -28,7 +29,7 @@ func main() { } type envGetter func(string) (string, bool) -type tokenExchanger func(ctx context.Context, token, caBundle, apiEndpoint string) (*clientauthenticationv1beta1.ExecCredential, error) +type tokenExchanger func(ctx context.Context, token, caBundle, apiEndpoint string) (*client.Credential, error) const ErrMissingEnvVar = constable.Error("failed to login: environment variable not set") @@ -51,11 +52,28 @@ func run(envGetter envGetter, tokenExchanger tokenExchanger, outputWriter io.Wri return envVarNotSetError("PLACEHOLDER_NAME_K8S_API_ENDPOINT") } - execCredential, err := tokenExchanger(ctx, token, caBundle, apiEndpoint) + cred, err := tokenExchanger(ctx, token, caBundle, apiEndpoint) if err != nil { return fmt.Errorf("failed to login: %w", err) } + var expiration *metav1.Time + if cred.ExpirationTimestamp != nil { + t := metav1.NewTime(*cred.ExpirationTimestamp) + expiration = &t + } + execCredential := clientauthenticationv1beta1.ExecCredential{ + TypeMeta: metav1.TypeMeta{ + Kind: "ExecCredential", + APIVersion: "client.authentication.k8s.io/v1beta1", + }, + Status: &clientauthenticationv1beta1.ExecCredentialStatus{ + ExpirationTimestamp: expiration, + Token: cred.Token, + ClientCertificateData: cred.ClientCertificateData, + ClientKeyData: cred.ClientKeyData, + }, + } err = json.NewEncoder(outputWriter).Encode(execCredential) if err != nil { return fmt.Errorf("failed to marshal response to stdout: %w", err) diff --git a/cmd/placeholder-name/main_test.go b/cmd/placeholder-name/main_test.go index 37dd5b0c..8ef72342 100644 --- a/cmd/placeholder-name/main_test.go +++ b/cmd/placeholder-name/main_test.go @@ -15,9 +15,8 @@ import ( "github.com/sclevine/spec" "github.com/sclevine/spec/report" "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1" + "github.com/suzerain-io/placeholder-name/pkg/client" "github.com/suzerain-io/placeholder-name/test/library" ) @@ -68,7 +67,7 @@ func TestRun(t *testing.T) { when("the token exchange fails", func() { it.Before(func() { - tokenExchanger = func(ctx context.Context, token, caBundle, apiEndpoint string) (*clientauthenticationv1beta1.ExecCredential, error) { + tokenExchanger = func(ctx context.Context, token, caBundle, apiEndpoint string) (*client.Credential, error) { return nil, fmt.Errorf("some error") } }) @@ -81,10 +80,8 @@ func TestRun(t *testing.T) { when("the JSON encoder fails", func() { it.Before(func() { - tokenExchanger = func(ctx context.Context, token, caBundle, apiEndpoint string) (*clientauthenticationv1beta1.ExecCredential, error) { - return &clientauthenticationv1beta1.ExecCredential{ - Status: &clientauthenticationv1beta1.ExecCredentialStatus{Token: "some token"}, - }, nil + tokenExchanger = func(ctx context.Context, token, caBundle, apiEndpoint string) (*client.Credential, error) { + return &client.Credential{Token: "some token"}, nil } }) @@ -96,12 +93,10 @@ func TestRun(t *testing.T) { when("the token exchange times out", func() { it.Before(func() { - tokenExchanger = func(ctx context.Context, token, caBundle, apiEndpoint string) (*clientauthenticationv1beta1.ExecCredential, error) { + tokenExchanger = func(ctx context.Context, token, caBundle, apiEndpoint string) (*client.Credential, error) { select { case <-time.After(100 * time.Millisecond): - return &clientauthenticationv1beta1.ExecCredential{ - Status: &clientauthenticationv1beta1.ExecCredentialStatus{Token: "some token"}, - }, nil + return &client.Credential{Token: "some token"}, nil case <-ctx.Done(): return nil, ctx.Err() } @@ -118,14 +113,14 @@ func TestRun(t *testing.T) { var actualToken, actualCaBundle, actualAPIEndpoint string it.Before(func() { - tokenExchanger = func(ctx context.Context, token, caBundle, apiEndpoint string) (*clientauthenticationv1beta1.ExecCredential, error) { + tokenExchanger = func(ctx context.Context, token, caBundle, apiEndpoint string) (*client.Credential, error) { actualToken, actualCaBundle, actualAPIEndpoint = token, caBundle, apiEndpoint - return &clientauthenticationv1beta1.ExecCredential{ - TypeMeta: metav1.TypeMeta{ - Kind: "ExecCredential", - APIVersion: "client.authentication.k8s.io/v1beta1", - }, - Status: &clientauthenticationv1beta1.ExecCredentialStatus{Token: "some token"}, + now := time.Date(2020, 7, 29, 1, 2, 3, 0, time.UTC) + return &client.Credential{ + ExpirationTimestamp: &now, + ClientCertificateData: "some certificate", + ClientKeyData: "some key", + Token: "some token", }, nil } }) @@ -141,6 +136,9 @@ func TestRun(t *testing.T) { "apiVersion": "client.authentication.k8s.io/v1beta1", "spec": {}, "status": { + "expirationTimestamp":"2020-07-29T01:02:03Z", + "clientCertificateData": "some certificate", + "clientKeyData":"some key", "token": "some token" } }` diff --git a/go.mod b/go.mod index 7764b378..96d5e59e 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/stretchr/testify v1.6.1 github.com/suzerain-io/placeholder-name-api v0.0.0-20200724000517-dc602fd8d75e github.com/suzerain-io/placeholder-name-client-go v0.0.0-20200714203950-a414963b4f95 + github.com/suzerain-io/placeholder-name/pkg/client v0.0.0-00010101000000-000000000000 golang.org/x/tools v0.0.0-20200707134715-9e0a013e855f // indirect k8s.io/api v0.19.0-rc.0 k8s.io/apimachinery v0.19.0-rc.0 @@ -23,3 +24,5 @@ require ( k8s.io/utils v0.0.0-20200619165400-6e3d28b6ed19 sigs.k8s.io/yaml v1.2.0 ) + +replace github.com/suzerain-io/placeholder-name/pkg/client => ./pkg/client diff --git a/pkg/client/client.go b/pkg/client/client.go index 279859f9..068f031e 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -6,77 +6,148 @@ SPDX-License-Identifier: Apache-2.0 package client import ( + "bytes" "context" + "crypto/tls" + "crypto/x509" + "encoding/json" "fmt" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1" - "k8s.io/client-go/tools/clientcmd" - clientcmdapi "k8s.io/client-go/tools/clientcmd/api" - - placeholderv1alpha1 "github.com/suzerain-io/placeholder-name-api/pkg/apis/placeholder/v1alpha1" - placeholderclientset "github.com/suzerain-io/placeholder-name-client-go/pkg/generated/clientset/versioned" - "github.com/suzerain-io/placeholder-name/internal/constable" + "net/http" + "net/url" + "path/filepath" + "time" ) -// ErrLoginFailed is returned by ExchangeToken when the server rejects the login request. -const ErrLoginFailed = constable.Error("login failed") +var ( + // ErrLoginFailed is returned by ExchangeToken when the server rejects the login request. + ErrLoginFailed = fmt.Errorf("login failed") -func ExchangeToken(ctx context.Context, token, caBundle, apiEndpoint string) (*clientauthenticationv1beta1.ExecCredential, error) { - clientset, err := getClient(apiEndpoint, caBundle) - if err != nil { - return nil, fmt.Errorf("could not get API client: %w", err) + // ErrInvalidAPIEndpoint is returned by ExchangeToken when the provided API endpoint is invalid. + ErrInvalidAPIEndpoint = fmt.Errorf("invalid API endpoint") + + // ErrInvalidCABundle is returned by ExchangeToken when the provided CA bundle is invalid. + ErrInvalidCABundle = fmt.Errorf("invalid CA bundle") +) + +const ( + // loginRequestsAPIPath is the API path for the v1alpha1 LoginRequest API. + loginRequestsAPIPath = "/apis/placeholder.suzerain-io.github.io/v1alpha1/loginrequests" + + // userAgent is the user agent header value sent with requests. + userAgent = "placeholder-name" +) + +func loginRequest(ctx context.Context, apiEndpoint *url.URL, token string) (*http.Request, error) { + type LoginRequestTokenCredential struct { + Value string `json:"value"` } - - resp, err := clientset.PlaceholderV1alpha1().LoginRequests().Create(ctx, &placeholderv1alpha1.LoginRequest{ - Spec: placeholderv1alpha1.LoginRequestSpec{ - Type: placeholderv1alpha1.TokenLoginCredentialType, - Token: &placeholderv1alpha1.LoginRequestTokenCredential{ - Value: token, - }, - }, - }, metav1.CreateOptions{}) - if err != nil { - return nil, fmt.Errorf("could not login: %w", err) + type LoginRequestSpec struct { + Type string `json:"type"` + Token *LoginRequestTokenCredential `json:"token"` } - if resp.Status.Credential == nil || resp.Status.Message != "" { - return nil, fmt.Errorf("%w: %s", ErrLoginFailed, resp.Status.Message) + body := struct { + APIVersion string `json:"apiVersion"` + Kind string `json:"kind"` + Metadata struct { + CreationTimestamp *string `json:"creationTimestamp"` + } `json:"metadata"` + Spec LoginRequestSpec `json:"spec"` + Status struct{} `json:"status"` + }{ + APIVersion: "placeholder.suzerain-io.github.io/v1alpha1", + Kind: "LoginRequest", + Spec: LoginRequestSpec{Type: "token", Token: &LoginRequestTokenCredential{Value: token}}, } - - return &clientauthenticationv1beta1.ExecCredential{ - TypeMeta: metav1.TypeMeta{ - Kind: "ExecCredential", - APIVersion: "client.authentication.k8s.io/v1beta1", - }, - Status: &clientauthenticationv1beta1.ExecCredentialStatus{ - ExpirationTimestamp: resp.Status.Credential.ExpirationTimestamp, - ClientCertificateData: resp.Status.Credential.ClientCertificateData, - ClientKeyData: resp.Status.Credential.ClientKeyData, - }, - }, nil -} - -// getClient returns an anonymous clientset for the placeholder-name API at the provided endpoint/CA bundle. -func getClient(apiEndpoint string, caBundle string) (placeholderclientset.Interface, error) { - cfg, err := clientcmd.NewNonInteractiveClientConfig(clientcmdapi.Config{ - Clusters: map[string]*clientcmdapi.Cluster{ - "cluster": { - Server: apiEndpoint, - CertificateAuthorityData: []byte(caBundle), - }, - }, - Contexts: map[string]*clientcmdapi.Context{ - "current": { - Cluster: "cluster", - AuthInfo: "client", - }, - }, - AuthInfos: map[string]*clientcmdapi.AuthInfo{ - "client": {}, - }, - }, "current", nil, nil).ClientConfig() + bodyJSON, err := json.Marshal(&body) if err != nil { return nil, err } - return placeholderclientset.NewForConfig(cfg) + + req, err := http.NewRequestWithContext(ctx, http.MethodPost, apiEndpoint.String(), bytes.NewReader(bodyJSON)) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("User-Agent", userAgent) + return req, nil +} + +// Credential is the output of an ExchangeToken operation. It is equivalent to the data +// in the Kubernetes client.authentication.k8s.io/v1beta1 ExecCredentialStatus type. +type Credential struct { + // ExpirationTimestamp indicates a time when the provided credentials expire. + ExpirationTimestamp *time.Time + + // Token is a bearer token used by the client for request authentication. + Token string + + // PEM-encoded client TLS certificates (including intermediates, if any). + ClientCertificateData string + + // PEM-encoded private key for the above certificate. + ClientKeyData string +} + +func ExchangeToken(ctx context.Context, token, caBundle, apiEndpoint string) (*Credential, error) { + // Parse and validate the provided API endpoint. + endpointURL, err := url.Parse(apiEndpoint) + if err != nil { + return nil, fmt.Errorf("%w: %s", ErrInvalidAPIEndpoint, err.Error()) + } + if endpointURL.Scheme != "https" { + return nil, fmt.Errorf(`%w: protocol must be "https", not %q`, ErrInvalidAPIEndpoint, endpointURL.Scheme) + } + + // Form the LoginRequest API URL by appending the API path to the main API endpoint. + placeholderEndpointURL := *endpointURL + placeholderEndpointURL.Path = filepath.Join(placeholderEndpointURL.Path, loginRequestsAPIPath) + + // Initialize a TLS client configuration from the provided CA bundle. + tlsConfig := tls.Config{ + MinVersion: tls.VersionTLS12, + RootCAs: x509.NewCertPool(), + } + if !tlsConfig.RootCAs.AppendCertsFromPEM([]byte(caBundle)) { + return nil, fmt.Errorf("%w: no certificates found", ErrInvalidCABundle) + } + + // Create a request object for the "POST /apis/placeholder.suzerain-io.github.io/v1alpha1/loginrequests" request. + req, err := loginRequest(ctx, &placeholderEndpointURL, token) + if err != nil { + return nil, fmt.Errorf("could not build request: %w", err) + } + + client := http.Client{Transport: &http.Transport{TLSClientConfig: &tlsConfig}} + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("could not login: %w", err) + } + defer func() { _ = resp.Body.Close() }() + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("could not login: server returned status %d", resp.StatusCode) + } + + var respBody struct { + APIVersion string `json:"apiVersion"` + Kind string `json:"kind"` + Status struct { + Credential *struct { + ClientCertificateData string `json:"clientCertificateData"` + ClientKeyData string `json:"clientKeyData"` + } + Message string `json:"message"` + } `json:"status"` + } + if err := json.NewDecoder(resp.Body).Decode(&respBody); err != nil { + return nil, fmt.Errorf("invalid login response: %w", err) + } + + if respBody.Status.Credential == nil || respBody.Status.Message != "" { + return nil, fmt.Errorf("%w: %s", ErrLoginFailed, respBody.Status.Message) + } + + return &Credential{ + ClientCertificateData: respBody.Status.Credential.ClientCertificateData, + ClientKeyData: respBody.Status.Credential.ClientKeyData, + }, nil } diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index 209fe81d..1204074c 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -7,7 +7,6 @@ package client import ( "context" - "encoding/json" "encoding/pem" "io/ioutil" "net/http" @@ -15,10 +14,6 @@ import ( "testing" "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1" - - placeholderv1alpha1 "github.com/suzerain-io/placeholder-name-api/pkg/apis/placeholder/v1alpha1" ) func startTestServer(t *testing.T, handler http.HandlerFunc) (string, string) { @@ -39,9 +34,36 @@ func TestExchangeToken(t *testing.T) { t.Run("invalid configuration", func(t *testing.T) { t.Parallel() - got, err := ExchangeToken(ctx, "", "", "") - require.EqualError(t, err, "could not get API client: invalid configuration: no configuration has been provided, try setting KUBERNETES_MASTER environment variable") - require.Nil(t, got) + for _, tt := range []struct { + name string + caBundle string + apiEndpoint string + wantErr string + }{ + { + name: "bad URL", + apiEndpoint: "%@Q$!", + wantErr: `invalid API endpoint: parse "%@Q$!": invalid URL escape "%@Q"`, + }, + { + name: "plain HTTP URL", + apiEndpoint: "http://example.com", + wantErr: `invalid API endpoint: protocol must be "https", not "http"`, + }, + { + name: "no CA certs", + apiEndpoint: "https://example.com", + caBundle: "", + wantErr: `invalid CA bundle: no certificates found`, + }, + } { + tt := tt + t.Run(tt.name, func(t *testing.T) { + got, err := ExchangeToken(ctx, "", tt.caBundle, tt.apiEndpoint) + require.EqualError(t, err, tt.wantErr) + require.Nil(t, got) + }) + } }) t.Run("server error", func(t *testing.T) { @@ -53,7 +75,20 @@ func TestExchangeToken(t *testing.T) { }) got, err := ExchangeToken(ctx, "", caBundle, endpoint) - require.EqualError(t, err, `could not login: an error on the server ("some server error") has prevented the request from succeeding (post loginrequests.placeholder.suzerain-io.github.io)`) + require.EqualError(t, err, `could not login: server returned status 500`) + require.Nil(t, got) + }) + + t.Run("server invalid JSON", func(t *testing.T) { + t.Parallel() + // Start a test server that returns only 500 errors. + caBundle, endpoint := startTestServer(t, func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusCreated) + _, _ = w.Write([]byte("not valid json")) + }) + + got, err := ExchangeToken(ctx, "", caBundle, endpoint) + require.EqualError(t, err, `invalid login response: invalid character 'o' in literal null (expecting 'u')`) require.Nil(t, got) }) @@ -62,10 +97,19 @@ func TestExchangeToken(t *testing.T) { // Start a test server that returns success but with an error message caBundle, endpoint := startTestServer(t, func(w http.ResponseWriter, r *http.Request) { w.Header().Set("content-type", "application/json") - _ = json.NewEncoder(w).Encode(&placeholderv1alpha1.LoginRequest{ - TypeMeta: metav1.TypeMeta{APIVersion: "placeholder.suzerain-io.github.io/v1alpha1", Kind: "LoginRequest"}, - Status: placeholderv1alpha1.LoginRequestStatus{Message: "some login failure"}, - }) + w.WriteHeader(http.StatusCreated) + _, _ = w.Write([]byte(` + { + "kind": "LoginRequest", + "apiVersion": "placeholder.suzerain-io.github.io/v1alpha1", + "metadata": { + "creationTimestamp": null + }, + "spec": {}, + "status": { + "message": "some login failure" + } + }`)) }) got, err := ExchangeToken(ctx, "", caBundle, endpoint) @@ -93,7 +137,9 @@ func TestExchangeToken(t *testing.T) { }, "spec": { "type": "token", - "token": {} + "token": { + "value": "test-token" + } }, "status": {} }`, @@ -101,28 +147,29 @@ func TestExchangeToken(t *testing.T) { ) w.Header().Set("content-type", "application/json") - _ = json.NewEncoder(w).Encode(&placeholderv1alpha1.LoginRequest{ - TypeMeta: metav1.TypeMeta{APIVersion: "placeholder.suzerain-io.github.io/v1alpha1", Kind: "LoginRequest"}, - Status: placeholderv1alpha1.LoginRequestStatus{ - Credential: &placeholderv1alpha1.LoginRequestCredential{ - ClientCertificateData: "test-certificate", - ClientKeyData: "test-key", - }, - }, - }) + w.WriteHeader(http.StatusCreated) + _, _ = w.Write([]byte(` + { + "kind": "LoginRequest", + "apiVersion": "placeholder.suzerain-io.github.io/v1alpha1", + "metadata": { + "creationTimestamp": null + }, + "spec": {}, + "status": { + "credential": { + "clientCertificateData": "test-certificate", + "clientKeyData": "test-key" + } + } + }`)) }) - got, err := ExchangeToken(ctx, "", caBundle, endpoint) + got, err := ExchangeToken(ctx, "test-token", caBundle, endpoint) require.NoError(t, err) - require.Equal(t, &clientauthenticationv1beta1.ExecCredential{ - TypeMeta: metav1.TypeMeta{ - Kind: "ExecCredential", - APIVersion: "client.authentication.k8s.io/v1beta1", - }, - Status: &clientauthenticationv1beta1.ExecCredentialStatus{ - ClientCertificateData: "test-certificate", - ClientKeyData: "test-key", - }, + require.Equal(t, &Credential{ + ClientCertificateData: "test-certificate", + ClientKeyData: "test-key", }, got) }) } diff --git a/pkg/client/go.mod b/pkg/client/go.mod new file mode 100644 index 00000000..f1fdc649 --- /dev/null +++ b/pkg/client/go.mod @@ -0,0 +1,5 @@ +module github.com/suzerain-io/placeholder-name/pkg/client + +go 1.14 + +require github.com/stretchr/testify v1.6.1 diff --git a/pkg/client/go.sum b/pkg/client/go.sum new file mode 100644 index 00000000..afe7890c --- /dev/null +++ b/pkg/client/go.sum @@ -0,0 +1,11 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/test/integration/client_test.go b/test/integration/client_test.go index 95f0fb7e..690d0ec3 100644 --- a/test/integration/client_test.go +++ b/test/integration/client_test.go @@ -69,7 +69,7 @@ func TestClient(t *testing.T) { require.NoError(t, err) // Create a client using the certificate and key returned by the token exchange. - validClient := library.NewClientsetWithConfig(t, library.NewClientConfigWithCertAndKey(t, resp.Status.ClientCertificateData, resp.Status.ClientKeyData)) + validClient := library.NewClientsetWithConfig(t, library.NewClientConfigWithCertAndKey(t, resp.ClientCertificateData, resp.ClientKeyData)) // Make a version request, which should succeed even without any authorization. _, err = validClient.Discovery().ServerVersion()