Commit 1f8e3e50 authored by Vicky Chijwani's avatar Vicky Chijwani Committed by Nick Thomas

Add websocket route for web terminal access to CI jobs

parent c366ddc0
...@@ -161,6 +161,7 @@ func (u *Upstream) configureRoutes() { ...@@ -161,6 +161,7 @@ func (u *Upstream) configureRoutes() {
// Terminal websocket // Terminal websocket
wsRoute(projectPattern+`environments/[0-9]+/terminal.ws\z`, terminal.Handler(api)), wsRoute(projectPattern+`environments/[0-9]+/terminal.ws\z`, terminal.Handler(api)),
wsRoute(projectPattern+`-/jobs/[0-9]+/terminal.ws\z`, terminal.Handler(api)),
// Long poll and limit capacity given to jobs/request and builds/register.json // Long poll and limit capacity given to jobs/request and builds/register.json
route("", apiPattern+`v4/jobs/request\z`, ciAPILongPolling), route("", apiPattern+`v4/jobs/request\z`, ciAPILongPolling),
......
...@@ -20,7 +20,10 @@ import ( ...@@ -20,7 +20,10 @@ import (
"gitlab.com/gitlab-org/gitlab-workhorse/internal/api" "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
) )
var terminalPath = fmt.Sprintf("%s/environments/1/terminal.ws", testProject) var (
envTerminalPath = fmt.Sprintf("%s/environments/1/terminal.ws", testProject)
jobTerminalPath = fmt.Sprintf("%s/-/jobs/1/terminal.ws", testProject)
)
type connWithReq struct { type connWithReq struct {
conn *websocket.Conn conn *websocket.Conn
...@@ -28,39 +31,50 @@ type connWithReq struct { ...@@ -28,39 +31,50 @@ type connWithReq struct {
} }
func TestTerminalHappyPath(t *testing.T) { func TestTerminalHappyPath(t *testing.T) {
serverConns, clientURL, close := wireupTerminal(nil, "channel.k8s.io") tests := []struct {
defer close() name string
terminalPath string
client, _, err := dialWebsocket(clientURL, nil, "terminal.gitlab.com") }{
if err != nil { {"environments", envTerminalPath},
t.Fatal(err) {"jobs", jobTerminalPath},
} }
for _, test := range tests {
server := (<-serverConns).conn t.Run(test.name, func(t *testing.T) {
defer server.Close() serverConns, clientURL, close := wireupTerminal(test.terminalPath, nil, "channel.k8s.io")
defer close()
message := "test message"
client, _, err := dialWebsocket(clientURL, nil, "terminal.gitlab.com")
// channel.k8s.io: server writes to channel 1, STDOUT if err != nil {
if err := say(server, "\x01"+message); err != nil { t.Fatal(err)
t.Fatal(err) }
server := (<-serverConns).conn
defer server.Close()
message := "test message"
// channel.k8s.io: server writes to channel 1, STDOUT
if err := say(server, "\x01"+message); err != nil {
t.Fatal(err)
}
assertReadMessage(t, client, websocket.BinaryMessage, message)
if err := say(client, message); err != nil {
t.Fatal(err)
}
// channel.k8s.io: client writes get put on channel 0, STDIN
assertReadMessage(t, server, websocket.BinaryMessage, "\x00"+message)
// Closing the client should send an EOT signal to the server's STDIN
client.Close()
assertReadMessage(t, server, websocket.BinaryMessage, "\x00\x04")
})
} }
assertReadMessage(t, client, websocket.BinaryMessage, message)
if err := say(client, message); err != nil {
t.Fatal(err)
}
// channel.k8s.io: client writes get put on channel 0, STDIN
assertReadMessage(t, server, websocket.BinaryMessage, "\x00"+message)
// Closing the client should send an EOT signal to the server's STDIN
client.Close()
assertReadMessage(t, server, websocket.BinaryMessage, "\x00\x04")
} }
func TestTerminalBadTLS(t *testing.T) { func TestTerminalBadTLS(t *testing.T) {
_, clientURL, close := wireupTerminal(badCA, "channel.k8s.io") _, clientURL, close := wireupTerminal(envTerminalPath, badCA, "channel.k8s.io")
defer close() defer close()
client, _, err := dialWebsocket(clientURL, nil, "terminal.gitlab.com") client, _, err := dialWebsocket(clientURL, nil, "terminal.gitlab.com")
...@@ -74,7 +88,7 @@ func TestTerminalBadTLS(t *testing.T) { ...@@ -74,7 +88,7 @@ func TestTerminalBadTLS(t *testing.T) {
} }
func TestTerminalSessionTimeout(t *testing.T) { func TestTerminalSessionTimeout(t *testing.T) {
serverConns, clientURL, close := wireupTerminal(timeout, "channel.k8s.io") serverConns, clientURL, close := wireupTerminal(envTerminalPath, timeout, "channel.k8s.io")
defer close() defer close()
client, _, err := dialWebsocket(clientURL, nil, "terminal.gitlab.com") client, _, err := dialWebsocket(clientURL, nil, "terminal.gitlab.com")
...@@ -96,7 +110,7 @@ func TestTerminalSessionTimeout(t *testing.T) { ...@@ -96,7 +110,7 @@ func TestTerminalSessionTimeout(t *testing.T) {
func TestTerminalProxyForwardsHeadersFromUpstream(t *testing.T) { func TestTerminalProxyForwardsHeadersFromUpstream(t *testing.T) {
hdr := make(http.Header) hdr := make(http.Header)
hdr.Set("Random-Header", "Value") hdr.Set("Random-Header", "Value")
serverConns, clientURL, close := wireupTerminal(setHeader(hdr), "channel.k8s.io") serverConns, clientURL, close := wireupTerminal(envTerminalPath, setHeader(hdr), "channel.k8s.io")
defer close() defer close()
client, _, err := dialWebsocket(clientURL, nil, "terminal.gitlab.com") client, _, err := dialWebsocket(clientURL, nil, "terminal.gitlab.com")
...@@ -113,7 +127,7 @@ func TestTerminalProxyForwardsHeadersFromUpstream(t *testing.T) { ...@@ -113,7 +127,7 @@ func TestTerminalProxyForwardsHeadersFromUpstream(t *testing.T) {
} }
func TestTerminalProxyForwardsXForwardedForFromClient(t *testing.T) { func TestTerminalProxyForwardsXForwardedForFromClient(t *testing.T) {
serverConns, clientURL, close := wireupTerminal(nil, "channel.k8s.io") serverConns, clientURL, close := wireupTerminal(envTerminalPath, nil, "channel.k8s.io")
defer close() defer close()
hdr := make(http.Header) hdr := make(http.Header)
...@@ -136,7 +150,7 @@ func TestTerminalProxyForwardsXForwardedForFromClient(t *testing.T) { ...@@ -136,7 +150,7 @@ func TestTerminalProxyForwardsXForwardedForFromClient(t *testing.T) {
} }
} }
func wireupTerminal(modifier func(*api.Response), subprotocols ...string) (chan connWithReq, string, func()) { func wireupTerminal(terminalPath string, modifier func(*api.Response), subprotocols ...string) (chan connWithReq, string, func()) {
serverConns, remote := startWebsocketServer(subprotocols...) serverConns, remote := startWebsocketServer(subprotocols...)
authResponse := terminalOkBody(remote, nil, subprotocols...) authResponse := terminalOkBody(remote, nil, subprotocols...)
if modifier != nil { if modifier != nil {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment