@adpt/cloud
Version:
AdaptJS cloud component library
227 lines • 8.49 kB
JavaScript
;
/*
* Copyright 2018-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 tslib_1 = require("tslib");
const core_1 = require("@adpt/core");
const ld = tslib_1.__importStar(require("lodash"));
const utils_1 = require("@adpt/utils");
const action_1 = require("../action");
const kubectl_1 = require("./kubectl");
const manifest_support_1 = require("./manifest_support");
/**
* Type assertion to see if an element is both a {@link k8s.Resource | Resource}
* and a {@link @adpt/core#FinalElement | FinalElement}
*
* @param e - element to test
* @returns `true` if e is both a FinalElement and a {@link k8s.Resource | Resource}, `false` otherwise
*
* @public
*/
function isResourceFinalElement(e) {
return core_1.isFinalDomElement(e) && e.componentType === Resource;
}
exports.isResourceFinalElement = isResourceFinalElement;
/**
* Decides if an existing Resource is scheduled for deletion
*/
function isDeleting(info) {
return (info !== undefined) && ("deletionTimestamp" in info.metadata);
}
/**
* Primitive Component recognized by the k8s plugin to represent resources
* @public
*/
class Resource extends action_1.Action {
constructor(props) {
super(props);
this.deployedWhen = async (goalStatus, helpers) => {
const kind = this.props.kind;
const info = manifest_support_1.getResourceInfo(kind);
const hand = this.props.handle;
if (!info)
throw new Error(`Invalid Resource kind ${kind}`);
if (!hand)
throw new Error("Invalid handle");
try {
const statObj = await helpers.elementStatus(hand);
if (goalStatus === core_1.DeployStatus.Destroyed) {
return core_1.waiting(`Waiting for ${kind} to be destroyed`);
}
return info.deployedWhen(statObj, core_1.DeployStatus.Deployed);
}
catch (err) {
if (ld.isError(err) && err.name === "K8sNotFound") {
if (goalStatus === core_1.DeployStatus.Destroyed)
return true;
return core_1.waiting(`${kind} not present`);
}
throw err;
}
};
}
validate() {
const children = core_1.childrenToArray(this.props.children);
if (!ld.isEmpty(children))
return "Resource elements cannot have children";
//Do other validations of Specs here
}
async shouldAct(op, ctx) {
const kubeconfig = this.props.config.kubeconfig;
const deployID = ctx.buildData.deployID;
const manifest = this.manifest(deployID);
const name = manifest.metadata.name;
const kind = manifest.kind;
const oldManifest = await kubectl_1.kubectlGet({
kubeconfig,
name,
kind
});
switch (op) {
case core_1.ChangeType.create:
case core_1.ChangeType.modify:
case core_1.ChangeType.replace:
if (oldManifest === undefined || isDeleting(oldManifest)) {
return {
act: true,
detail: `Creating ${kind} ${name}`
};
}
else {
const { forbidden, diff } = await kubectl_1.kubectlDiff({
kubeconfig,
manifest
});
const opStr = (forbidden || (op === core_1.ChangeType.replace)) ? "Replacing" : "Updating";
if (((diff !== undefined) && (diff !== "")) || forbidden) {
return {
act: true,
detail: `${opStr} ${kind} ${name}`
};
}
}
return false;
case core_1.ChangeType.delete:
if (oldManifest && !isDeleting(oldManifest)) {
return {
act: true,
detail: `Deleting ${kind} ${name}`
};
}
return false;
case core_1.ChangeType.none:
return false;
}
}
async action(op, ctx) {
const kubeconfig = this.props.config.kubeconfig;
const deployID = ctx.buildData.deployID;
const manifest = this.manifest(deployID);
const name = manifest.metadata.name;
const kind = manifest.kind;
const info = await kubectl_1.kubectlGet({
kubeconfig,
name,
kind
});
let deleted = false;
if (isDeleting(info)) {
//Wait for deleting to complete, else create/modify/apply will fail
await kubectl_1.kubectlOpManifest("delete", {
kubeconfig,
manifest,
wait: true
});
deleted = true;
}
if (op === core_1.ChangeType.modify) {
const { forbidden } = await kubectl_1.kubectlDiff({
kubeconfig,
manifest
});
op = (op === core_1.ChangeType.modify) && forbidden ? core_1.ChangeType.replace : op;
}
switch (op) {
case core_1.ChangeType.create:
case core_1.ChangeType.modify:
await kubectl_1.kubectlOpManifest("apply", {
kubeconfig,
manifest
});
return;
case core_1.ChangeType.replace:
if (!deleted) {
await kubectl_1.kubectlOpManifest("delete", {
kubeconfig,
manifest,
wait: true
});
}
await kubectl_1.kubectlOpManifest("apply", {
kubeconfig,
manifest
});
return;
case core_1.ChangeType.delete:
if (deleted)
return;
await kubectl_1.kubectlOpManifest("delete", {
kubeconfig,
manifest,
wait: false
});
return;
case core_1.ChangeType.none:
return;
}
}
async status(observe, buildData) {
const info = manifest_support_1.getResourceInfo(this.props.kind);
const statusQuery = info && info.statusQuery;
if (!statusQuery)
return { noStatus: "no status query defined for this kind" };
try {
return await statusQuery(this.props, observe, buildData);
}
catch (err) {
// If there's only one GQL error and it's K8sNotFound, throw
// that on up the stack. Otherwise, return a Status object.
const orig = core_1.gqlGetOriginalErrors(err);
if (orig && orig.length === 1 && orig[0].name === "K8sNotFound") {
throw orig[0];
}
return core_1.errorToNoStatus(err);
}
}
mountedElement() {
const handle = this.props.handle;
if (handle === undefined)
throw new utils_1.InternalError("element requested but props.handle undefined");
const elem = handle.mountedOrig;
if (elem == null)
throw new utils_1.InternalError(`element requested but handle.mountedOrig is ${elem}`);
return elem;
}
manifest(deployID) {
if (this.manifest_)
return this.manifest_;
const elem = this.mountedElement();
this.manifest_ = manifest_support_1.makeManifest(elem, deployID);
return this.manifest_;
}
}
exports.Resource = Resource;
//# sourceMappingURL=Resource.js.map