UNPKG

@adpt/cloud

Version:
227 lines 8.49 kB
"use strict"; /* * 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