UNPKG

@adpt/core

Version:
211 lines 7.91 kB
"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