const recentActivityDiv = document.getElementById("recent-activity-body");
const recentActivityDivLimit = 1000;
function addRecentActivityEntry(event, content, timestamp) {
const eventRow = document.createElement('tr');
eventRow.className = "event";
eventRow.innerHTML = `
${event} |
${content} |
${timestamp} | `;
recentActivityDiv.prepend(eventRow);
animateElementAddition(eventRow);
const childCount = recentActivityDiv.childElementCount;
if (childCount > recentActivityDivLimit) {
for (let index = recentActivityDivLimit; index < childCount; index++) {
recentActivityDiv.removeChild(
recentActivityDiv.children[index]
)
}
}
}
function animateElementAddition(e, color = "lightblue") {
e.style.backgroundColor = color;
e.style.lineHeight = "0px";
e.style.overflow = "hidden";
e.style.opacity = 0;
setTimeout(function() {
e.style.transition =
"background-color 1.2s ease-out, line-height 6s, opacity 0.3s ease-out";
e.style.backgroundColor = "";
e.style.lineHeight = "";
e.style.opacity = "";
}, 50);
}
function animateElementChange(e, color = "lightblue") {
e.style.transition = "background-color 0.6s ease-out";
setTimeout(function() {
e.style.backgroundColor = color;
setTimeout(function() {
e.style.backgroundColor = "";
}, 600);
}, 50);
}
function animateElementAndRemove(e, color) {
// TODO: This needs more work, as it needs to fit in with keeping the
// related bits that reference the number of builds up to date
e.remove();
// e.style.borderWidth = "20px;";
// e.style.borderStyle = "solid";
// e.style.borderColor = color;
// e.style.transition = "border 10s";
// e.addEventListener("transitionend", function() {
// e.remove();
// }, {once: true});
// setTimeout(function() {
// e.style.borderColor = "";
// }, 1000);
}
function buildDetailsFromDom(id) {
return document.getElementById("build-" + id).dataset;
}
function agentDetailsFromDom(id) {
return document.getElementById("agent-" + id).dataset;
}
function updateAgentBuildRelatedElements(agentId) {
const agentBuildsElement = document.getElementById(
"agent-" + agentId + "-builds"
);
const childCount = agentBuildsElement.childElementCount;
const noAllocatedBuildsElement = document.getElementById(
"agent-" + agentId + "-no-allocated-builds"
);
const plusXBuildsElement = document.getElementById(
"agent-" + agentId + "-plus-x-builds"
);
if (childCount == 0) {
noAllocatedBuildsElement.style.display = "block";
plusXBuildsElement.style.display = "none";
animateElementChange(noAllocatedBuildsElement);
} else {
for (let index = 0; index < Math.min(childCount, 4); index++) {
agentBuildsElement.children[index].style.display = "block";
}
if (childCount > 4) {
noAllocatedBuildsElement.style.display = "none";
plusXBuildsElement.style.display = "block";
const otherBuildCount = childCount - 4;
if (otherBuildCount == 1) {
plusXBuildsElement.textContent = "Plus 1 other build";
} else {
plusXBuildsElement.textContent =
`Plus ${otherBuildCount} other builds`;
}
animateElementChange(plusXBuildsElement);
} else {
noAllocatedBuildsElement.style.display = "none";
plusXBuildsElement.style.display = "none";
}
}
}
function buildSubmittedHandler(e) {
const data = JSON.parse(e.data);
console.log("build-submitted", data);
addRecentActivityEntry(
"Build submitted",
`${data.derivation.slice(44, -4)}`,
data.timestamp
);
}
function buildCanceledHandler(e) {
console.log("build-canceled", e);
addRecentActivityEntry(
"Build canceled",
`${data.derivation.slice(44, -4)}, tags: ${data.tags}`,
data.timestamp
);
}
function buildSuccessHandler(e) {
const data = JSON.parse(e.data);
console.log("build-success", data);
const buildElement = document.getElementById(
"build-" + data.build_id
);
const buildDetails = buildDetailsFromDom(data.build_id);
addRecentActivityEntry(
"Build success",
`${buildDetails.derivation.slice(44, -4)}`,
data.timestamp
);
animateElementAndRemove(buildElement, "green");
updateAgentBuildRelatedElements(data.agent_id);
}
function buildFailureHandler(e) {
const data = JSON.parse(e.data);
console.log("build-failure", data);
const buildElement = document.getElementById(
"build-" + data.build_id
);
animateElementAndRemove(buildElement, "red");
updateAgentBuildRelatedElements(data.agent_id);
}
function buildStartedHandler(e) {
const data = JSON.parse(e.data);
console.log("build-started", data);
const buildDetails = buildDetailsFromDom(data.build_id);
const agentDetails = agentDetailsFromDom(data.agent_id);
addRecentActivityEntry(
"Build started",
`${buildDetails.derivation.slice(44, -4)}, agent: ${agentDetails.name}`,
data.timestamp
);
}
function buildSetupFailureHandler(e) {
const data = JSON.parse(e.data);
console.log("build-setup-failure", data);
const buildDetails = buildDetailsFromDom(data.build_id);
const agentDetails = agentDetailsFromDom(data.agent_id);
addRecentActivityEntry(
"Build setup failure",
`${buildDetails.derivation.slice(44, -4)}, agent: ${agentDetails.name}`,
data.timestamp
);
const buildElement = document.getElementById(
"build-" + data.build_id
);
animateElementAndRemove(buildElement, "red");
updateAgentBuildRelatedElements(data.agent_id);
}
function allocationPlanUpdateHandler(e) {
const data = JSON.parse(e.data);
console.log("allocation-plan-update", data);
for (const agentId in data.allocation_plan_counts) {
const planSizeElement = document.getElementById(
"agent-" + agentId + "-plan-size"
);
const updatedValue = data.allocation_plan_counts[agentId];
if (updatedValue != planSizeElement.dataset.value) {
planSizeElement.textContent = `Plan size: ${data.allocation_plan_counts[agentId]}`
planSizeElement.dataset.value = updatedValue;
animateElementChange(planSizeElement);
}
}
}
function agentBuildsAllocatedHandler(e) {
const data = JSON.parse(e.data);
console.log("agent-builds-allocated", data);
const agentBuildsElement = document.getElementById(
"agent-" + data.agent_id + "-builds"
);
var newElements = [];
data.builds.forEach(function(build) {
const buildElement = document.getElementById("build-" + build.uuid);
if (!buildElement) {
const buildElement = document.createElement('div');
buildElement.id = "build-" + build.uuid;
buildElement.className = "build";
buildElement.dataset.derivation = build.derivation_name;
const drv = build.derivation_name.slice(44, -4);
buildElement.innerHTML = `
${drv}
`;
build.tags.sort(function(a, b) {
return a.key > b.key;
}).forEach(function(tag) {
var val;
if (tag.value.length == 40) {
val = tag.value.substring(0, 8);
} else {
val = tag.value;
}
buildElement.innerHTML += `
${tag.key}: ${val}
`;
});
if (agentBuildsElement.childElementCount > 4) {
buildElement.style.display = "none";
}
agentBuildsElement.append(buildElement);
newElements.push(buildElement);
}
});
if (agentBuildsElement.childElementCount > 4) {
document.getElementById(
"agent-" + data.agent_id + "-no-allocated-builds"
).style.display = "none";
const plusBuilds = document.getElementById(
"agent-" + data.agent_id + "-plus-x-builds"
)
plusBuilds.style.display = "block";
const otherBuildCount = agentBuildsElement.childElementCount - 4;
if (otherBuildCount == 1) {
plusBuilds.textContent = "Plus 1 other build";
} else {
plusBuilds.textContent =
`Plus ${otherBuildCount} other builds`;
}
animateElementChange(plusBuilds);
}
for (const element of newElements) {
setTimeout(
function() { animateElementChange(element); },
50
);
}
}
function agentStatusUpdateHandler(e) {
const data = JSON.parse(e.data);
console.log("agent-status-update", data);
const loadElement = document.getElementById(
"agent-" + data.agent_id + "-load"
);
const loadPercentage = Math.round((100 * data.load_average["1"]) / data.processor_count);
loadElement.textContent = `Load: ${loadPercentage}%`;
var loadClass;
if (loadPercentage < 150) {
loadClass = "agent-load-normal";
} else if (loadPercentage < 250) {
loadClass = "agent-load-medium";
} else {
loadClass = "agent-load-high";
}
loadElement.className = `agent-load ${loadClass}`;
animateElementChange(loadElement);
}
const lastEventId = document.getElementById("main").dataset.stateid;
const evtSource = new EventSource(`/events?last_event_id=${lastEventId}`);
evtSource.addEventListener("build-submitted", buildSubmittedHandler);
evtSource.addEventListener("build-success", buildSuccessHandler);
evtSource.addEventListener("build-failure", buildFailureHandler);
evtSource.addEventListener("build-started", buildStartedHandler);
evtSource.addEventListener("build-setup-failure", buildSetupFailureHandler);
evtSource.addEventListener("allocation-plan-update", allocationPlanUpdateHandler);
evtSource.addEventListener("agent-builds-allocated", agentBuildsAllocatedHandler);
evtSource.addEventListener("agent-status-update", agentStatusUpdateHandler);
console.log("starting listening");