2018-11-14 22:33:35 +01:00
|
|
|
// 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.
|
|
|
|
|
2019-02-01 03:08:17 +01:00
|
|
|
import { all, Input, Output, output } from "./output";
|
|
|
|
import { CustomResource, CustomResourceOptions } from "./resource";
|
2019-11-19 21:51:14 +01:00
|
|
|
import * as invoke from "./runtime/invoke";
|
2018-11-14 22:33:35 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Manages a reference to a Pulumi stack. The referenced stack's outputs are available via the
|
|
|
|
* `outputs` property or the `output` method.
|
|
|
|
*/
|
|
|
|
export class StackReference extends CustomResource {
|
|
|
|
/**
|
|
|
|
* The name of the referenced stack.
|
|
|
|
*/
|
2019-09-12 01:21:35 +02:00
|
|
|
public readonly name!: Output<string>;
|
2018-11-14 22:33:35 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The outputs of the referenced stack.
|
|
|
|
*/
|
2019-09-12 01:21:35 +02:00
|
|
|
public readonly outputs!: Output<{[name: string]: any}>;
|
2018-11-14 22:33:35 +01:00
|
|
|
|
Do not taint all stack outputs as secrets if just one is
When using StackReference, if the stack you reference contains any
secret outputs, we have to mark the entire `outputs` member as a
secret output. This is because we only track secretness on a per
`Output<T>` basis.
For `getSecret` and friends, however, we know the name of the output
you are looking up and we can be smarter about if the returned
`Output<T>` should be treated as a secret or not.
This change augments the provider for StackReference such that it also
returns a list of top level stack output names who's values contain
secrets. In the language SDKs, we use this information, when present,
to decide if we should return an `Output<T>` that is marked as a
secret or not. Since the SDK and CLI are independent components, care
is taken to ensure that when the CLI does not return this information,
we behave as we did before (i.e. if any output is a secret, we treat
every output as a secret).
Fixes #2744
2019-08-13 02:02:30 +02:00
|
|
|
/**
|
|
|
|
* The names of any stack outputs which contain secrets.
|
|
|
|
*/
|
2019-09-12 01:21:35 +02:00
|
|
|
public readonly secretOutputNames!: Output<string[]>;
|
Do not taint all stack outputs as secrets if just one is
When using StackReference, if the stack you reference contains any
secret outputs, we have to mark the entire `outputs` member as a
secret output. This is because we only track secretness on a per
`Output<T>` basis.
For `getSecret` and friends, however, we know the name of the output
you are looking up and we can be smarter about if the returned
`Output<T>` should be treated as a secret or not.
This change augments the provider for StackReference such that it also
returns a list of top level stack output names who's values contain
secrets. In the language SDKs, we use this information, when present,
to decide if we should return an `Output<T>` that is marked as a
secret or not. Since the SDK and CLI are independent components, care
is taken to ensure that when the CLI does not return this information,
we behave as we did before (i.e. if any output is a secret, we treat
every output as a secret).
Fixes #2744
2019-08-13 02:02:30 +02:00
|
|
|
|
2018-11-14 22:33:35 +01:00
|
|
|
/**
|
|
|
|
* Create a StackReference resource with the given unique name, arguments, and options.
|
|
|
|
*
|
|
|
|
* If args is not specified, the name of the referenced stack will be the name of the StackReference resource.
|
|
|
|
*
|
|
|
|
* @param name The _unique_ name of the stack reference.
|
|
|
|
* @param args The arguments to use to populate this resource's properties.
|
|
|
|
* @Param opts A bag of options that control this resource's behavior.
|
|
|
|
*/
|
|
|
|
constructor(name: string, args?: StackReferenceArgs, opts?: CustomResourceOptions) {
|
|
|
|
args = args || {};
|
|
|
|
|
2019-11-19 21:51:14 +01:00
|
|
|
const stackReferenceName = args.name || name;
|
|
|
|
|
2018-11-14 22:33:35 +01:00
|
|
|
super("pulumi:pulumi:StackReference", name, {
|
2019-11-19 21:51:14 +01:00
|
|
|
name: stackReferenceName,
|
2018-11-14 22:33:35 +01:00
|
|
|
outputs: undefined,
|
2019-08-14 21:12:23 +02:00
|
|
|
secretOutputNames: undefined,
|
2019-11-19 21:51:14 +01:00
|
|
|
}, { ...opts, id: stackReferenceName });
|
2018-11-14 22:33:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-08-01 20:27:32 +02:00
|
|
|
* Fetches the value of the named stack output, or undefined if the stack output was not found.
|
2018-11-14 22:33:35 +01:00
|
|
|
*
|
|
|
|
* @param name The name of the stack output to fetch.
|
|
|
|
*/
|
|
|
|
public getOutput(name: Input<string>): Output<any> {
|
2019-11-19 21:51:14 +01:00
|
|
|
// Note that this is subtly different from "apply" here. A default "apply" will set the secret bit if any
|
2019-08-14 21:12:23 +02:00
|
|
|
// of the inputs are a secret, and this.outputs is always a secret if it contains any secrets. We do this dance
|
|
|
|
// so we can ensure that the Output we return is not needlessly tainted as a secret.
|
Do not taint all stack outputs as secrets if just one is
When using StackReference, if the stack you reference contains any
secret outputs, we have to mark the entire `outputs` member as a
secret output. This is because we only track secretness on a per
`Output<T>` basis.
For `getSecret` and friends, however, we know the name of the output
you are looking up and we can be smarter about if the returned
`Output<T>` should be treated as a secret or not.
This change augments the provider for StackReference such that it also
returns a list of top level stack output names who's values contain
secrets. In the language SDKs, we use this information, when present,
to decide if we should return an `Output<T>` that is marked as a
secret or not. Since the SDK and CLI are independent components, care
is taken to ensure that when the CLI does not return this information,
we behave as we did before (i.e. if any output is a secret, we treat
every output as a secret).
Fixes #2744
2019-08-13 02:02:30 +02:00
|
|
|
const value = all([output(name), this.outputs]).apply(([n, os]) => os[n]);
|
2019-12-18 04:04:09 +01:00
|
|
|
|
|
|
|
// 'value' is an Output produced by our own `.apply` implementation. So it's safe to
|
|
|
|
// `.allResources!` on it.
|
|
|
|
return new Output(
|
|
|
|
value.resources(), value.promise(),
|
|
|
|
value.isKnown, isSecretOutputName(this, output(name)),
|
|
|
|
value.allResources!());
|
2018-11-14 22:33:35 +01:00
|
|
|
}
|
2019-06-17 21:25:56 +02:00
|
|
|
|
2019-08-01 20:27:32 +02:00
|
|
|
/**
|
|
|
|
* Fetches the value of the named stack output, or throws an error if the output was not found.
|
|
|
|
*
|
|
|
|
* @param name The name of the stack output to fetch.
|
|
|
|
*/
|
|
|
|
public requireOutput(name: Input<string>): Output<any> {
|
Do not taint all stack outputs as secrets if just one is
When using StackReference, if the stack you reference contains any
secret outputs, we have to mark the entire `outputs` member as a
secret output. This is because we only track secretness on a per
`Output<T>` basis.
For `getSecret` and friends, however, we know the name of the output
you are looking up and we can be smarter about if the returned
`Output<T>` should be treated as a secret or not.
This change augments the provider for StackReference such that it also
returns a list of top level stack output names who's values contain
secrets. In the language SDKs, we use this information, when present,
to decide if we should return an `Output<T>` that is marked as a
secret or not. Since the SDK and CLI are independent components, care
is taken to ensure that when the CLI does not return this information,
we behave as we did before (i.e. if any output is a secret, we treat
every output as a secret).
Fixes #2744
2019-08-13 02:02:30 +02:00
|
|
|
const value = all([output(this.name), output(name), this.outputs]).apply(([stackname, n, os]) => {
|
2019-08-01 20:27:32 +02:00
|
|
|
if (!os.hasOwnProperty(n)) {
|
|
|
|
throw new Error(`Required output '${n}' does not exist on stack '${stackname}'.`);
|
|
|
|
}
|
|
|
|
return os[n];
|
|
|
|
});
|
2019-12-18 04:04:09 +01:00
|
|
|
return new Output(
|
|
|
|
value.resources(), value.promise(),
|
|
|
|
value.isKnown, isSecretOutputName(this, output(name)),
|
|
|
|
value.allResources!());
|
2019-08-01 20:27:32 +02:00
|
|
|
}
|
|
|
|
|
2019-06-17 21:25:56 +02:00
|
|
|
/**
|
2019-11-19 21:51:14 +01:00
|
|
|
* Fetches the value promptly of the named stack output. May return undefined if the value is
|
2019-06-17 21:25:56 +02:00
|
|
|
* not known for some reason.
|
|
|
|
*
|
2019-11-19 21:51:14 +01:00
|
|
|
* This operation is not supported (and will throw) if the named stack output is a secret.
|
2019-06-17 21:25:56 +02:00
|
|
|
*
|
|
|
|
* @param name The name of the stack output to fetch.
|
|
|
|
*/
|
2019-12-02 22:53:08 +01:00
|
|
|
public async getOutputValue(name: string): Promise<any> {
|
|
|
|
const [out, isSecret] = await this.readOutputValue("getOutputValue", name, false /*required*/);
|
2019-06-17 21:25:56 +02:00
|
|
|
if (isSecret) {
|
2019-12-02 22:53:08 +01:00
|
|
|
throw new Error("Cannot call 'getOutputValue' if the referenced stack output is a secret. Use 'getOutput' instead.");
|
2019-06-17 21:25:56 +02:00
|
|
|
}
|
2019-11-19 21:51:14 +01:00
|
|
|
return out;
|
2019-06-17 21:25:56 +02:00
|
|
|
}
|
2019-08-01 20:27:32 +02:00
|
|
|
|
|
|
|
/**
|
2019-11-19 21:51:14 +01:00
|
|
|
* Fetches the value promptly of the named stack output. Throws an error if the stack output is
|
2019-08-01 20:27:32 +02:00
|
|
|
* not found.
|
|
|
|
*
|
2019-11-19 21:51:14 +01:00
|
|
|
* This operation is not supported (and will throw) if the named stack output is a secret.
|
2019-08-01 20:27:32 +02:00
|
|
|
*
|
|
|
|
* @param name The name of the stack output to fetch.
|
|
|
|
*/
|
2019-12-02 22:53:08 +01:00
|
|
|
public async requireOutputValue(name: string): Promise<any> {
|
|
|
|
const [out, isSecret] = await this.readOutputValue("requireOutputSync", name, true /*required*/);
|
2019-08-01 20:27:32 +02:00
|
|
|
if (isSecret) {
|
2019-12-02 22:53:08 +01:00
|
|
|
throw new Error("Cannot call 'requireOutputValue' if the referenced stack output is a secret. Use 'requireOutput' instead.");
|
2019-11-19 21:51:14 +01:00
|
|
|
}
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2019-12-02 22:53:08 +01:00
|
|
|
private async readOutputValue(callerName: string, outputName: string, required: boolean): Promise<[any, boolean]> {
|
2019-11-19 21:51:14 +01:00
|
|
|
const out = required ? this.requireOutput(outputName) : this.getOutput(outputName);
|
2019-12-02 22:53:08 +01:00
|
|
|
return Promise.all([out.promise(), out.isSecret]);
|
2019-08-01 20:27:32 +02:00
|
|
|
}
|
2018-11-14 22:33:35 +01:00
|
|
|
}
|
|
|
|
|
2019-11-19 21:51:14 +01:00
|
|
|
// Shape of the result that the engine returns to us when we invoke 'pulumi:pulumi:readStackOutputs'
|
|
|
|
interface ReadStackOutputsResult {
|
|
|
|
name: string;
|
|
|
|
outputs: Record<string, any>;
|
|
|
|
secretOutputNames: string[];
|
|
|
|
}
|
|
|
|
|
2018-11-14 22:33:35 +01:00
|
|
|
/**
|
|
|
|
* The set of arguments for constructing a StackReference resource.
|
|
|
|
*/
|
|
|
|
export interface StackReferenceArgs {
|
|
|
|
/**
|
|
|
|
* The name of the stack to reference.
|
|
|
|
*/
|
|
|
|
readonly name?: Input<string>;
|
|
|
|
}
|
2019-08-14 21:12:23 +02:00
|
|
|
|
|
|
|
async function isSecretOutputName(sr: StackReference, name: Input<string>): Promise<boolean> {
|
|
|
|
const nameOutput = output(name);
|
|
|
|
|
|
|
|
// If either the name or set of secret outputs is unknown, we can't do anything smart, so we just copy the
|
|
|
|
// secretness from the entire outputs value.
|
|
|
|
if (!((await nameOutput.isKnown) && (await sr.secretOutputNames.isKnown))) {
|
2019-08-22 20:04:29 +02:00
|
|
|
return await sr.outputs.isSecret;
|
2019-08-14 21:12:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, if we have a list of outputs we know are secret, we can use that list to determine if this
|
|
|
|
// output should be secret. Names could be falsy here in cases where we are using an older CLI that did
|
|
|
|
// not return this information (in this case we again fallback to the secretness of outputs value).
|
|
|
|
const names = await sr.secretOutputNames.promise();
|
|
|
|
if (!names) {
|
|
|
|
return await sr.outputs.isSecret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return names.includes(await nameOutput.promise());
|
|
|
|
}
|