@adpt/core
Version:
AdaptJS core library
211 lines • 7.91 kB
JavaScript
"use strict";
/*
* Copyright 2019 Unbounded Systems, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@adpt/utils");
const util_1 = require("util");
const error_1 = require("../error");
const jsx_1 = require("../jsx");
const deploy_types_1 = require("./deploy_types");
async function createStatusTracker(options) {
const tracker = new StatusTrackerImpl(options);
await tracker.initDeploymentStatus();
return tracker;
}
exports.createStatusTracker = createStatusTracker;
class StatusTrackerImpl {
constructor(options) {
this.deployment = options.deployment;
this.deployOpID = options.deployOpID;
this.dryRun = options.dryRun;
this.goalStatus = options.goalStatus;
this.nodeStatus = this.newStatus();
this.nodeStatus.Waiting = options.nodes.length;
this.primStatus = this.newStatus();
this.nonPrimStatus = this.newStatus();
this.taskMap = new Map();
const tGroup = options.taskObserver.childGroup({ serial: false });
this.statMap = new Map(options.nodes.map((n) => {
if (n.element) {
if (jsx_1.isFinalDomElement(n.element))
this.primStatus.Waiting++;
else
this.nonPrimStatus.Waiting++;
if (shouldTrackStatus(n)) {
const id = n.element.id;
const trivial = isTrivial(n);
const tasks = tGroup.add({ [id]: n.element.componentName }, { createOnly: false, trivial });
this.taskMap.set(n, tasks[id]);
}
}
return [n, deploy_types_1.DeployStatusExt.Waiting];
}));
}
async initDeploymentStatus() {
if (this.dryRun)
return;
this.stepID = await this.deployment.newStepID(this.deployOpID);
const deploymentDeployStatus = deploy_types_1.goalToInProgress(this.goalStatus);
const elementStatus = {};
this.statMap.forEach((extStatus, n) => {
const el = n.element;
if (el == null)
return;
elementStatus[el.id] = { deployStatus: deploy_types_1.toDeployStatus(extStatus) };
});
await this.deployment.status(this.stepID, {
deployStatus: deploymentDeployStatus,
goalStatus: this.goalStatus,
elementStatus,
});
}
get(n) {
const stat = this.statMap.get(n);
if (stat === undefined) {
throw new error_1.InternalError(`Unrecognized node: ${util_1.inspect(n)}`);
}
return stat;
}
// Returns true when status was changed, false when node was already
// in a final state or already in the requested state.
async set(n, statExt, err, description) {
const oldStat = this.get(n);
if (statExt === oldStat || deploy_types_1.isFinalStatus(oldStat))
return false;
const deployStatus = deploy_types_1.toDeployStatus(statExt);
this.statMap.set(n, statExt);
this.updateCount(n, deploy_types_1.toDeployStatus(oldStat), deployStatus);
this.updateTask(n, oldStat, deployStatus, err, description);
await this.writeStatus(n, err);
return true;
}
isFinal(n) {
return deploy_types_1.isFinalStatus(this.get(n));
}
isActive(n) {
return !deploy_types_1.isFinalStatus(this.get(n));
}
output(n, s) {
const task = this.getTask(n);
if (!task)
return;
task.updateStatus(s);
}
async complete(stateChanged) {
if (this.nodeStatus.Initial > 0) {
throw new error_1.InternalError(`Nodes should not be in Initial state ${JSON.stringify(this.nodeStatus)}`);
}
const atGoal = this.nodeStatus.Deployed + this.nodeStatus.Destroyed;
const deploymentStatus = (this.nodeStatus.Failed > 0) ? deploy_types_1.DeployStatus.Failed :
(atGoal === this.statMap.size) ? this.goalStatus :
stateChanged ? deploy_types_1.DeployOpStatus.StateChanged :
deploy_types_1.goalToInProgress(this.goalStatus);
if (this.stepID != null) {
await this.deployment.status(this.stepID, { deployStatus: deploymentStatus });
}
return {
deploymentStatus,
nodeStatus: this.nodeStatus,
nonPrimStatus: this.nonPrimStatus,
primStatus: this.primStatus,
stateChanged,
};
}
debug(getId) {
const entries = [...this.statMap]
.map(([n, stat]) => ` ${getId(n).padEnd(20)} => ${stat}`)
.join("\n");
return `StatusTracker {\n${entries}\n}`;
}
getTask(n) {
if (!shouldTrackStatus(n))
return undefined;
const task = this.taskMap.get(n);
if (!task) {
throw new error_1.InternalError(`No task observer found for node (${n.element && n.element.id})`);
}
return task;
}
async writeStatus(n, err) {
if (n.element == null || this.stepID == null)
return;
const statExt = this.get(n);
const deployStatus = deploy_types_1.toDeployStatus(statExt);
const s = { deployStatus };
if (err)
s.error = err.message;
await this.deployment.elementStatus(this.stepID, { [n.element.id]: s });
}
updateTask(n, oldStat, newStat, err, description) {
const task = this.getTask(n);
if (!task)
return;
if (description)
task.description = description;
if (err)
return task.failed(err);
if (this.dryRun) {
if (deploy_types_1.isGoalStatus(newStat)) {
if (task.state === utils_1.TaskState.Created ||
task.state === utils_1.TaskState.Started)
task.skipped();
}
}
else {
if (deploy_types_1.isInProgress(newStat) && !deploy_types_1.isProxying(oldStat)) {
if (task.state === utils_1.TaskState.Created)
task.started();
}
else if (deploy_types_1.isGoalStatus(newStat)) {
if (task.state === utils_1.TaskState.Started)
task.complete();
}
}
}
updateCount(n, oldStat, newStat) {
this.nodeStatus[oldStat]--;
this.nodeStatus[newStat]++;
if (n.element) {
if (jsx_1.isFinalDomElement(n.element)) {
this.primStatus[oldStat]--;
this.primStatus[newStat]++;
}
else {
this.nonPrimStatus[oldStat]--;
this.nonPrimStatus[newStat]++;
}
}
}
newStatus() {
const stat = {};
Object.keys(deploy_types_1.DeployStatus).forEach((k) => stat[k] = 0);
return stat;
}
}
exports.StatusTrackerImpl = StatusTrackerImpl;
function shouldTrackStatus(n) {
return n.element != null;
}
exports.shouldTrackStatus = shouldTrackStatus;
function isTrivial(n) {
if (n.waitInfo.activeAction)
return false;
if (n.element)
return n.element.deployedWhenIsTrivial;
return true;
}
exports.isTrivial = isTrivial;
//# sourceMappingURL=status_tracker.js.map