Merge pull request #60012 from atlassian/dial-with-context

Automatic merge from submit-queue (batch tested with PRs 60012, 63692, 63977, 63960, 64008). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Use Dial with context

**What this PR does / why we need it**:
`net/http/Transport.Dial` field is deprecated:
```go
// DialContext specifies the dial function for creating unencrypted TCP connections.
// If DialContext is nil (and the deprecated Dial below is also nil),
// then the transport dials using package net.
DialContext func(ctx context.Context, network, addr string) (net.Conn, error)

// Dial specifies the dial function for creating unencrypted TCP connections.
//
// Deprecated: Use DialContext instead, which allows the transport
// to cancel dials as soon as they are no longer needed.
// If both are set, DialContext takes priority.
Dial func(network, addr string) (net.Conn, error)
```
This PR switches all `Dial` usages to `DialContext`. Fixes #63455.

**Special notes for your reviewer**:
Also related: https://github.com/kubernetes/kubernetes/pull/59287 https://github.com/kubernetes/kubernetes/pull/58532 https://github.com/kubernetes/kubernetes/issues/815 https://github.com/kubernetes/community/pull/1166 https://github.com/kubernetes/kubernetes/pull/58677 https://github.com/kubernetes/kubernetes/pull/57932

**Release note**:
```release-note
HTTP transport now uses `context.Context` to cancel dial operations. k8s.io/client-go/transport/Config struct has been updated to accept a function with a `context.Context` parameter. This is a breaking change if you use this field in your code.
```
/sig api-machinery
/kind enhancement
/cc @sttts

Kubernetes-commit: ddf551c24b7d88454f8332ce6855e53281440958
This commit is contained in:
Kubernetes Publisher 2018-05-18 23:35:13 -07:00
commit d9cf977510
8 changed files with 78 additions and 100 deletions

100
Godeps/Godeps.json generated
View File

@ -388,203 +388,203 @@
},
{
"ImportPath": "k8s.io/apimachinery/pkg/api/equality",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/api/errors",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/api/meta",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/api/resource",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/api/testing",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/api/testing/fuzzer",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/api/testing/roundtrip",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/fuzzer",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/internalversion",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1beta1",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/conversion",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/fields",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/labels",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/runtime",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/runtime/schema",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/json",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/protobuf",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/recognizer",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/streaming",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/versioning",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/selection",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/types",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/cache",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/clock",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/diff",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/errors",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/framer",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/httpstream",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/httpstream/spdy",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/intstr",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/json",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/mergepatch",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/net",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/remotecommand",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/runtime",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/sets",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/strategicpatch",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/validation",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/validation/field",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/wait",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/yaml",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/version",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/watch",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/json",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/netutil",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/reflect",
"Rev": "3492ef8dace114d765f20d9c1a03f306449e5252"
"Rev": "e226be1f5ed46b19fedc971f410f84de5b3dab8a"
},
{
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto",

View File

@ -44,12 +44,8 @@ const (
defaultRetries = 2
// protobuf mime type
mimePb = "application/com.github.proto-openapi.spec.v2@v1.0+protobuf"
)
var (
// defaultTimeout is the maximum amount of time per request when no timeout has been set on a RESTClient.
// Defaults to 32s in order to have a distinguishable length of time, relative to other timeouts that exist.
// It's a variable to be able to change it in tests.
defaultTimeout = 32 * time.Second
)

View File

@ -23,12 +23,11 @@ import (
"net/http"
"net/http/httptest"
"reflect"
"strings"
"testing"
"time"
"github.com/gogo/protobuf/proto"
"github.com/googleapis/gnostic/OpenAPIv2"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -131,31 +130,11 @@ func TestGetServerGroupsWithBrokenServer(t *testing.T) {
}
}
}
func TestGetServerGroupsWithTimeout(t *testing.T) {
done := make(chan bool)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
// first we need to write headers, otherwise http client will complain about
// exceeding timeout awaiting headers, only after we can block the call
w.Header().Set("Connection", "keep-alive")
if wf, ok := w.(http.Flusher); ok {
wf.Flush()
}
<-done
}))
defer server.Close()
defer close(done)
client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL, Timeout: 2 * time.Second})
_, err := client.ServerGroups()
// the error we're getting here is wrapped in errors.errorString which makes
// it impossible to unwrap and check it's attributes, so instead we're checking
// the textual output which is presenting http.httpError with timeout set to true
if err == nil {
t.Fatal("missing error")
}
if !strings.Contains(err.Error(), "timeout:true") &&
!strings.Contains(err.Error(), "context.deadlineExceededError") {
t.Fatalf("unexpected error: %v", err)
}
func TestTimeoutIsSet(t *testing.T) {
cfg := &restclient.Config{}
setDiscoveryDefaults(cfg)
assert.Equal(t, defaultTimeout, cfg.Timeout)
}
func TestGetServerResourcesWithV1Server(t *testing.T) {

View File

@ -17,6 +17,7 @@ limitations under the License.
package rest
import (
"context"
"fmt"
"io/ioutil"
"net"
@ -110,7 +111,7 @@ type Config struct {
Timeout time.Duration
// Dial specifies the dial function for creating unencrypted TCP connections.
Dial func(network, addr string) (net.Conn, error)
Dial func(ctx context.Context, network, address string) (net.Conn, error)
// Version forces a specific version to be used (if registered)
// Do we need this?

View File

@ -17,6 +17,8 @@ limitations under the License.
package rest
import (
"context"
"errors"
"io"
"net"
"net/http"
@ -25,8 +27,6 @@ import (
"strings"
"testing"
fuzz "github.com/google/gofuzz"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -35,8 +35,7 @@ import (
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/client-go/util/flowcontrol"
"errors"
fuzz "github.com/google/gofuzz"
"github.com/stretchr/testify/assert"
)
@ -208,7 +207,7 @@ func (n *fakeNegotiatedSerializer) DecoderToVersion(serializer runtime.Decoder,
return &fakeCodec{}
}
var fakeDialFunc = func(network, addr string) (net.Conn, error) {
var fakeDialFunc = func(ctx context.Context, network, addr string) (net.Conn, error) {
return nil, fakeDialerError
}
var fakeDialerError = errors.New("fakedialer")
@ -253,7 +252,7 @@ func TestAnonymousConfig(t *testing.T) {
r.Config = map[string]string{}
},
// Dial does not require fuzzer
func(r *func(network, addr string) (net.Conn, error), f fuzz.Continue) {},
func(r *func(ctx context.Context, network, addr string) (net.Conn, error), f fuzz.Continue) {},
)
for i := 0; i < 20; i++ {
original := &Config{}
@ -284,10 +283,10 @@ func TestAnonymousConfig(t *testing.T) {
expected.WrapTransport = nil
}
if actual.Dial != nil {
_, actualError := actual.Dial("", "")
_, expectedError := actual.Dial("", "")
_, actualError := actual.Dial(context.Background(), "", "")
_, expectedError := expected.Dial(context.Background(), "", "")
if !reflect.DeepEqual(expectedError, actualError) {
t.Fatalf("CopyConfig dropped the Dial field")
t.Fatalf("CopyConfig dropped the Dial field")
}
} else {
actual.Dial = nil
@ -329,7 +328,7 @@ func TestCopyConfig(t *testing.T) {
func(r *AuthProviderConfigPersister, f fuzz.Continue) {
*r = fakeAuthProviderConfigPersister{}
},
func(r *func(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
},
)
@ -351,8 +350,8 @@ func TestCopyConfig(t *testing.T) {
expected.WrapTransport = nil
}
if actual.Dial != nil {
_, actualError := actual.Dial("", "")
_, expectedError := actual.Dial("", "")
_, actualError := actual.Dial(context.Background(), "", "")
_, expectedError := expected.Dial(context.Background(), "", "")
if !reflect.DeepEqual(expectedError, actualError) {
t.Fatalf("CopyConfig dropped the Dial field")
}
@ -361,7 +360,7 @@ func TestCopyConfig(t *testing.T) {
expected.Dial = nil
if actual.AuthConfigPersister != nil {
actualError := actual.AuthConfigPersister.Persist(nil)
expectedError := actual.AuthConfigPersister.Persist(nil)
expectedError := expected.AuthConfigPersister.Persist(nil)
if !reflect.DeepEqual(expectedError, actualError) {
t.Fatalf("CopyConfig dropped the Dial field")
}

View File

@ -85,7 +85,7 @@ func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) {
dial = (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial
}).DialContext
}
// Cache a single transport for these options
c.transports[key] = utilnet.SetTransportDefaults(&http.Transport{
@ -93,7 +93,7 @@ func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) {
TLSHandshakeTimeout: 10 * time.Second,
TLSClientConfig: tlsConfig,
MaxIdleConnsPerHost: idleConnsPerHost,
Dial: dial,
DialContext: dial,
})
return c.transports[key], nil
}

View File

@ -17,6 +17,7 @@ limitations under the License.
package transport
import (
"context"
"net"
"net/http"
"testing"
@ -52,10 +53,11 @@ func TestTLSConfigKey(t *testing.T) {
}
// Make sure config fields that affect the tls config affect the cache key
dialer := net.Dialer{}
uniqueConfigurations := map[string]*Config{
"no tls": {},
"dialer": {Dial: net.Dial},
"dialer2": {Dial: func(network, address string) (net.Conn, error) { return nil, nil }},
"dialer": {Dial: dialer.DialContext},
"dialer2": {Dial: func(ctx context.Context, network, address string) (net.Conn, error) { return nil, nil }},
"insecure": {TLS: TLSConfig{Insecure: true}},
"cadata 1": {TLS: TLSConfig{CAData: []byte{1}}},
"cadata 2": {TLS: TLSConfig{CAData: []byte{2}}},

View File

@ -17,6 +17,7 @@ limitations under the License.
package transport
import (
"context"
"net"
"net/http"
)
@ -53,7 +54,7 @@ type Config struct {
WrapTransport func(rt http.RoundTripper) http.RoundTripper
// Dial specifies the dial function for creating unencrypted TCP connections.
Dial func(network, addr string) (net.Conn, error)
Dial func(ctx context.Context, network, address string) (net.Conn, error)
}
// ImpersonationConfig has all the available impersonation options