"use strict";
/*
 * Copyright 2020 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 = tslib_1.__importStar(require("@adpt/core"));
const lodash_1 = require("lodash");
const util_1 = require("util");
const common_1 = require("../common");
const common_2 = require("./common");
const k8s_observer_1 = require("./k8s_observer");
const manifest_support_1 = require("./manifest_support");
const Resource_1 = require("./Resource");
const utils_1 = require("./utils");
function checkChildren(children) {
    if ((util_1.isArray(children) && children.length !== 1) || children == null) {
        throw new Error(`Deployment must only have a single Pod as a child, found ${children == null ? 0 : children.length}`);
    }
    const child = util_1.isArray(children) ? children[0] : children;
    if (!utils_1.isResourcePodTemplate(child))
        throw new Error(`Deployment child must be a Pod with the isTemplate prop set`);
    return child;
}
function makeDeploymentManifest(props, id, helpers) {
    const { metadata, minReadySeconds, paused, progressDeadlineSeconds, replicas, revisionHistoryLimit, selector: userSelector, strategy, children } = props;
    const child = checkChildren(children);
    const { deployID } = helpers;
    const labels = {
        [manifest_support_1.labelKey("deployment")]: manifest_support_1.resourceIdToName(props.key, id, deployID),
        [manifest_support_1.labelKey("deployID")]: manifest_support_1.deployIDToLabel(deployID),
    };
    const podMetadataOrig = child.props.metadata || {};
    const podMetadata = Object.assign({}, podMetadataOrig, { labels: Object.assign({}, podMetadataOrig.labels, labels), annotations: Object.assign({}, podMetadataOrig.annotations || {}, { [manifest_support_1.labelKey("deployID")]: helpers.deployID }) });
    const podSpec = child.props.spec;
    const selector = userSelector ? userSelector : { matchLabels: labels };
    const spec = {
        minReadySeconds,
        paused,
        progressDeadlineSeconds,
        replicas,
        revisionHistoryLimit,
        selector,
        strategy,
        template: {
            metadata: podMetadata,
            spec: podSpec
        }
    };
    return {
        metadata,
        spec
    };
}
/**
 * Component for Kubernetes Deployment
 *
 * @public
 */
class Deployment extends core_1.DeferredComponent {
    constructor(props) {
        super(props);
        this.deployedWhen = () => {
            if (core_1.childrenIsEmpty(this.props.children)) {
                return core_1.waiting("No pods specified, or waiting for Pod elements to not build to null");
            }
            return true;
        };
        if (!core_1.childrenIsEmpty(props.children))
            checkChildren(props.children);
    }
    build(helpers) {
        const props = this.props;
        const { key, config } = props;
        if (core_1.childrenIsEmpty(props.children))
            return null;
        const manifest = makeDeploymentManifest(props, common_1.mountedElement(props).id, helpers);
        return core_1.default.createElement(Resource_1.Resource, { key: key, config: config, apiVersion: "apps/v1", kind: "Deployment", metadata: manifest.metadata, spec: manifest.spec });
    }
    async status(_observe, buildData) {
        const succ = buildData.successor;
        if (!succ)
            return undefined;
        return succ.status();
    }
}
Deployment.defaultProps = {
    metadata: {},
    paused: false,
    replicas: 1,
    progressDeadlineSeconds: 600,
    minReadySeconds: 0,
    revisionHistoryLimit: 10,
    strategy: {
        type: "RollingUpdate",
        rollingUpdate: { maxSurge: "25%", maxUnavailable: "25%" },
    }
};
exports.Deployment = Deployment;
function deployedWhen(statusObj) {
    const status = statusObj;
    if (!status || !status.status)
        return core_1.waiting(`Kubernetes cluster returned invalid status for Deployment`);
    const { availableReplicas, readyReplicas, replicas, updatedReplicas, conditions, } = status.status;
    const replicasDesired = status.spec.replicas;
    const ready = lodash_1.minBy([
        { val: availableReplicas, field: "available" },
        { val: readyReplicas, field: "ready" },
        { val: updatedReplicas, field: "updated" },
        { val: replicas, field: "current" }
    ], (val) => val.val == null ? 0 : val.val);
    if (ready.val >= replicasDesired)
        return true;
    const conditionText = conditions && util_1.isArray(conditions)
        ? "\n" + conditions.map((c) => c.message).filter((c) => c.status !== "True").join("\n")
        : "";
    return core_1.waiting(`Waiting for enough pods (${ready.val != null ? ready.val : 0} (${ready.field})/${replicasDesired} (desired)`
        + conditionText);
}
/** @internal */
exports.deploymentResourceInfo = {
    kind: "Deployment",
    deployedWhen,
    statusQuery: async (props, observe, buildData) => {
        const obs = await observe(k8s_observer_1.K8sObserver, core_1.gql `
            query ($name: String!, $kubeconfig: JSON!, $namespace: String!) {
                withKubeconfig(kubeconfig: $kubeconfig) {
                    readAppsV1NamespacedDeployment(name: $name, namespace: $namespace) @all(depth: 100)
                }
            }`, {
            name: manifest_support_1.resourceIdToName(props.key, buildData.id, buildData.deployID),
            kubeconfig: props.config.kubeconfig,
            namespace: common_2.computeNamespaceFromMetadata(props.metadata)
        });
        return obs.withKubeconfig.readAppsV1NamespacedDeployment;
    },
};
manifest_support_1.registerResourceKind(exports.deploymentResourceInfo);
//# sourceMappingURL=Deployment.js.map