diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 61c27b2f9..87b69c2a1 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -8,3 +8,6 @@ - [sdk/{go,python,nodejs}] - Rehydrate provider resources in `Construct`. [#7624](https://github.com/pulumi/pulumi/pull/7624) + +- [sdk/nodejs] - Fix `pulumi up --logflow` causing Node multi-lang components to hang + [#7644](https://github.com/pulumi/pulumi/pull/) diff --git a/sdk/nodejs/provider/internals.ts b/sdk/nodejs/provider/internals.ts new file mode 100644 index 000000000..d35063781 --- /dev/null +++ b/sdk/nodejs/provider/internals.ts @@ -0,0 +1,50 @@ +// Copyright 2016-2021, 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. + +// Functionality exported for unit testing. + +// The parsing here is approximate for the moment. +// +// When Pulumi CLI decides how to structure command line arguments for +// plugins that will be parsed with this function, it uses the +// following code: +// +// https://github.com/pulumi/pulumi/blob/master/sdk/go/common/resource/plugin/plugin.go#L281 +// +// The code can prepend `--logtostderr` and verbosity e.g. `-v=9` +// arguments. We ignore these for the moment. +export function parseArgs(args: string[]): ({engineAddress: string} | undefined) { + const cleanArgs = []; + + for (let i = 0; i < args.length; i++) { + const v = args[i]; + if (v === "--logtostderr") { + continue; + } + if (v.startsWith("-v=")) { + continue; + } + if (v === "--tracing") { + i += 1; + continue; + } + cleanArgs.push(v); + } + + if (cleanArgs.length === 0) { + return undefined; + } + + return {engineAddress: cleanArgs[0]}; +} diff --git a/sdk/nodejs/provider/server.ts b/sdk/nodejs/provider/server.ts index 800611bad..66c4af11c 100644 --- a/sdk/nodejs/provider/server.ts +++ b/sdk/nodejs/provider/server.ts @@ -24,6 +24,7 @@ import { Inputs, Output, output } from "../output"; import * as resource from "../resource"; import * as runtime from "../runtime"; import { version } from "../version"; +import { parseArgs } from "./internals"; const requireFromString = require("require-from-string"); const anyproto = require("google-protobuf/google/protobuf/any_pb.js"); @@ -570,14 +571,16 @@ export async function main(provider: Provider, args: string[]) { } }); + const parsedArgs = parseArgs(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) { + if (parsedArgs === undefined) { console.error("fatal: Missing address"); process.exit(-1); return; } - const engineAddr: string = args[0]; + const engineAddr: string = parsedArgs.engineAddress; // Finally connect up the gRPC client/server and listen for incoming requests. const server = new grpc.Server({ diff --git a/sdk/nodejs/tests/provider.spec.ts b/sdk/nodejs/tests/provider.spec.ts new file mode 100644 index 000000000..110cd2bc5 --- /dev/null +++ b/sdk/nodejs/tests/provider.spec.ts @@ -0,0 +1,29 @@ +// Copyright 2016-2021, 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. + +import * as assert from "assert"; +import { asyncTest } from "./util"; + +import * as internals from "../provider/internals"; + +describe("provider", () => { + it("parses arguments generated by --logflow", asyncTest(async () => { + const parsedArgs = internals.parseArgs(["--logtostderr", "-v=9", "--tracing", "127.0.0.1:6007", "127.0.0.1:12345"]); + if (parsedArgs !== undefined) { + assert.strictEqual("127.0.0.1:12345", parsedArgs.engineAddress); + } else { + assert.fail("failed to parse"); + } + })); +}); diff --git a/sdk/nodejs/tsconfig.json b/sdk/nodejs/tsconfig.json index 4df9cdd6d..33369f709 100644 --- a/sdk/nodejs/tsconfig.json +++ b/sdk/nodejs/tsconfig.json @@ -37,6 +37,7 @@ "provider/index.ts", "provider/provider.ts", + "provider/internals.ts", "provider/server.ts", "runtime/index.ts", @@ -84,6 +85,7 @@ "tests/options.spec.ts", "tests/output.spec.ts", "tests/resource.spec.ts", + "tests/provider.spec.ts", "tests/testmode.spec.ts", "tests/unwrap.spec.ts", "tests/util.ts",