mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-03 09:22:44 +00:00
Merge pull request #10529 from jlowdermilk/upgrade-aware-proxy-ssh
Make UpgradeAwareProxyHandler use transport.Dial if provided
This commit is contained in:
commit
00193bf1ac
@ -174,23 +174,56 @@ func (h *UpgradeAwareProxyHandler) tryUpgrade(w http.ResponseWriter, req *http.R
|
|||||||
func (h *UpgradeAwareProxyHandler) dialURL() (net.Conn, error) {
|
func (h *UpgradeAwareProxyHandler) dialURL() (net.Conn, error) {
|
||||||
dialAddr := netutil.CanonicalAddr(h.Location)
|
dialAddr := netutil.CanonicalAddr(h.Location)
|
||||||
|
|
||||||
|
var dialer func(network, addr string) (net.Conn, error)
|
||||||
|
if httpTransport, ok := h.Transport.(*http.Transport); ok && httpTransport.Dial != nil {
|
||||||
|
dialer = httpTransport.Dial
|
||||||
|
}
|
||||||
|
|
||||||
switch h.Location.Scheme {
|
switch h.Location.Scheme {
|
||||||
case "http":
|
case "http":
|
||||||
|
if dialer != nil {
|
||||||
|
return dialer("tcp", dialAddr)
|
||||||
|
}
|
||||||
return net.Dial("tcp", dialAddr)
|
return net.Dial("tcp", dialAddr)
|
||||||
case "https":
|
case "https":
|
||||||
|
// TODO: this TLS logic can probably be cleaned up; it's messy in an attempt
|
||||||
|
// to preserve behavior that we don't know for sure is exercised.
|
||||||
|
|
||||||
// Get the tls config from the transport if we recognize it
|
// Get the tls config from the transport if we recognize it
|
||||||
var tlsConfig *tls.Config
|
var tlsConfig *tls.Config
|
||||||
|
var tlsConn *tls.Conn
|
||||||
|
var err error
|
||||||
if h.Transport != nil {
|
if h.Transport != nil {
|
||||||
httpTransport, ok := h.Transport.(*http.Transport)
|
httpTransport, ok := h.Transport.(*http.Transport)
|
||||||
if ok {
|
if ok {
|
||||||
tlsConfig = httpTransport.TLSClientConfig
|
tlsConfig = httpTransport.TLSClientConfig
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if dialer != nil {
|
||||||
|
// We have a dialer; use it to open the connection, then
|
||||||
|
// create a tls client using the connection.
|
||||||
|
netConn, err := dialer("tcp", dialAddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// tls.Client requires non-nil config
|
||||||
|
if tlsConfig == nil {
|
||||||
|
glog.Warningf("using custom dialer with no TLSClientConfig. Defaulting to InsecureSkipVerify")
|
||||||
|
tlsConfig = &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tlsConn = tls.Client(netConn, tlsConfig)
|
||||||
|
if err := tlsConn.Handshake(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Dial
|
} else {
|
||||||
tlsConn, err := tls.Dial("tcp", dialAddr, tlsConfig)
|
// Dial
|
||||||
if err != nil {
|
tlsConn, err = tls.Dial("tcp", dialAddr, tlsConfig)
|
||||||
return nil, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
@ -202,7 +235,7 @@ func (h *UpgradeAwareProxyHandler) dialURL() (net.Conn, error) {
|
|||||||
|
|
||||||
return tlsConn, nil
|
return tlsConn, nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("Unknown scheme: %s", h.Location.Scheme)
|
return nil, fmt.Errorf("unknown scheme: %s", h.Location.Scheme)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,10 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -42,6 +45,14 @@ const (
|
|||||||
kubectlProxyPort = 8011
|
kubectlProxyPort = 8011
|
||||||
guestbookStartupTimeout = 10 * time.Minute
|
guestbookStartupTimeout = 10 * time.Minute
|
||||||
guestbookResponseTimeout = 3 * time.Minute
|
guestbookResponseTimeout = 3 * time.Minute
|
||||||
|
simplePodSelector = "name=nginx"
|
||||||
|
simplePodName = "nginx"
|
||||||
|
nginxDefaultOutput = "Welcome to nginx!"
|
||||||
|
simplePodPort = 80
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
portForwardRegexp = regexp.MustCompile("Forwarding from 127.0.0.1:([0-9]+) -> 80")
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("Kubectl client", func() {
|
var _ = Describe("Kubectl client", func() {
|
||||||
@ -127,8 +138,85 @@ var _ = Describe("Kubectl client", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Describe("Simple pod", func() {
|
||||||
|
var podPath string
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
podPath = filepath.Join(testContext.RepoRoot, "examples/pod.yaml")
|
||||||
|
By("creating the pod")
|
||||||
|
runKubectl("create", "-f", podPath, fmt.Sprintf("--namespace=%v", ns))
|
||||||
|
checkPodsRunningReady(c, ns, []string{simplePodName}, podStartTimeout)
|
||||||
|
|
||||||
|
})
|
||||||
|
AfterEach(func() {
|
||||||
|
cleanup(podPath, ns, simplePodSelector)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should support exec", func() {
|
||||||
|
By("executing a command in the container")
|
||||||
|
execOutput := runKubectl("exec", fmt.Sprintf("--namespace=%v", ns), simplePodName, "echo", "running", "in", "container")
|
||||||
|
expectedExecOutput := "running in container"
|
||||||
|
if execOutput != expectedExecOutput {
|
||||||
|
Failf("Unexpected kubectl exec output. Wanted '%s', got '%s'", execOutput, expectedExecOutput)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
It("should support port-forward", func() {
|
||||||
|
By("forwarding the container port to a local port")
|
||||||
|
cmd := kubectlCmd("port-forward", fmt.Sprintf("--namespace=%v", ns), "-p", simplePodName, fmt.Sprintf(":%d", simplePodPort))
|
||||||
|
defer func() {
|
||||||
|
if err := cmd.Process.Kill(); err != nil {
|
||||||
|
Logf("ERROR failed to kill kubectl port-forward command! The process may leak")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// This is somewhat ugly but is the only way to retrieve the port that was picked
|
||||||
|
// by the port-forward command. We don't want to hard code the port as we have no
|
||||||
|
// way of guaranteeing we can pick one that isn't in use, particularly on Jenkins.
|
||||||
|
Logf("starting port-forward command and streaming output")
|
||||||
|
stdout, stderr, err := startCmdAndStreamOutput(cmd)
|
||||||
|
if err != nil {
|
||||||
|
Failf("Failed to start port-forward command: %v", err)
|
||||||
|
}
|
||||||
|
defer stdout.Close()
|
||||||
|
defer stderr.Close()
|
||||||
|
|
||||||
|
buf := make([]byte, 128)
|
||||||
|
var n int
|
||||||
|
Logf("reading from `kubectl port-forward` command's stderr")
|
||||||
|
if n, err = stderr.Read(buf); err != nil {
|
||||||
|
Failf("Failed to read from kubectl port-forward stderr: %v", err)
|
||||||
|
}
|
||||||
|
portForwardOutput := string(buf[:n])
|
||||||
|
match := portForwardRegexp.FindStringSubmatch(portForwardOutput)
|
||||||
|
if len(match) != 2 {
|
||||||
|
Failf("Failed to parse kubectl port-forward output: %s", portForwardOutput)
|
||||||
|
}
|
||||||
|
By("curling local port output")
|
||||||
|
localAddr := fmt.Sprintf("http://localhost:%s", match[1])
|
||||||
|
body, err := curl(localAddr)
|
||||||
|
if err != nil {
|
||||||
|
Failf("Failed http.Get of forwarded port (%s): %v", localAddr, err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(body, nginxDefaultOutput) {
|
||||||
|
Failf("Container port output missing expected value. Wanted:'%s', got: %s", nginxDefaultOutput, body)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
func curl(addr string) (string, error) {
|
||||||
|
resp, err := http.Get(addr)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(body[:]), nil
|
||||||
|
}
|
||||||
|
|
||||||
func validateGuestbookApp(c *client.Client, ns string) {
|
func validateGuestbookApp(c *client.Client, ns string) {
|
||||||
Logf("Waiting for frontend to serve content.")
|
Logf("Waiting for frontend to serve content.")
|
||||||
if !waitForGuestbookResponse(c, "get", "", `{"data": ""}`, guestbookStartupTimeout, ns) {
|
if !waitForGuestbookResponse(c, "get", "", `{"data": ""}`, guestbookStartupTimeout, ns) {
|
||||||
|
@ -19,6 +19,7 @@ package e2e
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
@ -832,6 +833,20 @@ func runKubectl(args ...string) string {
|
|||||||
return strings.TrimSpace(stdout.String())
|
return strings.TrimSpace(stdout.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func startCmdAndStreamOutput(cmd *exec.Cmd) (stdout, stderr io.ReadCloser, err error) {
|
||||||
|
stdout, err = cmd.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
stderr, err = cmd.StderrPipe()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Logf("Asyncronously running '%s %s'", cmd.Path, strings.Join(cmd.Args, " "))
|
||||||
|
err = cmd.Start()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// testContainerOutput runs testContainerOutputInNamespace with the default namespace.
|
// testContainerOutput runs testContainerOutputInNamespace with the default namespace.
|
||||||
func testContainerOutput(scenarioName string, c *client.Client, pod *api.Pod, containerIndex int, expectedOutput []string) {
|
func testContainerOutput(scenarioName string, c *client.Client, pod *api.Pod, containerIndex int, expectedOutput []string) {
|
||||||
testContainerOutputInNamespace(scenarioName, c, pod, containerIndex, expectedOutput, api.NamespaceDefault)
|
testContainerOutputInNamespace(scenarioName, c, pod, containerIndex, expectedOutput, api.NamespaceDefault)
|
||||||
|
Loading…
Reference in New Issue
Block a user