...

Source file src/k8s.io/client-go/rest/exec_test.go

Documentation: k8s.io/client-go/rest

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package rest
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"net"
    23  	"net/http"
    24  	"net/url"
    25  	"strings"
    26  	"testing"
    27  
    28  	"github.com/google/go-cmp/cmp"
    29  	fuzz "github.com/google/gofuzz"
    30  	"k8s.io/apimachinery/pkg/runtime"
    31  	clientauthenticationapi "k8s.io/client-go/pkg/apis/clientauthentication"
    32  	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
    33  	"k8s.io/client-go/transport"
    34  	"k8s.io/client-go/util/flowcontrol"
    35  )
    36  
    37  func TestConfigToExecCluster(t *testing.T) {
    38  	t.Parallel()
    39  
    40  	const proxyURL = "https://some-proxy-url.com/tuna/fish"
    41  	proxy := func(r *http.Request) (*url.URL, error) {
    42  		return url.Parse(proxyURL)
    43  	}
    44  
    45  	tests := []struct {
    46  		name            string
    47  		in              Config
    48  		wantOut         clientauthenticationapi.Cluster
    49  		wantErrorPrefix string
    50  	}{
    51  		{
    52  			name: "CA data from memory",
    53  			in: Config{
    54  				ExecProvider: &clientcmdapi.ExecConfig{
    55  					ProvideClusterInfo: true,
    56  					Config: &runtime.Unknown{
    57  						Raw: []byte("stuff"),
    58  					},
    59  				},
    60  				Host: "some-host",
    61  				TLSClientConfig: TLSClientConfig{
    62  					ServerName: "some-server-name",
    63  					Insecure:   true,
    64  					CAData:     []byte("some-ca-data"),
    65  				},
    66  				Proxy: proxy,
    67  			},
    68  			wantOut: clientauthenticationapi.Cluster{
    69  				Server:                   "some-host",
    70  				TLSServerName:            "some-server-name",
    71  				InsecureSkipTLSVerify:    true,
    72  				CertificateAuthorityData: []byte("some-ca-data"),
    73  				ProxyURL:                 proxyURL,
    74  				Config: &runtime.Unknown{
    75  					Raw: []byte("stuff"),
    76  				},
    77  			},
    78  		},
    79  		{
    80  			name: "CA data from file",
    81  			in: Config{
    82  				ExecProvider: &clientcmdapi.ExecConfig{
    83  					ProvideClusterInfo: true,
    84  					Config: &runtime.Unknown{
    85  						Raw: []byte("stuff"),
    86  					},
    87  				},
    88  				Host: "some-host",
    89  				TLSClientConfig: TLSClientConfig{
    90  					ServerName: "some-server-name",
    91  					Insecure:   true,
    92  					CAFile:     "testdata/ca.pem",
    93  				},
    94  				Proxy: proxy,
    95  			},
    96  			wantOut: clientauthenticationapi.Cluster{
    97  				Server:                   "some-host",
    98  				TLSServerName:            "some-server-name",
    99  				InsecureSkipTLSVerify:    true,
   100  				CertificateAuthorityData: []byte("a CA bundle lives here"),
   101  				ProxyURL:                 proxyURL,
   102  				Config: &runtime.Unknown{
   103  					Raw: []byte("stuff"),
   104  				},
   105  			},
   106  		},
   107  		{
   108  			name: "no CA data",
   109  			in: Config{
   110  				ExecProvider: &clientcmdapi.ExecConfig{
   111  					ProvideClusterInfo: true,
   112  				},
   113  				TLSClientConfig: TLSClientConfig{
   114  					CAFile: "this-file-does-not-exist",
   115  				},
   116  			},
   117  			wantErrorPrefix: "failed to load CA bundle for execProvider: ",
   118  		},
   119  		{
   120  			name: "nil proxy",
   121  			in: Config{
   122  				ExecProvider: &clientcmdapi.ExecConfig{
   123  					ProvideClusterInfo: true,
   124  					Config: &runtime.Unknown{
   125  						Raw: []byte("stuff"),
   126  					},
   127  				},
   128  				Host: "some-host",
   129  				TLSClientConfig: TLSClientConfig{
   130  					ServerName: "some-server-name",
   131  					Insecure:   true,
   132  					CAFile:     "testdata/ca.pem",
   133  				},
   134  			},
   135  			wantOut: clientauthenticationapi.Cluster{
   136  				Server:                   "some-host",
   137  				TLSServerName:            "some-server-name",
   138  				InsecureSkipTLSVerify:    true,
   139  				CertificateAuthorityData: []byte("a CA bundle lives here"),
   140  				Config: &runtime.Unknown{
   141  					Raw: []byte("stuff"),
   142  				},
   143  			},
   144  		},
   145  		{
   146  			name: "bad proxy",
   147  			in: Config{
   148  				ExecProvider: &clientcmdapi.ExecConfig{
   149  					ProvideClusterInfo: true,
   150  				},
   151  				Proxy: func(_ *http.Request) (*url.URL, error) {
   152  					return nil, errors.New("some proxy error")
   153  				},
   154  			},
   155  			wantErrorPrefix: "failed to get proxy URL for execProvider: some proxy error",
   156  		},
   157  		{
   158  			name: "proxy returns nil",
   159  			in: Config{
   160  				ExecProvider: &clientcmdapi.ExecConfig{
   161  					ProvideClusterInfo: true,
   162  				},
   163  				Proxy: func(_ *http.Request) (*url.URL, error) {
   164  					return nil, nil
   165  				},
   166  				Host: "some-host",
   167  				TLSClientConfig: TLSClientConfig{
   168  					ServerName: "some-server-name",
   169  					Insecure:   true,
   170  					CAFile:     "testdata/ca.pem",
   171  				},
   172  			},
   173  			wantOut: clientauthenticationapi.Cluster{
   174  				Server:                   "some-host",
   175  				TLSServerName:            "some-server-name",
   176  				InsecureSkipTLSVerify:    true,
   177  				CertificateAuthorityData: []byte("a CA bundle lives here"),
   178  			},
   179  		},
   180  		{
   181  			name: "invalid config host",
   182  			in: Config{
   183  				ExecProvider: &clientcmdapi.ExecConfig{
   184  					ProvideClusterInfo: true,
   185  				},
   186  				Proxy: func(_ *http.Request) (*url.URL, error) {
   187  					return nil, nil
   188  				},
   189  				Host: "invalid-config-host\n",
   190  			},
   191  			wantErrorPrefix: "failed to create proxy URL request for execProvider: ",
   192  		},
   193  	}
   194  	for _, test := range tests {
   195  		test := test
   196  		t.Run(test.name, func(t *testing.T) {
   197  			out, err := ConfigToExecCluster(&test.in)
   198  			if test.wantErrorPrefix != "" {
   199  				if err == nil {
   200  					t.Error("wanted error")
   201  				} else if !strings.HasPrefix(err.Error(), test.wantErrorPrefix) {
   202  					t.Errorf("wanted error prefix %q, got %q", test.wantErrorPrefix, err.Error())
   203  				}
   204  			} else if diff := cmp.Diff(&test.wantOut, out); diff != "" {
   205  				t.Errorf("unexpected returned cluster: -got, +want:\n %s", diff)
   206  			}
   207  		})
   208  	}
   209  }
   210  
   211  func TestConfigToExecClusterRoundtrip(t *testing.T) {
   212  	t.Parallel()
   213  
   214  	f := fuzz.New().NilChance(0.5).NumElements(1, 1)
   215  	f.Funcs(
   216  		func(r *runtime.Codec, f fuzz.Continue) {
   217  			codec := &fakeCodec{}
   218  			f.Fuzz(codec)
   219  			*r = codec
   220  		},
   221  		func(r *http.RoundTripper, f fuzz.Continue) {
   222  			roundTripper := &fakeRoundTripper{}
   223  			f.Fuzz(roundTripper)
   224  			*r = roundTripper
   225  		},
   226  		func(fn *func(http.RoundTripper) http.RoundTripper, f fuzz.Continue) {
   227  			*fn = fakeWrapperFunc
   228  		},
   229  		func(fn *transport.WrapperFunc, f fuzz.Continue) {
   230  			*fn = fakeWrapperFunc
   231  		},
   232  		func(r *runtime.NegotiatedSerializer, f fuzz.Continue) {
   233  			serializer := &fakeNegotiatedSerializer{}
   234  			f.Fuzz(serializer)
   235  			*r = serializer
   236  		},
   237  		func(r *flowcontrol.RateLimiter, f fuzz.Continue) {
   238  			limiter := &fakeLimiter{}
   239  			f.Fuzz(limiter)
   240  			*r = limiter
   241  		},
   242  		func(h *WarningHandler, f fuzz.Continue) {
   243  			*h = &fakeWarningHandler{}
   244  		},
   245  		// Authentication does not require fuzzer
   246  		func(r *AuthProviderConfigPersister, f fuzz.Continue) {},
   247  		func(r *clientcmdapi.AuthProviderConfig, f fuzz.Continue) {
   248  			r.Config = map[string]string{}
   249  		},
   250  		func(r *func(ctx context.Context, network, addr string) (net.Conn, error), f fuzz.Continue) {
   251  			*r = fakeDialFunc
   252  		},
   253  		func(r *func(*http.Request) (*url.URL, error), f fuzz.Continue) {
   254  			*r = fakeProxyFunc
   255  		},
   256  		func(r *runtime.Object, f fuzz.Continue) {
   257  			unknown := &runtime.Unknown{}
   258  			f.Fuzz(unknown)
   259  			*r = unknown
   260  		},
   261  	)
   262  	for i := 0; i < 100; i++ {
   263  		expected := &Config{}
   264  		f.Fuzz(expected)
   265  
   266  		// This is the list of known fields that this roundtrip doesn't care about. We should add new
   267  		// fields to this list if we don't want to roundtrip them on exec cluster conversion.
   268  		expected.APIPath = ""
   269  		expected.ContentConfig = ContentConfig{}
   270  		expected.Username = ""
   271  		expected.Password = ""
   272  		expected.BearerToken = ""
   273  		expected.BearerTokenFile = ""
   274  		expected.Impersonate = ImpersonationConfig{}
   275  		expected.AuthProvider = nil
   276  		expected.AuthConfigPersister = nil
   277  		expected.ExecProvider = &clientcmdapi.ExecConfig{} // ConfigToExecCluster assumes != nil.
   278  		expected.TLSClientConfig.CertFile = ""
   279  		expected.TLSClientConfig.KeyFile = ""
   280  		expected.TLSClientConfig.CAFile = ""
   281  		expected.TLSClientConfig.CertData = nil
   282  		expected.TLSClientConfig.KeyData = nil
   283  		expected.TLSClientConfig.NextProtos = nil
   284  		expected.UserAgent = ""
   285  		expected.DisableCompression = false
   286  		expected.Transport = nil
   287  		expected.WrapTransport = nil
   288  		expected.QPS = 0.0
   289  		expected.Burst = 0
   290  		expected.RateLimiter = nil
   291  		expected.WarningHandler = nil
   292  		expected.Timeout = 0
   293  		expected.Dial = nil
   294  
   295  		// Manually set URLs so we don't get an error when parsing these during the roundtrip.
   296  		if expected.Host != "" {
   297  			expected.Host = "https://some-server-url.com/tuna/fish"
   298  		}
   299  		if expected.Proxy != nil {
   300  			expected.Proxy = func(_ *http.Request) (*url.URL, error) {
   301  				return url.Parse("https://some-proxy-url.com/tuna/fish")
   302  			}
   303  		}
   304  
   305  		cluster, err := ConfigToExecCluster(expected)
   306  		if err != nil {
   307  			t.Fatal(err)
   308  		}
   309  
   310  		actual, err := ExecClusterToConfig(cluster)
   311  		if err != nil {
   312  			t.Fatal(err)
   313  		}
   314  
   315  		if actual.Proxy != nil {
   316  			actualURL, actualErr := actual.Proxy(nil)
   317  			expectedURL, expectedErr := expected.Proxy(nil)
   318  			if actualErr != nil {
   319  				t.Fatalf("failed to get url from actual proxy func: %s", actualErr.Error())
   320  			}
   321  			if expectedErr != nil {
   322  				t.Fatalf("failed to get url from expected proxy func: %s", actualErr.Error())
   323  			}
   324  			if diff := cmp.Diff(actualURL, expectedURL); diff != "" {
   325  				t.Fatal("we dropped the Config.Proxy field during conversion")
   326  			}
   327  		}
   328  		actual.Proxy = nil
   329  		expected.Proxy = nil
   330  
   331  		if actual.ExecProvider != nil {
   332  			t.Fatal("expected actual Config.ExecProvider field to be set to nil")
   333  		}
   334  		actual.ExecProvider = nil
   335  		expected.ExecProvider = nil
   336  
   337  		if diff := cmp.Diff(actual, expected); diff != "" {
   338  			t.Fatalf("we dropped some Config fields during roundtrip, -got, +want:\n %s", diff)
   339  		}
   340  	}
   341  }
   342  
   343  func TestExecClusterToConfigRoundtrip(t *testing.T) {
   344  	t.Parallel()
   345  
   346  	f := fuzz.New().NilChance(0.5).NumElements(1, 1)
   347  	f.Funcs(
   348  		func(r *runtime.Object, f fuzz.Continue) {
   349  			// We don't expect the clientauthentication.Cluster.Config to show up in the Config that
   350  			// comes back from the roundtrip, so just set it to nil.
   351  			*r = nil
   352  		},
   353  	)
   354  	for i := 0; i < 100; i++ {
   355  		expected := &clientauthenticationapi.Cluster{}
   356  		f.Fuzz(expected)
   357  
   358  		// Manually set URLs so we don't get an error when parsing these during the roundtrip.
   359  		if expected.Server != "" {
   360  			expected.Server = "https://some-server-url.com/tuna/fish"
   361  		}
   362  		if expected.ProxyURL != "" {
   363  			expected.ProxyURL = "https://some-proxy-url.com/tuna/fish"
   364  		}
   365  
   366  		config, err := ExecClusterToConfig(expected)
   367  		if err != nil {
   368  			t.Fatal(err)
   369  		}
   370  
   371  		// ConfigToExecCluster assumes config.ExecProvider is not nil.
   372  		config.ExecProvider = &clientcmdapi.ExecConfig{}
   373  
   374  		actual, err := ConfigToExecCluster(config)
   375  		if err != nil {
   376  			t.Fatal(err)
   377  		}
   378  
   379  		if diff := cmp.Diff(actual, expected); diff != "" {
   380  			t.Fatalf("we dropped some Cluster fields during roundtrip: -got, +want:\n %s", diff)
   381  		}
   382  	}
   383  }
   384  

View as plain text