Basic cluster functionality

Signed-off-by: Romein van Buren <romein@vburen.nl>
This commit is contained in:
Romein van Buren 2022-07-11 14:21:57 +02:00
parent aace2dd2de
commit b626f23792
Signed by: romein
GPG Key ID: 0EFF8478ADDF6C49
5 changed files with 110 additions and 31 deletions

View File

@ -9,7 +9,7 @@ const { default: resolve } = require('@rollup/plugin-node-resolve');
const svelte = require('rollup-plugin-svelte'); const svelte = require('rollup-plugin-svelte');
const { terser } = require('rollup-plugin-terser'); const { terser } = require('rollup-plugin-terser');
async function build(server) { async function build({ server, settings }) {
const serverDomain = server.settings.domain || 'localhost'; const serverDomain = server.settings.domain || 'localhost';
const serverPort = server.settings.port || 80; const serverPort = server.settings.port || 80;
const serverBase = `${serverDomain}:${serverPort}`; const serverBase = `${serverDomain}:${serverPort}`;
@ -49,6 +49,7 @@ async function build(server) {
preventAssignment: false, preventAssignment: false,
values: { values: {
'__SERVER__': serverBase, '__SERVER__': serverBase,
'__CLUSTERS__': JSON.stringify(settings.clusters),
}, },
}), }),
], ],
@ -62,6 +63,7 @@ async function build(server) {
}); });
return { return {
map: output[0].map.toUrl(),
code: output[0].code, code: output[0].code,
css: cssOutput.css, css: cssOutput.css,
}; };

View File

@ -10,10 +10,15 @@ async function createDashboardSocket(server) {
route: '/statusdashboard/socket', route: '/statusdashboard/socket',
onOpen: async ws => { onOpen: async ws => {
function sendTime() { function sendTime() {
ws.send(JSON.stringify({ try {
cmd: 'time', ws.send(JSON.stringify({
time: new Date().getTime(), cmd: 'time',
})); time: new Date().getTime(),
}));
}
catch {
return;
}
} }
sendTime(); sendTime();
@ -36,13 +41,19 @@ async function createDashboardSocket(server) {
mappedServices[s.id] = { mappedServices[s.id] = {
name: s.name, name: s.name,
lastBeat: lastBeat || {}, lastBeat: lastBeat || {},
cluster: s.cluster,
}; };
} }
ws.send(JSON.stringify({ try {
cmd: 'data', ws.send(JSON.stringify({
data: mappedServices, cmd: 'data',
})); data: mappedServices,
}));
}
catch {
return;
}
} }
sendStatuses(); sendStatuses();
@ -51,7 +62,6 @@ async function createDashboardSocket(server) {
onUpgrade: async () => ({ id: makeId(10) }), onUpgrade: async () => ({ id: makeId(10) }),
onMessage: async (ws, msg) => { onMessage: async (ws, msg) => {
msg = JSON.parse(decoder.decode(msg)); msg = JSON.parse(decoder.decode(msg));
console.log('msg', msg);
if (!msg || !msg.command) { if (!msg || !msg.command) {
return; return;

View File

@ -24,6 +24,7 @@ module.exports = {
'name', 'name',
'state', 'state',
'public', 'public',
'cluster',
'tags', 'tags',
'channels', 'channels',
], ],
@ -95,6 +96,18 @@ module.exports = {
], ],
}, },
cluster: {
label: 'cluster',
fields: [
{ key: 'cluster',
editor: 'select',
placeholder: 'select a cluster for this service...',
visible: !!(settings.clusters && Object.keys(settings.clusters).length),
options: settings.clusters,
},
],
},
tags: { tags: {
label: 'tags', label: 'tags',
fields: [ fields: [

View File

@ -10,7 +10,13 @@
let servicesUp = {}; let servicesUp = {};
let servicesDown = {}; let servicesDown = {};
let servicesUnknown = {}; let servicesUnknown = {};
let servicesInCluster = {};
let servicesInClusterUnknown = {};
const clusters = JSON.parse('__CLUSTERS__');
const clusterKeys = clusters ? Object.keys(clusters) : false;
let currentClusterIndex = -1;
let loading = true; let loading = true;
let lock = false;
onMount(() => { onMount(() => {
const ws = new WebSocket('ws://__SERVER__/statusdashboard/socket'); const ws = new WebSocket('ws://__SERVER__/statusdashboard/socket');
@ -27,6 +33,8 @@
break; break;
case 'data': case 'data':
while (lock) {}
services = data.data; services = data.data;
const ids = Object.keys(services); const ids = Object.keys(services);
@ -60,6 +68,39 @@
} }
} }
}); });
$: if (clusterKeys?.length && !loading) {
function nextCluster() {
lock = true;
currentClusterIndex++;
if (currentClusterIndex >= clusterKeys.length) {
currentClusterIndex = 0;
}
const inClusterTemp = {};
const inClusterTempUnknown = {};
const currentClusterKey = clusterKeys[currentClusterIndex];
for (const [ id, s ] of Object.entries(services)) {
if (s.cluster === currentClusterKey) {
if (!s.lastBeat || !s.lastBeat.date) {
inClusterTempUnknown[id] = s;
}
else {
inClusterTemp[id] = s;
}
}
}
servicesInCluster = inClusterTemp;
servicesInClusterUnknown = inClusterTempUnknown;
lock = false;
}
nextCluster();
setInterval(() => nextCluster, 10_000);
}
</script> </script>
<Settings /> <Settings />
@ -71,8 +112,14 @@
{#if !loading} {#if !loading}
<Tiles services={servicesDown} color="red" value="down" /> <Tiles services={servicesDown} color="red" value="down" />
<Tiles services={servicesUp} color="green" value="up" />
<Tiles services={servicesUnknown} color="grey" value="no data" /> {#if !clusterKeys?.length}
<Tiles services={servicesUp} color="green" value="up" />
<Tiles services={servicesUnknown} color="grey" value="no data" />
{:else}
<Tiles services={servicesInCluster} color="green" value="up" />
<Tiles services={servicesInClusterUnknown} color="grey" value="no data" />
{/if}
{:else} {:else}
loading loading
{/if} {/if}

View File

@ -85,17 +85,11 @@ module.exports = {
}, },
settings: { settings: {
emailSender: { clusters: {
type: 'string', type: 'keys',
label: 'notification sender', label: 'clusters',
description: 'Sender of notifications about service statuses. Format: Name <email@example.com>', description: 'Clusters can be used to catogorise web services into groups.',
default: '', default: {},
},
emailRecipient: {
type: 'array',
label: 'notification recipients',
description: 'Recipients of notifications about service statuses. Format: Name <email@example.com>',
default: [],
}, },
serviceTags: { serviceTags: {
type: 'keys', type: 'keys',
@ -109,10 +103,22 @@ module.exports = {
description: 'Tags that can be assigned to outage messages to categorise them.', description: 'Tags that can be assigned to outage messages to categorise them.',
default: {}, default: {},
}, },
emailSender: {
type: 'string',
label: 'notification sender',
description: 'Sender of notifications about service statuses. Format: Name <email@example.com>',
default: '',
},
emailRecipient: {
type: 'array',
label: 'notification recipients',
description: 'Recipients of notifications about service statuses. Format: Name <email@example.com>',
default: [],
},
draftOutageEntries: { draftOutageEntries: {
type: 'boolean', type: 'boolean',
label: 'draft outage entries', label: 'draft outage entries',
description: 'Automatically draft outage entry when a service is down.', description: 'Automatically draft an outage entry when a service is down?',
default: true, default: true,
}, },
}, },
@ -200,10 +206,8 @@ module.exports = {
{ id: 'startDashboardSocket', { id: 'startDashboardSocket',
event: 'boot', event: 'boot',
order: 100, order: 100,
purpose: 'Start the websocket for the dashboard after server has booted', purpose: 'Start the websocket for the dashboard after boot',
handler: async ({ server }) => { handler: () => createDashboardSocket(server),
createDashboardSocket(server);
},
}, },
{ id: 'autotestOnSave', { id: 'autotestOnSave',
@ -232,7 +236,7 @@ module.exports = {
}, },
], ],
routes: ({ server }) => [ routes: ({ server, settings }) => [
// Get all services // Get all services
{ route: '/webservices', { route: '/webservices',
@ -533,7 +537,7 @@ module.exports = {
handler: async (req, res) => { handler: async (req, res) => {
try { try {
if (!renderedDashboard) { if (!renderedDashboard) {
renderedDashboard = await buildDashboard(server); renderedDashboard = await buildDashboard({ server, settings });
renderedDashboard.globalCss = await readFile( renderedDashboard.globalCss = await readFile(
__dirname + '/gui/dashboard/app.css' __dirname + '/gui/dashboard/app.css'
); );
@ -550,7 +554,10 @@ module.exports = {
<style>${renderedDashboard.css || ''}</style> <style>${renderedDashboard.css || ''}</style>
</head> </head>
<body> <body>
<script>${renderedDashboard.code || ''}</script> <script>
${renderedDashboard.code || ''}
//# sourceMappingURL=${renderedDashboard.map || ''}
</script>
</body> </body>
</html> </html>
`); `);