Prevent resource registration from overwriting unknown properties at random points in the future. (#2176)

This commit is contained in:
CyrusNajmabadi 2018-11-07 20:24:16 -08:00 committed by GitHub
parent 4ae39b8bec
commit 08fc305b7f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 31 additions and 33 deletions

View file

@ -144,25 +144,24 @@ export function resolveProperties(
}
// Otherwise, unmarshal the value, and store it on the resource object.
let resolve = resolvers[k];
const resolve = resolvers[k];
if (resolve === undefined) {
let resolveValue: (v: any) => void;
let resolveIsKnown: (v: boolean) => void;
resolve = (v, isKnown) => {
resolveValue(v);
resolveIsKnown(isKnown);
};
// If there is no property yet, zero initialize it. This ensures unexpected properties
// are still made available on the object. This isn't ideal, because any code running
// prior to the actual resource CRUD operation can't hang computations off of it, but
// it's better than tossing it.
(res as any)[k] = new Output(
res,
debuggablePromise(new Promise<any>(r => resolveValue = r)),
debuggablePromise(new Promise<boolean>(r => resolveIsKnown = r)));
// engine returned a property that was not in our initial property-map. This can happen
// for outputs that were registered through direct calls to 'registerOutputs'. We do
// *not* want to do anything with these returned properties. First, the component
// resources that were calling 'registerOutputs' will have already assigned these fields
// directly on them themselves. Second, if we were to try to assign here we would have
// an incredibly bad race condition for two reasons:
//
// 1. This call to 'resolveProperties' happens asynchronously at some point far after
// the resource was constructed. So the user will have been able to observe the
// initial value up until we get to this point.
//
// 2. The component resource will have often assigned a value of some arbitrary type
// (say, a 'string'). If we overwrite this with an `Output<string>` we'll be changing
// the type at some non-deterministic point in the future.
continue;
}
try {

View file

@ -290,22 +290,21 @@ async def resolve_outputs(res: 'Resource', props: 'Inputs', outputs: struct_pb2.
log.debug(f"looking for resolver using translated name {key}")
resolve = resolvers.get(key)
if resolve is None:
# If there is no property yet, zero initialize it. This ensures unexpected properties
# are still made available on the object. This isn't ideal, because any code running
# prior to the actual resource CRUD operation can't hang computations off of it, but
# it's better than tossing it.
resolve_value = asyncio.Future()
resolve_known = asyncio.Future()
def do_resolve(resolve_fut, known_fut, value, known):
resolve_fut.set_result(value)
known_fut.set_result(known)
# Note: the functools.partial is REQUIRED here. It's not safe to capture variables
# by reference in a loop. We use functools.partial here to capture the value of
# resolve_value and resolve_known at the point of invocation and not by reference.
resolve = functools.partial(do_resolve, resolve_value, resolve_known)
res.__setattr__(key, known_types.new_output({res}, resolve_value, resolve_known))
# engine returned a property that was not in our initial property-map. This can happen
# for outputs that were registered through direct calls to 'registerOutputs'. We do
# *not* want to do anything with these returned properties. First, the component
# resources that were calling 'registerOutputs' will have already assigned these fields
# directly on them themselves. Second, if we were to try to assign here we would have
# an incredibly bad race condition for two reasons:
#
# 1. This call to 'resolveProperties' happens asynchronously at some point far after
# the resource was constructed. So the user will have been able to observe the
# initial value up until we get to this point.
#
# 2. The component resource will have often assigned a value of some arbitrary type
# (say, a 'string'). If we overwrite this with an `Output<string>` we'll be changing
# the type at some non-deterministic point in the future.
continue
# If either we are performing a real deployment, or this is a stable property value, we
# can propagate its final value. Otherwise, it must be undefined, since we don't know