1
0
mirror of https://github.com/minio/minio.git synced 2025-04-08 21:55:44 -04:00

update x/net/http2 to address few bugs ()

additionally also configure http2 healthcheck
values to quickly detect unstable connections
and let them timeout.

also use single transport for proxying requests
This commit is contained in:
Harshavardhana 2020-12-21 21:42:38 -08:00 committed by GitHub
parent c987313431
commit 5c451d1690
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 78 additions and 57 deletions

@ -388,7 +388,7 @@ func getObjectLocation(r *http.Request, domains []string, bucket, object string)
} }
proto := handlers.GetSourceScheme(r) proto := handlers.GetSourceScheme(r)
if proto == "" { if proto == "" {
proto = getURLScheme(globalIsSSL) proto = getURLScheme(globalIsTLS)
} }
u := &url.URL{ u := &url.URL{
Host: r.Host, Host: r.Host,

@ -350,7 +350,7 @@ func (sys *BucketTargetSys) getRemoteTargetClient(tcfg *madmin.BucketTarget) (*m
creds := credentials.NewStaticV4(config.AccessKey, config.SecretKey, "") creds := credentials.NewStaticV4(config.AccessKey, config.SecretKey, "")
getRemoteTargetInstanceTransportOnce.Do(func() { getRemoteTargetInstanceTransportOnce.Do(func() {
getRemoteTargetInstanceTransport = newGatewayHTTPTransport(1 * time.Hour) getRemoteTargetInstanceTransport = newGatewayHTTPTransport(10 * time.Minute)
}) })
core, err := miniogo.NewCore(tcfg.URL().Host, &miniogo.Options{ core, err := miniogo.NewCore(tcfg.URL().Host, &miniogo.Options{

@ -57,8 +57,8 @@ var encryptRequestTests = []struct {
} }
func TestEncryptRequest(t *testing.T) { func TestEncryptRequest(t *testing.T) {
defer func(flag bool) { globalIsSSL = flag }(globalIsSSL) defer func(flag bool) { globalIsTLS = flag }(globalIsTLS)
globalIsSSL = true globalIsTLS = true
for i, test := range encryptRequestTests { for i, test := range encryptRequestTests {
content := bytes.NewReader(make([]byte, 64)) content := bytes.NewReader(make([]byte, 64))
req := &http.Request{Header: http.Header{}} req := &http.Request{Header: http.Header{}}

@ -18,7 +18,6 @@ package cmd
import ( import (
"context" "context"
"crypto/tls"
"errors" "errors"
"fmt" "fmt"
"net" "net"
@ -38,7 +37,6 @@ import (
"github.com/minio/minio/cmd/config" "github.com/minio/minio/cmd/config"
xhttp "github.com/minio/minio/cmd/http" xhttp "github.com/minio/minio/cmd/http"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/cmd/rest"
"github.com/minio/minio/pkg/env" "github.com/minio/minio/pkg/env"
"github.com/minio/minio/pkg/mountinfo" "github.com/minio/minio/pkg/mountinfo"
xnet "github.com/minio/minio/pkg/net" xnet "github.com/minio/minio/pkg/net"
@ -59,7 +57,7 @@ const (
// See proxyRequest() for details. // See proxyRequest() for details.
type ProxyEndpoint struct { type ProxyEndpoint struct {
Endpoint Endpoint
Transport *http.Transport Transport http.RoundTripper
} }
// Endpoint - any type of endpoint. // Endpoint - any type of endpoint.
@ -881,20 +879,9 @@ func GetProxyEndpoints(endpointServerPools EndpointServerPools) []ProxyEndpoint
} }
proxyEpSet.Add(host) proxyEpSet.Add(host)
var tlsConfig *tls.Config
if globalIsSSL {
tlsConfig = &tls.Config{
ServerName: endpoint.Hostname(),
RootCAs: globalRootCAs,
}
}
// allow transport to be HTTP/1.1 for proxying.
tr := newCustomHTTPProxyTransport(tlsConfig, rest.DefaultTimeout)()
proxyEps = append(proxyEps, ProxyEndpoint{ proxyEps = append(proxyEps, ProxyEndpoint{
Endpoint: endpoint, Endpoint: endpoint,
Transport: tr, Transport: globalProxyTransport,
}) })
} }
} }

@ -179,7 +179,7 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
// Check and load TLS certificates. // Check and load TLS certificates.
var err error var err error
globalPublicCerts, globalTLSCerts, globalIsSSL, err = getTLSConfig() globalPublicCerts, globalTLSCerts, globalIsTLS, err = getTLSConfig()
logger.FatalIf(err, "Invalid TLS certificate file") logger.FatalIf(err, "Invalid TLS certificate file")
// Check and load Root CAs. // Check and load Root CAs.
@ -211,7 +211,7 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
if host == "" { if host == "" {
host = sortIPs(localIP4.ToSlice())[0] host = sortIPs(localIP4.ToSlice())[0]
} }
return fmt.Sprintf("%s://%s", getURLScheme(globalIsSSL), net.JoinHostPort(host, globalMinioPort)) return fmt.Sprintf("%s://%s", getURLScheme(globalIsTLS), net.JoinHostPort(host, globalMinioPort))
}() }()
// Handle gateway specific env // Handle gateway specific env

@ -44,7 +44,7 @@ func printGatewayStartupMessage(apiEndPoints []string, backendType string) {
// SSL is configured reads certification chain, prints // SSL is configured reads certification chain, prints
// authority and expiry. // authority and expiry.
if color.IsTerminal() && !globalCLIContext.Anonymous { if color.IsTerminal() && !globalCLIContext.Anonymous {
if globalIsSSL { if globalIsTLS {
printCertificateMsg(globalPublicCerts) printCertificateMsg(globalPublicCerts)
} }
} }

@ -665,7 +665,7 @@ func (f bucketForwardingHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
} }
if globalDomainIPs.Intersection(set.CreateStringSet(getHostsSlice(sr)...)).IsEmpty() { if globalDomainIPs.Intersection(set.CreateStringSet(getHostsSlice(sr)...)).IsEmpty() {
r.URL.Scheme = "http" r.URL.Scheme = "http"
if globalIsSSL { if globalIsTLS {
r.URL.Scheme = "https" r.URL.Scheme = "https"
} }
r.URL.Host = getHostFromSrv(sr) r.URL.Host = getHostFromSrv(sr)
@ -715,7 +715,7 @@ func (f bucketForwardingHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
} }
if globalDomainIPs.Intersection(set.CreateStringSet(getHostsSlice(sr)...)).IsEmpty() { if globalDomainIPs.Intersection(set.CreateStringSet(getHostsSlice(sr)...)).IsEmpty() {
r.URL.Scheme = "http" r.URL.Scheme = "http"
if globalIsSSL { if globalIsTLS {
r.URL.Scheme = "https" r.URL.Scheme = "https"
} }
r.URL.Host = getHostFromSrv(sr) r.URL.Host = getHostFromSrv(sr)
@ -798,7 +798,7 @@ type sseTLSHandler struct{ handler http.Handler }
func (h sseTLSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h sseTLSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Deny SSE-C requests if not made over TLS // Deny SSE-C requests if not made over TLS
if !globalIsSSL && (crypto.SSEC.IsRequested(r.Header) || crypto.SSECopy.IsRequested(r.Header)) { if !globalIsTLS && (crypto.SSEC.IsRequested(r.Header) || crypto.SSECopy.IsRequested(r.Header)) {
if r.Method == http.MethodHead { if r.Method == http.MethodHead {
writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(ErrInsecureSSECustomerRequest)) writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(ErrInsecureSSECustomerRequest))
} else { } else {

@ -225,13 +225,13 @@ var sseTLSHandlerTests = []struct {
} }
func TestSSETLSHandler(t *testing.T) { func TestSSETLSHandler(t *testing.T) {
defer func(isSSL bool) { globalIsSSL = isSSL }(globalIsSSL) // reset globalIsSSL after test defer func(isSSL bool) { globalIsTLS = isSSL }(globalIsTLS) // reset globalIsTLS after test
var okHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) { var okHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
} }
for i, test := range sseTLSHandlerTests { for i, test := range sseTLSHandlerTests {
globalIsSSL = test.IsTLS globalIsTLS = test.IsTLS
w := httptest.NewRecorder() w := httptest.NewRecorder()
r := new(http.Request) r := new(http.Request)

@ -171,7 +171,7 @@ var (
globalRootCAs *x509.CertPool globalRootCAs *x509.CertPool
// IsSSL indicates if the server is configured with SSL. // IsSSL indicates if the server is configured with SSL.
globalIsSSL bool globalIsTLS bool
globalTLSCerts *certs.Manager globalTLSCerts *certs.Manager
@ -280,6 +280,8 @@ var (
globalInternodeTransport http.RoundTripper globalInternodeTransport http.RoundTripper
globalProxyTransport http.RoundTripper
globalDNSCache *xhttp.DNSCache globalDNSCache *xhttp.DNSCache
// Add new variable global values here. // Add new variable global values here.
) )

@ -516,7 +516,7 @@ func proxyRequest(ctx context.Context, w http.ResponseWriter, r *http.Request, e
}) })
r.URL.Scheme = "http" r.URL.Scheme = "http"
if globalIsSSL { if globalIsTLS {
r.URL.Scheme = "https" r.URL.Scheme = "https"
} }

@ -165,7 +165,7 @@ func getAPIEndpoints() (apiEndpoints []string) {
} }
for _, ip := range ipList { for _, ip := range ipList {
endpoint := fmt.Sprintf("%s://%s", getURLScheme(globalIsSSL), net.JoinHostPort(ip, globalMinioPort)) endpoint := fmt.Sprintf("%s://%s", getURLScheme(globalIsTLS), net.JoinHostPort(ip, globalMinioPort))
apiEndpoints = append(apiEndpoints, endpoint) apiEndpoints = append(apiEndpoints, endpoint)
} }

@ -752,7 +752,7 @@ var getRemoteInstanceClient = func(r *http.Request, host string) (*miniogo.Core,
// and hence expected to have same credentials. // and hence expected to have same credentials.
core, err := miniogo.NewCore(host, &miniogo.Options{ core, err := miniogo.NewCore(host, &miniogo.Options{
Creds: credentials.NewStaticV4(cred.AccessKey, cred.SecretKey, ""), Creds: credentials.NewStaticV4(cred.AccessKey, cred.SecretKey, ""),
Secure: globalIsSSL, Secure: globalIsTLS,
Transport: getRemoteInstanceTransport, Transport: getRemoteInstanceTransport,
}) })
if err != nil { if err != nil {

@ -213,8 +213,8 @@ func testAPIHeadObjectHandlerWithEncryption(obj ObjectLayer, instanceType, bucke
credentials auth.Credentials, t *testing.T) { credentials auth.Credentials, t *testing.T) {
// Set SSL to on to do encryption tests // Set SSL to on to do encryption tests
globalIsSSL = true globalIsTLS = true
defer func() { globalIsSSL = false }() defer func() { globalIsTLS = false }()
var ( var (
oneMiB int64 = 1024 * 1024 oneMiB int64 = 1024 * 1024
@ -659,8 +659,8 @@ func testAPIGetObjectWithMPHandler(obj ObjectLayer, instanceType, bucketName str
credentials auth.Credentials, t *testing.T) { credentials auth.Credentials, t *testing.T) {
// Set SSL to on to do encryption tests // Set SSL to on to do encryption tests
globalIsSSL = true globalIsTLS = true
defer func() { globalIsSSL = false }() defer func() { globalIsTLS = false }()
var ( var (
oneMiB int64 = 1024 * 1024 oneMiB int64 = 1024 * 1024
@ -857,8 +857,8 @@ func testAPIGetObjectWithPartNumberHandler(obj ObjectLayer, instanceType, bucket
credentials auth.Credentials, t *testing.T) { credentials auth.Credentials, t *testing.T) {
// Set SSL to on to do encryption tests // Set SSL to on to do encryption tests
globalIsSSL = true globalIsTLS = true
defer func() { globalIsSSL = false }() defer func() { globalIsTLS = false }()
var ( var (
oneMiB int64 = 1024 * 1024 oneMiB int64 = 1024 * 1024

@ -859,7 +859,7 @@ func newPeerRestClients(endpoints EndpointServerPools) (remote, all []*peerRESTC
// Returns a peer rest client. // Returns a peer rest client.
func newPeerRESTClient(peer *xnet.Host) *peerRESTClient { func newPeerRESTClient(peer *xnet.Host) *peerRESTClient {
scheme := "http" scheme := "http"
if globalIsSSL { if globalIsTLS {
scheme = "https" scheme = "https"
} }

@ -173,7 +173,7 @@ func IsServerResolvable(endpoint Endpoint) error {
} }
var tlsConfig *tls.Config var tlsConfig *tls.Config
if globalIsSSL { if globalIsTLS {
tlsConfig = &tls.Config{ tlsConfig = &tls.Config{
ServerName: endpoint.Hostname(), ServerName: endpoint.Hostname(),
RootCAs: globalRootCAs, RootCAs: globalRootCAs,

@ -119,7 +119,7 @@ func serverHandleCmdArgs(ctx *cli.Context) {
var setupType SetupType var setupType SetupType
// Check and load TLS certificates. // Check and load TLS certificates.
globalPublicCerts, globalTLSCerts, globalIsSSL, err = getTLSConfig() globalPublicCerts, globalTLSCerts, globalIsTLS, err = getTLSConfig()
logger.FatalIf(err, "Unable to load the TLS configuration") logger.FatalIf(err, "Unable to load the TLS configuration")
// Check and load Root CAs. // Check and load Root CAs.
@ -140,6 +140,10 @@ func serverHandleCmdArgs(ctx *cli.Context) {
globalEndpoints, setupType, err = createServerEndpoints(globalCLIContext.Addr, serverCmdArgs(ctx)...) globalEndpoints, setupType, err = createServerEndpoints(globalCLIContext.Addr, serverCmdArgs(ctx)...)
logger.FatalIf(err, "Invalid command line arguments") logger.FatalIf(err, "Invalid command line arguments")
// allow transport to be HTTP/1.1 for proxying.
globalProxyTransport = newCustomHTTPProxyTransport(&tls.Config{
RootCAs: globalRootCAs,
}, rest.DefaultTimeout)()
globalProxyEndpoints = GetProxyEndpoints(globalEndpoints) globalProxyEndpoints = GetProxyEndpoints(globalEndpoints)
globalInternodeTransport = newInternodeHTTPTransport(&tls.Config{ globalInternodeTransport = newInternodeHTTPTransport(&tls.Config{
RootCAs: globalRootCAs, RootCAs: globalRootCAs,
@ -384,15 +388,15 @@ func serverMain(ctx *cli.Context) {
if host == "" { if host == "" {
host = sortIPs(localIP4.ToSlice())[0] host = sortIPs(localIP4.ToSlice())[0]
} }
return fmt.Sprintf("%s://%s", getURLScheme(globalIsSSL), net.JoinHostPort(host, globalMinioPort)) return fmt.Sprintf("%s://%s", getURLScheme(globalIsTLS), net.JoinHostPort(host, globalMinioPort))
}() }()
// Is distributed setup, error out if no certificates are found for HTTPS endpoints. // Is distributed setup, error out if no certificates are found for HTTPS endpoints.
if globalIsDistErasure { if globalIsDistErasure {
if globalEndpoints.HTTPS() && !globalIsSSL { if globalEndpoints.HTTPS() && !globalIsTLS {
logger.Fatal(config.ErrNoCertsAndHTTPSEndpoints(nil), "Unable to start the server") logger.Fatal(config.ErrNoCertsAndHTTPSEndpoints(nil), "Unable to start the server")
} }
if !globalEndpoints.HTTPS() && globalIsSSL { if !globalEndpoints.HTTPS() && globalIsTLS {
logger.Fatal(config.ErrCertsAndHTTPEndpoints(nil), "Unable to start the server") logger.Fatal(config.ErrCertsAndHTTPEndpoints(nil), "Unable to start the server")
} }
} }

@ -85,7 +85,7 @@ func printStartupMessage(apiEndpoints []string, err error) {
// SSL is configured reads certification chain, prints // SSL is configured reads certification chain, prints
// authority and expiry. // authority and expiry.
if color.IsTerminal() && !globalCLIContext.Anonymous { if color.IsTerminal() && !globalCLIContext.Anonymous {
if globalIsSSL { if globalIsTLS {
printCertificateMsg(globalPublicCerts) printCertificateMsg(globalPublicCerts)
} }
} }

@ -463,7 +463,7 @@ func ToS3ETag(etag string) string {
return etag return etag
} }
func newInternodeHTTPTransport(tlsConfig *tls.Config, dialTimeout time.Duration) func() *http.Transport { func newInternodeHTTPTransport(tlsConfig *tls.Config, dialTimeout time.Duration) func() http.RoundTripper {
// For more details about various values used here refer // For more details about various values used here refer
// https://golang.org/pkg/net/http/#Transport documentation // https://golang.org/pkg/net/http/#Transport documentation
tr := &http.Transport{ tr := &http.Transport{
@ -481,11 +481,23 @@ func newInternodeHTTPTransport(tlsConfig *tls.Config, dialTimeout time.Duration)
DisableCompression: true, DisableCompression: true,
} }
if tlsConfig != nil { if globalIsTLS {
http2.ConfigureTransport(tr) trhttp2, _ := http2.ConfigureTransports(tr)
if trhttp2 != nil {
// ReadIdleTimeout is the timeout after which a health check using ping
// frame will be carried out if no frame is received on the
// connection. 5 minutes is sufficient time for any idle connection.
trhttp2.ReadIdleTimeout = 5 * time.Minute
// PingTimeout is the timeout after which the connection will be closed
// if a response to Ping is not received.
trhttp2.PingTimeout = dialTimeout
// DisableCompression, if true, prevents the Transport from
// requesting compression with an "Accept-Encoding: gzip"
trhttp2.DisableCompression = true
}
} }
return func() *http.Transport { return func() http.RoundTripper {
return tr return tr
} }
} }
@ -532,8 +544,23 @@ func newCustomHTTPTransport(tlsConfig *tls.Config, dialTimeout time.Duration) fu
DisableCompression: true, DisableCompression: true,
} }
if tlsConfig != nil { if globalIsTLS {
http2.ConfigureTransport(tr) trhttp2, _ := http2.ConfigureTransports(tr)
if trhttp2 != nil {
// ReadIdleTimeout is the timeout after which a health check using ping
// frame will be carried out if no frame is received on the
// connection. 5 minutes is above maximum sane scrape interval,
// we should not have this small overhead on the scrape connections.
// For other cases, this is used to validate that the connection can
// still be used.
trhttp2.ReadIdleTimeout = 5 * time.Minute
// PingTimeout is the timeout after which the connection will be closed
// if a response to Ping is not received.
trhttp2.PingTimeout = dialTimeout
// DisableCompression, if true, prevents the Transport from
// requesting compression with an "Accept-Encoding: gzip"
trhttp2.DisableCompression = true
}
} }
return func() *http.Transport { return func() *http.Transport {
@ -543,8 +570,6 @@ func newCustomHTTPTransport(tlsConfig *tls.Config, dialTimeout time.Duration) fu
// NewGatewayHTTPTransport returns a new http configuration // NewGatewayHTTPTransport returns a new http configuration
// used while communicating with the cloud backends. // used while communicating with the cloud backends.
// This sets the value for MaxIdleConnsPerHost from 2 (go default)
// to 256.
func NewGatewayHTTPTransport() *http.Transport { func NewGatewayHTTPTransport() *http.Transport {
return newGatewayHTTPTransport(1 * time.Minute) return newGatewayHTTPTransport(1 * time.Minute)
} }

@ -2208,7 +2208,7 @@ func (web *webAPIHandlers) LoginSTS(r *http.Request, args *LoginSTSArgs, reply *
if sourceScheme := handlers.GetSourceScheme(r); sourceScheme != "" { if sourceScheme := handlers.GetSourceScheme(r); sourceScheme != "" {
scheme = sourceScheme scheme = sourceScheme
} }
if globalIsSSL { if globalIsTLS {
scheme = "https" scheme = "https"
} }
@ -2227,8 +2227,6 @@ func (web *webAPIHandlers) LoginSTS(r *http.Request, args *LoginSTSArgs, reply *
clnt := &http.Client{ clnt := &http.Client{
Transport: NewGatewayHTTPTransport(), Transport: NewGatewayHTTPTransport(),
} }
defer clnt.CloseIdleConnections()
resp, err := clnt.Do(req) resp, err := clnt.Do(req)
if err != nil { if err != nil {
return toJSONError(ctx, err) return toJSONError(ctx, err)

4
go.mod

@ -81,8 +81,8 @@ require (
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c
go.etcd.io/etcd v0.0.0-20201125193152-8a03d2e9614b go.etcd.io/etcd v0.0.0-20201125193152-8a03d2e9614b
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392
golang.org/x/net v0.0.0-20200904194848-62affa334b73 golang.org/x/net v0.0.0-20201216054612-986b41b23924
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68
golang.org/x/tools v0.0.0-20200929223013-bf155c11ec6f // indirect golang.org/x/tools v0.0.0-20200929223013-bf155c11ec6f // indirect
google.golang.org/api v0.5.0 google.golang.org/api v0.5.0
gopkg.in/jcmturner/gokrb5.v7 v7.5.0 gopkg.in/jcmturner/gokrb5.v7 v7.5.0

5
go.sum

@ -713,6 +713,8 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA=
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201216054612-986b41b23924 h1:QsnDpLLOKwHBBDa8nDws4DYNc/ryVW2vCpxCs09d4PY=
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -759,7 +761,10 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1 h1:a/mKvvZr9Jcc8oKfcmgzyp7OwF73JPWsQLvH1z2Kxck= golang.org/x/sys v0.0.0-20201101102859-da207088b7d1 h1:a/mKvvZr9Jcc8oKfcmgzyp7OwF73JPWsQLvH1z2Kxck=
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=