Compare commits

...

2 Commits

Author SHA1 Message Date
Raven Scott
5e8c085446 streamline 2024-12-02 05:05:43 -05:00
Raven Scott
55d502c5f4 streamline stats 2024-12-02 05:05:42 -05:00
3 changed files with 90 additions and 97 deletions

11
app.js
View File

@ -64,7 +64,7 @@ function startStatsInterval() {
if (window.activePeer) { if (window.activePeer) {
const now = Date.now(); const now = Date.now();
if (now - lastStatsUpdate >= 500) { // Ensure at least 500ms between updates if (now - lastStatsUpdate >= 500) { // Ensure at least 500ms between updates
sendCommand('stats', {}); // Adjust command if necessary // sendCommand('allStats', {}); // Adjust command if necessary
lastStatsUpdate = now; lastStatsUpdate = now;
} }
} else { } else {
@ -334,12 +334,9 @@ function handlePeerData(data, topicId, peer) {
// Delegate handling based on the response type // Delegate handling based on the response type
switch (response.type) { switch (response.type) {
case 'stats': case 'allStats':
console.log('[INFO] Updating container stats...'); console.log('[INFO] Received aggregated stats for all containers.');
const stats = response.data; response.data.forEach((stats) => updateContainerStats(stats));
stats.ip = stats.ip || 'No IP Assigned'; // Add a fallback for missing IPs
console.log(`[DEBUG] Passing stats to updateContainerStats: ${JSON.stringify(stats, null, 2)}`);
updateContainerStats(stats);
break; break;
case 'containers': case 'containers':

View File

@ -243,4 +243,4 @@ deployForm.addEventListener('submit', async (e) => {
document.addEventListener('DOMContentLoaded', fetchTemplates); document.addEventListener('DOMContentLoaded', fetchTemplates);
// Export required functions // Export required functions
export { fetchTemplates, displayTemplateList, openDeployModal }; export { fetchTemplates, displayTemplateList, openDeployModal };

View File

@ -206,7 +206,9 @@ swarm.on('connection', (peer) => {
await docker.getContainer(parsedData.args.id).start(); await docker.getContainer(parsedData.args.id).start();
response = { success: true, message: `Container ${parsedData.args.id} started` }; response = { success: true, message: `Container ${parsedData.args.id} started` };
break; break;
// case 'allStats':
// await handleallStatsRequest(peer);
// return; // No further response needed
case 'stopContainer': case 'stopContainer':
console.log(`[INFO] Handling 'stopContainer' command for container: ${parsedData.args.id}`); console.log(`[INFO] Handling 'stopContainer' command for container: ${parsedData.args.id}`);
await docker.getContainer(parsedData.args.id).stop(); await docker.getContainer(parsedData.args.id).stop();
@ -528,44 +530,6 @@ docker.listContainers({ all: true }, async (err, containers) => {
ipAddress = networks[0].IPAddress; // Use the first network's IP ipAddress = networks[0].IPAddress; // Use the first network's IP
} }
} }
// Start streaming container stats
container.stats({ stream: true }, (statsErr, stream) => {
if (statsErr) {
console.error(`[ERROR] Failed to get stats for container ${containerInfo.Id}: ${statsErr.message}`);
return;
}
stream.on('data', (data) => {
try {
const stats = JSON.parse(data.toString());
const cpuUsage = calculateCPUPercent(stats);
const memoryUsage = stats.memory_stats.usage || 0; // Default to 0 if undefined
const statsData = {
id: containerInfo.Id,
cpu: cpuUsage,
memory: memoryUsage,
ip: ipAddress, // Use the pre-inspected IP address
};
// Broadcast stats to all connected peers
for (const peer of connectedPeers) {
peer.write(JSON.stringify({ type: 'stats', data: statsData }));
}
} catch (parseErr) {
// console.error(`[ERROR] Failed to parse stats for container ${containerInfo.Id}: ${parseErr.message}`);
}
});
stream.on('error', (streamErr) => {
// console.error(`[ERROR] Stats stream error for container ${containerInfo.Id}: ${streamErr.message}`);
});
stream.on('close', () => {
// console.log(`[INFO] Stats stream closed for container ${containerInfo.Id}`);
});
});
}); });
}); });
}); });
@ -672,63 +636,95 @@ function handleKillTerminal(containerId, peer) {
} }
} }
function streamContainerStats(containerInfo) { async function collectContainerStats(containerStats) {
const currentContainers = await docker.listContainers({ all: true });
const currentIds = currentContainers.map((c) => c.Id);
// Collect stats for all containers, including newly added ones
for (const containerInfo of currentContainers) {
if (!containerStats[containerInfo.Id]) {
console.log(`[INFO] Found new container: ${containerInfo.Names[0]?.replace(/^\//, '')}`);
containerStats[containerInfo.Id] = await initializeContainerStats(containerInfo);
}
}
// Remove containers that no longer exist
Object.keys(containerStats).forEach((id) => {
if (!currentIds.includes(id)) {
console.log(`[INFO] Removing stats tracking for container: ${id}`);
delete containerStats[id];
}
});
return containerStats;
}
async function initializeContainerStats(containerInfo) {
const container = docker.getContainer(containerInfo.Id); const container = docker.getContainer(containerInfo.Id);
// Use the same logic as listContainers to get the IP address // Inspect container for IP address
container.inspect((inspectErr, details) => { let ipAddress = 'No IP Assigned';
let ipAddress = 'No IP Assigned'; // Default IP address fallback try {
const details = await container.inspect();
const networks = details.NetworkSettings?.Networks || {};
ipAddress = Object.values(networks)[0]?.IPAddress || 'No IP Assigned';
} catch (err) {
console.error(`[ERROR] Failed to inspect container ${containerInfo.Id}: ${err.message}`);
}
if (inspectErr) { const statsData = {
console.error(`[ERROR] Failed to inspect container ${containerInfo.Id}: ${inspectErr.message}`); id: containerInfo.Id,
} else if (details.NetworkSettings && details.NetworkSettings.Networks) { name: containerInfo.Names[0]?.replace(/^\//, '') || 'Unknown',
const networks = Object.values(details.NetworkSettings.Networks); cpu: 0,
if (networks.length > 0 && networks[0].IPAddress) { memory: 0,
ipAddress = networks[0].IPAddress; // Retrieve the first network's IP address ip: ipAddress,
};
// Start streaming stats for the container
try {
const statsStream = await container.stats({ stream: true });
statsStream.on('data', (data) => {
try {
const stats = JSON.parse(data.toString());
statsData.cpu = calculateCPUPercent(stats);
statsData.memory = stats.memory_stats.usage || 0;
} catch (err) {
console.error(`[ERROR] Failed to parse stats for container ${containerInfo.Id}: ${err.message}`);
} }
}
// Start streaming container stats
container.stats({ stream: true }, (statsErr, stream) => {
if (statsErr) {
console.error(`[ERROR] Failed to get stats for container ${containerInfo.Id}: ${statsErr.message}`);
return;
}
stream.on('data', (data) => {
try {
const stats = JSON.parse(data.toString());
const cpuUsage = calculateCPUPercent(stats);
const memoryUsage = stats.memory_stats.usage || 0; // Default to 0 if undefined
// Use the extracted IP address in the stats data
const statsData = {
id: containerInfo.Id,
cpu: cpuUsage,
memory: memoryUsage,
ip: ipAddress, // Use the IP address retrieved during inspection
};
// Broadcast stats to all connected peers
for (const peer of connectedPeers) {
peer.write(JSON.stringify({ type: 'stats', data: statsData }));
}
} catch (parseErr) {
console.error(`[ERROR] Failed to parse stats for container ${containerInfo.Id}: ${parseErr.message}`);
}
});
stream.on('error', (streamErr) => {
console.error(`[ERROR] Stats stream error for container ${containerInfo.Id}: ${streamErr.message}`);
});
stream.on('close', () => {
console.log(`[INFO] Stats stream closed for container ${containerInfo.Id}`);
});
}); });
});
statsStream.on('error', (err) => {
console.error(`[ERROR] Stats stream error for container ${containerInfo.Id}: ${err.message}`);
});
statsStream.on('close', () => {
console.log(`[INFO] Stats stream closed for container ${containerInfo.Id}`);
});
} catch (err) {
console.error(`[ERROR] Failed to start stats stream for container ${containerInfo.Id}: ${err.message}`);
}
return statsData;
} }
async function handleStatsBroadcast() {
const containerStats = {};
// Periodically update stats and broadcast
setInterval(async () => {
await collectContainerStats(containerStats);
const aggregatedStats = Object.values(containerStats);
const response = { type: 'allStats', data: aggregatedStats };
for (const peer of connectedPeers) {
peer.write(JSON.stringify(response));
}
}, 1000); // Send stats every 500ms
}
// Start the stats broadcast
handleStatsBroadcast();
// Handle process termination // Handle process termination