mirror of
https://github.com/kemko/nomad.git
synced 2026-01-06 10:25:42 +03:00
Add exec heartbeat keepalive (#8759)
This closes #8727, thanks to @jfcantu for the suggestion. The CLI implementation of exec already has a 10-second heartbeat so this mirrors that: https://github.com/hashicorp/nomad/blob/v0.12.3/api/allocations.go#L161-L173
This commit is contained in:
@@ -5,6 +5,7 @@ IMPROVEMENTS:
|
||||
* api: Added node purge SDK functionality. [[GH-8142](https://github.com/hashicorp/nomad/issues/8142)]
|
||||
* csi: Improved the accuracy of plugin `Expected` allocation counts. [[GH-8699](https://github.com/hashicorp/nomad/pull/8699)]
|
||||
* driver/docker: Allow configurable image pull context timeout setting. [[GH-5718](https://github.com/hashicorp/nomad/issues/5718)]
|
||||
* ui: Added exec keepalive heartbeat. [[GH-8759](https://github.com/hashicorp/nomad/pull/8759)]
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ const ANSI_UI_GRAY_400 = '\x1b[38;2;142;150;163m';
|
||||
import base64js from 'base64-js';
|
||||
import { TextDecoderLite, TextEncoderLite } from 'text-encoder-lite';
|
||||
|
||||
export const HEARTBEAT_INTERVAL = 10000; // ten seconds
|
||||
|
||||
export default class ExecSocketXtermAdapter {
|
||||
constructor(terminal, socket, token) {
|
||||
this.terminal = terminal;
|
||||
@@ -12,6 +14,7 @@ export default class ExecSocketXtermAdapter {
|
||||
socket.onopen = () => {
|
||||
this.sendWsHandshake();
|
||||
this.sendTtySize();
|
||||
this.startHeartbeat();
|
||||
|
||||
terminal.onData(data => {
|
||||
this.handleData(data);
|
||||
@@ -28,6 +31,7 @@ export default class ExecSocketXtermAdapter {
|
||||
};
|
||||
|
||||
socket.onclose = () => {
|
||||
this.stopHeartbeat();
|
||||
this.terminal.writeln('');
|
||||
this.terminal.write(ANSI_UI_GRAY_400);
|
||||
this.terminal.writeln('The connection has closed.');
|
||||
@@ -49,6 +53,16 @@ export default class ExecSocketXtermAdapter {
|
||||
this.socket.send(JSON.stringify({ version: 1, auth_token: this.token || '' }));
|
||||
}
|
||||
|
||||
startHeartbeat() {
|
||||
this.heartbeatTimer = setInterval(() => {
|
||||
this.socket.send(JSON.stringify({}));
|
||||
}, HEARTBEAT_INTERVAL);
|
||||
}
|
||||
|
||||
stopHeartbeat() {
|
||||
clearInterval(this.heartbeatTimer);
|
||||
}
|
||||
|
||||
handleData(data) {
|
||||
this.socket.send(JSON.stringify({ stdin: { data: encodeString(data) } }));
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import { module, test } from 'qunit';
|
||||
import { render, settled } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import { Terminal } from 'xterm';
|
||||
import { HEARTBEAT_INTERVAL } from 'nomad-ui/utils/classes/exec-socket-xterm-adapter';
|
||||
import sinon from 'sinon';
|
||||
|
||||
module('Integration | Utility | exec-socket-xterm-adapter', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
@@ -26,6 +28,7 @@ module('Integration | Utility | exec-socket-xterm-adapter', function(hooks) {
|
||||
if (firstMessage) {
|
||||
firstMessage = false;
|
||||
assert.deepEqual(message, JSON.stringify({ version: 1, auth_token: 'mysecrettoken' }));
|
||||
mockSocket.onclose();
|
||||
done();
|
||||
}
|
||||
},
|
||||
@@ -56,6 +59,7 @@ module('Integration | Utility | exec-socket-xterm-adapter', function(hooks) {
|
||||
if (firstMessage) {
|
||||
firstMessage = false;
|
||||
assert.deepEqual(message, JSON.stringify({ version: 1, auth_token: '' }));
|
||||
mockSocket.onclose();
|
||||
done();
|
||||
}
|
||||
},
|
||||
@@ -68,6 +72,40 @@ module('Integration | Utility | exec-socket-xterm-adapter', function(hooks) {
|
||||
await settled();
|
||||
});
|
||||
|
||||
test('a heartbeat is sent periodically', async function(assert) {
|
||||
let done = assert.async();
|
||||
|
||||
const clock = sinon.useFakeTimers({
|
||||
now: new Date(),
|
||||
shouldAdvanceTime: true,
|
||||
});
|
||||
|
||||
let terminal = new Terminal();
|
||||
this.set('terminal', terminal);
|
||||
|
||||
await render(hbs`
|
||||
<ExecTerminal @terminal={{terminal}} />
|
||||
`);
|
||||
|
||||
await settled();
|
||||
|
||||
let mockSocket = new Object({
|
||||
send(message) {
|
||||
if (!message.includes('version') && !message.includes('tty_size')) {
|
||||
assert.deepEqual(message, JSON.stringify({}));
|
||||
clock.restore();
|
||||
mockSocket.onclose();
|
||||
done();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
new ExecSocketXtermAdapter(terminal, mockSocket, null);
|
||||
mockSocket.onopen();
|
||||
await settled();
|
||||
clock.tick(HEARTBEAT_INTERVAL);
|
||||
});
|
||||
|
||||
test('resizing the window passes a resize message through the socket', async function(assert) {
|
||||
let done = assert.async();
|
||||
|
||||
@@ -86,6 +124,7 @@ module('Integration | Utility | exec-socket-xterm-adapter', function(hooks) {
|
||||
message,
|
||||
JSON.stringify({ tty_size: { width: terminal.cols, height: terminal.rows } })
|
||||
);
|
||||
mockSocket.onclose();
|
||||
done();
|
||||
},
|
||||
});
|
||||
@@ -120,6 +159,7 @@ module('Integration | Utility | exec-socket-xterm-adapter', function(hooks) {
|
||||
});
|
||||
|
||||
await settled();
|
||||
mockSocket.onclose();
|
||||
});
|
||||
|
||||
test('stderr frames are ignored', async function(assert) {
|
||||
@@ -155,5 +195,7 @@ module('Integration | Utility | exec-socket-xterm-adapter', function(hooks) {
|
||||
.trim(),
|
||||
'sh-3.2 🥳$'
|
||||
);
|
||||
|
||||
mockSocket.onclose();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user