@@ -41,6 +41,15 @@ type terminalSize struct {
41
41
const (
42
42
// Time allowed to write a message to the peer.
43
43
writeWait = 10 * time .Second
44
+
45
+ // Time allowed to read the next pong message from the peer.
46
+ pongWait = 60 * time .Second
47
+
48
+ // Send pings to peer with this period. Must be less than pongWait.
49
+ pingPeriod = (pongWait * 9 ) / 10
50
+
51
+ // Time to wait before force close on connection.
52
+ closeGracePeriod = 10 * time .Second
44
53
)
45
54
46
55
// Start handles web-socket request to launch a Kubernetes Terminal
@@ -60,7 +69,7 @@ func (k *KubeTerminal) Start(c echo.Context) error {
60
69
if ! ok {
61
70
return errors .New ("Could not get token" )
62
71
}
63
-
72
+
64
73
// This is the kube config for the kubernetes endpoint that we want configured in the Terminal
65
74
kubeConfig , err := k .Kube .GetKubeConfigForEndpoint (cnsiRecord .APIEndpoint .String (), tokenRecord , "" )
66
75
if err != nil {
@@ -79,7 +88,10 @@ func (k *KubeTerminal) Start(c echo.Context) error {
79
88
defer ws .Close ()
80
89
defer pingTicker .Stop ()
81
90
82
- // We are now in web socket land - we don't want any middleware to change the HTTP response
91
+ // At this point we aer using web sockets, so we can not return errors to the client as the connection
92
+ // has been upgraded to a web socket
93
+
94
+ // We are now in web socket land - we don't want any middleware to change the HTTP response
83
95
c .Set ("Stratos-WebSocket" , "true" )
84
96
85
97
// Send a message to say that we are creating the pod
@@ -95,8 +107,8 @@ func (k *KubeTerminal) Start(c echo.Context) error {
95
107
k .cleanupPodAndSecret (podData )
96
108
97
109
// Send error message
98
- sendProgressMessage (ws , "!" + err .Error ())
99
- return err
110
+ sendProgressMessage (ws , "!" + err .Error ())
111
+ return nil
100
112
}
101
113
102
114
// API Endpoint to SSH/exec into a container
@@ -131,36 +143,40 @@ func (k *KubeTerminal) Start(c echo.Context) error {
131
143
132
144
stdoutDone := make (chan bool )
133
145
go pumpStdout (ws , wsConn , stdoutDone )
146
+ go ping (ws , stdoutDone )
134
147
135
148
// If the downstream connection is closed, close the other web socket as well
136
- ws .SetCloseHandler (func (code int , text string ) error {
149
+ ws .SetCloseHandler (func (code int , text string ) error {
137
150
wsConn .Close ()
151
+ // Cleanup
152
+ k .cleanupPodAndSecret (podData )
153
+ podData = nil
138
154
return nil
139
155
})
140
156
157
+ // Wait a while when reading - can take some time for the container to launch
158
+ ws .SetReadDeadline (time .Now ().Add (pongWait ))
159
+ ws .SetPongHandler (func (string ) error { ws .SetReadDeadline (time .Now ().Add (pongWait )); return nil })
160
+
141
161
// Read the input from the web socket and pipe it to the SSH client
142
162
for {
143
163
_ , r , err := ws .ReadMessage ()
144
164
if err != nil {
145
- // Check to see if this was because the web socket was closed cleanly
146
- closed := false
147
- select {
148
- case msg := <- stdoutDone :
149
- closed = msg
150
- }
151
- if ! closed {
152
- log .Errorf ("Kubernetes terminal: error reading message from web socket: %+v" , err )
153
- }
154
- log .Debug ("Kube Terminal cleaning up ...." )
165
+ // Error reading - so clean up
155
166
k .cleanupPodAndSecret (podData )
167
+ podData = nil
168
+
169
+ ws .SetWriteDeadline (time .Now ().Add (writeWait ))
170
+ ws .WriteMessage (websocket .CloseMessage , websocket .FormatCloseMessage (websocket .CloseNormalClosure , "" ))
171
+ time .Sleep (closeGracePeriod )
172
+ ws .Close ()
156
173
157
174
// No point returning an error - we've already upgraded to web sockets, so we can't use the HTTP response now
158
175
return nil
159
176
}
160
177
161
178
res := KeyCode {}
162
179
json .Unmarshal (r , & res )
163
-
164
180
if res .Cols == 0 {
165
181
slice := make ([]byte , 1 )
166
182
slice [0 ] = 0
@@ -177,11 +193,6 @@ func (k *KubeTerminal) Start(c echo.Context) error {
177
193
wsConn .WriteMessage (websocket .TextMessage , slice )
178
194
}
179
195
}
180
-
181
- // Cleanup
182
- log .Error ("Kubernetes Terminal is cleaning up" )
183
-
184
- return k .cleanupPodAndSecret (podData )
185
196
}
186
197
187
198
func pumpStdout (ws * websocket.Conn , source * websocket.Conn , done chan bool ) {
@@ -202,3 +213,18 @@ func pumpStdout(ws *websocket.Conn, source *websocket.Conn, done chan bool) {
202
213
}
203
214
}
204
215
}
216
+
217
+ func ping (ws * websocket.Conn , done chan bool ) {
218
+ ticker := time .NewTicker (pingPeriod )
219
+ defer ticker .Stop ()
220
+ for {
221
+ select {
222
+ case <- ticker .C :
223
+ if err := ws .WriteControl (websocket .PingMessage , []byte {}, time .Now ().Add (writeWait )); err != nil {
224
+ log .Errorf ("Web socket ping error: %+v" , err )
225
+ }
226
+ case <- done :
227
+ return
228
+ }
229
+ }
230
+ }
0 commit comments