rest.Config: support configuring an explict proxy URL

With support of http, https, and socks5 proxy support. We already
support configuring this via environmnet variables, but this approach
becomes inconvenient dealing with multiple clusters on different
networks, that require different proxies to connect to. Most solutions
require wrapping clients (like kubectl) in bash scripts.

Part of: https://github.com/kubernetes/client-go/issues/351

Kubernetes-commit: f3f666d5f1f6f74a8c948a5c64af993696178244
This commit is contained in:
Mike Danese
2019-05-03 13:50:17 -07:00
committed by Kubernetes Publisher
parent 06f6a9f888
commit 0caa50056a
14 changed files with 291 additions and 27 deletions

View File

@@ -23,6 +23,7 @@ import (
"io"
"net"
"net/http"
"net/url"
"path/filepath"
"reflect"
"strings"
@@ -32,12 +33,12 @@ import (
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/client-go/kubernetes/scheme"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/client-go/transport"
"k8s.io/client-go/util/flowcontrol"
"github.com/google/go-cmp/cmp"
fuzz "github.com/google/gofuzz"
"github.com/stretchr/testify/assert"
)
@@ -274,8 +275,13 @@ func (n *fakeNegotiatedSerializer) DecoderToVersion(serializer runtime.Decoder,
var fakeDialFunc = func(ctx context.Context, network, addr string) (net.Conn, error) {
return nil, fakeDialerError
}
var fakeDialerError = errors.New("fakedialer")
func fakeProxyFunc(*http.Request) (*url.URL, error) {
return nil, errors.New("fakeproxy")
}
type fakeAuthProviderConfigPersister struct{}
func (fakeAuthProviderConfigPersister) Persist(map[string]string) error {
@@ -318,8 +324,12 @@ func TestAnonymousConfig(t *testing.T) {
func(r *clientcmdapi.AuthProviderConfig, f fuzz.Continue) {
r.Config = map[string]string{}
},
// Dial does not require fuzzer
func(r *func(ctx context.Context, network, addr string) (net.Conn, error), f fuzz.Continue) {},
func(r *func(ctx context.Context, network, addr string) (net.Conn, error), f fuzz.Continue) {
*r = fakeDialFunc
},
func(r *func(*http.Request) (*url.URL, error), f fuzz.Continue) {
*r = fakeProxyFunc
},
)
for i := 0; i < 20; i++ {
original := &Config{}
@@ -350,13 +360,22 @@ func TestAnonymousConfig(t *testing.T) {
if !reflect.DeepEqual(expectedError, actualError) {
t.Fatalf("AnonymousClientConfig dropped the Dial field")
}
} else {
actual.Dial = nil
expected.Dial = nil
}
actual.Dial = nil
expected.Dial = nil
if !reflect.DeepEqual(*actual, expected) {
t.Fatalf("AnonymousClientConfig dropped unexpected fields, identify whether they are security related or not: %s", diff.ObjectGoPrintDiff(expected, actual))
if actual.Proxy != nil {
_, actualError := actual.Proxy(nil)
_, expectedError := expected.Proxy(nil)
if !reflect.DeepEqual(expectedError, actualError) {
t.Fatalf("AnonymousClientConfig dropped the Proxy field")
}
}
actual.Proxy = nil
expected.Proxy = nil
if diff := cmp.Diff(*actual, expected); diff != "" {
t.Fatalf("AnonymousClientConfig dropped unexpected fields, identify whether they are security related or not (-got, +want): %s", diff)
}
}
}
@@ -396,6 +415,9 @@ func TestCopyConfig(t *testing.T) {
func(r *func(ctx context.Context, network, addr string) (net.Conn, error), f fuzz.Continue) {
*r = fakeDialFunc
},
func(r *func(*http.Request) (*url.URL, error), f fuzz.Continue) {
*r = fakeProxyFunc
},
)
for i := 0; i < 20; i++ {
original := &Config{}
@@ -410,10 +432,10 @@ func TestCopyConfig(t *testing.T) {
// function return the expected object.
if actual.WrapTransport == nil || !reflect.DeepEqual(expected.WrapTransport(nil), &fakeRoundTripper{}) {
t.Fatalf("CopyConfig dropped the WrapTransport field")
} else {
actual.WrapTransport = nil
expected.WrapTransport = nil
}
actual.WrapTransport = nil
expected.WrapTransport = nil
if actual.Dial != nil {
_, actualError := actual.Dial(context.Background(), "", "")
_, expectedError := expected.Dial(context.Background(), "", "")
@@ -423,6 +445,7 @@ func TestCopyConfig(t *testing.T) {
}
actual.Dial = nil
expected.Dial = nil
if actual.AuthConfigPersister != nil {
actualError := actual.AuthConfigPersister.Persist(nil)
expectedError := expected.AuthConfigPersister.Persist(nil)
@@ -433,8 +456,18 @@ func TestCopyConfig(t *testing.T) {
actual.AuthConfigPersister = nil
expected.AuthConfigPersister = nil
if !reflect.DeepEqual(*actual, expected) {
t.Fatalf("CopyConfig dropped unexpected fields, identify whether they are security related or not: %s", diff.ObjectReflectDiff(expected, *actual))
if actual.Proxy != nil {
_, actualError := actual.Proxy(nil)
_, expectedError := expected.Proxy(nil)
if !reflect.DeepEqual(expectedError, actualError) {
t.Fatalf("CopyConfig dropped the Proxy field")
}
}
actual.Proxy = nil
expected.Proxy = nil
if diff := cmp.Diff(*actual, expected); diff != "" {
t.Fatalf("CopyConfig dropped unexpected fields, identify whether they are security related or not (-got, +want): %s", diff)
}
}
}
@@ -564,10 +597,11 @@ func TestConfigSprint(t *testing.T) {
RateLimiter: &fakeLimiter{},
Timeout: 3 * time.Second,
Dial: fakeDialFunc,
Proxy: fakeProxyFunc,
}
want := fmt.Sprintf(
`&rest.Config{Host:"localhost:8080", APIPath:"v1", ContentConfig:rest.ContentConfig{AcceptContentTypes:"application/json", ContentType:"application/json", GroupVersion:(*schema.GroupVersion)(nil), NegotiatedSerializer:runtime.NegotiatedSerializer(nil)}, Username:"gopher", Password:"--- REDACTED ---", BearerToken:"--- REDACTED ---", BearerTokenFile:"", Impersonate:rest.ImpersonationConfig{UserName:"gopher2", Groups:[]string(nil), Extra:map[string][]string(nil)}, AuthProvider:api.AuthProviderConfig{Name: "gopher", Config: map[string]string{--- REDACTED ---}}, AuthConfigPersister:rest.AuthProviderConfigPersister(--- REDACTED ---), ExecProvider:api.AuthProviderConfig{Command: "sudo", Args: []string{"--- REDACTED ---"}, Env: []ExecEnvVar{--- REDACTED ---}, APIVersion: ""}, TLSClientConfig:rest.sanitizedTLSClientConfig{Insecure:false, ServerName:"", CertFile:"a.crt", KeyFile:"a.key", CAFile:"", CertData:[]uint8{0x2d, 0x2d, 0x2d, 0x20, 0x54, 0x52, 0x55, 0x4e, 0x43, 0x41, 0x54, 0x45, 0x44, 0x20, 0x2d, 0x2d, 0x2d}, KeyData:[]uint8{0x2d, 0x2d, 0x2d, 0x20, 0x52, 0x45, 0x44, 0x41, 0x43, 0x54, 0x45, 0x44, 0x20, 0x2d, 0x2d, 0x2d}, CAData:[]uint8(nil), NextProtos:[]string{"h2", "http/1.1"}}, UserAgent:"gobot", DisableCompression:false, Transport:(*rest.fakeRoundTripper)(%p), WrapTransport:(transport.WrapperFunc)(%p), QPS:1, Burst:2, RateLimiter:(*rest.fakeLimiter)(%p), Timeout:3000000000, Dial:(func(context.Context, string, string) (net.Conn, error))(%p)}`,
c.Transport, fakeWrapperFunc, c.RateLimiter, fakeDialFunc,
`&rest.Config{Host:"localhost:8080", APIPath:"v1", ContentConfig:rest.ContentConfig{AcceptContentTypes:"application/json", ContentType:"application/json", GroupVersion:(*schema.GroupVersion)(nil), NegotiatedSerializer:runtime.NegotiatedSerializer(nil)}, Username:"gopher", Password:"--- REDACTED ---", BearerToken:"--- REDACTED ---", BearerTokenFile:"", Impersonate:rest.ImpersonationConfig{UserName:"gopher2", Groups:[]string(nil), Extra:map[string][]string(nil)}, AuthProvider:api.AuthProviderConfig{Name: "gopher", Config: map[string]string{--- REDACTED ---}}, AuthConfigPersister:rest.AuthProviderConfigPersister(--- REDACTED ---), ExecProvider:api.AuthProviderConfig{Command: "sudo", Args: []string{"--- REDACTED ---"}, Env: []ExecEnvVar{--- REDACTED ---}, APIVersion: ""}, TLSClientConfig:rest.sanitizedTLSClientConfig{Insecure:false, ServerName:"", CertFile:"a.crt", KeyFile:"a.key", CAFile:"", CertData:[]uint8{0x2d, 0x2d, 0x2d, 0x20, 0x54, 0x52, 0x55, 0x4e, 0x43, 0x41, 0x54, 0x45, 0x44, 0x20, 0x2d, 0x2d, 0x2d}, KeyData:[]uint8{0x2d, 0x2d, 0x2d, 0x20, 0x52, 0x45, 0x44, 0x41, 0x43, 0x54, 0x45, 0x44, 0x20, 0x2d, 0x2d, 0x2d}, CAData:[]uint8(nil), NextProtos:[]string{"h2", "http/1.1"}}, UserAgent:"gobot", DisableCompression:false, Transport:(*rest.fakeRoundTripper)(%p), WrapTransport:(transport.WrapperFunc)(%p), QPS:1, Burst:2, RateLimiter:(*rest.fakeLimiter)(%p), Timeout:3000000000, Dial:(func(context.Context, string, string) (net.Conn, error))(%p), Proxy:(func(*http.Request) (*url.URL, error))(%p)}`,
c.Transport, fakeWrapperFunc, c.RateLimiter, fakeDialFunc, fakeProxyFunc,
)
for _, f := range []string{"%s", "%v", "%+v", "%#v"} {