I have two Pulumi resources, call them `pA` and `p...
# general
f
I have two Pulumi resources, call them
pA
and
pB
, that need to be deployed. Before
pB
can be deployed, I have some custom CLI stuff that needs to be run that depends on
pA
outputs. I understand that right now it's not possible to hook into the lifecycle, and that it will likely be added as a feature in the future. As a workaround for now, I was told I could run the CLI stuff in an
apply
that contains
pA
outputs. How would I ensure that
pB
does not start provisioning until the CLI stuff running in the
apply
completes without exception?
m
You can do this by using the output of the
apply
as an input to
pB
.
e.g. if
gate
is the
apply
you want to gate on and
foo
is the actual input you need, you can do this;
Copy code
const gate = /* some apply */;
const foo = /* some value */;
const pB = new B("b", {
    ...
    input: pulumi.all([foo, gate]).apply(([f]) => f);
});
f
@microscopic-florist-22719 What if
B
doesn't need to use the output of
pulumi.all
?
m
Does
B
have any inputs? If so, you can mix it into one of those
If not, then this gets trickier
f
It has inputs but none that have to do with the result of the apply.
m
That's okay
The snippet I posted above throws away the result of the apply
The only reason the
all
is in there is to ensure that the apply has finished before
pB
is created
Let me decompose that example into two steps
say we start with this for `pB`:
Copy code
const pB = new B("b", {
    ...
    input: someInput,
});
then we write an
apply
that needs to run before
pB
is registered:
Copy code
const gate = pA.foo.apply(foo => ...);
Now we mix the result of the apply in to `pB`'s inputs using an `all`:
Copy code
const pB = new B("b", {
    ...
    input: pulumi.all([someInput, gate]).apply(([i]) => i),
});
this expression:
f
Oh I see what's happening. Are other users taking this approach?
m
IIRC @creamy-potato-29402 blogged about it
and I have one or two uses of this in the wild
hm, maybe he did not post his example as a blog. I know that he has an example of using this technique to gate k8s updates on Prometheus checks.
f
Cool. I'll try it out.
@microscopic-florist-22719 If I wanted to use this multiple times, would a function like this work?
Copy code
function waitForDatomic<T>(val: T): pulumi.Output<T> {
    return pulumi.all([val, datomicGate]).apply(([v]) => v);
}
Hmm the return type there is quite right because
pulumi.all
returns
Output<Unwrap<T>>
. Not sure what the
Unwrap
does...
m
The short answer on "what
Unwrap
does is "it's complicated" 🙂. @lemon-spoon-91807 can provide a longer answer.
But a function like that would work, yes
f
Where is the
Unwrap
type declared?
l
f
How do I use it? My IDE says namespace has no exported member 'Unwrap'.
l
the idea behind 'unwrap' is is deep clone of your object with 'complex' types removed.
f
When doing
pulumi.Unwrap
.
l
so, for example, if you have:
{ foo: Promise<number>, bar: Promise<Promise<bool>[]> }
then 'Unwrap' of that type would be
{ foo: number, bar: bool[] }
right now they're not exported types. You can just not give a return type for this method and TS will iner the right type
we could export them to help out here. haven't had a need yet, but i don't see anything wrong with that given that they are in the signatures of helper functions we do export
(i actually wish TS would warn/error about this).
because it's possible to export signatures that refer to types that are themselves not exported.