mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-02 16:29:21 +00:00
Merge pull request #81443 from mikedanese/socks5
rest.Config: support configuring an explict proxy URL
This commit is contained in:
commit
ba35704b51
@ -1034,7 +1034,7 @@ func TestServeExecInContainerIdleTimeout(t *testing.T) {
|
|||||||
|
|
||||||
url := fw.testHTTPServer.URL + "/exec/" + podNamespace + "/" + podName + "/" + expectedContainerName + "?c=ls&c=-a&" + api.ExecStdinParam + "=1"
|
url := fw.testHTTPServer.URL + "/exec/" + podNamespace + "/" + podName + "/" + expectedContainerName + "?c=ls&c=-a&" + api.ExecStdinParam + "=1"
|
||||||
|
|
||||||
upgradeRoundTripper := spdy.NewSpdyRoundTripper(nil, true, true)
|
upgradeRoundTripper := spdy.NewRoundTripper(nil, true, true)
|
||||||
c := &http.Client{Transport: upgradeRoundTripper}
|
c := &http.Client{Transport: upgradeRoundTripper}
|
||||||
|
|
||||||
resp, err := c.Do(makeReq(t, "POST", url, "v4.channel.k8s.io"))
|
resp, err := c.Do(makeReq(t, "POST", url, "v4.channel.k8s.io"))
|
||||||
|
@ -76,19 +76,20 @@ var _ utilnet.TLSClientConfigHolder = &SpdyRoundTripper{}
|
|||||||
var _ httpstream.UpgradeRoundTripper = &SpdyRoundTripper{}
|
var _ httpstream.UpgradeRoundTripper = &SpdyRoundTripper{}
|
||||||
var _ utilnet.Dialer = &SpdyRoundTripper{}
|
var _ utilnet.Dialer = &SpdyRoundTripper{}
|
||||||
|
|
||||||
// NewRoundTripper creates a new SpdyRoundTripper that will use
|
// NewRoundTripper creates a new SpdyRoundTripper that will use the specified
|
||||||
// the specified tlsConfig.
|
// tlsConfig.
|
||||||
func NewRoundTripper(tlsConfig *tls.Config, followRedirects, requireSameHostRedirects bool) httpstream.UpgradeRoundTripper {
|
func NewRoundTripper(tlsConfig *tls.Config, followRedirects, requireSameHostRedirects bool) *SpdyRoundTripper {
|
||||||
return NewSpdyRoundTripper(tlsConfig, followRedirects, requireSameHostRedirects)
|
return NewRoundTripperWithProxy(tlsConfig, followRedirects, requireSameHostRedirects, utilnet.NewProxierWithNoProxyCIDR(http.ProxyFromEnvironment))
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSpdyRoundTripper creates a new SpdyRoundTripper that will use
|
// NewRoundTripperWithProxy creates a new SpdyRoundTripper that will use the
|
||||||
// the specified tlsConfig. This function is mostly meant for unit tests.
|
// specified tlsConfig and proxy func.
|
||||||
func NewSpdyRoundTripper(tlsConfig *tls.Config, followRedirects, requireSameHostRedirects bool) *SpdyRoundTripper {
|
func NewRoundTripperWithProxy(tlsConfig *tls.Config, followRedirects, requireSameHostRedirects bool, proxier func(*http.Request) (*url.URL, error)) *SpdyRoundTripper {
|
||||||
return &SpdyRoundTripper{
|
return &SpdyRoundTripper{
|
||||||
tlsConfig: tlsConfig,
|
tlsConfig: tlsConfig,
|
||||||
followRedirects: followRedirects,
|
followRedirects: followRedirects,
|
||||||
requireSameHostRedirects: requireSameHostRedirects,
|
requireSameHostRedirects: requireSameHostRedirects,
|
||||||
|
proxier: proxier,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,11 +117,7 @@ func (s *SpdyRoundTripper) Dial(req *http.Request) (net.Conn, error) {
|
|||||||
// dial dials the host specified by req, using TLS if appropriate, optionally
|
// dial dials the host specified by req, using TLS if appropriate, optionally
|
||||||
// using a proxy server if one is configured via environment variables.
|
// using a proxy server if one is configured via environment variables.
|
||||||
func (s *SpdyRoundTripper) dial(req *http.Request) (net.Conn, error) {
|
func (s *SpdyRoundTripper) dial(req *http.Request) (net.Conn, error) {
|
||||||
proxier := s.proxier
|
proxyURL, err := s.proxier(req)
|
||||||
if proxier == nil {
|
|
||||||
proxier = utilnet.NewProxierWithNoProxyCIDR(http.ProxyFromEnvironment)
|
|
||||||
}
|
|
||||||
proxyURL, err := proxier(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -282,7 +282,7 @@ func TestRoundTripAndNewConnection(t *testing.T) {
|
|||||||
t.Fatalf("%s: Error creating request: %s", k, err)
|
t.Fatalf("%s: Error creating request: %s", k, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
spdyTransport := NewSpdyRoundTripper(testCase.clientTLS, redirect, redirect)
|
spdyTransport := NewRoundTripper(testCase.clientTLS, redirect, redirect)
|
||||||
|
|
||||||
var proxierCalled bool
|
var proxierCalled bool
|
||||||
var proxyCalledWithHost string
|
var proxyCalledWithHost string
|
||||||
@ -425,7 +425,7 @@ func TestRoundTripRedirects(t *testing.T) {
|
|||||||
t.Fatalf("Error creating request: %s", err)
|
t.Fatalf("Error creating request: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
spdyTransport := NewSpdyRoundTripper(nil, true, true)
|
spdyTransport := NewRoundTripper(nil, true, true)
|
||||||
client := &http.Client{Transport: spdyTransport}
|
client := &http.Client{Transport: spdyTransport}
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
|
@ -38,6 +38,7 @@ go_test(
|
|||||||
"//staging/src/k8s.io/client-go/transport:go_default_library",
|
"//staging/src/k8s.io/client-go/transport:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/util/flowcontrol:go_default_library",
|
"//staging/src/k8s.io/client-go/util/flowcontrol:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/util/testing:go_default_library",
|
"//staging/src/k8s.io/client-go/util/testing:go_default_library",
|
||||||
|
"//vendor/github.com/google/go-cmp/cmp:go_default_library",
|
||||||
"//vendor/github.com/google/gofuzz:go_default_library",
|
"//vendor/github.com/google/gofuzz:go_default_library",
|
||||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||||
"//vendor/k8s.io/klog:go_default_library",
|
"//vendor/k8s.io/klog:go_default_library",
|
||||||
|
@ -18,16 +18,16 @@ package rest
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"net/http/httputil"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
v1beta1 "k8s.io/api/extensions/v1beta1"
|
v1beta1 "k8s.io/api/extensions/v1beta1"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
@ -37,6 +37,8 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/diff"
|
"k8s.io/apimachinery/pkg/util/diff"
|
||||||
"k8s.io/client-go/kubernetes/scheme"
|
"k8s.io/client-go/kubernetes/scheme"
|
||||||
utiltesting "k8s.io/client-go/util/testing"
|
utiltesting "k8s.io/client-go/util/testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TestParam struct {
|
type TestParam struct {
|
||||||
@ -252,7 +254,7 @@ func validate(testParam TestParam, t *testing.T, body []byte, fakeHandler *utilt
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHttpMethods(t *testing.T) {
|
func TestHTTPMethods(t *testing.T) {
|
||||||
testServer, _, _ := testServerEnv(t, 200)
|
testServer, _, _ := testServerEnv(t, 200)
|
||||||
defer testServer.Close()
|
defer testServer.Close()
|
||||||
c, _ := restClient(testServer)
|
c, _ := restClient(testServer)
|
||||||
@ -283,6 +285,57 @@ func TestHttpMethods(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHTTPProxy(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
testServer, fh, _ := testServerEnv(t, 200)
|
||||||
|
fh.ResponseBody = "backend data"
|
||||||
|
defer testServer.Close()
|
||||||
|
|
||||||
|
testProxyServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
to, err := url.Parse(req.RequestURI)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
w.Write([]byte("proxied: "))
|
||||||
|
httputil.NewSingleHostReverseProxy(to).ServeHTTP(w, req)
|
||||||
|
}))
|
||||||
|
defer testProxyServer.Close()
|
||||||
|
|
||||||
|
t.Logf(testProxyServer.URL)
|
||||||
|
|
||||||
|
u, err := url.Parse(testProxyServer.URL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to parse test proxy server url: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := RESTClientFor(&Config{
|
||||||
|
Host: testServer.URL,
|
||||||
|
ContentConfig: ContentConfig{
|
||||||
|
GroupVersion: &v1.SchemeGroupVersion,
|
||||||
|
NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
|
||||||
|
},
|
||||||
|
Proxy: http.ProxyURL(u),
|
||||||
|
Username: "user",
|
||||||
|
Password: "pass",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create client: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
request := c.Get()
|
||||||
|
if request == nil {
|
||||||
|
t.Fatalf("Get: Object returned should not be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := request.DoRaw(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
if got, want := string(b), "proxied: backend data"; !cmp.Equal(got, want) {
|
||||||
|
t.Errorf("unexpected body: %v", cmp.Diff(want, got))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCreateBackoffManager(t *testing.T) {
|
func TestCreateBackoffManager(t *testing.T) {
|
||||||
|
|
||||||
theUrl, _ := url.Parse("http://localhost")
|
theUrl, _ := url.Parse("http://localhost")
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
gruntime "runtime"
|
gruntime "runtime"
|
||||||
@ -128,6 +129,13 @@ type Config struct {
|
|||||||
// Dial specifies the dial function for creating unencrypted TCP connections.
|
// Dial specifies the dial function for creating unencrypted TCP connections.
|
||||||
Dial func(ctx context.Context, network, address string) (net.Conn, error)
|
Dial func(ctx context.Context, network, address string) (net.Conn, error)
|
||||||
|
|
||||||
|
// Proxy is the the proxy func to be used for all requests made by this
|
||||||
|
// transport. If Proxy is nil, http.ProxyFromEnvironment is used. If Proxy
|
||||||
|
// returns a nil *URL, no proxy is used.
|
||||||
|
//
|
||||||
|
// socks5 proxying does not currently support spdy streaming endpoints.
|
||||||
|
Proxy func(*http.Request) (*url.URL, error)
|
||||||
|
|
||||||
// Version forces a specific version to be used (if registered)
|
// Version forces a specific version to be used (if registered)
|
||||||
// Do we need this?
|
// Do we need this?
|
||||||
// Version string
|
// Version string
|
||||||
@ -560,6 +568,7 @@ func AnonymousClientConfig(config *Config) *Config {
|
|||||||
Burst: config.Burst,
|
Burst: config.Burst,
|
||||||
Timeout: config.Timeout,
|
Timeout: config.Timeout,
|
||||||
Dial: config.Dial,
|
Dial: config.Dial,
|
||||||
|
Proxy: config.Proxy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -601,5 +610,6 @@ func CopyConfig(config *Config) *Config {
|
|||||||
RateLimiter: config.RateLimiter,
|
RateLimiter: config.RateLimiter,
|
||||||
Timeout: config.Timeout,
|
Timeout: config.Timeout,
|
||||||
Dial: config.Dial,
|
Dial: config.Dial,
|
||||||
|
Proxy: config.Proxy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
@ -32,12 +33,12 @@ import (
|
|||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/util/diff"
|
|
||||||
"k8s.io/client-go/kubernetes/scheme"
|
"k8s.io/client-go/kubernetes/scheme"
|
||||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||||
"k8s.io/client-go/transport"
|
"k8s.io/client-go/transport"
|
||||||
"k8s.io/client-go/util/flowcontrol"
|
"k8s.io/client-go/util/flowcontrol"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
fuzz "github.com/google/gofuzz"
|
fuzz "github.com/google/gofuzz"
|
||||||
"github.com/stretchr/testify/assert"
|
"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) {
|
var fakeDialFunc = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
return nil, fakeDialerError
|
return nil, fakeDialerError
|
||||||
}
|
}
|
||||||
|
|
||||||
var fakeDialerError = errors.New("fakedialer")
|
var fakeDialerError = errors.New("fakedialer")
|
||||||
|
|
||||||
|
func fakeProxyFunc(*http.Request) (*url.URL, error) {
|
||||||
|
return nil, errors.New("fakeproxy")
|
||||||
|
}
|
||||||
|
|
||||||
type fakeAuthProviderConfigPersister struct{}
|
type fakeAuthProviderConfigPersister struct{}
|
||||||
|
|
||||||
func (fakeAuthProviderConfigPersister) Persist(map[string]string) error {
|
func (fakeAuthProviderConfigPersister) Persist(map[string]string) error {
|
||||||
@ -318,8 +324,12 @@ func TestAnonymousConfig(t *testing.T) {
|
|||||||
func(r *clientcmdapi.AuthProviderConfig, f fuzz.Continue) {
|
func(r *clientcmdapi.AuthProviderConfig, f fuzz.Continue) {
|
||||||
r.Config = map[string]string{}
|
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++ {
|
for i := 0; i < 20; i++ {
|
||||||
original := &Config{}
|
original := &Config{}
|
||||||
@ -350,13 +360,22 @@ func TestAnonymousConfig(t *testing.T) {
|
|||||||
if !reflect.DeepEqual(expectedError, actualError) {
|
if !reflect.DeepEqual(expectedError, actualError) {
|
||||||
t.Fatalf("AnonymousClientConfig dropped the Dial field")
|
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) {
|
if actual.Proxy != nil {
|
||||||
t.Fatalf("AnonymousClientConfig dropped unexpected fields, identify whether they are security related or not: %s", diff.ObjectGoPrintDiff(expected, actual))
|
_, 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) {
|
func(r *func(ctx context.Context, network, addr string) (net.Conn, error), f fuzz.Continue) {
|
||||||
*r = fakeDialFunc
|
*r = fakeDialFunc
|
||||||
},
|
},
|
||||||
|
func(r *func(*http.Request) (*url.URL, error), f fuzz.Continue) {
|
||||||
|
*r = fakeProxyFunc
|
||||||
|
},
|
||||||
)
|
)
|
||||||
for i := 0; i < 20; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
original := &Config{}
|
original := &Config{}
|
||||||
@ -410,10 +432,10 @@ func TestCopyConfig(t *testing.T) {
|
|||||||
// function return the expected object.
|
// function return the expected object.
|
||||||
if actual.WrapTransport == nil || !reflect.DeepEqual(expected.WrapTransport(nil), &fakeRoundTripper{}) {
|
if actual.WrapTransport == nil || !reflect.DeepEqual(expected.WrapTransport(nil), &fakeRoundTripper{}) {
|
||||||
t.Fatalf("CopyConfig dropped the WrapTransport field")
|
t.Fatalf("CopyConfig dropped the WrapTransport field")
|
||||||
} else {
|
|
||||||
actual.WrapTransport = nil
|
|
||||||
expected.WrapTransport = nil
|
|
||||||
}
|
}
|
||||||
|
actual.WrapTransport = nil
|
||||||
|
expected.WrapTransport = nil
|
||||||
|
|
||||||
if actual.Dial != nil {
|
if actual.Dial != nil {
|
||||||
_, actualError := actual.Dial(context.Background(), "", "")
|
_, actualError := actual.Dial(context.Background(), "", "")
|
||||||
_, expectedError := expected.Dial(context.Background(), "", "")
|
_, expectedError := expected.Dial(context.Background(), "", "")
|
||||||
@ -423,6 +445,7 @@ func TestCopyConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
actual.Dial = nil
|
actual.Dial = nil
|
||||||
expected.Dial = nil
|
expected.Dial = nil
|
||||||
|
|
||||||
if actual.AuthConfigPersister != nil {
|
if actual.AuthConfigPersister != nil {
|
||||||
actualError := actual.AuthConfigPersister.Persist(nil)
|
actualError := actual.AuthConfigPersister.Persist(nil)
|
||||||
expectedError := expected.AuthConfigPersister.Persist(nil)
|
expectedError := expected.AuthConfigPersister.Persist(nil)
|
||||||
@ -433,8 +456,18 @@ func TestCopyConfig(t *testing.T) {
|
|||||||
actual.AuthConfigPersister = nil
|
actual.AuthConfigPersister = nil
|
||||||
expected.AuthConfigPersister = nil
|
expected.AuthConfigPersister = nil
|
||||||
|
|
||||||
if !reflect.DeepEqual(*actual, expected) {
|
if actual.Proxy != nil {
|
||||||
t.Fatalf("CopyConfig dropped unexpected fields, identify whether they are security related or not: %s", diff.ObjectReflectDiff(expected, *actual))
|
_, 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{},
|
RateLimiter: &fakeLimiter{},
|
||||||
Timeout: 3 * time.Second,
|
Timeout: 3 * time.Second,
|
||||||
Dial: fakeDialFunc,
|
Dial: fakeDialFunc,
|
||||||
|
Proxy: fakeProxyFunc,
|
||||||
}
|
}
|
||||||
want := fmt.Sprintf(
|
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)}`,
|
`&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,
|
c.Transport, fakeWrapperFunc, c.RateLimiter, fakeDialFunc, fakeProxyFunc,
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, f := range []string{"%s", "%v", "%+v", "%#v"} {
|
for _, f := range []string{"%s", "%v", "%+v", "%#v"} {
|
||||||
|
@ -85,7 +85,8 @@ func (c *Config) TransportConfig() (*transport.Config, error) {
|
|||||||
Groups: c.Impersonate.Groups,
|
Groups: c.Impersonate.Groups,
|
||||||
Extra: c.Impersonate.Extra,
|
Extra: c.Impersonate.Extra,
|
||||||
},
|
},
|
||||||
Dial: c.Dial,
|
Dial: c.Dial,
|
||||||
|
Proxy: c.Proxy,
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.ExecProvider != nil && c.AuthProvider != nil {
|
if c.ExecProvider != nil && c.AuthProvider != nil {
|
||||||
|
@ -82,6 +82,17 @@ type Cluster struct {
|
|||||||
// CertificateAuthorityData contains PEM-encoded certificate authority certificates. Overrides CertificateAuthority
|
// CertificateAuthorityData contains PEM-encoded certificate authority certificates. Overrides CertificateAuthority
|
||||||
// +optional
|
// +optional
|
||||||
CertificateAuthorityData []byte `json:"certificate-authority-data,omitempty"`
|
CertificateAuthorityData []byte `json:"certificate-authority-data,omitempty"`
|
||||||
|
// ProxyURL is the URL to the proxy to be used for all requests made by this
|
||||||
|
// client. URLs with "http", "https", and "socks5" schemes are supported. If
|
||||||
|
// this configuration is not provided or the empty string, the client
|
||||||
|
// attempts to construct a proxy configuration from http_proxy and
|
||||||
|
// https_proxy environment variables. If these environment variables are not
|
||||||
|
// set, the client does not attempt to proxy requests.
|
||||||
|
//
|
||||||
|
// socks5 proxying does not currently support spdy streaming endpoints (exec,
|
||||||
|
// attach, port forward).
|
||||||
|
// +optional
|
||||||
|
ProxyURL string `json:"proxy-url,omitempty"`
|
||||||
// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
|
// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
|
||||||
// +optional
|
// +optional
|
||||||
Extensions map[string]runtime.Object `json:"extensions,omitempty"`
|
Extensions map[string]runtime.Object `json:"extensions,omitempty"`
|
||||||
|
@ -75,6 +75,17 @@ type Cluster struct {
|
|||||||
// CertificateAuthorityData contains PEM-encoded certificate authority certificates. Overrides CertificateAuthority
|
// CertificateAuthorityData contains PEM-encoded certificate authority certificates. Overrides CertificateAuthority
|
||||||
// +optional
|
// +optional
|
||||||
CertificateAuthorityData []byte `json:"certificate-authority-data,omitempty"`
|
CertificateAuthorityData []byte `json:"certificate-authority-data,omitempty"`
|
||||||
|
// ProxyURL is the URL to the proxy to be used for all requests made by this
|
||||||
|
// client. URLs with "http", "https", and "socks5" schemes are supported. If
|
||||||
|
// this configuration is not provided or the empty string, the client
|
||||||
|
// attempts to construct a proxy configuration from http_proxy and
|
||||||
|
// https_proxy environment variables. If these environment variables are not
|
||||||
|
// set, the client does not attempt to proxy requests.
|
||||||
|
//
|
||||||
|
// socks5 proxying does not currently support spdy streaming endpoints (exec,
|
||||||
|
// attach, port forward).
|
||||||
|
// +optional
|
||||||
|
ProxyURL string `json:"proxy-url,omitempty"`
|
||||||
// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
|
// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
|
||||||
// +optional
|
// +optional
|
||||||
Extensions []NamedExtension `json:"extensions,omitempty"`
|
Extensions []NamedExtension `json:"extensions,omitempty"`
|
||||||
|
@ -237,6 +237,7 @@ func autoConvert_v1_Cluster_To_api_Cluster(in *Cluster, out *api.Cluster, s conv
|
|||||||
out.InsecureSkipTLSVerify = in.InsecureSkipTLSVerify
|
out.InsecureSkipTLSVerify = in.InsecureSkipTLSVerify
|
||||||
out.CertificateAuthority = in.CertificateAuthority
|
out.CertificateAuthority = in.CertificateAuthority
|
||||||
out.CertificateAuthorityData = *(*[]byte)(unsafe.Pointer(&in.CertificateAuthorityData))
|
out.CertificateAuthorityData = *(*[]byte)(unsafe.Pointer(&in.CertificateAuthorityData))
|
||||||
|
out.ProxyURL = in.ProxyURL
|
||||||
if err := Convert_Slice_v1_NamedExtension_To_Map_string_To_runtime_Object(&in.Extensions, &out.Extensions, s); err != nil {
|
if err := Convert_Slice_v1_NamedExtension_To_Map_string_To_runtime_Object(&in.Extensions, &out.Extensions, s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -255,6 +256,7 @@ func autoConvert_api_Cluster_To_v1_Cluster(in *api.Cluster, out *Cluster, s conv
|
|||||||
out.InsecureSkipTLSVerify = in.InsecureSkipTLSVerify
|
out.InsecureSkipTLSVerify = in.InsecureSkipTLSVerify
|
||||||
out.CertificateAuthority = in.CertificateAuthority
|
out.CertificateAuthority = in.CertificateAuthority
|
||||||
out.CertificateAuthorityData = *(*[]byte)(unsafe.Pointer(&in.CertificateAuthorityData))
|
out.CertificateAuthorityData = *(*[]byte)(unsafe.Pointer(&in.CertificateAuthorityData))
|
||||||
|
out.ProxyURL = in.ProxyURL
|
||||||
if err := Convert_Map_string_To_runtime_Object_To_Slice_v1_NamedExtension(&in.Extensions, &out.Extensions, s); err != nil {
|
if err := Convert_Map_string_To_runtime_Object_To_Slice_v1_NamedExtension(&in.Extensions, &out.Extensions, s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -20,16 +20,17 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/imdario/mergo"
|
|
||||||
"k8s.io/klog"
|
|
||||||
|
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
clientauth "k8s.io/client-go/tools/auth"
|
clientauth "k8s.io/client-go/tools/auth"
|
||||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||||
|
"k8s.io/klog"
|
||||||
|
|
||||||
|
"github.com/imdario/mergo"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -150,6 +151,13 @@ func (config *DirectClientConfig) ClientConfig() (*restclient.Config, error) {
|
|||||||
|
|
||||||
clientConfig := &restclient.Config{}
|
clientConfig := &restclient.Config{}
|
||||||
clientConfig.Host = configClusterInfo.Server
|
clientConfig.Host = configClusterInfo.Server
|
||||||
|
if configClusterInfo.ProxyURL != "" {
|
||||||
|
u, err := parseProxyURL(configClusterInfo.ProxyURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
clientConfig.Proxy = http.ProxyURL(u)
|
||||||
|
}
|
||||||
|
|
||||||
if len(config.overrides.Timeout) > 0 {
|
if len(config.overrides.Timeout) > 0 {
|
||||||
timeout, err := ParseTimeout(config.overrides.Timeout)
|
timeout, err := ParseTimeout(config.overrides.Timeout)
|
||||||
|
@ -23,10 +23,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/imdario/mergo"
|
|
||||||
|
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||||
|
|
||||||
|
"github.com/imdario/mergo"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMergoSemantics(t *testing.T) {
|
func TestMergoSemantics(t *testing.T) {
|
||||||
@ -330,6 +330,84 @@ func TestCertificateData(t *testing.T) {
|
|||||||
matchByteArg(keyData, clientConfig.TLSClientConfig.KeyData, t)
|
matchByteArg(keyData, clientConfig.TLSClientConfig.KeyData, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProxyURL(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
desc string
|
||||||
|
proxyURL string
|
||||||
|
expectErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "no proxy-url",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "socks5 proxy-url",
|
||||||
|
proxyURL: "socks5://example.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "https proxy-url",
|
||||||
|
proxyURL: "https://example.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "http proxy-url",
|
||||||
|
proxyURL: "http://example.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "bad scheme proxy-url",
|
||||||
|
proxyURL: "socks6://example.com",
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "no scheme proxy-url",
|
||||||
|
proxyURL: "example.com",
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "not a url proxy-url",
|
||||||
|
proxyURL: "chewbacca@example.com",
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.proxyURL, func(t *testing.T) {
|
||||||
|
|
||||||
|
config := clientcmdapi.NewConfig()
|
||||||
|
config.Clusters["clean"] = &clientcmdapi.Cluster{
|
||||||
|
Server: "https://localhost:8443",
|
||||||
|
ProxyURL: test.proxyURL,
|
||||||
|
}
|
||||||
|
config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{}
|
||||||
|
config.Contexts["clean"] = &clientcmdapi.Context{
|
||||||
|
Cluster: "clean",
|
||||||
|
AuthInfo: "clean",
|
||||||
|
}
|
||||||
|
config.CurrentContext = "clean"
|
||||||
|
|
||||||
|
clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}, nil)
|
||||||
|
|
||||||
|
clientConfig, err := clientBuilder.ClientConfig()
|
||||||
|
if test.expectErr {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected error constructing config")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error constructing config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.proxyURL == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gotURL, err := clientConfig.Proxy(nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error from proxier: %v", err)
|
||||||
|
}
|
||||||
|
matchStringArg(test.proxyURL, gotURL.String(), t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBasicAuthData(t *testing.T) {
|
func TestBasicAuthData(t *testing.T) {
|
||||||
username := "myuser"
|
username := "myuser"
|
||||||
password := "mypass" // Fake value for testing.
|
password := "mypass" // Fake value for testing.
|
||||||
|
@ -18,6 +18,7 @@ package clientcmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -33,3 +34,17 @@ func ParseTimeout(duration string) (time.Duration, error) {
|
|||||||
}
|
}
|
||||||
return 0, fmt.Errorf("Invalid timeout value. Timeout must be a single integer in seconds, or an integer followed by a corresponding time unit (e.g. 1s | 2m | 3h)")
|
return 0, fmt.Errorf("Invalid timeout value. Timeout must be a single integer in seconds, or an integer followed by a corresponding time unit (e.g. 1s | 2m | 3h)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseProxyURL(proxyURL string) (*url.URL, error) {
|
||||||
|
u, err := url.Parse(proxyURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not parse: %v", proxyURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch u.Scheme {
|
||||||
|
case "http", "https", "socks5":
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported scheme %q, must be http, https, or socks5", u.Scheme)
|
||||||
|
}
|
||||||
|
return u, nil
|
||||||
|
}
|
||||||
|
@ -227,6 +227,11 @@ func validateClusterInfo(clusterName string, clusterInfo clientcmdapi.Cluster) [
|
|||||||
validationErrors = append(validationErrors, fmt.Errorf("no server found for cluster %q", clusterName))
|
validationErrors = append(validationErrors, fmt.Errorf("no server found for cluster %q", clusterName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if proxyURL := clusterInfo.ProxyURL; proxyURL != "" {
|
||||||
|
if _, err := parseProxyURL(proxyURL); err != nil {
|
||||||
|
validationErrors = append(validationErrors, fmt.Errorf("invalid 'proxy-url' %q for cluster %q: %v", proxyURL, clusterName, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
// Make sure CA data and CA file aren't both specified
|
// Make sure CA data and CA file aren't both specified
|
||||||
if len(clusterInfo.CertificateAuthority) != 0 && len(clusterInfo.CertificateAuthorityData) != 0 {
|
if len(clusterInfo.CertificateAuthority) != 0 && len(clusterInfo.CertificateAuthorityData) != 0 {
|
||||||
validationErrors = append(validationErrors, fmt.Errorf("certificate-authority-data and certificate-authority are both specified for %v. certificate-authority-data will override.", clusterName))
|
validationErrors = append(validationErrors, fmt.Errorf("certificate-authority-data and certificate-authority are both specified for %v. certificate-authority-data will override.", clusterName))
|
||||||
|
@ -52,6 +52,7 @@ type tlsCacheKey struct {
|
|||||||
nextProtos string
|
nextProtos string
|
||||||
dial string
|
dial string
|
||||||
disableCompression bool
|
disableCompression bool
|
||||||
|
proxy string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t tlsCacheKey) String() string {
|
func (t tlsCacheKey) String() string {
|
||||||
@ -59,7 +60,7 @@ func (t tlsCacheKey) String() string {
|
|||||||
if len(t.keyData) > 0 {
|
if len(t.keyData) > 0 {
|
||||||
keyText = "<redacted>"
|
keyText = "<redacted>"
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("insecure:%v, caData:%#v, certData:%#v, keyData:%s, getCert: %s, serverName:%s, dial:%s disableCompression:%t", t.insecure, t.caData, t.certData, keyText, t.getCert, t.serverName, t.dial, t.disableCompression)
|
return fmt.Sprintf("insecure:%v, caData:%#v, certData:%#v, keyData:%s, getCert: %s, serverName:%s, dial:%s disableCompression:%t, proxy: %s", t.insecure, t.caData, t.certData, keyText, t.getCert, t.serverName, t.dial, t.disableCompression, t.proxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) {
|
func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) {
|
||||||
@ -83,7 +84,7 @@ func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// The options didn't require a custom TLS config
|
// The options didn't require a custom TLS config
|
||||||
if tlsConfig == nil && config.Dial == nil {
|
if tlsConfig == nil && config.Dial == nil && config.Proxy == nil {
|
||||||
return http.DefaultTransport, nil
|
return http.DefaultTransport, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,9 +105,14 @@ func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) {
|
|||||||
go dynamicCertDialer.Run(wait.NeverStop)
|
go dynamicCertDialer.Run(wait.NeverStop)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proxy := http.ProxyFromEnvironment
|
||||||
|
if config.Proxy != nil {
|
||||||
|
proxy = config.Proxy
|
||||||
|
}
|
||||||
|
|
||||||
// Cache a single transport for these options
|
// Cache a single transport for these options
|
||||||
c.transports[key] = utilnet.SetTransportDefaults(&http.Transport{
|
c.transports[key] = utilnet.SetTransportDefaults(&http.Transport{
|
||||||
Proxy: http.ProxyFromEnvironment,
|
Proxy: proxy,
|
||||||
TLSHandshakeTimeout: 10 * time.Second,
|
TLSHandshakeTimeout: 10 * time.Second,
|
||||||
TLSClientConfig: tlsConfig,
|
TLSClientConfig: tlsConfig,
|
||||||
MaxIdleConnsPerHost: idleConnsPerHost,
|
MaxIdleConnsPerHost: idleConnsPerHost,
|
||||||
@ -130,6 +136,7 @@ func tlsConfigKey(c *Config) (tlsCacheKey, error) {
|
|||||||
nextProtos: strings.Join(c.TLS.NextProtos, ","),
|
nextProtos: strings.Join(c.TLS.NextProtos, ","),
|
||||||
dial: fmt.Sprintf("%p", c.Dial),
|
dial: fmt.Sprintf("%p", c.Dial),
|
||||||
disableCompression: c.DisableCompression,
|
disableCompression: c.DisableCompression,
|
||||||
|
proxy: fmt.Sprintf("%p", c.Proxy),
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.TLS.ReloadTLSFiles {
|
if c.TLS.ReloadTLSFiles {
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -157,3 +158,23 @@ func TestTLSConfigKey(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTLSConfigKeyFuncPtr(t *testing.T) {
|
||||||
|
keys := make(map[tlsCacheKey]struct{})
|
||||||
|
makeKey := func(p func(*http.Request) (*url.URL, error)) tlsCacheKey {
|
||||||
|
key, err := tlsConfigKey(&Config{Proxy: p})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error creating cache key: %v", err)
|
||||||
|
}
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
keys[makeKey(http.ProxyFromEnvironment)] = struct{}{}
|
||||||
|
keys[makeKey(http.ProxyFromEnvironment)] = struct{}{}
|
||||||
|
keys[makeKey(http.ProxyURL(nil))] = struct{}{}
|
||||||
|
keys[makeKey(nil)] = struct{}{}
|
||||||
|
|
||||||
|
if got, want := len(keys), 3; got != want {
|
||||||
|
t.Fatalf("Unexpected number of keys: got=%d want=%d", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config holds various options for establishing a transport.
|
// Config holds various options for establishing a transport.
|
||||||
@ -68,6 +69,13 @@ type Config struct {
|
|||||||
|
|
||||||
// Dial specifies the dial function for creating unencrypted TCP connections.
|
// Dial specifies the dial function for creating unencrypted TCP connections.
|
||||||
Dial func(ctx context.Context, network, address string) (net.Conn, error)
|
Dial func(ctx context.Context, network, address string) (net.Conn, error)
|
||||||
|
|
||||||
|
// Proxy is the the proxy func to be used for all requests made by this
|
||||||
|
// transport. If Proxy is nil, http.ProxyFromEnvironment is used. If Proxy
|
||||||
|
// returns a nil *URL, no proxy is used.
|
||||||
|
//
|
||||||
|
// socks5 proxying does not currently support spdy streaming endpoints.
|
||||||
|
Proxy func(*http.Request) (*url.URL, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImpersonationConfig has all the available impersonation options
|
// ImpersonationConfig has all the available impersonation options
|
||||||
|
@ -38,7 +38,11 @@ func RoundTripperFor(config *restclient.Config) (http.RoundTripper, Upgrader, er
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
upgradeRoundTripper := spdy.NewRoundTripper(tlsConfig, true, false)
|
proxy := http.ProxyFromEnvironment
|
||||||
|
if config.Proxy != nil {
|
||||||
|
proxy = config.Proxy
|
||||||
|
}
|
||||||
|
upgradeRoundTripper := spdy.NewRoundTripperWithProxy(tlsConfig, true, false, proxy)
|
||||||
wrapper, err := restclient.HTTPWrappersForConfig(config, upgradeRoundTripper)
|
wrapper, err := restclient.HTTPWrappersForConfig(config, upgradeRoundTripper)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
Loading…
Reference in New Issue
Block a user