"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(`DaemonSet 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(`DaemonSet child must be a Pod with the isTemplate prop`);
    return child;
}
function makeDaemonSetManifest(props, id, helpers) {
    const { metadata, minReadySeconds, revisionHistoryLimit, selector: userSelector, updateStrategy, children } = props;
    const child = checkChildren(children);
    const { deployID } = helpers;
    const labels = {
        [manifest_support_1.labelKey("daemonset")]: 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,
        revisionHistoryLimit,
        selector,
        updateStrategy,
        template: {
            metadata: podMetadata,
            spec: podSpec
        }
    };
    return {
        metadata,
        spec
    };
}
/**
 * Component for Kubernetes DaemonSet
 *
 * @public
 */
class DaemonSet extends core_1.DeferredComponent {
    constructor(props) {
        checkChildren(props.children);
        super(props);
    }
    build(helpers) {
        const props = this.props;
        const { key, config } = props;
        const manifest = makeDaemonSetManifest(props, common_1.mountedElement(props).id, helpers);
        return core_1.default.createElement(Resource_1.Resource, { key: key, config: config, apiVersion: "apps/v1", kind: "DaemonSet", metadata: manifest.metadata, spec: manifest.spec });
    }
    async status(_observe, buildData) {
        const succ = buildData.successor;
        if (!succ)
            return undefined;
        return succ.status();
    }
}
DaemonSet.defaultProps = {
    metadata: {},
    minReadySeconds: 0,
    revisionHistoryLimit: 10,
    updateStrategy: {
        type: "RollingUpdate",
        rollingUpdate: { maxUnavailable: 1 },
    }
};
exports.DaemonSet = DaemonSet;
function deployedWhen(statusObj) {
    const status = statusObj;
    if (!status || !status.status)
        return core_1.waiting(`Kubernetes cluster returned invalid status for DaemonSet`);
    const { currentNumberScheduled, desiredNumberScheduled, numberAvailable, updatedNumberScheduled, numberReady, } = status.status;
    const ready = lodash_1.minBy([
        { val: numberAvailable, field: "available" },
        { val: numberReady, field: "ready" },
        { val: updatedNumberScheduled, field: "updated" },
        { val: currentNumberScheduled, field: "scheduled" }
    ], (val) => val.val == null ? 0 : val.val);
    if (ready.val >= desiredNumberScheduled)
        return true;
    // FIXME(manishv) we should really also query the status of all the pods related to
    // the DaemonSet and report if any have failed, but only if some are not ready.
    // For example, it would be bad to query the status of 10,000 pods on a 10k node
    // cluster for every status update.  But we'd want to query if there were a pod
    // failure and report the failure.  Not sure if there is a good way to do this though.
    return core_1.waiting(`Waiting for enough pods ${ready.val != null ? ready.val : 0} (${ready.field})/${desiredNumberScheduled} (desired)\n`
        + `Desired: ${desiredNumberScheduled}, Updated: ${updatedNumberScheduled}, Available: ${numberAvailable}, Ready: ${numberReady}, Scheduled: ${currentNumberScheduled}`);
}
/** @internal */
exports.daemonSetResourceInfo = {
    kind: "DaemonSet",
    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) {
                    readAppsV1NamespacedDaemonSet(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.readAppsV1NamespacedDaemonSet;
    },
};
manifest_support_1.registerResourceKind(exports.daemonSetResourceInfo);
//# sourceMappingURL=DaemonSet.js.map