Compare commits
2 Commits
58335ead6d
...
5e8c085446
Author | SHA1 | Date | |
---|---|---|---|
|
5e8c085446 | ||
|
55d502c5f4 |
11
app.js
11
app.js
@ -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':
|
||||||
|
@ -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 };
|
174
server/server.js
174
server/server.js
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user