Compare commits

...

8 commits

Author SHA1 Message Date
Justin Van Patten ec076ee0f4 Update CHANGELOG_PENDING.md 2021-11-12 16:51:30 -08:00
Justin Van Patten 52f3437100 [sdk/dotnet] Don't send deps maps when using output values 2021-11-12 16:13:25 -08:00
Justin Van Patten fca55516e6 [sdk/go] Don't send deps maps when using output values 2021-11-12 16:13:18 -08:00
Justin Van Patten 9a5bd22b4d [sdk/nodejs] Don't send deps maps when using output values 2021-11-12 15:53:04 -08:00
Justin Van Patten 14f03a7b0e [sdk/python] Don't send deps maps when using output values 2021-11-12 15:53:04 -08:00
Justin Van Patten 97daa0a34a [sdk/go] Minor optimization in Go provider
First check for empty deps before checking to see if the gathered dependencies are equivalent to the specified dependencies map.
2021-11-12 15:52:29 -08:00
Justin Van Patten 7487e99d94 Deprecate deps maps when using output values
When output values are being sent to a provider, there's no need to also send the dependencies map, because the output values themselves contain the dependencies. This allows for a simpler implementation in the provider.
2021-11-12 15:13:45 -08:00
Justin Van Patten 7888623aca Add hasOutputs to RegisterRequestRequest
This can be set by an SDK to indicate the inputs were serialized with output values.
2021-11-12 15:13:45 -08:00
15 changed files with 348 additions and 166 deletions

View file

@ -10,6 +10,9 @@
- [sdk/dotnet] - Marshal output values.
[#8316](https://github.com/pulumi/pulumi/pull/8316)
- Deprecate use of dependencies maps when sending output values.
[#8410](https://github.com/pulumi/pulumi/pull/8410)
### Bug Fixes
- [engine] - Compute dependents correctly during targeted deletes.

View file

@ -631,13 +631,16 @@ func (rm *resmon) Call(ctx context.Context, req *pulumirpc.CallRequest) (*pulumi
return nil, errors.Wrapf(err, "failed to unmarshal %v args", tok)
}
argDependencies := map[resource.PropertyKey][]resource.URN{}
for name, deps := range req.GetArgDependencies() {
urns := make([]resource.URN, len(deps.Urns))
for i, urn := range deps.Urns {
urns[i] = resource.URN(urn)
var argDependencies map[resource.PropertyKey][]resource.URN
if len(req.GetArgDependencies()) > 0 {
argDependencies = map[resource.PropertyKey][]resource.URN{}
for name, deps := range req.GetArgDependencies() {
urns := make([]resource.URN, len(deps.Urns))
for i, urn := range deps.Urns {
urns[i] = resource.URN(urn)
}
argDependencies[resource.PropertyKey(name)] = urns
}
argDependencies[resource.PropertyKey(name)] = urns
}
info := plugin.CallInfo{
@ -853,6 +856,7 @@ func (rm *resmon) RegisterResource(ctx context.Context,
replaceOnChanges := req.GetReplaceOnChanges()
id := resource.ID(req.GetImportId())
customTimeouts := req.GetCustomTimeouts()
hasOutputs := req.GetHasOutputs()
// Custom resources must have a three-part type so that we can 1) identify if they are providers and 2) retrieve the
// provider responsible for managing a particular resource (based on the type's Package).
@ -913,7 +917,7 @@ func (rm *resmon) RegisterResource(ctx context.Context,
KeepResources: true,
// To initially scope the use of this new feature, we only keep output values when unmarshaling
// properties for RegisterResource (when remote is true for multi-lang components) and Call.
KeepOutputValues: remote,
KeepOutputValues: remote || hasOutputs,
})
if err != nil {
return nil, err
@ -993,11 +997,14 @@ func (rm *resmon) RegisterResource(ctx context.Context,
// Invoke the provider's Construct RPC method.
options := plugin.ConstructOptions{
Aliases: aliases,
Dependencies: dependencies,
Protect: protect,
PropertyDependencies: propertyDependencies,
Providers: providerRefs,
Aliases: aliases,
Dependencies: dependencies,
Protect: protect,
Providers: providerRefs,
}
if !hasOutputs {
// Only include property dependencies when we don't have output values in the properties.
options.PropertyDependencies = propertyDependencies
}
constructResult, err := provider.Construct(rm.constructInfo, t, name, parent, props, options)
if err != nil {

View file

@ -52,11 +52,13 @@ namespace Pulumi
argsDict = argsDict.SetItem("__self__", self);
}
var keepOutputs = await MonitorSupportsOutputValues().ConfigureAwait(false);
var (serialized, argDependencies) = await SerializeFilteredPropertiesAsync(
$"call:{token}",
argsDict, _ => true,
keepResources: true,
keepOutputValues: await MonitorSupportsOutputValues().ConfigureAwait(false)).ConfigureAwait(false);
keepOutputValues: keepOutputs).ConfigureAwait(false);
Log.Debug($"Call RPC prepared: token={token}" +
(_excessiveDebugOutput ? $", obj={serialized}" : ""));
@ -84,13 +86,17 @@ namespace Pulumi
Args = serialized,
};
// Add arg dependencies to the request.
foreach (var (argName, directDependencies) in argDependencies)
// Only include the arg dependencies map in the request when *not* keeping output values.
// When keeping output values, the dependencies will already exist within the args.
if (!keepOutputs)
{
var urns = await GetAllTransitivelyReferencedResourceUrnsAsync(directDependencies).ConfigureAwait(false);
var deps = new CallRequest.Types.ArgumentDependencies();
deps.Urns.AddRange(urns);
request.ArgDependencies.Add(argName, deps);
foreach (var (argName, directDependencies) in argDependencies)
{
var urns = await GetAllTransitivelyReferencedResourceUrnsAsync(directDependencies).ConfigureAwait(false);
var deps = new CallRequest.Types.ArgumentDependencies();
deps.Urns.AddRange(urns);
request.ArgDependencies.Add(argName, deps);
}
}
// Kick off the call.

View file

@ -21,7 +21,10 @@ namespace Pulumi
var label = $"resource:{name}[{type}]";
Log.Debug($"Registering resource start: t={type}, name={name}, custom={custom}, remote={remote}");
var request = CreateRegisterResourceRequest(type, name, custom, remote, options);
// Keep track of whether we've kept output values when serializing.
var hasOutputs = remote && await MonitorSupportsOutputValues().ConfigureAwait(false);
var request = CreateRegisterResourceRequest(type, name, custom, remote, options, hasOutputs);
Log.Debug($"Preparing resource: t={type}, name={name}, custom={custom}, remote={remote}");
var prepareResult = await PrepareResourceAsync(label, resource, custom, remote, args, options).ConfigureAwait(false);
@ -65,7 +68,7 @@ namespace Pulumi
}
private static RegisterResourceRequest CreateRegisterResourceRequest(
string type, string name, bool custom, bool remote, ResourceOptions options)
string type, string name, bool custom, bool remote, ResourceOptions options, bool hasOutputs)
{
var customOpts = options as CustomResourceOptions;
var deleteBeforeReplace = customOpts?.DeleteBeforeReplace;
@ -89,6 +92,7 @@ namespace Pulumi
Update = TimeoutString(options.CustomTimeouts?.Update),
},
Remote = remote,
HasOutputs = hasOutputs,
};
if (customOpts != null)

View file

@ -20,6 +20,7 @@ import (
"fmt"
"io"
"os"
"sort"
"strings"
"github.com/blang/semver"
@ -1125,14 +1126,24 @@ func (p *provider) Construct(info ConstructInfo, typ tokens.Type, name tokens.QN
dependencies[i] = string(dep)
}
// Marshal the property dependencies.
inputDependencies := map[string]*pulumirpc.ConstructRequest_PropertyDependencies{}
for name, dependencies := range options.PropertyDependencies {
urns := make([]string, len(dependencies))
for i, urn := range dependencies {
urns[i] = string(urn)
// If the provider accepts outputs, the marshaled inputs will have output values with dependencies,
// so there's no need to specify the dependencies map.
var inputDependencies map[string]*pulumirpc.ConstructRequest_PropertyDependencies
if !p.acceptOutputs {
// If the provider doesn't accept outputs, pass along the dependencies map.
dependenciesMap := options.PropertyDependencies
if len(dependenciesMap) == 0 {
// If the dependencies map is empty, "polyfill" it based on dependencies gathered from the inputs.
dependenciesMap = gatherDependenciesMap(inputs)
}
inputDependencies = make(map[string]*pulumirpc.ConstructRequest_PropertyDependencies, len(dependenciesMap))
for name, dependencies := range dependenciesMap {
urns := make([]string, len(dependencies))
for i, urn := range dependencies {
urns[i] = string(urn)
}
inputDependencies[string(name)] = &pulumirpc.ConstructRequest_PropertyDependencies{Urns: urns}
}
inputDependencies[string(name)] = &pulumirpc.ConstructRequest_PropertyDependencies{Urns: urns}
}
// Marshal the config.
@ -1367,14 +1378,24 @@ func (p *provider) Call(tok tokens.ModuleMember, args resource.PropertyMap, info
return CallResult{}, err
}
// Marshal the arg dependencies.
argDependencies := map[string]*pulumirpc.CallRequest_ArgumentDependencies{}
for name, dependencies := range options.ArgDependencies {
urns := make([]string, len(dependencies))
for i, urn := range dependencies {
urns[i] = string(urn)
// If the provider accepts outputs, the marshaled args will have output values with dependencies,
// so there's no need to specify the dependencies map.
var argDependencies map[string]*pulumirpc.CallRequest_ArgumentDependencies
if !p.acceptOutputs {
// If the provider doesn't accept outputs, pass along the dependencies map.
dependenciesMap := options.ArgDependencies
if len(dependenciesMap) == 0 {
// If the dependencies map is empty, "polyfill" it based on dependencies gathered from the args.
dependenciesMap = gatherDependenciesMap(args)
}
argDependencies = make(map[string]*pulumirpc.CallRequest_ArgumentDependencies, len(dependenciesMap))
for name, dependencies := range dependenciesMap {
urns := make([]string, len(dependencies))
for i, urn := range dependencies {
urns[i] = string(urn)
}
argDependencies[string(name)] = &pulumirpc.CallRequest_ArgumentDependencies{Urns: urns}
}
argDependencies[string(name)] = &pulumirpc.CallRequest_ArgumentDependencies{Urns: urns}
}
// Marshal the config.
@ -1613,3 +1634,55 @@ func decorateProviderSpans(span opentracing.Span, method string, req, resp inter
span.SetTag("pulumi-decorator", req.(*pulumirpc.InvokeRequest).Tok)
}
}
// gatherDependenciesMap deeply gathers dependencies in the input's output values and resource
// references to "polyfill" a dependencies map for providers that don't accept output values.
func gatherDependenciesMap(inputs resource.PropertyMap) map[resource.PropertyKey][]resource.URN {
type urnSet = map[resource.URN]struct{}
add := func(s urnSet, urn resource.URN) {
s[urn] = struct{}{}
}
sortedValues := func(s urnSet) []resource.URN {
sorted := make([]resource.URN, 0, len(s))
for k := range s {
sorted = append(sorted, k)
}
sort.Slice(sorted, func(i, j int) bool { return sorted[i] < sorted[j] })
return sorted
}
var gatherDeps func(v resource.PropertyValue, deps urnSet)
gatherDeps = func(v resource.PropertyValue, deps urnSet) {
switch {
case v.IsSecret():
gatherDeps(v.SecretValue().Element, deps)
case v.IsComputed():
gatherDeps(v.Input().Element, deps)
case v.IsOutput():
for _, urn := range v.OutputValue().Dependencies {
add(deps, urn)
}
gatherDeps(v.OutputValue().Element, deps)
case v.IsResourceReference():
add(deps, v.ResourceReferenceValue().URN)
case v.IsArray():
for _, e := range v.ArrayValue() {
gatherDeps(e, deps)
}
case v.IsObject():
for _, e := range v.ObjectValue() {
gatherDeps(e, deps)
}
}
}
result := make(map[resource.PropertyKey][]resource.URN, len(inputs))
for k, v := range inputs {
deps := urnSet{}
gatherDeps(v, deps)
result[k] = sortedValues(deps)
}
return result
}

View file

@ -403,21 +403,25 @@ func (ctx *Context) Call(tok string, args Input, output Output, self Resource, o
return nil, fmt.Errorf("marshaling args: %w", err)
}
// Convert the arg dependencies map for RPC and remove duplicates.
rpcArgDeps := make(map[string]*pulumirpc.CallRequest_ArgumentDependencies)
for k, deps := range argDeps {
sort.Slice(deps, func(i, j int) bool { return deps[i] < deps[j] })
// Only include the arg dependencies map in the request when the monitor does *not* support output values.
// When the monitor *does* support output values, the dependencies will already exist within the args.
var rpcArgDeps map[string]*pulumirpc.CallRequest_ArgumentDependencies
if !ctx.keepOutputValues {
rpcArgDeps = make(map[string]*pulumirpc.CallRequest_ArgumentDependencies, len(argDeps))
for k, deps := range argDeps {
sort.Slice(deps, func(i, j int) bool { return deps[i] < deps[j] })
urns := make([]string, 0, len(deps))
for i, d := range deps {
if i > 0 && urns[i-1] == string(d) {
continue
urns := make([]string, 0, len(deps))
for i, d := range deps {
if i > 0 && urns[i-1] == string(d) {
continue
}
urns = append(urns, string(d))
}
urns = append(urns, string(d))
}
rpcArgDeps[k] = &pulumirpc.CallRequest_ArgumentDependencies{
Urns: urns,
rpcArgDeps[k] = &pulumirpc.CallRequest_ArgumentDependencies{
Urns: urns,
}
}
}
@ -822,6 +826,7 @@ func (ctx *Context) registerResource(
Version: inputs.version,
Remote: remote,
ReplaceOnChanges: inputs.replaceOnChanges,
HasOutputs: inputs.hasOutputs,
})
if err != nil {
logging.V(9).Infof("RegisterResource(%s, %s): error: %v", t, name, err)
@ -1165,6 +1170,7 @@ type resourceInputs struct {
additionalSecretOutputs []string
version string
replaceOnChanges []string
hasOutputs bool
}
// prepareResourceInputs prepares the inputs for a resource operation, shared between read and register.
@ -1184,15 +1190,17 @@ func (ctx *Context) prepareResourceInputs(res Resource, props Input, t string, o
return nil, fmt.Errorf("marshaling properties: %w", err)
}
// To initially scope the use of this new feature, we only keep output values when
// remote is true (for multi-lang components).
keepOutputs := remote && ctx.keepOutputValues
// Marshal all properties for the RPC call.
rpcProps, err := plugin.MarshalProperties(
resolvedProps,
ctx.withKeepOrRejectUnknowns(plugin.MarshalOptions{
KeepSecrets: true,
KeepResources: ctx.keepResources,
// To initially scope the use of this new feature, we only keep output values when
// remote is true (for multi-lang components).
KeepOutputValues: remote && ctx.keepOutputValues,
KeepSecrets: true,
KeepResources: ctx.keepResources,
KeepOutputValues: keepOutputs,
}))
if err != nil {
return nil, fmt.Errorf("marshaling properties: %w", err)
@ -1250,6 +1258,7 @@ func (ctx *Context) prepareResourceInputs(res Resource, props Input, t string, o
additionalSecretOutputs: resOpts.additionalSecretOutputs,
version: state.version,
replaceOnChanges: resOpts.replaceOnChanges,
hasOutputs: keepOutputs,
}, nil
}

View file

@ -215,6 +215,13 @@ func (ci constructInput) Dependencies(ctx *Context) []Resource {
return result
}
// HasEquivalentDependencies returns true if the deps are equal to or a subset of the gathered nested dependencies.
func (ci constructInput) HasEquivalentDependencies() bool {
deps := urnSet{}
gatherDeps(ci.value, deps)
return deps.contains(ci.deps)
}
// constructInputsMap returns the inputs as a Map.
func constructInputsMap(ctx *Context, inputs map[string]interface{}) (Map, error) {
result := make(Map, len(inputs))
@ -577,13 +584,9 @@ func constructInputsCopyTo(ctx *Context, inputs map[string]interface{}, args int
continue
}
// Find all nested dependencies.
deps := urnSet{}
gatherDeps(ci.value, deps)
// If the top-level property dependencies are equal to (or a subset of) the gathered nested
// dependencies, we don't necessarily need to create a top-level output for the property.
if deps.contains(ci.deps) {
// If there aren't any dependencies or the dependencies are equivalent, we don't necessarily
// need to create a top-level output for the property.
if len(ci.deps) == 0 || ci.HasEquivalentDependencies() {
if err := copyInputTo(ctx, ci.value, fieldV); err != nil {
return fmt.Errorf("copying input %q: %w", k, err)
}

View file

@ -1285,7 +1285,8 @@ proto.pulumirpc.RegisterResourceRequest.toObject = function(includeInstance, msg
remote: jspb.Message.getBooleanFieldWithDefault(msg, 20, false),
acceptresources: jspb.Message.getBooleanFieldWithDefault(msg, 21, false),
providersMap: (f = msg.getProvidersMap()) ? f.toObject(includeInstance, undefined) : [],
replaceonchangesList: (f = jspb.Message.getRepeatedField(msg, 23)) == null ? undefined : f
replaceonchangesList: (f = jspb.Message.getRepeatedField(msg, 23)) == null ? undefined : f,
hasoutputs: jspb.Message.getBooleanFieldWithDefault(msg, 24, false)
};
if (includeInstance) {
@ -1420,6 +1421,10 @@ proto.pulumirpc.RegisterResourceRequest.deserializeBinaryFromReader = function(m
var value = /** @type {string} */ (reader.readString());
msg.addReplaceonchanges(value);
break;
case 24:
var value = /** @type {boolean} */ (reader.readBool());
msg.setHasoutputs(value);
break;
default:
reader.skipField();
break;
@ -1606,6 +1611,13 @@ proto.pulumirpc.RegisterResourceRequest.serializeBinaryToWriter = function(messa
f
);
}
f = message.getHasoutputs();
if (f) {
writer.writeBool(
24,
f
);
}
};
@ -2510,6 +2522,24 @@ proto.pulumirpc.RegisterResourceRequest.prototype.clearReplaceonchangesList = fu
};
/**
* optional bool hasOutputs = 24;
* @return {boolean}
*/
proto.pulumirpc.RegisterResourceRequest.prototype.getHasoutputs = function() {
return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 24, false));
};
/**
* @param {boolean} value
* @return {!proto.pulumirpc.RegisterResourceRequest} returns this
*/
proto.pulumirpc.RegisterResourceRequest.prototype.setHasoutputs = function(value) {
return jspb.Message.setProto3BooleanField(this, 24, value);
};
/**
* List of repeated fields within this message type.

View file

@ -29,6 +29,7 @@ import {
getMonitor,
rpcKeepAlive,
terminateRpcs,
monitorSupportsOutputValues,
} from "./settings";
import { DependencyResource, ProviderResource, Resource } from "../resource";
@ -400,16 +401,20 @@ async function createCallRequest(tok: string, serialized: Record<string, any>,
req.setProvider(provider);
req.setVersion(version || "");
const argDependencies = req.getArgdependenciesMap();
for (const [key, propertyDeps] of serializedDeps) {
const urns = new Set<string>();
for (const dep of propertyDeps) {
const urn = await dep.urn.promise();
urns.add(urn);
// Only include `argDependencies` in the request when the monitor does *not* support output values.
// When the monitor *does* support output values, the dependencies will already exist within the args.
if (!await monitorSupportsOutputValues()) {
const argDependencies = req.getArgdependenciesMap();
for (const [key, propertyDeps] of serializedDeps) {
const urns = new Set<string>();
for (const dep of propertyDeps) {
const urn = await dep.urn.promise();
urns.add(urn);
}
const deps = new providerproto.CallRequest.ArgumentDependencies();
deps.setUrnsList(Array.from(urns));
argDependencies.set(key, deps);
}
const deps = new providerproto.CallRequest.ArgumentDependencies();
deps.setUrnsList(Array.from(urns));
argDependencies.set(key, deps);
}
return req;

View file

@ -54,6 +54,7 @@ import {
getStack,
isDryRun,
isLegacyApplyEnabled,
monitorSupportsOutputValues,
rpcKeepAlive,
serialize,
terminateRpcs,
@ -89,6 +90,8 @@ interface ResourceResolverOperation {
aliases: URN[];
// An ID to import, if any.
import: ID | undefined;
// true if the object was serialized with output values.
hasOutputs: boolean;
}
/**
@ -317,6 +320,7 @@ export function registerResource(res: Resource, t: string, name: string, custom:
req.setSupportspartialvalues(true);
req.setRemote(remote);
req.setReplaceonchangesList(opts.replaceOnChanges || []);
req.setHasoutputs(resop.hasOutputs);
const customTimeouts = new resproto.RegisterResourceRequest.CustomTimeouts();
if (opts.customTimeouts != null) {
@ -513,9 +517,13 @@ async function prepareResource(label: string, res: Resource, custom: boolean, re
const [serializedProps, propertyToDirectDependencies] = await serializeResourceProperties(label, props, {
// To initially scope the use of this new feature, we only keep output values when
// remote is true (for multi-lang components).
// Note: The serializer will check with the monitor to see if it supports output values.
keepOutputValues: remote,
});
// Keep track of whether we've kept output values when serializing.
const hasOutputs = remote && await monitorSupportsOutputValues();
// Wait for the parent to complete.
// If no parent was provided, parent to the root resource.
const parentURN = opts.parent
@ -586,6 +594,7 @@ async function prepareResource(label: string, res: Resource, custom: boolean, re
propertyToDirectDependencyURNs: propertyToDirectDependencyURNs,
aliases: aliases,
import: importID,
hasOutputs,
};
} finally {

View file

@ -307,6 +307,7 @@ type RegisterResourceRequest struct {
AcceptResources bool `protobuf:"varint,21,opt,name=acceptResources,proto3" json:"acceptResources,omitempty"`
Providers map[string]string `protobuf:"bytes,22,rep,name=providers,proto3" json:"providers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
ReplaceOnChanges []string `protobuf:"bytes,23,rep,name=replaceOnChanges,proto3" json:"replaceOnChanges,omitempty"`
HasOutputs bool `protobuf:"varint,24,opt,name=hasOutputs,proto3" json:"hasOutputs,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -498,6 +499,13 @@ func (m *RegisterResourceRequest) GetReplaceOnChanges() []string {
return nil
}
func (m *RegisterResourceRequest) GetHasOutputs() bool {
if m != nil {
return m.HasOutputs
}
return false
}
// PropertyDependencies describes the resources that a particular property depends on.
type RegisterResourceRequest_PropertyDependencies struct {
Urns []string `protobuf:"bytes,1,rep,name=urns,proto3" json:"urns,omitempty"`
@ -792,71 +800,71 @@ func init() {
func init() { proto.RegisterFile("resource.proto", fileDescriptor_d1b72f771c35e3b8) }
var fileDescriptor_d1b72f771c35e3b8 = []byte{
// 1010 bytes of a gzipped FileDescriptorProto
// 1020 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x57, 0x5f, 0x6f, 0x1b, 0x45,
0x10, 0x8f, 0xed, 0xd4, 0xb1, 0x27, 0xa9, 0x13, 0x36, 0xa9, 0xbd, 0x3d, 0x50, 0x08, 0x07, 0x0f,
0xa6, 0x0f, 0x4e, 0x13, 0x90, 0x9a, 0xa2, 0x02, 0x12, 0x69, 0x41, 0x7d, 0x28, 0x29, 0x17, 0x84,
0x00, 0x09, 0xa4, 0x8d, 0x6f, 0x92, 0x1e, 0x39, 0xdf, 0x5e, 0x77, 0xf7, 0x22, 0xf9, 0x0d, 0xbe,
0x07, 0x9f, 0x06, 0xf1, 0xca, 0x77, 0x42, 0xbb, 0x7b, 0xeb, 0xde, 0xf9, 0xce, 0x89, 0xd3, 0xbe,
0xdd, 0xfc, 0xe6, 0x9f, 0x77, 0xe6, 0x37, 0xb3, 0x6b, 0xe8, 0x09, 0x94, 0x3c, 0x13, 0x63, 0x1c,
0xa5, 0x82, 0x2b, 0x4e, 0xba, 0x69, 0x16, 0x67, 0x93, 0x48, 0xa4, 0x63, 0xef, 0xfd, 0x0b, 0xce,
0x2f, 0x62, 0xdc, 0x37, 0x8a, 0xb3, 0xec, 0x7c, 0x1f, 0x27, 0xa9, 0x9a, 0x5a, 0x3b, 0xef, 0x83,
0x79, 0xa5, 0x54, 0x22, 0x1b, 0xab, 0x5c, 0xdb, 0x4b, 0x05, 0xbf, 0x8a, 0x42, 0x14, 0x56, 0xf6,
0x87, 0xd0, 0x3f, 0xcd, 0xd2, 0x94, 0x0b, 0x25, 0xbf, 0x45, 0xa6, 0x32, 0x81, 0x01, 0xbe, 0xce,
0x50, 0x2a, 0xd2, 0x83, 0x66, 0x14, 0xd2, 0xc6, 0x5e, 0x63, 0xd8, 0x0d, 0x9a, 0x51, 0xe8, 0x3f,
0x86, 0x41, 0xc5, 0x52, 0xa6, 0x3c, 0x91, 0x48, 0x76, 0x01, 0x5e, 0x31, 0x99, 0x6b, 0x8d, 0x4b,
0x27, 0x28, 0x20, 0xfe, 0xdf, 0x2d, 0xd8, 0x0e, 0x90, 0x85, 0x41, 0x7e, 0xa2, 0x05, 0x29, 0x08,
0x81, 0x55, 0x35, 0x4d, 0x91, 0x36, 0x0d, 0x62, 0xbe, 0x35, 0x96, 0xb0, 0x09, 0xd2, 0x96, 0xc5,
0xf4, 0x37, 0xe9, 0x43, 0x3b, 0x65, 0x02, 0x13, 0x45, 0x57, 0x0d, 0x9a, 0x4b, 0xe4, 0x11, 0x40,
0x2a, 0x78, 0x8a, 0x42, 0x45, 0x28, 0xe9, 0x9d, 0xbd, 0xc6, 0x70, 0xfd, 0x70, 0x30, 0xb2, 0xf5,
0x18, 0xb9, 0x7a, 0x8c, 0x4e, 0x4d, 0x3d, 0x82, 0x82, 0x29, 0xf1, 0x61, 0x23, 0xc4, 0x14, 0x93,
0x10, 0x93, 0xb1, 0x76, 0x6d, 0xef, 0xb5, 0x86, 0xdd, 0xa0, 0x84, 0x11, 0x0f, 0x3a, 0xae, 0x76,
0x74, 0xcd, 0xa4, 0x9d, 0xc9, 0x84, 0xc2, 0xda, 0x15, 0x0a, 0x19, 0xf1, 0x84, 0x76, 0x8c, 0xca,
0x89, 0xe4, 0x13, 0xb8, 0xcb, 0xc6, 0x63, 0x4c, 0xd5, 0x29, 0x8e, 0x05, 0x2a, 0x49, 0xbb, 0xa6,
0x3a, 0x65, 0x90, 0x1c, 0xc1, 0x80, 0x85, 0x61, 0xa4, 0x22, 0x9e, 0xb0, 0xd8, 0x82, 0x27, 0x99,
0x4a, 0x33, 0x25, 0x29, 0x98, 0x9f, 0xb2, 0x48, 0xad, 0x33, 0xb3, 0x38, 0x62, 0x12, 0x25, 0x5d,
0x37, 0x96, 0x4e, 0x24, 0x43, 0xd8, 0xb4, 0x49, 0x5c, 0xd5, 0x25, 0xdd, 0x30, 0xb9, 0xe7, 0x61,
0x9f, 0xc1, 0x4e, 0xb9, 0x3b, 0x79, 0x5b, 0xb7, 0xa0, 0x95, 0x89, 0x24, 0xef, 0x8f, 0xfe, 0x9c,
0x2b, 0x70, 0x73, 0xe9, 0x02, 0xfb, 0xff, 0x01, 0x0c, 0x02, 0xbc, 0x88, 0xa4, 0x42, 0x31, 0xcf,
0x02, 0xd7, 0xf5, 0x46, 0x4d, 0xd7, 0x9b, 0xb5, 0x5d, 0x6f, 0x95, 0xba, 0xde, 0x87, 0xf6, 0x38,
0x93, 0x8a, 0x4f, 0x0c, 0x1b, 0x3a, 0x41, 0x2e, 0x91, 0x7d, 0x68, 0xf3, 0xb3, 0x3f, 0x70, 0xac,
0x6e, 0x62, 0x42, 0x6e, 0xa6, 0x6b, 0xa9, 0x55, 0xda, 0xa3, 0x6d, 0x22, 0x39, 0xb1, 0xc2, 0x8f,
0xb5, 0x1b, 0xf8, 0xd1, 0x99, 0xe3, 0x47, 0x0a, 0x3b, 0x79, 0x31, 0xa6, 0x4f, 0x8b, 0x71, 0xba,
0x7b, 0xad, 0xe1, 0xfa, 0xe1, 0x93, 0xd1, 0x6c, 0xb4, 0x47, 0x0b, 0x8a, 0x34, 0x7a, 0x59, 0xe3,
0xfe, 0x2c, 0x51, 0x62, 0x1a, 0xd4, 0x46, 0x26, 0x0f, 0x61, 0x3b, 0xc4, 0x18, 0x15, 0x7e, 0x83,
0xe7, 0x5c, 0x8f, 0x6a, 0x1a, 0xb3, 0x31, 0x52, 0x30, 0xe7, 0xaa, 0x53, 0x15, 0x39, 0xbc, 0x5e,
0xe1, 0x70, 0x74, 0x91, 0x70, 0x81, 0xc7, 0xaf, 0x58, 0x72, 0x61, 0x78, 0xa4, 0x8f, 0x5f, 0x06,
0xab, 0x4c, 0xbf, 0x7b, 0x4b, 0xa6, 0xf7, 0x96, 0x66, 0xfa, 0x66, 0x99, 0xe9, 0x1e, 0x74, 0xa2,
0x89, 0x5e, 0x34, 0xcf, 0x43, 0xba, 0x65, 0x2b, 0xef, 0x64, 0xf2, 0x0b, 0xf4, 0x2c, 0x1d, 0x7e,
0x8c, 0x26, 0xc8, 0x75, 0x9a, 0xf7, 0x0c, 0x19, 0x0e, 0x96, 0xa8, 0xf9, 0x71, 0xc9, 0x31, 0x98,
0x0b, 0x44, 0xbe, 0x02, 0xaf, 0xa6, 0x8e, 0x4f, 0xf1, 0x3c, 0x4a, 0x30, 0xa4, 0xc4, 0x9c, 0xfe,
0x1a, 0x0b, 0xf2, 0x39, 0xdc, 0x93, 0xf9, 0x42, 0x7d, 0xc9, 0x84, 0x8a, 0x58, 0xfc, 0x13, 0x8b,
0x33, 0x94, 0x74, 0xdb, 0xb8, 0xd6, 0x2b, 0x35, 0xdb, 0x05, 0x4e, 0xb8, 0x42, 0xba, 0x63, 0xd9,
0x6e, 0xa5, 0xba, 0x71, 0xbf, 0x57, 0x3b, 0xee, 0xe4, 0x04, 0xba, 0x8e, 0x98, 0x92, 0xf6, 0x0d,
0x03, 0x0f, 0x96, 0x63, 0xa0, 0xf5, 0xb1, 0xb4, 0x7b, 0x13, 0x83, 0x3c, 0x80, 0x2d, 0x61, 0x8f,
0x76, 0x92, 0x38, 0x8a, 0x0c, 0x4c, 0x8b, 0x2a, 0xb8, 0xf7, 0x00, 0x76, 0xea, 0xa8, 0xac, 0x07,
0x3e, 0x13, 0x89, 0xa4, 0x0d, 0xe3, 0x67, 0xbe, 0xbd, 0x9f, 0xa1, 0x57, 0x6e, 0x81, 0x19, 0x75,
0x81, 0x4c, 0xb9, 0x65, 0x91, 0x4b, 0x1a, 0xcf, 0xd2, 0x50, 0xe3, 0x76, 0x61, 0xe4, 0x92, 0xc6,
0x6d, 0x03, 0xdc, 0xca, 0xb0, 0x92, 0xf7, 0x67, 0x03, 0xee, 0x2f, 0x9c, 0x28, 0xbd, 0xf7, 0x2e,
0x71, 0xea, 0xf6, 0xde, 0x25, 0x4e, 0xc9, 0x0b, 0xb8, 0x73, 0xa5, 0xcb, 0x9f, 0xaf, 0xbc, 0x47,
0x6f, 0x39, 0xb0, 0x81, 0x8d, 0xf2, 0x45, 0xf3, 0xa8, 0xe1, 0x3d, 0x81, 0x5e, 0xb9, 0xa2, 0x35,
0x69, 0x77, 0x8a, 0x69, 0xbb, 0x05, 0x6f, 0xff, 0x9f, 0x16, 0xd0, 0x6a, 0xe6, 0x85, 0x7b, 0xdb,
0x5e, 0xb4, 0xcd, 0xd9, 0x45, 0xfb, 0x66, 0x35, 0xb6, 0x96, 0x5b, 0x8d, 0x7d, 0x68, 0x4b, 0xc5,
0xce, 0x62, 0x74, 0x3b, 0xd6, 0x4a, 0x7a, 0x28, 0xed, 0x97, 0xbe, 0x6e, 0xcd, 0x50, 0xe6, 0x22,
0x79, 0xbd, 0x60, 0xe5, 0xb5, 0x0d, 0xe1, 0xbe, 0xbc, 0xb6, 0x82, 0xf6, 0x1c, 0xb7, 0xdd, 0x79,
0xb7, 0xe2, 0xd6, 0x5f, 0xb7, 0x64, 0xc0, 0xf7, 0x65, 0x06, 0x1c, 0xbd, 0xed, 0xef, 0x2f, 0x36,
0x11, 0x61, 0x77, 0xde, 0x37, 0x5f, 0x76, 0xee, 0x6a, 0xac, 0x76, 0xf2, 0x00, 0xd6, 0x78, 0xbe,
0x2f, 0x6f, 0xb8, 0x7e, 0x9d, 0xdd, 0xe1, 0xbf, 0xab, 0xb0, 0xe9, 0xe2, 0xbf, 0xe0, 0x49, 0xa4,
0xb8, 0x20, 0xbf, 0xc2, 0xe6, 0xdc, 0x63, 0x8e, 0x7c, 0x54, 0x38, 0x52, 0xfd, 0x93, 0xd0, 0xf3,
0xaf, 0x33, 0xb1, 0x87, 0xf6, 0x57, 0xc8, 0xd7, 0xd0, 0x7e, 0x9e, 0x5c, 0xf1, 0x4b, 0x24, 0xb4,
0x60, 0x6f, 0x21, 0x17, 0xe9, 0x7e, 0x8d, 0x66, 0x16, 0xe0, 0x3b, 0xd8, 0x38, 0x55, 0x02, 0xd9,
0xe4, 0x9d, 0xc2, 0x3c, 0x6c, 0x90, 0xc7, 0xb0, 0x7a, 0xcc, 0xe2, 0x98, 0xf4, 0x0b, 0x66, 0x1a,
0x70, 0xee, 0x83, 0x0a, 0x3e, 0xfb, 0x0d, 0x3f, 0xc0, 0x46, 0xf1, 0x4d, 0x44, 0x76, 0x4b, 0x0d,
0xaf, 0x3c, 0x65, 0xbd, 0x0f, 0x17, 0xea, 0x67, 0x21, 0x7f, 0x83, 0xad, 0xf9, 0x76, 0x13, 0xff,
0xe6, 0x4d, 0xe2, 0x7d, 0xbc, 0x04, 0xd7, 0xfc, 0x15, 0xf2, 0x7b, 0xf5, 0x85, 0xe5, 0xae, 0xce,
0x4f, 0xaf, 0x89, 0x50, 0x66, 0x9c, 0xd7, 0xaf, 0xd0, 0xe9, 0x99, 0xfe, 0x6f, 0xe1, 0xaf, 0x9c,
0xb5, 0x0d, 0xf2, 0xd9, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0xf1, 0x1f, 0xaf, 0x2d, 0x98, 0x0c,
0x00, 0x00,
0x07, 0x9f, 0x06, 0xf1, 0x75, 0xf8, 0x0e, 0x68, 0x77, 0x6f, 0xdd, 0x3b, 0xdf, 0x39, 0x71, 0xda,
0xb7, 0x9b, 0xdf, 0xfc, 0xf3, 0xce, 0xfc, 0x66, 0x76, 0x0d, 0x3d, 0x81, 0x92, 0x67, 0x62, 0x8c,
0xa3, 0x54, 0x70, 0xc5, 0x49, 0x37, 0xcd, 0xe2, 0x6c, 0x12, 0x89, 0x74, 0xec, 0xbd, 0x7f, 0xc1,
0xf9, 0x45, 0x8c, 0xfb, 0x46, 0x71, 0x96, 0x9d, 0xef, 0xe3, 0x24, 0x55, 0x53, 0x6b, 0xe7, 0x7d,
0x30, 0xaf, 0x94, 0x4a, 0x64, 0x63, 0x95, 0x6b, 0x7b, 0xa9, 0xe0, 0x57, 0x51, 0x88, 0xc2, 0xca,
0xfe, 0x10, 0xfa, 0xa7, 0x59, 0x9a, 0x72, 0xa1, 0xe4, 0xb7, 0xc8, 0x54, 0x26, 0x30, 0xc0, 0xd7,
0x19, 0x4a, 0x45, 0x7a, 0xd0, 0x8c, 0x42, 0xda, 0xd8, 0x6b, 0x0c, 0xbb, 0x41, 0x33, 0x0a, 0xfd,
0xc7, 0x30, 0xa8, 0x58, 0xca, 0x94, 0x27, 0x12, 0xc9, 0x2e, 0xc0, 0x2b, 0x26, 0x73, 0xad, 0x71,
0xe9, 0x04, 0x05, 0xc4, 0xff, 0xbb, 0x05, 0xdb, 0x01, 0xb2, 0x30, 0xc8, 0x4f, 0xb4, 0x20, 0x05,
0x21, 0xb0, 0xaa, 0xa6, 0x29, 0xd2, 0xa6, 0x41, 0xcc, 0xb7, 0xc6, 0x12, 0x36, 0x41, 0xda, 0xb2,
0x98, 0xfe, 0x26, 0x7d, 0x68, 0xa7, 0x4c, 0x60, 0xa2, 0xe8, 0xaa, 0x41, 0x73, 0x89, 0x3c, 0x02,
0x48, 0x05, 0x4f, 0x51, 0xa8, 0x08, 0x25, 0xbd, 0xb3, 0xd7, 0x18, 0xae, 0x1f, 0x0e, 0x46, 0xb6,
0x1e, 0x23, 0x57, 0x8f, 0xd1, 0xa9, 0xa9, 0x47, 0x50, 0x30, 0x25, 0x3e, 0x6c, 0x84, 0x98, 0x62,
0x12, 0x62, 0x32, 0xd6, 0xae, 0xed, 0xbd, 0xd6, 0xb0, 0x1b, 0x94, 0x30, 0xe2, 0x41, 0xc7, 0xd5,
0x8e, 0xae, 0x99, 0xb4, 0x33, 0x99, 0x50, 0x58, 0xbb, 0x42, 0x21, 0x23, 0x9e, 0xd0, 0x8e, 0x51,
0x39, 0x91, 0x7c, 0x02, 0x77, 0xd9, 0x78, 0x8c, 0xa9, 0x3a, 0xc5, 0xb1, 0x40, 0x25, 0x69, 0xd7,
0x54, 0xa7, 0x0c, 0x92, 0x23, 0x18, 0xb0, 0x30, 0x8c, 0x54, 0xc4, 0x13, 0x16, 0x5b, 0xf0, 0x24,
0x53, 0x69, 0xa6, 0x24, 0x05, 0xf3, 0x53, 0x16, 0xa9, 0x75, 0x66, 0x16, 0x47, 0x4c, 0xa2, 0xa4,
0xeb, 0xc6, 0xd2, 0x89, 0x64, 0x08, 0x9b, 0x36, 0x89, 0xab, 0xba, 0xa4, 0x1b, 0x26, 0xf7, 0x3c,
0xec, 0x33, 0xd8, 0x29, 0x77, 0x27, 0x6f, 0xeb, 0x16, 0xb4, 0x32, 0x91, 0xe4, 0xfd, 0xd1, 0x9f,
0x73, 0x05, 0x6e, 0x2e, 0x5d, 0x60, 0xff, 0x3f, 0x80, 0x41, 0x80, 0x17, 0x91, 0x54, 0x28, 0xe6,
0x59, 0xe0, 0xba, 0xde, 0xa8, 0xe9, 0x7a, 0xb3, 0xb6, 0xeb, 0xad, 0x52, 0xd7, 0xfb, 0xd0, 0x1e,
0x67, 0x52, 0xf1, 0x89, 0x61, 0x43, 0x27, 0xc8, 0x25, 0xb2, 0x0f, 0x6d, 0x7e, 0xf6, 0x07, 0x8e,
0xd5, 0x4d, 0x4c, 0xc8, 0xcd, 0x74, 0x2d, 0xb5, 0x4a, 0x7b, 0xb4, 0x4d, 0x24, 0x27, 0x56, 0xf8,
0xb1, 0x76, 0x03, 0x3f, 0x3a, 0x73, 0xfc, 0x48, 0x61, 0x27, 0x2f, 0xc6, 0xf4, 0x69, 0x31, 0x4e,
0x77, 0xaf, 0x35, 0x5c, 0x3f, 0x7c, 0x32, 0x9a, 0x8d, 0xf6, 0x68, 0x41, 0x91, 0x46, 0x2f, 0x6b,
0xdc, 0x9f, 0x25, 0x4a, 0x4c, 0x83, 0xda, 0xc8, 0xe4, 0x21, 0x6c, 0x87, 0x18, 0xa3, 0xc2, 0x6f,
0xf0, 0x9c, 0xeb, 0x51, 0x4d, 0x63, 0x36, 0x46, 0x0a, 0xe6, 0x5c, 0x75, 0xaa, 0x22, 0x87, 0xd7,
0x2b, 0x1c, 0x8e, 0x2e, 0x12, 0x2e, 0xf0, 0xf8, 0x15, 0x4b, 0x2e, 0x0c, 0x8f, 0xf4, 0xf1, 0xcb,
0x60, 0x95, 0xe9, 0x77, 0x6f, 0xc9, 0xf4, 0xde, 0xd2, 0x4c, 0xdf, 0x2c, 0x33, 0xdd, 0x83, 0x4e,
0x34, 0xd1, 0x8b, 0xe6, 0x79, 0x48, 0xb7, 0x6c, 0xe5, 0x9d, 0x4c, 0x7e, 0x81, 0x9e, 0xa5, 0xc3,
0x8f, 0xd1, 0x04, 0xb9, 0x4e, 0xf3, 0x9e, 0x21, 0xc3, 0xc1, 0x12, 0x35, 0x3f, 0x2e, 0x39, 0x06,
0x73, 0x81, 0xc8, 0x57, 0xe0, 0xd5, 0xd4, 0xf1, 0x29, 0x9e, 0x47, 0x09, 0x86, 0x94, 0x98, 0xd3,
0x5f, 0x63, 0x41, 0x3e, 0x87, 0x7b, 0x32, 0x5f, 0xa8, 0x2f, 0x99, 0x50, 0x11, 0x8b, 0x7f, 0x62,
0x71, 0x86, 0x92, 0x6e, 0x1b, 0xd7, 0x7a, 0xa5, 0x66, 0xbb, 0xc0, 0x09, 0x57, 0x48, 0x77, 0x2c,
0xdb, 0xad, 0x54, 0x37, 0xee, 0xf7, 0x6a, 0xc7, 0x9d, 0x9c, 0x40, 0xd7, 0x11, 0x53, 0xd2, 0xbe,
0x61, 0xe0, 0xc1, 0x72, 0x0c, 0xb4, 0x3e, 0x96, 0x76, 0x6f, 0x62, 0x90, 0x07, 0xb0, 0x25, 0xec,
0xd1, 0x4e, 0x12, 0x47, 0x91, 0x81, 0x69, 0x51, 0x05, 0xcf, 0xaf, 0x0a, 0xd7, 0x72, 0x3a, 0xbb,
0x2a, 0x72, 0xc4, 0x7b, 0x00, 0x3b, 0x75, 0x54, 0xd7, 0x0b, 0x21, 0x13, 0x89, 0xa4, 0x0d, 0x13,
0xd7, 0x7c, 0x7b, 0x3f, 0x43, 0xaf, 0xdc, 0x22, 0xb3, 0x0a, 0x04, 0x32, 0xe5, 0x96, 0x49, 0x2e,
0x69, 0x3c, 0x4b, 0x43, 0x8d, 0xdb, 0x85, 0x92, 0x4b, 0x1a, 0xb7, 0x0d, 0x72, 0x2b, 0xc5, 0x4a,
0xde, 0x9f, 0x0d, 0xb8, 0xbf, 0x70, 0xe2, 0xf4, 0x5e, 0xbc, 0xc4, 0xa9, 0xdb, 0x8b, 0x97, 0x38,
0x25, 0x2f, 0xe0, 0xce, 0x95, 0x6e, 0x4f, 0xbe, 0x12, 0x1f, 0xbd, 0xe5, 0x40, 0x07, 0x36, 0xca,
0x17, 0xcd, 0xa3, 0x86, 0xf7, 0x04, 0x7a, 0xe5, 0x8a, 0xd7, 0xa4, 0xdd, 0x29, 0xa6, 0xed, 0x16,
0xbc, 0xfd, 0x7f, 0x5a, 0x40, 0xab, 0x99, 0x17, 0xee, 0x75, 0x7b, 0x11, 0x37, 0x67, 0x17, 0xf1,
0x9b, 0xd5, 0xd9, 0x5a, 0x6e, 0x75, 0xf6, 0xa1, 0x2d, 0x15, 0x3b, 0x8b, 0xd1, 0xed, 0x60, 0x2b,
0xe9, 0xa1, 0xb5, 0x5f, 0xfa, 0x3a, 0x36, 0x43, 0x9b, 0x8b, 0xe4, 0xf5, 0x82, 0x95, 0xd8, 0x36,
0x84, 0xfc, 0xf2, 0xda, 0x0a, 0xda, 0x73, 0xdc, 0x76, 0x27, 0xde, 0x8a, 0x5b, 0x7f, 0xdd, 0x92,
0x01, 0xdf, 0x97, 0x19, 0x70, 0xf4, 0xb6, 0xbf, 0xbf, 0xd8, 0x44, 0x84, 0xdd, 0x79, 0xdf, 0x7c,
0x4c, 0xdc, 0xd5, 0x59, 0xed, 0xe4, 0x01, 0xac, 0xf1, 0x7c, 0xb8, 0x6e, 0xb8, 0x9e, 0x9d, 0xdd,
0xe1, 0xbf, 0xab, 0xb0, 0xe9, 0xe2, 0xbf, 0xe0, 0x49, 0xa4, 0xb8, 0x20, 0xbf, 0xc2, 0xe6, 0xdc,
0x63, 0x8f, 0x7c, 0x54, 0x38, 0x52, 0xfd, 0x93, 0xd1, 0xf3, 0xaf, 0x33, 0xb1, 0x87, 0xf6, 0x57,
0xc8, 0xd7, 0xd0, 0x7e, 0x9e, 0x5c, 0xf1, 0x4b, 0x24, 0xb4, 0x60, 0x6f, 0x21, 0x17, 0xe9, 0x7e,
0x8d, 0x66, 0x16, 0xe0, 0x3b, 0xd8, 0x38, 0x55, 0x02, 0xd9, 0xe4, 0x9d, 0xc2, 0x3c, 0x6c, 0x90,
0xc7, 0xb0, 0x7a, 0xcc, 0xe2, 0x98, 0xf4, 0x0b, 0x66, 0x1a, 0x70, 0xee, 0x83, 0x0a, 0x3e, 0xfb,
0x0d, 0x3f, 0xc0, 0x46, 0xf1, 0xcd, 0x44, 0x76, 0x4b, 0x0d, 0xaf, 0x3c, 0x75, 0xbd, 0x0f, 0x17,
0xea, 0x67, 0x21, 0x7f, 0x83, 0xad, 0xf9, 0x76, 0x13, 0xff, 0xe6, 0x4d, 0xe2, 0x7d, 0xbc, 0x04,
0xd7, 0xfc, 0x15, 0xf2, 0x7b, 0xf5, 0x05, 0xe6, 0xae, 0xd6, 0x4f, 0xaf, 0x89, 0x50, 0x66, 0x9c,
0xd7, 0xaf, 0xd0, 0xe9, 0x99, 0xfe, 0xef, 0xe1, 0xaf, 0x9c, 0xb5, 0x0d, 0xf2, 0xd9, 0xff, 0x01,
0x00, 0x00, 0xff, 0xff, 0xa6, 0x36, 0x75, 0xcc, 0xb8, 0x0c, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.

View file

@ -105,6 +105,7 @@ message RegisterResourceRequest {
bool acceptResources = 21; // when true operations should return resource references as strongly typed.
map<string, string> providers = 22; // an optional reference to the provider map to manage this resource's CRUD operations.
repeated string replaceOnChanges = 23; // a list of properties that if changed should force a replacement.
bool hasOutputs = 24; // true if the object was serialized with output values.
}
// RegisterResourceResponse is returned by the engine after a resource has finished being initialized. It includes the

View file

@ -24,7 +24,7 @@ from ..invoke import InvokeOptions
from ..runtime.proto import provider_pb2
from . import rpc
from .rpc_manager import RPC_MANAGER
from .settings import get_monitor, grpc_error_to_exception, handle_grpc_error
from .settings import get_monitor, grpc_error_to_exception, handle_grpc_error, monitor_supports_output_values
from .sync_await import _sync_await
if TYPE_CHECKING:
@ -182,29 +182,35 @@ def call(tok: str, props: 'Inputs', res: Optional['Resource'] = None, typ: Optio
# Serialize out all props to their final values. In doing so, we'll also collect all the Resources pointed to
# by any Dependency objects we encounter, adding them to 'implicit_dependencies'.
# Note: If the monitor supports output values, we will not pass these collected dependencies as the
# dependencies are implicitly contained within the output values within the inputs.
property_dependencies_resources: Dict[str, List['Resource']] = {}
# We keep output values when serializing inputs for call.
inputs = await rpc.serialize_properties(props, property_dependencies_resources, keep_output_values=True)
property_dependencies = {}
for key, property_deps in property_dependencies_resources.items():
urns = set()
for dep in property_deps:
urn = await dep.urn.future()
urns.add(urn)
property_dependencies[key] = provider_pb2.CallRequest.ArgumentDependencies(urns=list(urns))
# Prepare the CallRequest arguments.
req_args = {
"tok": tok,
"args": inputs,
"provider": provider_ref,
"version": version,
}
req = provider_pb2.CallRequest(
tok=tok,
args=inputs,
argDependencies=property_dependencies,
provider=provider_ref,
version=version,
)
# Only include `argDependencies` in the request when the monitor does *not* support output values.
# When the monitor *does* support output values, the dependencies will already exist within the inputs.
if not await monitor_supports_output_values():
arg_dependencies = {}
for key, property_deps in property_dependencies_resources.items():
urns = set()
for dep in property_deps:
urn = await dep.urn.future()
urns.add(urn)
arg_dependencies[key] = provider_pb2.CallRequest.ArgumentDependencies(urns=list(urns))
req_args["argDependencies"] = arg_dependencies
def do_rpc_call():
try:
return monitor.Call(req)
return monitor.Call(provider_pb2.CallRequest(**req_args))
except grpc.RpcError as exn:
handle_grpc_error(exn)
return None

View file

@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor(
package='pulumirpc',
syntax='proto3',
serialized_options=None,
serialized_pb=b'\n\x0eresource.proto\x12\tpulumirpc\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x0eprovider.proto\"$\n\x16SupportsFeatureRequest\x12\n\n\x02id\x18\x01 \x01(\t\"-\n\x17SupportsFeatureResponse\x12\x12\n\nhasSupport\x18\x01 \x01(\x08\"\x95\x02\n\x13ReadResourceRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x0e\n\x06parent\x18\x04 \x01(\t\x12+\n\nproperties\x18\x05 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x14\n\x0c\x64\x65pendencies\x18\x06 \x03(\t\x12\x10\n\x08provider\x18\x07 \x01(\t\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\racceptSecrets\x18\t \x01(\x08\x12\x1f\n\x17\x61\x64\x64itionalSecretOutputs\x18\n \x03(\t\x12\x0f\n\x07\x61liases\x18\x0b \x03(\t\x12\x17\n\x0f\x61\x63\x63\x65ptResources\x18\x0c \x01(\x08\"P\n\x14ReadResourceResponse\x12\x0b\n\x03urn\x18\x01 \x01(\t\x12+\n\nproperties\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\xda\x07\n\x17RegisterResourceRequest\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0e\n\x06parent\x18\x03 \x01(\t\x12\x0e\n\x06\x63ustom\x18\x04 \x01(\x08\x12\'\n\x06object\x18\x05 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x0f\n\x07protect\x18\x06 \x01(\x08\x12\x14\n\x0c\x64\x65pendencies\x18\x07 \x03(\t\x12\x10\n\x08provider\x18\x08 \x01(\t\x12Z\n\x14propertyDependencies\x18\t \x03(\x0b\x32<.pulumirpc.RegisterResourceRequest.PropertyDependenciesEntry\x12\x1b\n\x13\x64\x65leteBeforeReplace\x18\n \x01(\x08\x12\x0f\n\x07version\x18\x0b \x01(\t\x12\x15\n\rignoreChanges\x18\x0c \x03(\t\x12\x15\n\racceptSecrets\x18\r \x01(\x08\x12\x1f\n\x17\x61\x64\x64itionalSecretOutputs\x18\x0e \x03(\t\x12\x0f\n\x07\x61liases\x18\x0f \x03(\t\x12\x10\n\x08importId\x18\x10 \x01(\t\x12I\n\x0e\x63ustomTimeouts\x18\x11 \x01(\x0b\x32\x31.pulumirpc.RegisterResourceRequest.CustomTimeouts\x12\"\n\x1a\x64\x65leteBeforeReplaceDefined\x18\x12 \x01(\x08\x12\x1d\n\x15supportsPartialValues\x18\x13 \x01(\x08\x12\x0e\n\x06remote\x18\x14 \x01(\x08\x12\x17\n\x0f\x61\x63\x63\x65ptResources\x18\x15 \x01(\x08\x12\x44\n\tproviders\x18\x16 \x03(\x0b\x32\x31.pulumirpc.RegisterResourceRequest.ProvidersEntry\x12\x18\n\x10replaceOnChanges\x18\x17 \x03(\t\x1a$\n\x14PropertyDependencies\x12\x0c\n\x04urns\x18\x01 \x03(\t\x1a@\n\x0e\x43ustomTimeouts\x12\x0e\n\x06\x63reate\x18\x01 \x01(\t\x12\x0e\n\x06update\x18\x02 \x01(\t\x12\x0e\n\x06\x64\x65lete\x18\x03 \x01(\t\x1at\n\x19PropertyDependenciesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x46\n\x05value\x18\x02 \x01(\x0b\x32\x37.pulumirpc.RegisterResourceRequest.PropertyDependencies:\x02\x38\x01\x1a\x30\n\x0eProvidersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xf7\x02\n\x18RegisterResourceResponse\x12\x0b\n\x03urn\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\t\x12\'\n\x06object\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x0e\n\x06stable\x18\x04 \x01(\x08\x12\x0f\n\x07stables\x18\x05 \x03(\t\x12[\n\x14propertyDependencies\x18\x06 \x03(\x0b\x32=.pulumirpc.RegisterResourceResponse.PropertyDependenciesEntry\x1a$\n\x14PropertyDependencies\x12\x0c\n\x04urns\x18\x01 \x03(\t\x1au\n\x19PropertyDependenciesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12G\n\x05value\x18\x02 \x01(\x0b\x32\x38.pulumirpc.RegisterResourceResponse.PropertyDependencies:\x02\x38\x01\"W\n\x1eRegisterResourceOutputsRequest\x12\x0b\n\x03urn\x18\x01 \x01(\t\x12(\n\x07outputs\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct2\xc4\x04\n\x0fResourceMonitor\x12Z\n\x0fSupportsFeature\x12!.pulumirpc.SupportsFeatureRequest\x1a\".pulumirpc.SupportsFeatureResponse\"\x00\x12?\n\x06Invoke\x12\x18.pulumirpc.InvokeRequest\x1a\x19.pulumirpc.InvokeResponse\"\x00\x12G\n\x0cStreamInvoke\x12\x18.pulumirpc.InvokeRequest\x1a\x19.pulumirpc.InvokeResponse\"\x00\x30\x01\x12\x39\n\x04\x43\x61ll\x12\x16.pulumirpc.CallRequest\x1a\x17.pulumirpc.CallResponse\"\x00\x12Q\n\x0cReadResource\x12\x1e.pulumirpc.ReadResourceRequest\x1a\x1f.pulumirpc.ReadResourceResponse\"\x00\x12]\n\x10RegisterResource\x12\".pulumirpc.RegisterResourceRequest\x1a#.pulumirpc.RegisterResourceResponse\"\x00\x12^\n\x17RegisterResourceOutputs\x12).pulumirpc.RegisterResourceOutputsRequest\x1a\x16.google.protobuf.Empty\"\x00\x62\x06proto3'
serialized_pb=b'\n\x0eresource.proto\x12\tpulumirpc\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x0eprovider.proto\"$\n\x16SupportsFeatureRequest\x12\n\n\x02id\x18\x01 \x01(\t\"-\n\x17SupportsFeatureResponse\x12\x12\n\nhasSupport\x18\x01 \x01(\x08\"\x95\x02\n\x13ReadResourceRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x0e\n\x06parent\x18\x04 \x01(\t\x12+\n\nproperties\x18\x05 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x14\n\x0c\x64\x65pendencies\x18\x06 \x03(\t\x12\x10\n\x08provider\x18\x07 \x01(\t\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\racceptSecrets\x18\t \x01(\x08\x12\x1f\n\x17\x61\x64\x64itionalSecretOutputs\x18\n \x03(\t\x12\x0f\n\x07\x61liases\x18\x0b \x03(\t\x12\x17\n\x0f\x61\x63\x63\x65ptResources\x18\x0c \x01(\x08\"P\n\x14ReadResourceResponse\x12\x0b\n\x03urn\x18\x01 \x01(\t\x12+\n\nproperties\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\xee\x07\n\x17RegisterResourceRequest\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0e\n\x06parent\x18\x03 \x01(\t\x12\x0e\n\x06\x63ustom\x18\x04 \x01(\x08\x12\'\n\x06object\x18\x05 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x0f\n\x07protect\x18\x06 \x01(\x08\x12\x14\n\x0c\x64\x65pendencies\x18\x07 \x03(\t\x12\x10\n\x08provider\x18\x08 \x01(\t\x12Z\n\x14propertyDependencies\x18\t \x03(\x0b\x32<.pulumirpc.RegisterResourceRequest.PropertyDependenciesEntry\x12\x1b\n\x13\x64\x65leteBeforeReplace\x18\n \x01(\x08\x12\x0f\n\x07version\x18\x0b \x01(\t\x12\x15\n\rignoreChanges\x18\x0c \x03(\t\x12\x15\n\racceptSecrets\x18\r \x01(\x08\x12\x1f\n\x17\x61\x64\x64itionalSecretOutputs\x18\x0e \x03(\t\x12\x0f\n\x07\x61liases\x18\x0f \x03(\t\x12\x10\n\x08importId\x18\x10 \x01(\t\x12I\n\x0e\x63ustomTimeouts\x18\x11 \x01(\x0b\x32\x31.pulumirpc.RegisterResourceRequest.CustomTimeouts\x12\"\n\x1a\x64\x65leteBeforeReplaceDefined\x18\x12 \x01(\x08\x12\x1d\n\x15supportsPartialValues\x18\x13 \x01(\x08\x12\x0e\n\x06remote\x18\x14 \x01(\x08\x12\x17\n\x0f\x61\x63\x63\x65ptResources\x18\x15 \x01(\x08\x12\x44\n\tproviders\x18\x16 \x03(\x0b\x32\x31.pulumirpc.RegisterResourceRequest.ProvidersEntry\x12\x18\n\x10replaceOnChanges\x18\x17 \x03(\t\x12\x12\n\nhasOutputs\x18\x18 \x01(\x08\x1a$\n\x14PropertyDependencies\x12\x0c\n\x04urns\x18\x01 \x03(\t\x1a@\n\x0e\x43ustomTimeouts\x12\x0e\n\x06\x63reate\x18\x01 \x01(\t\x12\x0e\n\x06update\x18\x02 \x01(\t\x12\x0e\n\x06\x64\x65lete\x18\x03 \x01(\t\x1at\n\x19PropertyDependenciesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x46\n\x05value\x18\x02 \x01(\x0b\x32\x37.pulumirpc.RegisterResourceRequest.PropertyDependencies:\x02\x38\x01\x1a\x30\n\x0eProvidersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xf7\x02\n\x18RegisterResourceResponse\x12\x0b\n\x03urn\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\t\x12\'\n\x06object\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x0e\n\x06stable\x18\x04 \x01(\x08\x12\x0f\n\x07stables\x18\x05 \x03(\t\x12[\n\x14propertyDependencies\x18\x06 \x03(\x0b\x32=.pulumirpc.RegisterResourceResponse.PropertyDependenciesEntry\x1a$\n\x14PropertyDependencies\x12\x0c\n\x04urns\x18\x01 \x03(\t\x1au\n\x19PropertyDependenciesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12G\n\x05value\x18\x02 \x01(\x0b\x32\x38.pulumirpc.RegisterResourceResponse.PropertyDependencies:\x02\x38\x01\"W\n\x1eRegisterResourceOutputsRequest\x12\x0b\n\x03urn\x18\x01 \x01(\t\x12(\n\x07outputs\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct2\xc4\x04\n\x0fResourceMonitor\x12Z\n\x0fSupportsFeature\x12!.pulumirpc.SupportsFeatureRequest\x1a\".pulumirpc.SupportsFeatureResponse\"\x00\x12?\n\x06Invoke\x12\x18.pulumirpc.InvokeRequest\x1a\x19.pulumirpc.InvokeResponse\"\x00\x12G\n\x0cStreamInvoke\x12\x18.pulumirpc.InvokeRequest\x1a\x19.pulumirpc.InvokeResponse\"\x00\x30\x01\x12\x39\n\x04\x43\x61ll\x12\x16.pulumirpc.CallRequest\x1a\x17.pulumirpc.CallResponse\"\x00\x12Q\n\x0cReadResource\x12\x1e.pulumirpc.ReadResourceRequest\x1a\x1f.pulumirpc.ReadResourceResponse\"\x00\x12]\n\x10RegisterResource\x12\".pulumirpc.RegisterResourceRequest\x1a#.pulumirpc.RegisterResourceResponse\"\x00\x12^\n\x17RegisterResourceOutputs\x12).pulumirpc.RegisterResourceOutputsRequest\x1a\x16.google.protobuf.Empty\"\x00\x62\x06proto3'
,
dependencies=[google_dot_protobuf_dot_empty__pb2.DESCRIPTOR,google_dot_protobuf_dot_struct__pb2.DESCRIPTOR,provider__pb2.DESCRIPTOR,])
@ -262,8 +262,8 @@ _REGISTERRESOURCEREQUEST_PROPERTYDEPENDENCIES = _descriptor.Descriptor(
extension_ranges=[],
oneofs=[
],
serialized_start=1268,
serialized_end=1304,
serialized_start=1288,
serialized_end=1324,
)
_REGISTERRESOURCEREQUEST_CUSTOMTIMEOUTS = _descriptor.Descriptor(
@ -306,8 +306,8 @@ _REGISTERRESOURCEREQUEST_CUSTOMTIMEOUTS = _descriptor.Descriptor(
extension_ranges=[],
oneofs=[
],
serialized_start=1306,
serialized_end=1370,
serialized_start=1326,
serialized_end=1390,
)
_REGISTERRESOURCEREQUEST_PROPERTYDEPENDENCIESENTRY = _descriptor.Descriptor(
@ -343,8 +343,8 @@ _REGISTERRESOURCEREQUEST_PROPERTYDEPENDENCIESENTRY = _descriptor.Descriptor(
extension_ranges=[],
oneofs=[
],
serialized_start=1372,
serialized_end=1488,
serialized_start=1392,
serialized_end=1508,
)
_REGISTERRESOURCEREQUEST_PROVIDERSENTRY = _descriptor.Descriptor(
@ -380,8 +380,8 @@ _REGISTERRESOURCEREQUEST_PROVIDERSENTRY = _descriptor.Descriptor(
extension_ranges=[],
oneofs=[
],
serialized_start=1490,
serialized_end=1538,
serialized_start=1510,
serialized_end=1558,
)
_REGISTERRESOURCEREQUEST = _descriptor.Descriptor(
@ -552,6 +552,13 @@ _REGISTERRESOURCEREQUEST = _descriptor.Descriptor(
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='hasOutputs', full_name='pulumirpc.RegisterResourceRequest.hasOutputs', index=23,
number=24, type=8, cpp_type=7, label=1,
has_default_value=False, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
@ -565,7 +572,7 @@ _REGISTERRESOURCEREQUEST = _descriptor.Descriptor(
oneofs=[
],
serialized_start=552,
serialized_end=1538,
serialized_end=1558,
)
@ -595,8 +602,8 @@ _REGISTERRESOURCERESPONSE_PROPERTYDEPENDENCIES = _descriptor.Descriptor(
extension_ranges=[],
oneofs=[
],
serialized_start=1268,
serialized_end=1304,
serialized_start=1288,
serialized_end=1324,
)
_REGISTERRESOURCERESPONSE_PROPERTYDEPENDENCIESENTRY = _descriptor.Descriptor(
@ -632,8 +639,8 @@ _REGISTERRESOURCERESPONSE_PROPERTYDEPENDENCIESENTRY = _descriptor.Descriptor(
extension_ranges=[],
oneofs=[
],
serialized_start=1799,
serialized_end=1916,
serialized_start=1819,
serialized_end=1936,
)
_REGISTERRESOURCERESPONSE = _descriptor.Descriptor(
@ -697,8 +704,8 @@ _REGISTERRESOURCERESPONSE = _descriptor.Descriptor(
extension_ranges=[],
oneofs=[
],
serialized_start=1541,
serialized_end=1916,
serialized_start=1561,
serialized_end=1936,
)
@ -735,8 +742,8 @@ _REGISTERRESOURCEOUTPUTSREQUEST = _descriptor.Descriptor(
extension_ranges=[],
oneofs=[
],
serialized_start=1918,
serialized_end=2005,
serialized_start=1938,
serialized_end=2025,
)
_READRESOURCEREQUEST.fields_by_name['properties'].message_type = google_dot_protobuf_dot_struct__pb2._STRUCT
@ -873,8 +880,8 @@ _RESOURCEMONITOR = _descriptor.ServiceDescriptor(
file=DESCRIPTOR,
index=0,
serialized_options=None,
serialized_start=2008,
serialized_end=2588,
serialized_start=2028,
serialized_end=2608,
methods=[
_descriptor.MethodDescriptor(
name='SupportsFeature',

View file

@ -75,6 +75,11 @@ class ResourceResolverOperations(NamedTuple):
A list of aliases applied to this resource.
"""
has_outputs: bool
"""
true if the object was serialized with output values.
"""
# Prepares for an RPC that will manufacture a resource, and hence deals with input and output properties.
# pylint: disable=too-many-locals
@ -102,9 +107,13 @@ async def prepare_resource(res: 'Resource',
# To initially scope the use of this new feature, we only keep output values when
# remote is true (for multi-lang components).
# Note: The serializer will check with the monitor to see if it supports output values.
serialized_props = await rpc.serialize_properties(props, property_dependencies_resources, translate, typ,
keep_output_values=remote)
# Keep track of whether we've kept output values when serializing.
has_outputs = remote and await settings.monitor_supports_output_values()
# Wait for our parent to resolve
parent_urn: Optional[str] = ""
if opts is not None and opts.parent is not None:
@ -165,6 +174,7 @@ async def prepare_resource(res: 'Resource',
provider_refs,
property_dependencies,
aliases,
has_outputs,
)
@ -504,6 +514,7 @@ def register_resource(res: 'Resource',
supportsPartialValues=True,
remote=remote,
replaceOnChanges=replace_on_changes,
hasOutputs=resolver.has_outputs,
)
from ..resource import create_urn # pylint: disable=import-outside-toplevel