-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
stackReference.ts
213 lines (193 loc) 路 7.81 KB
/
stackReference.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// 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.
import { all, Input, Output, output } from "./output";
import { CustomResource, CustomResourceOptions } from "./resource";
/**
* 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.
*/
public readonly name!: Output<string>;
/**
* The outputs of the referenced stack.
*/
public readonly outputs!: Output<{ [name: string]: any }>;
/**
* The names of any stack outputs which contain secrets.
*/
public readonly secretOutputNames!: Output<string[]>;
/**
* 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 || {};
const stackReferenceName = args.name || name;
super(
"pulumi:pulumi:StackReference",
name,
{
name: stackReferenceName,
outputs: undefined,
secretOutputNames: undefined,
},
{ ...opts, id: stackReferenceName },
);
}
/**
* Fetches the value of the named stack output, or undefined if the stack output was not found.
*
* @param name The name of the stack output to fetch.
*/
public getOutput(name: Input<string>): Output<any> {
// Note that this is subtly different from "apply" here. A default "apply" will set the secret bit if any
// 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.
const value = all([output(name), this.outputs]).apply(([n, os]) => os[n]);
// '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!(),
);
}
/**
* 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> {
const value = all([output(this.name), output(name), this.outputs]).apply(([stackname, n, os]) => {
if (!os.hasOwnProperty(n)) {
throw new Error(`Required output '${n}' does not exist on stack '${stackname}'.`);
}
return os[n];
});
return new Output(
value.resources(),
value.promise(),
value.isKnown,
isSecretOutputName(this, output(name)),
value.allResources!(),
);
}
/**
* Fetches the value of the named stack output
* and builds a StackReferenceOutputDetails with it.
*
* The returned object has its `value` or `secretValue` fields set
* depending on wehther the output is a secret.
* Neither field is set if the output was not found.
*
* @param name The name of the stack output to fetch.
*/
public async getOutputDetails(name: string): Promise<StackReferenceOutputDetails> {
const [out, isSecret] = await this.readOutputValue("getOutputValueDetails", name, false /*required*/);
if (isSecret) {
return { secretValue: out };
} else {
return { value: out };
}
}
/**
* Fetches the value promptly of the named stack output. May return undefined if the value is
* not known for some reason.
*
* This operation is not supported (and will throw) if the named stack output is a secret.
*
* @param name The name of the stack output to fetch.
*/
public async getOutputValue(name: string): Promise<any> {
const [out, isSecret] = await this.readOutputValue("getOutputValue", name, false /*required*/);
if (isSecret) {
throw new Error(
"Cannot call 'getOutputValue' if the referenced stack output is a secret. Use 'getOutput' instead.",
);
}
return out;
}
/**
* Fetches the value promptly of the named stack output. Throws an error if the stack output is
* not found.
*
* This operation is not supported (and will throw) if the named stack output is a secret.
*
* @param name The name of the stack output to fetch.
*/
public async requireOutputValue(name: string): Promise<any> {
const [out, isSecret] = await this.readOutputValue("requireOutputSync", name, true /*required*/);
if (isSecret) {
throw new Error(
"Cannot call 'requireOutputValue' if the referenced stack output is a secret. Use 'requireOutput' instead.",
);
}
return out;
}
private async readOutputValue(callerName: string, outputName: string, required: boolean): Promise<[any, boolean]> {
const out = required ? this.requireOutput(outputName) : this.getOutput(outputName);
return Promise.all([out.promise(), out.isSecret]);
}
}
/**
* The set of arguments for constructing a StackReference resource.
*/
export interface StackReferenceArgs {
/**
* The name of the stack to reference.
*/
readonly name?: Input<string>;
}
/**
* Records the output of a StackReference.
* At most one of th evalue and secretValue fields will be set.
*/
export interface StackReferenceOutputDetails {
/**
* Output value returned by the StackReference.
* This is null if the value is a secret or it does not exist.
*/
readonly value?: any;
/**
* Secret value returned by the StackReference.
* This is null if the value is not a secret or it does not exist.
*/
readonly secretValue?: any;
}
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))) {
return await sr.outputs.isSecret;
}
// 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());
}