{"size": 14262, "ext": "js", "max_stars_repo_path": "dist/node_modules/@pulumi/hcloud/node_modules/@pulumi/pulumi/cmd/dynamic-provider/index.js", "max_stars_repo_name": "akoenig/pulumi-hcloud", "max_stars_repo_stars_event_min_datetime": "2019-04-27T00:52:33.000Z", "max_stars_repo_stars_event_max_datetime": "2020-04-28T18:57:11.000Z", "max_issues_repo_path": "dist/node_modules/@pulumi/hcloud/node_modules/@pulumi/pulumi/cmd/dynamic-provider/index.js", "max_issues_repo_name": "akoenig/pulumi-hcloud", "max_issues_repo_issues_event_min_datetime": "2019-10-02T15:48:55.000Z", "max_issues_repo_issues_event_max_datetime": "2019-10-02T15:48:55.000Z", "max_forks_repo_path": "dist/node_modules/@pulumi/hcloud/node_modules/@pulumi/pulumi/cmd/dynamic-provider/index.js", "max_forks_repo_name": "akoenig/pulumi-hcloud", "max_forks_repo_forks_event_min_datetime": "2019-12-17T15:45:22.000Z", "max_forks_repo_forks_event_max_datetime": "2019-12-17T15:45:22.000Z"}
{"max_stars_count": 10, "max_issues_count": 1, "max_forks_count": 1, "avg_line_length": 44.9905362776, "max_line_length": 151, "alphanum_fraction": 0.619828916}
"use strict";
// Copyright 2016-2018, Pulumi Corporation.
//
// 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.
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const runtime = require("../../runtime");
const version_1 = require("../../version");
const requireFromString = require("require-from-string");
const grpc = require("grpc");
const anyproto = require("google-protobuf/google/protobuf/any_pb.js");
const emptyproto = require("google-protobuf/google/protobuf/empty_pb.js");
const structproto = require("google-protobuf/google/protobuf/struct_pb.js");
const provproto = require("../../proto/provider_pb.js");
const provrpc = require("../../proto/provider_grpc_pb.js");
const plugproto = require("../../proto/plugin_pb.js");
const statusproto = require("../../proto/status_pb.js");
const providerKey = "__provider";
function getProvider(props) {
    // TODO[pulumi/pulumi#414]: investigate replacing requireFromString with eval
    return requireFromString(props[providerKey]).handler();
}
// Each of the *RPC functions below implements a single method of the resource provider gRPC interface. The CRUD
// functions--checkRPC, diffRPC, createRPC, updateRPC, and deleteRPC--all operate in a similar fashion:
//     1. Deserialize the dyanmic provider for the resource on which the function is operating
//     2. Call the dynamic provider's corresponding {check,diff,create,update,delete} method
//     3. Convert and return the results
// In all cases, the dynamic provider is available in its serialized form as a property of the resource;
// getProvider` is responsible for handling its deserialization. In the case of diffRPC, if the provider itself
// has changed, `diff` reports that the resource requires replacement and does not delegate to the dynamic provider.
// This allows the creation of the replacement resource to use the new provider while the deletion of the old
// resource uses the provider with which it was created.
function cancelRPC(call, callback) {
    callback(undefined, new emptyproto.Empty());
}
function configureRPC(call, callback) {
    callback(undefined, new emptyproto.Empty());
}
function invokeRPC(call, callback) {
    return __awaiter(this, void 0, void 0, function* () {
        const req = call.request;
        // TODO[pulumi/pulumi#406]: implement this.
        callback(new Error(`unknown function ${req.getTok()}`), undefined);
    });
}
function checkRPC(call, callback) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const req = call.request;
            const resp = new provproto.CheckResponse();
            const olds = req.getOlds().toJavaScript();
            const news = req.getNews().toJavaScript();
            const provider = getProvider(news[providerKey] === runtime.unknownValue ? olds : news);
            let inputs = news;
            let failures = [];
            if (provider.check) {
                const result = yield provider.check(olds, news);
                if (result.inputs) {
                    inputs = result.inputs;
                }
                if (result.failures) {
                    failures = result.failures;
                }
            }
            else {
                // If no check method was provided, propagate the new inputs as-is.
                inputs = news;
            }
            inputs[providerKey] = news[providerKey];
            resp.setInputs(structproto.Struct.fromJavaScript(inputs));
            if (failures.length !== 0) {
                const failureList = [];
                for (const f of failures) {
                    const failure = new provproto.CheckFailure();
                    failure.setProperty(f.property);
                    failure.setReason(f.reason);
                    failureList.push(failure);
                }
                resp.setFailuresList(failureList);
            }
            callback(undefined, resp);
        }
        catch (e) {
            console.error(`${e}: ${e.stack}`);
            callback(e, undefined);
        }
    });
}
function checkConfigRPC(call, callback) {
    callback(undefined, new emptyproto.Empty());
}
function diffRPC(call, callback) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const req = call.request;
            const resp = new provproto.DiffResponse();
            // Note that we do not take any special action if the provider has changed. This allows a user to iterate on a
            // dynamic provider's implementation. This does require some care on the part of the user: each iteration of a
            // dynamic provider's implementation must be able to handle all state produced by prior iterations.
            //
            // Prior versions of the dynamic provider required that a dynamic resource be replaced any time its provider
            // implementation changed. This made iteration painful, especially if the dynamic resource was managing a
            // physical resource--in this case, the physical resource would be unnecessarily deleted and recreated each
            // time the provider was updated.
            const olds = req.getOlds().toJavaScript();
            const news = req.getNews().toJavaScript();
            const provider = getProvider(news[providerKey] === runtime.unknownValue ? olds : news);
            if (provider.diff) {
                const result = yield provider.diff(req.getId(), olds, news);
                if (result.changes === true) {
                    resp.setChanges(provproto.DiffResponse.DiffChanges.DIFF_SOME);
                }
                else if (result.changes === false) {
                    resp.setChanges(provproto.DiffResponse.DiffChanges.DIFF_NONE);
                }
                else {
                    resp.setChanges(provproto.DiffResponse.DiffChanges.DIFF_UNKNOWN);
                }
                if (result.replaces && result.replaces.length !== 0) {
                    resp.setReplacesList(result.replaces);
                }
                if (result.deleteBeforeReplace) {
                    resp.setDeletebeforereplace(result.deleteBeforeReplace);
                }
            }
            callback(undefined, resp);
        }
        catch (e) {
            console.error(`${e}: ${e.stack}`);
            callback(e, undefined);
        }
    });
}
function diffConfigRPC(call, callback) {
    callback(undefined, new emptyproto.Empty());
}
function createRPC(call, callback) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const req = call.request;
            const resp = new provproto.CreateResponse();
            const props = req.getProperties().toJavaScript();
            const provider = getProvider(props);
            const result = yield provider.create(props);
            const resultProps = resultIncludingProvider(result.outs, props);
            resp.setId(result.id);
            resp.setProperties(structproto.Struct.fromJavaScript(resultProps));
            callback(undefined, resp);
        }
        catch (e) {
            return callback(grpcResponseFromError(e));
        }
    });
}
function readRPC(call, callback) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const req = call.request;
            const resp = new provproto.ReadResponse();
            const id = req.getId();
            const props = req.getProperties().toJavaScript();
            const provider = getProvider(props);
            if (provider.read) {
                // If there's a read function, consult the provider. Ensure to propagate the special __provider
                // value too, so that the provider's CRUD operations continue to function after a refresh.
                const result = yield provider.read(id, props);
                resp.setId(result.id);
                const resultProps = resultIncludingProvider(result.props, props);
                resp.setProperties(structproto.Struct.fromJavaScript(resultProps));
            }
            else {
                // In the event of a missing read, simply return back the input state.
                resp.setId(id);
                resp.setProperties(req.getProperties());
            }
            callback(undefined, resp);
        }
        catch (e) {
            console.error(`${e}: ${e.stack}`);
            callback(e, undefined);
        }
    });
}
function updateRPC(call, callback) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const req = call.request;
            const resp = new provproto.UpdateResponse();
            const olds = req.getOlds().toJavaScript();
            const news = req.getNews().toJavaScript();
            let result = {};
            const provider = getProvider(news);
            if (provider.update) {
                result = (yield provider.update(req.getId(), olds, news)) || {};
            }
            const resultProps = resultIncludingProvider(result.outs, news);
            resp.setProperties(structproto.Struct.fromJavaScript(resultProps));
            callback(undefined, resp);
        }
        catch (e) {
            return callback(grpcResponseFromError(e));
        }
    });
}
function deleteRPC(call, callback) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const req = call.request;
            const props = req.getProperties().toJavaScript();
            const provider = yield getProvider(props);
            if (provider.delete) {
                yield provider.delete(req.getId(), props);
            }
            callback(undefined, new emptyproto.Empty());
        }
        catch (e) {
            console.error(`${e}: ${e.stack}`);
            callback(e, undefined);
        }
    });
}
function getPluginInfoRPC(call, callback) {
    return __awaiter(this, void 0, void 0, function* () {
        const resp = new plugproto.PluginInfo();
        resp.setVersion(version_1.version);
        callback(undefined, resp);
    });
}
function resultIncludingProvider(result, props) {
    return Object.assign(result || {}, {
        [providerKey]: props[providerKey],
    });
}
// grpcResponseFromError creates a gRPC response representing an error from a dynamic provider's
// resource. This is typically either a creation error, in which the API server has (virtually)
// rejected the resource, or an initialization error, where the API server has accepted the
// resource, but it failed to initialize (e.g., the app code is continually crashing and the
// resource has failed to become alive).
function grpcResponseFromError(e) {
    // Create response object.
    const resp = new statusproto.Status();
    resp.setCode(grpc.status.UNKNOWN);
    resp.setMessage(e.message);
    const metadata = new grpc.Metadata();
    if (e.id) {
        // Object created successfully, but failed to initialize. Pack initialization failure into
        // details.
        const detail = new provproto.ErrorResourceInitFailed();
        detail.setId(e.id);
        detail.setProperties(structproto.Struct.fromJavaScript(e.properties || {}));
        detail.setReasonsList(e.reasons || []);
        const details = new anyproto.Any();
        details.pack(detail.serializeBinary(), "pulumirpc.ErrorResourceInitFailed");
        // Add details to metadata.
        resp.addDetails(details);
        // NOTE: `grpc-status-details-bin` is a magic field that allows us to send structured
        // protobuf data as an error back through gRPC. This notion of details is a first-class in
        // the Go gRPC implementation, and the nodejs implementation has not quite caught up to it,
        // which is why it's cumbersome here.
        metadata.add("grpc-status-details-bin", Buffer.from(resp.serializeBinary()));
    }
    return {
        code: grpc.status.UNKNOWN,
        message: e.message,
        metadata: metadata,
    };
}
function main(args) {
    // The program requires a single argument: the address of the RPC endpoint for the engine.  It
    // optionally also takes a second argument, a reference back to the engine, but this may be missing.
    if (args.length === 0) {
        console.error("fatal: Missing <engine> address");
        process.exit(-1);
        return;
    }
    const engineAddr = args[0];
    // Finally connect up the gRPC client/server and listen for incoming requests.
    const server = new grpc.Server();
    server.addService(provrpc.ResourceProviderService, {
        cancel: cancelRPC,
        configure: configureRPC,
        invoke: invokeRPC,
        check: checkRPC,
        checkConfig: checkConfigRPC,
        diff: diffRPC,
        diffConfig: diffConfigRPC,
        create: createRPC,
        read: readRPC,
        update: updateRPC,
        delete: deleteRPC,
        getPluginInfo: getPluginInfoRPC,
    });
    const port = server.bind(`0.0.0.0:0`, grpc.ServerCredentials.createInsecure());
    server.start();
    // Emit the address so the monitor can read it to connect.  The gRPC server will keep the message loop alive.
    console.log(port);
}
exports.main = main;
main(process.argv.slice(2));
