1
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
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
267
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{}
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
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
350
351 *r = nil
352 },
353 )
354 for i := 0; i < 100; i++ {
355 expected := &clientauthenticationapi.Cluster{}
356 f.Fuzz(expected)
357
358
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
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