Allow specifying scheme when proxying

pull/6/head
Jordan Liggitt 2015-10-02 02:45:32 -04:00
parent 3902f76512
commit b9ba3bd7c2
11 changed files with 235 additions and 70 deletions

View File

@ -41,19 +41,23 @@ import (
type UpgradeAwareProxyHandler struct { type UpgradeAwareProxyHandler struct {
UpgradeRequired bool UpgradeRequired bool
Location *url.URL Location *url.URL
Transport http.RoundTripper // Transport provides an optional round tripper to use to proxy. If nil, the default proxy transport is used
FlushInterval time.Duration Transport http.RoundTripper
MaxBytesPerSec int64 // WrapTransport indicates whether the provided Transport should be wrapped with default proxy transport behavior (URL rewriting, X-Forwarded-* header setting)
err error WrapTransport bool
FlushInterval time.Duration
MaxBytesPerSec int64
err error
} }
const defaultFlushInterval = 200 * time.Millisecond const defaultFlushInterval = 200 * time.Millisecond
// NewUpgradeAwareProxyHandler creates a new proxy handler with a default flush interval // NewUpgradeAwareProxyHandler creates a new proxy handler with a default flush interval
func NewUpgradeAwareProxyHandler(location *url.URL, transport http.RoundTripper, upgradeRequired bool) *UpgradeAwareProxyHandler { func NewUpgradeAwareProxyHandler(location *url.URL, transport http.RoundTripper, wrapTransport, upgradeRequired bool) *UpgradeAwareProxyHandler {
return &UpgradeAwareProxyHandler{ return &UpgradeAwareProxyHandler{
Location: location, Location: location,
Transport: transport, Transport: transport,
WrapTransport: wrapTransport,
UpgradeRequired: upgradeRequired, UpgradeRequired: upgradeRequired,
FlushInterval: defaultFlushInterval, FlushInterval: defaultFlushInterval,
} }
@ -101,8 +105,8 @@ func (h *UpgradeAwareProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Re
return return
} }
if h.Transport == nil { if h.Transport == nil || h.WrapTransport {
h.Transport = h.defaultProxyTransport(req.URL) h.Transport = h.defaultProxyTransport(req.URL, h.Transport)
} }
newReq, err := http.NewRequest(req.Method, loc.String(), req.Body) newReq, err := http.NewRequest(req.Method, loc.String(), req.Body)
@ -258,7 +262,7 @@ func (h *UpgradeAwareProxyHandler) dialURL() (net.Conn, error) {
} }
} }
func (h *UpgradeAwareProxyHandler) defaultProxyTransport(url *url.URL) http.RoundTripper { func (h *UpgradeAwareProxyHandler) defaultProxyTransport(url *url.URL, internalTransport http.RoundTripper) http.RoundTripper {
scheme := url.Scheme scheme := url.Scheme
host := url.Host host := url.Host
suffix := h.Location.Path suffix := h.Location.Path
@ -266,13 +270,14 @@ func (h *UpgradeAwareProxyHandler) defaultProxyTransport(url *url.URL) http.Roun
suffix += "/" suffix += "/"
} }
pathPrepend := strings.TrimSuffix(url.Path, suffix) pathPrepend := strings.TrimSuffix(url.Path, suffix)
internalTransport := &proxy.Transport{ rewritingTransport := &proxy.Transport{
Scheme: scheme, Scheme: scheme,
Host: host, Host: host,
PathPrepend: pathPrepend, PathPrepend: pathPrepend,
RoundTripper: internalTransport,
} }
return &corsRemovingTransport{ return &corsRemovingTransport{
RoundTripper: internalTransport, RoundTripper: rewritingTransport,
} }
} }

View File

@ -388,7 +388,7 @@ func TestDefaultProxyTransport(t *testing.T) {
h := UpgradeAwareProxyHandler{ h := UpgradeAwareProxyHandler{
Location: locURL, Location: locURL,
} }
result := h.defaultProxyTransport(URL) result := h.defaultProxyTransport(URL, nil)
transport := result.(*corsRemovingTransport).RoundTripper.(*proxy.Transport) transport := result.(*corsRemovingTransport).RoundTripper.(*proxy.Transport)
if transport.Scheme != test.expectedScheme { if transport.Scheme != test.expectedScheme {
t.Errorf("%s: unexpected scheme. Actual: %s, Expected: %s", test.name, transport.Scheme, test.expectedScheme) t.Errorf("%s: unexpected scheme. Actual: %s, Expected: %s", test.name, transport.Scheme, test.expectedScheme)

View File

@ -137,7 +137,7 @@ func MatchNode(label labels.Selector, field fields.Selector) generic.Matcher {
// ResourceLocation returns an URL and transport which one can use to send traffic for the specified node. // ResourceLocation returns an URL and transport which one can use to send traffic for the specified node.
func ResourceLocation(getter ResourceGetter, connection client.ConnectionInfoGetter, ctx api.Context, id string) (*url.URL, http.RoundTripper, error) { func ResourceLocation(getter ResourceGetter, connection client.ConnectionInfoGetter, ctx api.Context, id string) (*url.URL, http.RoundTripper, error) {
name, portReq, valid := util.SplitPort(id) schemeReq, name, portReq, valid := util.SplitSchemeNamePort(id)
if !valid { if !valid {
return nil, nil, errors.NewBadRequest(fmt.Sprintf("invalid node request %q", id)) return nil, nil, errors.NewBadRequest(fmt.Sprintf("invalid node request %q", id))
} }
@ -154,6 +154,7 @@ func ResourceLocation(getter ResourceGetter, connection client.ConnectionInfoGet
host := hostIP.String() host := hostIP.String()
if portReq == "" || strconv.Itoa(ports.KubeletPort) == portReq { if portReq == "" || strconv.Itoa(ports.KubeletPort) == portReq {
// Ignore requested scheme, use scheme provided by GetConnectionInfo
scheme, port, transport, err := connection.GetConnectionInfo(host) scheme, port, transport, err := connection.GetConnectionInfo(host)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -168,5 +169,5 @@ func ResourceLocation(getter ResourceGetter, connection client.ConnectionInfoGet
transport, transport,
nil nil
} }
return &url.URL{Host: net.JoinHostPort(host, portReq)}, nil, nil return &url.URL{Scheme: schemeReq, Host: net.JoinHostPort(host, portReq)}, nil, nil
} }

View File

@ -285,12 +285,13 @@ func (r *ProxyREST) Connect(ctx api.Context, id string, opts runtime.Object) (re
if !ok { if !ok {
return nil, fmt.Errorf("Invalid options object: %#v", opts) return nil, fmt.Errorf("Invalid options object: %#v", opts)
} }
location, _, err := pod.ResourceLocation(r.store, ctx, id) location, transport, err := pod.ResourceLocation(r.store, ctx, id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
location.Path = path.Join(location.Path, proxyOpts.Path) location.Path = path.Join(location.Path, proxyOpts.Path)
return newUpgradeAwareProxyHandler(location, nil, false), nil // Return a proxy handler that uses the desired transport, wrapped with additional proxy handling (to get URL rewriting, X-Forwarded-* headers, etc)
return newThrottledUpgradeAwareProxyHandler(location, transport, true, false), nil
} }
// Support both GET and POST methods. Over time, we want to move all clients to start using POST and then stop supporting GET. // Support both GET and POST methods. Over time, we want to move all clients to start using POST and then stop supporting GET.
@ -321,7 +322,7 @@ func (r *AttachREST) Connect(ctx api.Context, name string, opts runtime.Object)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return genericrest.NewUpgradeAwareProxyHandler(location, transport, true), nil return newThrottledUpgradeAwareProxyHandler(location, transport, false, true), nil
} }
// NewConnectOptions returns the versioned object that represents exec parameters // NewConnectOptions returns the versioned object that represents exec parameters
@ -359,7 +360,7 @@ func (r *ExecREST) Connect(ctx api.Context, name string, opts runtime.Object) (r
if err != nil { if err != nil {
return nil, err return nil, err
} }
return newUpgradeAwareProxyHandler(location, transport, true), nil return newThrottledUpgradeAwareProxyHandler(location, transport, false, true), nil
} }
// NewConnectOptions returns the versioned object that represents exec parameters // NewConnectOptions returns the versioned object that represents exec parameters
@ -403,11 +404,11 @@ func (r *PortForwardREST) Connect(ctx api.Context, name string, opts runtime.Obj
if err != nil { if err != nil {
return nil, err return nil, err
} }
return newUpgradeAwareProxyHandler(location, transport, true), nil return newThrottledUpgradeAwareProxyHandler(location, transport, false, true), nil
} }
func newUpgradeAwareProxyHandler(location *url.URL, transport http.RoundTripper, upgradeRequired bool) *genericrest.UpgradeAwareProxyHandler { func newThrottledUpgradeAwareProxyHandler(location *url.URL, transport http.RoundTripper, wrapTransport, upgradeRequired bool) *genericrest.UpgradeAwareProxyHandler {
handler := genericrest.NewUpgradeAwareProxyHandler(location, transport, upgradeRequired) handler := genericrest.NewUpgradeAwareProxyHandler(location, transport, wrapTransport, upgradeRequired)
handler.MaxBytesPerSec = capabilities.Get().PerConnectionBandwidthLimitBytesPerSec handler.MaxBytesPerSec = capabilities.Get().PerConnectionBandwidthLimitBytesPerSec
return handler return handler
} }

View File

@ -17,6 +17,7 @@ limitations under the License.
package pod package pod
import ( import (
"crypto/tls"
"fmt" "fmt"
"net" "net"
"net/http" "net/http"
@ -46,6 +47,13 @@ type podStrategy struct {
// objects via the REST API. // objects via the REST API.
var Strategy = podStrategy{api.Scheme, api.SimpleNameGenerator} var Strategy = podStrategy{api.Scheme, api.SimpleNameGenerator}
// PodProxyTransport is used by the API proxy to connect to pods
// Exported to allow overriding TLS options (like adding a client certificate)
var PodProxyTransport = util.SetTransportDefaults(&http.Transport{
// Turn off hostname verification, because connections are to assigned IPs, not deterministic
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
})
// NamespaceScoped is true for pods. // NamespaceScoped is true for pods.
func (podStrategy) NamespaceScoped() bool { func (podStrategy) NamespaceScoped() bool {
return true return true
@ -188,9 +196,9 @@ func getPod(getter ResourceGetter, ctx api.Context, name string) (*api.Pod, erro
// ResourceLocation returns a URL to which one can send traffic for the specified pod. // ResourceLocation returns a URL to which one can send traffic for the specified pod.
func ResourceLocation(getter ResourceGetter, ctx api.Context, id string) (*url.URL, http.RoundTripper, error) { func ResourceLocation(getter ResourceGetter, ctx api.Context, id string) (*url.URL, http.RoundTripper, error) {
// Allow ID as "podname" or "podname:port". If port is not specified, // Allow ID as "podname" or "podname:port" or "scheme:podname:port".
// try to use the first defined port on the pod. // If port is not specified, try to use the first defined port on the pod.
name, port, valid := util.SplitPort(id) scheme, name, port, valid := util.SplitSchemeNamePort(id)
if !valid { if !valid {
return nil, nil, errors.NewBadRequest(fmt.Sprintf("invalid pod request %q", id)) return nil, nil, errors.NewBadRequest(fmt.Sprintf("invalid pod request %q", id))
} }
@ -211,15 +219,15 @@ func ResourceLocation(getter ResourceGetter, ctx api.Context, id string) (*url.U
} }
} }
// We leave off the scheme ('http://') because we have no idea what sort of server loc := &url.URL{
// is listening at this endpoint. Scheme: scheme,
loc := &url.URL{} }
if port == "" { if port == "" {
loc.Host = pod.Status.PodIP loc.Host = pod.Status.PodIP
} else { } else {
loc.Host = net.JoinHostPort(pod.Status.PodIP, port) loc.Host = net.JoinHostPort(pod.Status.PodIP, port)
} }
return loc, nil, nil return loc, PodProxyTransport, nil
} }
// LogLocation returns the log URL for a pod container. If opts.Container is blank // LogLocation returns the log URL for a pod container. If opts.Container is blank

View File

@ -17,6 +17,7 @@ limitations under the License.
package service package service
import ( import (
"crypto/tls"
"fmt" "fmt"
"math/rand" "math/rand"
"net" "net"
@ -49,6 +50,13 @@ type REST struct {
serviceNodePorts portallocator.Interface serviceNodePorts portallocator.Interface
} }
// ServiceProxyTransport is used by the API proxy to connect to services
// Exported to allow overriding TLS options (like adding a client certificate)
var ServiceProxyTransport = util.SetTransportDefaults(&http.Transport{
// Turn off hostname verification, because connections are to assigned IPs, not deterministic
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
})
// NewStorage returns a new REST. // NewStorage returns a new REST.
func NewStorage(registry Registry, endpoints endpoint.Registry, serviceIPs ipallocator.Interface, func NewStorage(registry Registry, endpoints endpoint.Registry, serviceIPs ipallocator.Interface,
serviceNodePorts portallocator.Interface) *REST { serviceNodePorts portallocator.Interface) *REST {
@ -277,8 +285,8 @@ var _ = rest.Redirector(&REST{})
// ResourceLocation returns a URL to which one can send traffic for the specified service. // ResourceLocation returns a URL to which one can send traffic for the specified service.
func (rs *REST) ResourceLocation(ctx api.Context, id string) (*url.URL, http.RoundTripper, error) { func (rs *REST) ResourceLocation(ctx api.Context, id string) (*url.URL, http.RoundTripper, error) {
// Allow ID as "svcname" or "svcname:port". // Allow ID as "svcname", "svcname:port", or "scheme:svcname:port".
svcName, portStr, valid := util.SplitPort(id) svcScheme, svcName, portStr, valid := util.SplitSchemeNamePort(id)
if !valid { if !valid {
return nil, nil, errors.NewBadRequest(fmt.Sprintf("invalid service request %q", id)) return nil, nil, errors.NewBadRequest(fmt.Sprintf("invalid service request %q", id))
} }
@ -303,11 +311,10 @@ func (rs *REST) ResourceLocation(ctx api.Context, id string) (*url.URL, http.Rou
// Pick a random address. // Pick a random address.
ip := ss.Addresses[rand.Intn(len(ss.Addresses))].IP ip := ss.Addresses[rand.Intn(len(ss.Addresses))].IP
port := ss.Ports[i].Port port := ss.Ports[i].Port
// We leave off the scheme ('http://') because we have no idea what sort of server
// is listening at this endpoint.
return &url.URL{ return &url.URL{
Host: net.JoinHostPort(ip, strconv.Itoa(port)), Scheme: svcScheme,
}, nil, nil Host: net.JoinHostPort(ip, strconv.Itoa(port)),
}, ServiceProxyTransport, nil
} }
} }
} }

View File

@ -491,6 +491,18 @@ func TestServiceRegistryResourceLocation(t *testing.T) {
t.Errorf("Expected %v, but got %v", e, a) t.Errorf("Expected %v, but got %v", e, a)
} }
// Test a scheme + name + port.
location, _, err = redirector.ResourceLocation(ctx, "https:foo:p")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if location == nil {
t.Errorf("Unexpected nil: %v", location)
}
if e, a := "https://1.2.3.4:93", location.String(); e != a {
t.Errorf("Expected %v, but got %v", e, a)
}
// Test a non-existent name + port. // Test a non-existent name + port.
location, _, err = redirector.ResourceLocation(ctx, "foo:q") location, _, err = redirector.ResourceLocation(ctx, "foo:q")
if err == nil { if err == nil {

View File

@ -18,23 +18,60 @@ package util
import ( import (
"strings" "strings"
"k8s.io/kubernetes/pkg/util/sets"
) )
// Takes a string of the form "name:port" or "name". var validSchemes = sets.NewString("http", "https", "")
// * If id is of the form "name" or "name:", then return (name, "", true)
// * If id is of the form "name:port", then return (name, port, true) // SplitSchemeNamePort takes a string of the following forms:
// * Otherwise, return ("", "", false) // * "<name>", returns "", "<name>","", true
// Additionally, name must be non-empty or valid will be returned false. // * "<name>:<port>", returns "", "<name>","<port>",true
// * "<scheme>:<name>:<port>", returns "<scheme>","<name>","<port>",true
// //
// Name must be non-empty or valid will be returned false.
// Scheme must be "http" or "https" if specified
// Port is returned as a string, and it is not required to be numeric (could be // Port is returned as a string, and it is not required to be numeric (could be
// used for a named port, for example). // used for a named port, for example).
func SplitPort(id string) (name, port string, valid bool) { func SplitSchemeNamePort(id string) (scheme, name, port string, valid bool) {
parts := strings.Split(id, ":") parts := strings.Split(id, ":")
if len(parts) > 2 { switch len(parts) {
return "", "", false case 1:
name = parts[0]
case 2:
name = parts[0]
port = parts[1]
case 3:
scheme = parts[0]
name = parts[1]
port = parts[2]
default:
return "", "", "", false
} }
if len(parts) == 2 {
return parts[0], parts[1], len(parts[0]) > 0 if len(name) > 0 && validSchemes.Has(scheme) {
return scheme, name, port, true
} else {
return "", "", "", false
} }
return id, "", len(id) > 0 }
// JoinSchemeNamePort returns a string that specifies the scheme, name, and port:
// * "<name>"
// * "<name>:<port>"
// * "<scheme>:<name>:<port>"
// None of the parameters may contain a ':' character
// Name is required
// Scheme must be "", "http", or "https"
func JoinSchemeNamePort(scheme, name, port string) string {
if len(scheme) > 0 {
// Must include three segments to specify scheme
return scheme + ":" + name + ":" + port
}
if len(port) > 0 {
// Must include two segments to specify port
return name + ":" + port
}
// Return name alone
return name
} }

View File

@ -20,11 +20,12 @@ import (
"testing" "testing"
) )
func TestSplitPort(t *testing.T) { func TestSplitSchemeNamePort(t *testing.T) {
table := []struct { table := []struct {
in string in string
name, port string name, port, scheme string
valid bool valid bool
normalized bool
}{ }{
{ {
in: "aoeu:asdf", in: "aoeu:asdf",
@ -32,26 +33,50 @@ func TestSplitPort(t *testing.T) {
port: "asdf", port: "asdf",
valid: true, valid: true,
}, { }, {
in: "aoeu:", in: "http:aoeu:asdf",
name: "aoeu", scheme: "http",
valid: true, name: "aoeu",
port: "asdf",
valid: true,
}, { }, {
in: ":asdf", in: "https:aoeu:",
name: "", scheme: "https",
port: "asdf", name: "aoeu",
port: "",
valid: true,
normalized: false,
}, { }, {
in: "aoeu:asdf:htns", in: "https:aoeu:asdf",
scheme: "https",
name: "aoeu",
port: "asdf",
valid: true,
}, {
in: "aoeu:",
name: "aoeu",
valid: true,
normalized: false,
}, {
in: ":asdf",
valid: false,
}, {
in: "aoeu:asdf:htns",
valid: false,
}, { }, {
in: "aoeu", in: "aoeu",
name: "aoeu", name: "aoeu",
valid: true, valid: true,
}, { }, {
in: "", in: "",
valid: false,
}, },
} }
for _, item := range table { for _, item := range table {
name, port, valid := SplitPort(item.in) scheme, name, port, valid := SplitSchemeNamePort(item.in)
if e, a := item.scheme, scheme; e != a {
t.Errorf("%q: Wanted %q, got %q", item.in, e, a)
}
if e, a := item.name, name; e != a { if e, a := item.name, name; e != a {
t.Errorf("%q: Wanted %q, got %q", item.in, e, a) t.Errorf("%q: Wanted %q, got %q", item.in, e, a)
} }
@ -61,5 +86,26 @@ func TestSplitPort(t *testing.T) {
if e, a := item.valid, valid; e != a { if e, a := item.valid, valid; e != a {
t.Errorf("%q: Wanted %t, got %t", item.in, e, a) t.Errorf("%q: Wanted %t, got %t", item.in, e, a)
} }
// Make sure valid items round trip through JoinSchemeNamePort
if item.valid {
out := JoinSchemeNamePort(scheme, name, port)
if item.normalized && out != item.in {
t.Errorf("%q: Wanted %s, got %s", item.in, item.in, out)
}
scheme, name, port, valid := SplitSchemeNamePort(out)
if e, a := item.scheme, scheme; e != a {
t.Errorf("%q: Wanted %q, got %q", item.in, e, a)
}
if e, a := item.name, name; e != a {
t.Errorf("%q: Wanted %q, got %q", item.in, e, a)
}
if e, a := item.port, port; e != a {
t.Errorf("%q: Wanted %q, got %q", item.in, e, a)
}
if e, a := item.valid, valid; e != a {
t.Errorf("%q: Wanted %t, got %t", item.in, e, a)
}
}
} }
} }

View File

@ -78,6 +78,16 @@ func proxyContext(version string) {
Port: 81, Port: 81,
TargetPort: util.NewIntOrStringFromInt(162), TargetPort: util.NewIntOrStringFromInt(162),
}, },
{
Name: "tlsportname1",
Port: 443,
TargetPort: util.NewIntOrStringFromString("tlsdest1"),
},
{
Name: "tlsportname2",
Port: 444,
TargetPort: util.NewIntOrStringFromInt(462),
},
}, },
}, },
}) })
@ -93,7 +103,7 @@ func proxyContext(version string) {
pods := []*api.Pod{} pods := []*api.Pod{}
cfg := RCConfig{ cfg := RCConfig{
Client: f.Client, Client: f.Client,
Image: "gcr.io/google_containers/porter:59ad46ed2c56ba50fa7f1dc176c07c37", Image: "gcr.io/google_containers/porter:cd5cb5791ebaa8641955f0e8c2a9bed669b1eaab",
Name: service.Name, Name: service.Name,
Namespace: f.Namespace.Name, Namespace: f.Namespace.Name,
Replicas: 1, Replicas: 1,
@ -102,10 +112,17 @@ func proxyContext(version string) {
"SERVE_PORT_80": `<a href="/rewriteme">test</a>`, "SERVE_PORT_80": `<a href="/rewriteme">test</a>`,
"SERVE_PORT_160": "foo", "SERVE_PORT_160": "foo",
"SERVE_PORT_162": "bar", "SERVE_PORT_162": "bar",
"SERVE_TLS_PORT_443": `<a href="/tlsrewriteme">test</a>`,
"SERVE_TLS_PORT_460": `tls baz`,
"SERVE_TLS_PORT_462": `tls qux`,
}, },
Ports: map[string]int{ Ports: map[string]int{
"dest1": 160, "dest1": 160,
"dest2": 162, "dest2": 162,
"tlsdest1": 460,
"tlsdest2": 462,
}, },
Labels: labels, Labels: labels,
CreatedPods: &pods, CreatedPods: &pods,
@ -116,14 +133,44 @@ func proxyContext(version string) {
Expect(f.WaitForAnEndpoint(service.Name)).NotTo(HaveOccurred()) Expect(f.WaitForAnEndpoint(service.Name)).NotTo(HaveOccurred())
// Try proxying through the service and directly to through the pod. // Try proxying through the service and directly to through the pod.
svcPrefix := prefix + "/proxy/namespaces/" + f.Namespace.Name + "/services/" + service.Name svcProxyURL := func(scheme, port string) string {
podPrefix := prefix + "/proxy/namespaces/" + f.Namespace.Name + "/pods/" + pods[0].Name return prefix + "/proxy/namespaces/" + f.Namespace.Name + "/services/" + util.JoinSchemeNamePort(scheme, service.Name, port)
}
podProxyURL := func(scheme, port string) string {
return prefix + "/proxy/namespaces/" + f.Namespace.Name + "/pods/" + util.JoinSchemeNamePort(scheme, pods[0].Name, port)
}
subresourcePodProxyURL := func(scheme, port string) string {
return prefix + "/namespaces/" + f.Namespace.Name + "/pods/" + util.JoinSchemeNamePort(scheme, pods[0].Name, port) + "/proxy"
}
expectations := map[string]string{ expectations := map[string]string{
svcPrefix + ":portname1/": "foo", svcProxyURL("", "portname1") + "/": "foo",
svcPrefix + ":portname2/": "bar", svcProxyURL("", "portname2") + "/": "bar",
podPrefix + ":80/": `<a href="` + podPrefix + `:80/rewriteme">test</a>`,
podPrefix + ":160/": "foo", svcProxyURL("http", "portname1") + "/": "foo",
podPrefix + ":162/": "bar", svcProxyURL("http", "portname2") + "/": "bar",
svcProxyURL("https", "tlsportname1") + "/": "tls baz",
svcProxyURL("https", "tlsportname2") + "/": "tls qux",
podProxyURL("", "80") + "/": `<a href="` + podProxyURL("", "80") + `/rewriteme">test</a>`,
podProxyURL("", "160") + "/": "foo",
podProxyURL("", "162") + "/": "bar",
podProxyURL("http", "80") + "/": `<a href="` + podProxyURL("http", "80") + `/rewriteme">test</a>`,
podProxyURL("http", "160") + "/": "foo",
podProxyURL("http", "162") + "/": "bar",
subresourcePodProxyURL("", "") + "/": `<a href="` + subresourcePodProxyURL("", "") + `/rewriteme">test</a>`,
subresourcePodProxyURL("", "80") + "/": `<a href="` + subresourcePodProxyURL("", "80") + `/rewriteme">test</a>`,
subresourcePodProxyURL("http", "80") + "/": `<a href="` + subresourcePodProxyURL("http", "80") + `/rewriteme">test</a>`,
subresourcePodProxyURL("", "160") + "/": "foo",
subresourcePodProxyURL("http", "160") + "/": "foo",
subresourcePodProxyURL("", "162") + "/": "bar",
subresourcePodProxyURL("http", "162") + "/": "bar",
subresourcePodProxyURL("https", "443") + "/": `<a href="` + subresourcePodProxyURL("https", "443") + `/tlsrewriteme">test</a>`,
subresourcePodProxyURL("https", "460") + "/": "tls baz",
subresourcePodProxyURL("https", "462") + "/": "tls qux",
// TODO: below entries don't work, but I believe we should make them work. // TODO: below entries don't work, but I believe we should make them work.
// svcPrefix + ":80": "foo", // svcPrefix + ":80": "foo",
// svcPrefix + ":81": "bar", // svcPrefix + ":81": "bar",
@ -159,7 +206,8 @@ func proxyContext(version string) {
recordError(fmt.Sprintf("%v: path %v took %v > 15s", i, path, d)) recordError(fmt.Sprintf("%v: path %v took %v > 15s", i, path, d))
} }
}(i, path, val) }(i, path, val)
time.Sleep(150 * time.Millisecond) // default QPS is 5
time.Sleep(200 * time.Millisecond)
} }
} }
wg.Wait() wg.Wait()

View File

@ -8,7 +8,7 @@
"containers": [ "containers": [
{ {
"name": "porter", "name": "porter",
"image": "gcr.io/google_containers/porter:59ad46ed2c56ba50fa7f1dc176c07c37", "image": "gcr.io/google_containers/porter:cd5cb5791ebaa8641955f0e8c2a9bed669b1eaab",
"env": [ "env": [
{ {
"name": "SERVE_PORT_80", "name": "SERVE_PORT_80",