Add the ability to pulumi.unsecret an existing output (#6086)

Related: #5653

This will take an existing output and then unwrap the secret, and
return a new output

```
import * as pulumi from "@pulumi/pulumi";

const x = pulumi.secret("test")
export const xVal = x;

const y = pulumi.unsecret(x);
export const yVal = y;
```

```
▶ pulumi stack output
Current stack outputs (3):
    OUTPUT         VALUE
    xVal           [secret]
    yVal           test
```

Also adds the ability to check if an output is as secret:

```
import * as pulumi from "@pulumi/pulumi";

const x = pulumi.secret("test")
const isSecret = x.isSecret;

export const isSecretDeets = isSecret;
```
This commit is contained in:
Paul Stack 2021-01-14 20:36:52 +00:00 committed by GitHub
parent dd66d8d2ab
commit ae9a6db36e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 39 additions and 5 deletions

View file

@ -17,6 +17,12 @@ CHANGELOG
- [sdk/python] Fix python 3.6 support by removing annotations import.
[#6109](https://github.com/pulumi/pulumi/pull/6109)
- [sdk/nodejs] Added `pulumi.unsecret` which will take an existing secret output and
create a non-secret variant with an unwrapped secret value. Also adds,
`pulumi.isSecret` which will take an existing output and
determine if an output has a secret within the output.
[#6086](https://github.com/pulumi/pulumi/pull/6086)
## 2.17.1 (2021-01-13)
- Fix an issue with go sdk generation where optional strict enum values
@ -38,7 +44,7 @@ CHANGELOG
- [sdk/dotnet] Moved urn value retrieval into if statement
for MockMonitor
[#6081](https://github.com/pulumi/pulumi/pull/6081)
- [sdk/dotnet] Added `Pulumi.Output.Unsecret` which will
take an existing secret output and
create a non-secret variant with an unwrapped secret value.

View file

@ -388,9 +388,10 @@ async function applyHelperAsync<T, U>(
// Returns an promise denoting if the output is a secret or not. This is not the same as just calling `.isSecret`
// because in cases where the output does not have a `isSecret` property and it is a Proxy, we need to ignore
// the isSecret member that the proxy reports back.
// This calls the public implementation so that we only make any calculations in a single place.
/** @internal */
export function isSecretOutput<T>(o: Output<T>): Promise<boolean> {
return Output.isInstance(o.isSecret) ? Promise.resolve(false) : o.isSecret;
return isSecret(o);
}
// Helper function for `output`. This function trivially recurses through an object, copying it,
@ -521,7 +522,7 @@ export function output<T>(val: Input<T | undefined>): Output<Unwrap<T | undefine
}
/**
* [secret] behaves the same as [output] except the returned output is marked as contating sensitive data.
* [secret] behaves the same as [output] except the returned output is marked as containing sensitive data.
*/
export function secret<T>(val: Input<T>): Output<Unwrap<T>>;
export function secret<T>(val: Input<T> | undefined): Output<Unwrap<T | undefined>>;
@ -534,6 +535,19 @@ export function secret<T>(val: Input<T | undefined>): Output<Unwrap<T | undefine
o.isKnown, Promise.resolve(true), o.allResources!());
}
/**
* [unsecret] behaves the same as [output] except the returned output takes the existing output and unwraps the secret
*/
export function unsecret<T>(val: Output<T>): Output<T> {
return new Output(
val.resources(), val.promise(/*withUnknowns*/ true),
val.isKnown, Promise.resolve(false), val.allResources!());
}
export function isSecret<T>(val: Output<T>): Promise<boolean> {
return Output.isInstance(val.isSecret) ? Promise.resolve(false) : val.isSecret;
}
function createSimpleOutput(val: any) {
return new Output(
new Set(),
@ -779,7 +793,6 @@ export type UnwrappedObject<T> = {
*/
export interface OutputInstance<T> {
/** @internal */ allResources?: () => Promise<Set<Resource>>;
/** @internal */ readonly isKnown: Promise<boolean>;
/** @internal */ readonly isSecret: Promise<boolean>;
/** @internal */ promise(withUnknowns?: boolean): Promise<T>;

View file

@ -15,7 +15,7 @@
// tslint:disable
import * as assert from "assert";
import { Output, all, concat, interpolate, output, unknown } from "../output";
import { Output, all, concat, interpolate, output, unknown, secret, unsecret, isSecret } from "../output";
import { Resource } from "../resource";
import * as runtime from "../runtime";
import { asyncTest } from "./util";
@ -860,6 +860,21 @@ describe("output", () => {
}));
});
describe("secret operations", () => {
it("ensure secret", asyncTest(async () => {
const sec = secret("foo");
assert.strictEqual(await sec.isSecret, true)
}));
it("ensure that a secret can be unwrapped", asyncTest(async () => {
const sec = secret("foo");
assert.strictEqual(await isSecret(sec), true)
const unsec = unsecret(sec);
assert.strictEqual(await isSecret(unsec), false)
assert.strictEqual(await unsec.promise(), "foo")
}));
});
describe("lifted operations", () => {
it("lifts properties from inner object", asyncTest(async () => {
const output1 = output({ a: 1, b: true, c: "str", d: [2], e: { f: 3 }, g: undefined, h: null });