Compare commits
50 commits
master
...
features/m
Author | SHA1 | Date | |
---|---|---|---|
ee7824deb6 | |||
401ecb15a9 | |||
e078b718a4 | |||
cdac8f47a5 | |||
5fb46412de | |||
0c92e7bc04 | |||
63603f0133 | |||
2a35a7283a | |||
3d07de0535 | |||
f23932c020 | |||
c41bccffce | |||
204b779cbb | |||
18db3b1156 | |||
8b50f3da6d | |||
b8d1c7b589 | |||
4339f2ad84 | |||
4a9665cb0d | |||
efa1f92cb4 | |||
bc41ee33c3 | |||
d68eee6632 | |||
b8c864a559 | |||
9d6edeac1c | |||
5eda91bb9e | |||
11efdb2dd6 | |||
e5d6d950a5 | |||
94f7efcf54 | |||
8f6a02920e | |||
c3ae4a5100 | |||
c3587ecded | |||
7c323f8f60 | |||
e2d6058d2e | |||
b3a20dfe47 | |||
19c3268f7b | |||
c5e4bea769 | |||
fd7296797a | |||
033924efa9 | |||
35d924b589 | |||
ccbeae1f5f | |||
0c53db0587 | |||
ddc222d236 | |||
51adc500de | |||
b37e47f46f | |||
abab631605 | |||
092ee049dc | |||
4042adef9d | |||
c2942c3bcc | |||
0c914ebfb4 | |||
e8242bca33 | |||
a41316d5b7 | |||
5c5f41d3e6 |
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -22,4 +22,5 @@ coverage.cov
|
|||
# By default, we don't check in yarn.lock files
|
||||
**/yarn.lock
|
||||
|
||||
# Turning on MyPy in VSCode creates this workspace local folder
|
||||
.mypy_cache
|
||||
|
|
1
examples/multilang/.gitignore
vendored
Normal file
1
examples/multilang/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
Pulumi.*.yaml
|
3
examples/multilang/nodejs/Pulumi.yaml
Normal file
3
examples/multilang/nodejs/Pulumi.yaml
Normal file
|
@ -0,0 +1,3 @@
|
|||
name: multilang
|
||||
runtime: nodejs
|
||||
description: A small example that uses our multilang support.
|
18
examples/multilang/nodejs/index.ts
Normal file
18
examples/multilang/nodejs/index.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import * as aws from "@pulumi/aws";
|
||||
import * as pulumi from "@pulumi/pulumi";
|
||||
import * as mycomponent from "./mycomponent/nodejs";
|
||||
|
||||
// This should go inside `@pulumi/aws`.
|
||||
pulumi.runtime.registerProxyConstructor("aws:ec2/securityGroup:SecurityGroup", aws.ec2.SecurityGroup);
|
||||
|
||||
////////////////////////////////
|
||||
// This is code the user would write to use `mycomponent` from the guest language.
|
||||
|
||||
const res = new mycomponent.MyComponent("n", {
|
||||
input1: Promise.resolve(42),
|
||||
}, { ignoreChanges: ["input1"] /*, providers: { "aws": awsProvider } */ });
|
||||
|
||||
export const id2 = res.myid;
|
||||
export const output1 = res.output1;
|
||||
export const innerComponent = res.innerComponent.data;
|
||||
export const nodeSecurityGroupId = res.nodeSecurityGroup.id;
|
35
examples/multilang/nodejs/mycomponent/index.js
Normal file
35
examples/multilang/nodejs/mycomponent/index.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
const pulumi = require("@pulumi/pulumi");
|
||||
const aws = require("@pulumi/aws");
|
||||
|
||||
// TODO: This should be done in the AWS library. Also, not clear that providers work correctly
|
||||
// currently - we appear to end up with calls to `Check` a provider before it has been configured -
|
||||
// I suspect that the proxy is getting confused as a separate identity even though it's the same
|
||||
// URN.
|
||||
pulumi.runtime.registerProxyConstructor("pulumi:providers:aws", aws.Provider)
|
||||
|
||||
class MyInnerComponent extends pulumi.ComponentResource {
|
||||
constructor(name, args, opts) {
|
||||
super("my:mod:MyInnerComponent", name, {}, opts);
|
||||
this.data = "mydata";
|
||||
this.registerOutputs({
|
||||
data: "mydata",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class MyComponent extends pulumi.ComponentResource {
|
||||
constructor(name, args, opts) {
|
||||
super("my:mod:MyComponent", name, {}, opts);
|
||||
this.output1 = pulumi.output(args.input1);
|
||||
this.myid = pulumi.output("foo");
|
||||
this.innerComponent = new MyInnerComponent("inner", {}, { parent: this });
|
||||
this.nodeSecurityGroup = new aws.ec2.SecurityGroup("securityGroup", {}, { parent: this });
|
||||
this.registerOutputs({
|
||||
myid: this.myid,
|
||||
output1: this.output1,
|
||||
innerComponent: this.innerComponent,
|
||||
nodeSecurityGroup: this.nodeSecurityGroup,
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.MyComponent = MyComponent;
|
54
examples/multilang/nodejs/mycomponent/nodejs/index.ts
Normal file
54
examples/multilang/nodejs/mycomponent/nodejs/index.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
// This file would be autogenerated from schema.
|
||||
|
||||
import * as aws from "@pulumi/aws";
|
||||
import * as pulumi from "@pulumi/pulumi";
|
||||
|
||||
export interface MyComponentArgs {
|
||||
input1: pulumi.Input<number>;
|
||||
}
|
||||
|
||||
export class MyComponent extends pulumi.remote.ProxyComponentResource {
|
||||
public myid!: pulumi.Output<string>;
|
||||
public output1!: pulumi.Output<number>;
|
||||
public innerComponent!: MyInnerComponent;
|
||||
public nodeSecurityGroup!: aws.ec2.SecurityGroup;
|
||||
constructor(name: string, args: MyComponentArgs, opts?: pulumi.ComponentResourceOptions) {
|
||||
super(
|
||||
"my:mod:MyComponent",
|
||||
name,
|
||||
require.resolve(".."),
|
||||
"MyComponent",
|
||||
args,
|
||||
{
|
||||
myid: undefined,
|
||||
output1: undefined,
|
||||
innerComponent: undefined,
|
||||
nodeSecurityGroup: undefined,
|
||||
},
|
||||
opts,
|
||||
);
|
||||
}
|
||||
}
|
||||
pulumi.runtime.registerProxyConstructor("my:mod:MyComponent", MyComponent);
|
||||
|
||||
|
||||
export interface MyInnerComponentArgs {
|
||||
}
|
||||
|
||||
export class MyInnerComponent extends pulumi.remote.ProxyComponentResource {
|
||||
public data!: pulumi.Output<string>;
|
||||
constructor(name: string, args: MyInnerComponentArgs, opts?: pulumi.ComponentResourceOptions) {
|
||||
super(
|
||||
"my:mod:MyInnerComponent",
|
||||
name,
|
||||
require.resolve(".."),
|
||||
"MyInnerComponent",
|
||||
args,
|
||||
{
|
||||
data: undefined,
|
||||
},
|
||||
opts,
|
||||
);
|
||||
}
|
||||
}
|
||||
pulumi.runtime.registerProxyConstructor("my:mod:MyInnerComponent", MyInnerComponent);
|
7
examples/multilang/nodejs/mycomponent/package.json
Normal file
7
examples/multilang/nodejs/mycomponent/package.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "@pulumi/eks",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@pulumi/pulumi": "latest"
|
||||
}
|
||||
}
|
12
examples/multilang/nodejs/package.json
Normal file
12
examples/multilang/nodejs/package.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"name": "multilang",
|
||||
"devDependencies": {
|
||||
"@types/node": "^8.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@pulumi/pulumi": "latest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pulumi/aws": "^1.17.0"
|
||||
}
|
||||
}
|
18
examples/multilang/nodejs/tsconfig.json
Normal file
18
examples/multilang/nodejs/tsconfig.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"outDir": "bin",
|
||||
"target": "es2016",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"pretty": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitReturns": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"files": [
|
||||
"index.ts"
|
||||
]
|
||||
}
|
4
examples/multilang/python/.gitignore
vendored
Normal file
4
examples/multilang/python/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
__pycache__
|
||||
Pipfile.lock
|
||||
*.egg-info
|
||||
package-lock.json
|
14
examples/multilang/python/Pipfile
Normal file
14
examples/multilang/python/Pipfile
Normal file
|
@ -0,0 +1,14 @@
|
|||
[[source]]
|
||||
name = "pypi"
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
|
||||
[dev-packages]
|
||||
mypy = "*"
|
||||
|
||||
[packages]
|
||||
pulumi = {editable = true,path = "./../../../sdk/python/env/src"}
|
||||
pulumi-aws = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.7"
|
3
examples/multilang/python/Pulumi.yaml
Normal file
3
examples/multilang/python/Pulumi.yaml
Normal file
|
@ -0,0 +1,3 @@
|
|||
name: multilang-python
|
||||
runtime: python
|
||||
description: A small example that uses our multilang support.
|
31
examples/multilang/python/__main__.py
Normal file
31
examples/multilang/python/__main__.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
# 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 pulumi
|
||||
from pulumi import ResourceOptions
|
||||
from pulumi_aws import ec2
|
||||
|
||||
# TODO - this should go inside `pulumi_aws`.
|
||||
pulumi.runtime.register_proxy_constructor("aws:ec2/securityGroup:SecurityGroup", lambda name, opts: ec2.SecurityGroup(name, ResourceOptions(**opts)))
|
||||
|
||||
######
|
||||
# This is code the user would write to use `mycomponent` from the guest language.
|
||||
|
||||
from pulumi_mycomponent import MyComponent
|
||||
|
||||
res = MyComponent("n", input1=42)
|
||||
|
||||
pulumi.export("id2", res.myid)
|
||||
pulumi.export("output1", res.output1)
|
||||
pulumi.export("innerComponent", res.innerComponent.data)
|
||||
pulumi.export("nodeSecurityGroupId", res.nodeSecurityGroup.id)
|
1
examples/multilang/python/mycomponent/README.md
Normal file
1
examples/multilang/python/mycomponent/README.md
Normal file
|
@ -0,0 +1 @@
|
|||
# MyComponent in Python
|
35
examples/multilang/python/mycomponent/index.js
Normal file
35
examples/multilang/python/mycomponent/index.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
const pulumi = require("@pulumi/pulumi");
|
||||
const aws = require("@pulumi/aws");
|
||||
|
||||
// TODO: This should be done in the AWS library. Also, not clear that providers work correctly
|
||||
// currently - we appear to end up with calls to `Check` a provider before it has been configured -
|
||||
// I suspect that the proxy is getting confused as a separate identity even though it's the same
|
||||
// URN.
|
||||
pulumi.runtime.registerProxyConstructor("pulumi:providers:aws", aws.Provider)
|
||||
|
||||
class MyInnerComponent extends pulumi.ComponentResource {
|
||||
constructor(name, args, opts) {
|
||||
super("my:mod:MyInnerComponent", name, {}, opts);
|
||||
this.data = "mydata";
|
||||
this.registerOutputs({
|
||||
data: "mydata",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class MyComponent extends pulumi.ComponentResource {
|
||||
constructor(name, args, opts) {
|
||||
super("my:mod:MyComponent", name, {}, opts);
|
||||
this.output1 = pulumi.output(args.input1);
|
||||
this.myid = pulumi.output("foo");
|
||||
this.innerComponent = new MyInnerComponent("inner", {}, { parent: this });
|
||||
this.nodeSecurityGroup = new aws.ec2.SecurityGroup("securityGroup", {}, { parent: this });
|
||||
this.registerOutputs({
|
||||
myid: this.myid,
|
||||
output1: this.output1,
|
||||
innerComponent: this.innerComponent,
|
||||
nodeSecurityGroup: this.nodeSecurityGroup,
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.MyComponent = MyComponent;
|
8
examples/multilang/python/mycomponent/package.json
Normal file
8
examples/multilang/python/mycomponent/package.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "@pulumi/mycomponent",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@pulumi/pulumi": "latest",
|
||||
"@pulumi/aws": "latest"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
# 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 os
|
||||
from pulumi import Output, ResourceOptions, Input
|
||||
from pulumi.remote import ProxyComponentResource
|
||||
from pulumi.runtime import register_proxy_constructor
|
||||
from typing import Optional
|
||||
from pulumi_aws import ec2
|
||||
|
||||
class MyInnerComponent(ProxyComponentResource):
|
||||
data: Output[str]
|
||||
def __init__(__self__, resource_name: str, opts: Optional[ResourceOptions]=None) -> None:
|
||||
super().__init__(
|
||||
"my:mod:MyInnerComponent",
|
||||
resource_name,
|
||||
os.path.abspath(os.path.join(os.path.dirname(__file__),"..")),
|
||||
"MyInnerComponent",
|
||||
{},
|
||||
{
|
||||
"data": None
|
||||
},
|
||||
opts,
|
||||
)
|
||||
register_proxy_constructor("my:mod:MyInnerComponent", lambda name, opts: MyInnerComponent(name, ResourceOptions(**opts)))
|
||||
|
||||
class MyComponent(ProxyComponentResource):
|
||||
myid: Output[str]
|
||||
output1: Output[int]
|
||||
innerComponent: MyInnerComponent
|
||||
nodeSecurityGroup: ec2.SecurityGroup
|
||||
def __init__(__self__, resource_name: str, opts: Optional[ResourceOptions]=None, input1:Optional[Input]=None) -> None:
|
||||
super().__init__(
|
||||
"my:mod:MyComponent",
|
||||
resource_name,
|
||||
os.path.abspath(os.path.join(os.path.dirname(__file__),"..")),
|
||||
"MyComponent",
|
||||
{
|
||||
"input1": input1
|
||||
},
|
||||
{
|
||||
"myid": None,
|
||||
"output1": None,
|
||||
"innerComponent": None,
|
||||
"nodeSecurityGroup": None
|
||||
},
|
||||
opts,
|
||||
)
|
||||
register_proxy_constructor("my:mod:MyComponent", MyComponent)
|
59
examples/multilang/python/mycomponent/setup.py
Normal file
59
examples/multilang/python/mycomponent/setup.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
# Copyright 2016-2020, 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 errno
|
||||
from setuptools import setup, find_packages
|
||||
from setuptools.command.install import install
|
||||
from setuptools.command.develop import develop
|
||||
from subprocess import check_call
|
||||
|
||||
def npm_install():
|
||||
# Using `yarn` here because this package is designed specifically to be used in our tests, and we need to be able to
|
||||
# `yarn link` to test at this layer.
|
||||
check_call(['yarn', 'install'])
|
||||
|
||||
def npm_link_pulumi():
|
||||
# Using `yarn` here because this package is designed specifically to be used in our tests, and we need to be able to
|
||||
# `yarn link` to test at this layer.
|
||||
check_call(['yarn', 'link', '@pulumi/pulumi'])
|
||||
|
||||
class InstallNPMPackageCommand(install):
|
||||
def run(self):
|
||||
install.run(self)
|
||||
npm_install()
|
||||
|
||||
class DevelopNPMPackageCommand(develop):
|
||||
def run(self):
|
||||
develop.run(self)
|
||||
npm_install()
|
||||
npm_link_pulumi()
|
||||
|
||||
def readme():
|
||||
with open('README.md', encoding='utf-8') as f:
|
||||
return f.read()
|
||||
|
||||
setup(
|
||||
name='pulumi_mycomponent',
|
||||
version='0.0.1',
|
||||
description='MyComponent',
|
||||
long_description=readme(),
|
||||
long_description_content_type='text/markdown',
|
||||
cmdclass={
|
||||
'install': InstallNPMPackageCommand,
|
||||
'develop': DevelopNPMPackageCommand,
|
||||
},
|
||||
url='https://github.com/pulumi/pulumi',
|
||||
license='Apache 2.0',
|
||||
packages=find_packages(),
|
||||
zip_safe=False)
|
2
examples/multilang/python/requirements.txt
Normal file
2
examples/multilang/python/requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
pulumi>=1.0.0,<2.0.0
|
||||
pulumi_aws>=1.0.0,<2.0.0
|
|
@ -370,6 +370,10 @@ func filterPropertyMap(propertyMap resource.PropertyMap, debug bool) resource.Pr
|
|||
}
|
||||
case resource.Secret:
|
||||
return "[secret]"
|
||||
case resource.Resource:
|
||||
return resource.Resource{
|
||||
Urn: filterPropertyValue(t.Urn),
|
||||
}
|
||||
case resource.Computed:
|
||||
return resource.Computed{
|
||||
Element: filterPropertyValue(t.Element),
|
||||
|
|
|
@ -5936,3 +5936,116 @@ func TestProviderInheritanceGolangLifecycle(t *testing.T) {
|
|||
}
|
||||
p.Run(t, nil)
|
||||
}
|
||||
|
||||
func TestReadStackResource(t *testing.T) {
|
||||
loaders := []*deploytest.ProviderLoader{
|
||||
deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
|
||||
return &deploytest.Provider{
|
||||
CreateF: func(urn resource.URN,
|
||||
inputs resource.PropertyMap, timeout float64) (resource.ID, resource.PropertyMap, resource.Status, error) {
|
||||
return resource.ID("id"), resource.PropertyMap{
|
||||
resource.PropertyKey("inprop"): inputs[resource.PropertyKey("inprop")],
|
||||
resource.PropertyKey("outprop"): resource.NewStringProperty("goodbye"),
|
||||
}, resource.StatusOK, nil
|
||||
},
|
||||
}, nil
|
||||
}),
|
||||
}
|
||||
|
||||
// Register a resource then read it back using its URN.
|
||||
program := deploytest.NewLanguageRuntime(func(info plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
inPropValue := "hello"
|
||||
var outPropValue string
|
||||
|
||||
outputsKey := resource.PropertyKey("outputs")
|
||||
outrespropKey := resource.PropertyKey("outresprop")
|
||||
outpropKey := resource.PropertyKey("outprop")
|
||||
idKey := resource.PropertyKey("id")
|
||||
inpropKey := resource.PropertyKey("inprop")
|
||||
urnKey := resource.PropertyKey("urn")
|
||||
|
||||
////////////////////////////////
|
||||
// Part 1: Custom Resource
|
||||
////////////////////////////////
|
||||
|
||||
aUrn, _, aProps, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
|
||||
Inputs: resource.PropertyMap{
|
||||
inpropKey: resource.NewStringProperty(inPropValue),
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
if !info.DryRun {
|
||||
outPropValue = aProps[outpropKey].StringValue()
|
||||
}
|
||||
|
||||
aReadProps, _, err := monitor.Invoke(tokens.ModuleMember("pulumi:pulumi:readStackResource"), resource.PropertyMap{
|
||||
urnKey: resource.NewStringProperty(string(aUrn)),
|
||||
}, "", "1.0.0")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, string(aUrn), aReadProps[urnKey].StringValue())
|
||||
assert.Equal(t, inPropValue, aReadProps[outputsKey].ObjectValue()[inpropKey].StringValue())
|
||||
// These two fields of the result are not available during the preview step
|
||||
if !info.DryRun {
|
||||
assert.Equal(t, "id", aReadProps[idKey].StringValue())
|
||||
assert.Equal(t, outPropValue, aReadProps[outputsKey].ObjectValue()[outpropKey].StringValue())
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// Part 2: Component Resource
|
||||
// with nested Resources
|
||||
////////////////////////////////
|
||||
|
||||
bUrn, _, _, err := monitor.RegisterResource("pkgB:m:typB", "resB", false, deploytest.ResourceOptions{
|
||||
Inputs: resource.PropertyMap{
|
||||
inpropKey: resource.NewStringProperty(inPropValue),
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Try to read the resource before "RegisterResourceOutputs" is called
|
||||
bReadProps, _, err := monitor.Invoke(tokens.ModuleMember("pulumi:pulumi:readStackResource"), resource.PropertyMap{
|
||||
urnKey: resource.NewStringProperty(string(bUrn)),
|
||||
}, "", "1.0.0")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, string(bUrn), bReadProps[urnKey].StringValue())
|
||||
assert.Equal(t, "", bReadProps[idKey].StringValue())
|
||||
if info.DryRun {
|
||||
// TODO: This does not seem intentional (or good) - but currently inputs are propagated into results of this
|
||||
// function during preview - only if this is called before `RegisterResourceOutputs` - but are not visible
|
||||
// to this function during update.
|
||||
assert.Equal(t, inPropValue, bReadProps[outputsKey].ObjectValue()[inpropKey].StringValue())
|
||||
}
|
||||
|
||||
err = monitor.RegisterResourceOutputs(bUrn, resource.PropertyMap{
|
||||
outrespropKey: resource.NewResourceProperty(resource.Resource{
|
||||
Urn: resource.NewStringProperty(string(aUrn)),
|
||||
}),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Try to read the resource again after "RegisterResourceOutputs" is called
|
||||
bReadProps, _, err = monitor.Invoke(tokens.ModuleMember("pulumi:pulumi:readStackResource"), resource.PropertyMap{
|
||||
urnKey: resource.NewStringProperty(string(bUrn)),
|
||||
}, "", "1.0.0")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, string(bUrn), bReadProps[urnKey].StringValue())
|
||||
assert.Equal(t, "", bReadProps[idKey].StringValue())
|
||||
assert.Equal(t, string(aUrn), bReadProps[outputsKey].ObjectValue()[outrespropKey].ResourceValue().Urn.StringValue())
|
||||
|
||||
return nil
|
||||
})
|
||||
host := deploytest.NewPluginHost(nil, nil, program, loaders...)
|
||||
|
||||
p := &TestPlan{
|
||||
Options: UpdateOptions{host: host},
|
||||
}
|
||||
|
||||
p.Steps = []TestStep{{Op: Update}}
|
||||
snap := p.Run(t, nil)
|
||||
|
||||
assert.Len(t, snap.Resources, 4)
|
||||
assert.Equal(t, string(snap.Resources[0].URN.Name()), "default")
|
||||
assert.Equal(t, string(snap.Resources[1].URN.Name()), "resA")
|
||||
assert.Equal(t, string(snap.Resources[2].URN.Name()), "default_1_0_0")
|
||||
assert.Equal(t, string(snap.Resources[3].URN.Name()), "resB")
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/pulumi/pulumi/sdk/v2/go/common/resource/plugin"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/tokens"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/util/contract"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/util/logging"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/workspace"
|
||||
)
|
||||
|
||||
|
@ -19,14 +20,16 @@ type builtinProvider struct {
|
|||
backendClient BackendClient
|
||||
context context.Context
|
||||
cancel context.CancelFunc
|
||||
plan *Plan
|
||||
}
|
||||
|
||||
func newBuiltinProvider(backendClient BackendClient) *builtinProvider {
|
||||
func newBuiltinProvider(backendClient BackendClient, plan *Plan) *builtinProvider {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
return &builtinProvider{
|
||||
backendClient: backendClient,
|
||||
context: ctx,
|
||||
cancel: cancel,
|
||||
plan: plan,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,9 +151,18 @@ func (p *builtinProvider) Read(urn resource.URN, id resource.ID,
|
|||
}, resource.StatusOK, nil
|
||||
}
|
||||
|
||||
// readStackOutputs returns the full set of stack outputs from a target stack.
|
||||
const readStackOutputs = "pulumi:pulumi:readStackOutputs"
|
||||
|
||||
// readStackResourceOutputs returns the resource outputs for every resource in a taraget stack. If
|
||||
// targeting the current stack, it returns results from the initial checkpoint, not the live
|
||||
// registered resource state.
|
||||
const readStackResourceOutputs = "pulumi:pulumi:readStackResourceOutputs"
|
||||
|
||||
// readStackResource returns the live registered resource outputs for the target resource from the
|
||||
// current stack.
|
||||
const readStackResource = "pulumi:pulumi:readStackResource"
|
||||
|
||||
func (p *builtinProvider) Invoke(tok tokens.ModuleMember,
|
||||
args resource.PropertyMap) (resource.PropertyMap, []plugin.CheckFailure, error) {
|
||||
|
||||
|
@ -167,6 +179,12 @@ func (p *builtinProvider) Invoke(tok tokens.ModuleMember,
|
|||
return nil, nil, err
|
||||
}
|
||||
return outs, nil, nil
|
||||
case readStackResource:
|
||||
outs, err := p.readStackResource(args)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return outs, nil, nil
|
||||
default:
|
||||
return nil, nil, errors.Errorf("unrecognized function name: '%v'", tok)
|
||||
}
|
||||
|
@ -241,3 +259,30 @@ func (p *builtinProvider) readStackResourceOutputs(inputs resource.PropertyMap)
|
|||
"outputs": resource.NewObjectProperty(outputs),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *builtinProvider) readStackResource(inputs resource.PropertyMap) (resource.PropertyMap, error) {
|
||||
logging.V(9).Infof("builtinProvider: %v(inputs=%v)", readStackResource, inputs)
|
||||
urnProp, ok := inputs["urn"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("readStackResource missing required 'urn' argument")
|
||||
}
|
||||
if !urnProp.IsString() {
|
||||
return nil, fmt.Errorf("readStackResource 'urn' argument expected string got %v", urnProp)
|
||||
}
|
||||
urn := resource.URN(urnProp.StringValue())
|
||||
|
||||
urnState, ok := p.plan.current.Load(urn)
|
||||
if !ok || urnState == nil {
|
||||
return nil, fmt.Errorf("resource '%s' not yet registered in current stack state", urn)
|
||||
}
|
||||
state := urnState.(*resource.State)
|
||||
contract.Assert(state.URN == urn)
|
||||
|
||||
logging.V(9).Infof("builtinProvider: %v(inputs=%v) => (outputs=%v)", readStackResource, inputs, state.Outputs)
|
||||
|
||||
return resource.PropertyMap{
|
||||
"urn": resource.NewStringProperty(string(state.URN)),
|
||||
"id": resource.NewStringProperty(string(state.ID)),
|
||||
"outputs": resource.NewObjectProperty(state.Outputs),
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -133,6 +133,25 @@ func (rm *ResourceMonitor) RegisterResource(t tokens.Type, name string, custom b
|
|||
return resource.URN(resp.Urn), resource.ID(resp.Id), outs, nil
|
||||
}
|
||||
|
||||
func (rm *ResourceMonitor) RegisterResourceOutputs(urn resource.URN, outputs resource.PropertyMap) error {
|
||||
// marshal outputs
|
||||
outs, err := plugin.MarshalProperties(outputs, plugin.MarshalOptions{KeepUnknowns: true, KeepResources: true})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// submit request
|
||||
_, err = rm.resmon.RegisterResourceOutputs(context.Background(), &pulumirpc.RegisterResourceOutputsRequest{
|
||||
Urn: string(urn),
|
||||
Outputs: outs,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rm *ResourceMonitor) ReadResource(t tokens.Type, name string, id resource.ID, parent resource.URN,
|
||||
inputs resource.PropertyMap, provider string, version string) (resource.URN, resource.PropertyMap, error) {
|
||||
|
||||
|
@ -191,7 +210,10 @@ func (rm *ResourceMonitor) Invoke(tok tokens.ModuleMember, inputs resource.Prope
|
|||
}
|
||||
|
||||
// unmarshal outputs
|
||||
outs, err := plugin.UnmarshalProperties(resp.Return, plugin.MarshalOptions{KeepUnknowns: true})
|
||||
outs, err := plugin.UnmarshalProperties(resp.Return, plugin.MarshalOptions{
|
||||
KeepUnknowns: true,
|
||||
KeepResources: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ package deploy
|
|||
import (
|
||||
"context"
|
||||
"math"
|
||||
"sync"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/pkg/errors"
|
||||
|
@ -117,6 +118,7 @@ type Plan struct {
|
|||
preview bool // true if this plan is to be previewed rather than applied.
|
||||
depGraph *graph.DependencyGraph // the dependency graph of the old snapshot
|
||||
providers *providers.Registry // the provider registry for this plan.
|
||||
current *sync.Map // a map of all current resources
|
||||
}
|
||||
|
||||
// addDefaultProviders adds any necessary default provider definitions and references to the given snapshot. Version
|
||||
|
@ -258,8 +260,19 @@ func NewPlan(ctx *plugin.Context, target *Target, prev *Snapshot, source Source,
|
|||
depGraph = graph.NewDependencyGraph(oldResources)
|
||||
}
|
||||
|
||||
plan := &Plan{
|
||||
ctx: ctx,
|
||||
target: target,
|
||||
prev: prev,
|
||||
olds: olds,
|
||||
source: source,
|
||||
localPolicyPackPaths: localPolicyPackPaths,
|
||||
preview: preview,
|
||||
depGraph: depGraph,
|
||||
}
|
||||
|
||||
// Create a new builtin provider. This provider implements features such as `getStack`.
|
||||
builtins := newBuiltinProvider(backendClient)
|
||||
builtins := newBuiltinProvider(backendClient, plan)
|
||||
|
||||
// Create a new provider registry. Although we really only need to pass in any providers that were present in the
|
||||
// old resource list, the registry itself will filter out other sorts of resources when processing the prior state,
|
||||
|
@ -269,17 +282,9 @@ func NewPlan(ctx *plugin.Context, target *Target, prev *Snapshot, source Source,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return &Plan{
|
||||
ctx: ctx,
|
||||
target: target,
|
||||
prev: prev,
|
||||
olds: olds,
|
||||
source: source,
|
||||
localPolicyPackPaths: localPolicyPackPaths,
|
||||
preview: preview,
|
||||
depGraph: depGraph,
|
||||
providers: reg,
|
||||
}, nil
|
||||
plan.providers = reg
|
||||
|
||||
return plan, nil
|
||||
}
|
||||
|
||||
func (p *Plan) Ctx() *plugin.Context { return p.ctx }
|
||||
|
|
|
@ -168,6 +168,7 @@ func (pe *planExecutor) Execute(callerCtx context.Context, opts Options, preview
|
|||
|
||||
// Set up a step generator for this plan.
|
||||
pe.stepGen = newStepGenerator(pe.plan, opts, updateTargetsOpt, replaceTargetsOpt)
|
||||
pe.plan.current = pe.stepGen.resourceStates
|
||||
|
||||
// Retire any pending deletes that are currently present in this plan.
|
||||
if res := pe.retirePendingDeletes(callerCtx, opts, preview); res != nil {
|
||||
|
|
|
@ -563,8 +563,9 @@ func (rm *resmon) Invoke(ctx context.Context, req *pulumirpc.InvokeRequest) (*pu
|
|||
return nil, errors.Wrapf(err, "invocation of %v returned an error", tok)
|
||||
}
|
||||
mret, err := plugin.MarshalProperties(ret, plugin.MarshalOptions{
|
||||
Label: label,
|
||||
KeepUnknowns: true,
|
||||
Label: label,
|
||||
KeepUnknowns: true,
|
||||
KeepResources: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to marshal %v return", tok)
|
||||
|
|
|
@ -50,7 +50,7 @@ func NewQuerySource(cancel context.Context, plugctx *plugin.Context, client Back
|
|||
provs ProviderSource) (QuerySource, error) {
|
||||
|
||||
// Create a new builtin provider. This provider implements features such as `getStack`.
|
||||
builtins := newBuiltinProvider(client)
|
||||
builtins := newBuiltinProvider(client, nil)
|
||||
|
||||
reg, err := providers.NewRegistry(plugctx.Host, nil, false, builtins)
|
||||
if err != nil {
|
||||
|
|
|
@ -16,6 +16,7 @@ package deploy
|
|||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pulumi/pulumi/pkg/v2/resource/deploy/providers"
|
||||
|
@ -61,7 +62,9 @@ type stepGenerator struct {
|
|||
pendingDeletes map[*resource.State]bool // set of resources (not URNs!) that are pending deletion
|
||||
providers map[resource.URN]*resource.State // URN map of providers that we have seen so far.
|
||||
resourceGoals map[resource.URN]*resource.Goal // URN map of goals for ALL resources we have seen so far.
|
||||
resourceStates map[resource.URN]*resource.State // URN map of state for ALL resources we have seen so far.
|
||||
|
||||
// resourceStates is exposed to other goroutines so needs to be a sync.Map.
|
||||
resourceStates *sync.Map // URN map of state for ALL resources we have seen so far.
|
||||
|
||||
// a map from URN to a list of property keys that caused the replacement of a dependent resource during a
|
||||
// delete-before-replace.
|
||||
|
@ -256,7 +259,7 @@ func (sg *stepGenerator) generateSteps(event RegisterResourceEvent) ([]Step, res
|
|||
// Mark the URN/resource as having been seen. So we can run analyzers on all resources seen, as well as
|
||||
// lookup providers for calculating replacement of resources that use the provider.
|
||||
sg.resourceGoals[urn] = goal
|
||||
sg.resourceStates[urn] = new
|
||||
sg.resourceStates.Store(urn, new)
|
||||
if providers.IsProviderType(goal.Type) {
|
||||
sg.providers[urn] = new
|
||||
}
|
||||
|
@ -1286,8 +1289,11 @@ func (sg *stepGenerator) calculateDependentReplacements(root *resource.State) ([
|
|||
|
||||
func (sg *stepGenerator) AnalyzeResources() result.Result {
|
||||
resourcesSeen := sg.resourceStates
|
||||
resources := make([]plugin.AnalyzerStackResource, 0, len(resourcesSeen))
|
||||
for urn, v := range resourcesSeen {
|
||||
// resources := make([]plugin.AnalyzerStackResource, 0, resourcesSeen.len(resourcesSeen))
|
||||
var resources []plugin.AnalyzerStackResource
|
||||
resourcesSeen.Range(func(key, val interface{}) bool {
|
||||
urn := key.(resource.URN)
|
||||
v := val.(*resource.State)
|
||||
goal := sg.resourceGoals[urn]
|
||||
resource := plugin.AnalyzerStackResource{
|
||||
AnalyzerResource: plugin.AnalyzerResource{
|
||||
|
@ -1320,7 +1326,8 @@ func (sg *stepGenerator) AnalyzeResources() result.Result {
|
|||
}
|
||||
}
|
||||
resources = append(resources, resource)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
analyzers := sg.plan.ctx.Host.ListAnalyzers()
|
||||
for _, analyzer := range analyzers {
|
||||
|
@ -1335,7 +1342,7 @@ func (sg *stepGenerator) AnalyzeResources() result.Result {
|
|||
// the default root stack URN.
|
||||
var urn resource.URN
|
||||
if d.URN != "" {
|
||||
if _, ok := resourcesSeen[d.URN]; ok {
|
||||
if _, ok := resourcesSeen.Load(d.URN); ok {
|
||||
urn = d.URN
|
||||
}
|
||||
}
|
||||
|
@ -1369,7 +1376,7 @@ func newStepGenerator(
|
|||
pendingDeletes: make(map[*resource.State]bool),
|
||||
providers: make(map[resource.URN]*resource.State),
|
||||
resourceGoals: make(map[resource.URN]*resource.Goal),
|
||||
resourceStates: make(map[resource.URN]*resource.State),
|
||||
resourceStates: &sync.Map{},
|
||||
dependentReplaceKeys: make(map[resource.URN][]resource.PropertyKey),
|
||||
aliased: make(map[resource.URN]resource.URN),
|
||||
}
|
||||
|
|
|
@ -543,6 +543,17 @@ func DeserializePropertyValue(v interface{}, dec config.Decrypter,
|
|||
cachingCrypter.insert(prop.SecretValue(), plaintext, ciphertext)
|
||||
}
|
||||
return prop, nil
|
||||
case resource.ResourceSig:
|
||||
urn, ok := objmap["urn"].(string)
|
||||
if !ok {
|
||||
return resource.PropertyValue{}, errors.New("malformed resource value: missing urn")
|
||||
}
|
||||
ev, err := DeserializePropertyValue(urn, dec)
|
||||
if err != nil {
|
||||
return resource.PropertyValue{}, err
|
||||
}
|
||||
return ev, nil
|
||||
|
||||
default:
|
||||
return resource.PropertyValue{}, errors.Errorf("unrecognized signature '%v' in property map", sig)
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ type MarshalOptions struct {
|
|||
ComputeAssetHashes bool // true if we are computing missing asset hashes on the fly.
|
||||
KeepSecrets bool // true if we are keeping secrets (otherwise we replace them with their underlying value).
|
||||
RejectAssets bool // true if we should return errors on Asset and Archive values.
|
||||
KeepResources bool // true if we are keeping resoures (otherwise we return raw urn).
|
||||
SkipInternalKeys bool // true to skip internal property keys (keys that start with "__") in the resulting map.
|
||||
}
|
||||
|
||||
|
@ -161,6 +162,16 @@ func MarshalPropertyValue(v resource.PropertyValue, opts MarshalOptions) (*struc
|
|||
"value": v.SecretValue().Element,
|
||||
})
|
||||
return MarshalPropertyValue(secret, opts)
|
||||
} else if v.IsResource() {
|
||||
if !opts.KeepResources {
|
||||
logging.V(5).Infof("marshalling resource value as raw urn as opts.KeepResources is false")
|
||||
return MarshalPropertyValue(v.ResourceValue().Urn, opts)
|
||||
}
|
||||
res := resource.NewObjectProperty(resource.PropertyMap{
|
||||
resource.SigKey: resource.NewStringProperty(resource.ResourceSig),
|
||||
"urn": v.ResourceValue().Urn,
|
||||
})
|
||||
return MarshalPropertyValue(res, opts)
|
||||
}
|
||||
|
||||
contract.Failf("Unrecognized property value in RPC[%s]: %v (type=%v)", opts.Label, v.V, reflect.TypeOf(v.V))
|
||||
|
@ -345,6 +356,16 @@ func UnmarshalPropertyValue(v *structpb.Value, opts MarshalOptions) (*resource.P
|
|||
}
|
||||
s := resource.MakeSecret(value)
|
||||
return &s, nil
|
||||
case resource.ResourceSig:
|
||||
urn, ok := obj["urn"]
|
||||
if !ok {
|
||||
return nil, errors.New("malformed RPC resource: missing urn")
|
||||
}
|
||||
if !urn.IsString() {
|
||||
return nil, errors.New("malformed RPC resource: urn not a string")
|
||||
}
|
||||
r := resource.NewResourceProperty(resource.Resource{Urn: urn})
|
||||
return &r, nil
|
||||
default:
|
||||
return nil, errors.Errorf("unrecognized signature '%v' in property map", sig)
|
||||
}
|
||||
|
|
|
@ -96,6 +96,10 @@ type Secret struct {
|
|||
Element PropertyValue
|
||||
}
|
||||
|
||||
type Resource struct {
|
||||
Urn PropertyValue
|
||||
}
|
||||
|
||||
type ReqError struct {
|
||||
K PropertyKey
|
||||
}
|
||||
|
@ -189,6 +193,7 @@ func NewObjectProperty(v PropertyMap) PropertyValue { return PropertyValue{v}
|
|||
func NewComputedProperty(v Computed) PropertyValue { return PropertyValue{v} }
|
||||
func NewOutputProperty(v Output) PropertyValue { return PropertyValue{v} }
|
||||
func NewSecretProperty(v *Secret) PropertyValue { return PropertyValue{v} }
|
||||
func NewResourceProperty(v Resource) PropertyValue { return PropertyValue{v} }
|
||||
|
||||
func MakeComputed(v PropertyValue) PropertyValue {
|
||||
return NewComputedProperty(Computed{Element: v})
|
||||
|
@ -202,6 +207,10 @@ func MakeSecret(v PropertyValue) PropertyValue {
|
|||
return NewSecretProperty(&Secret{Element: v})
|
||||
}
|
||||
|
||||
func MakeResource(v PropertyValue) PropertyValue {
|
||||
return NewResourceProperty(Resource{Urn: v})
|
||||
}
|
||||
|
||||
// NewPropertyValue turns a value into a property value, provided it is of a legal "JSON-like" kind.
|
||||
func NewPropertyValue(v interface{}) PropertyValue {
|
||||
return NewPropertyValueRepl(v, nil, nil)
|
||||
|
@ -255,6 +264,8 @@ func NewPropertyValueRepl(v interface{},
|
|||
return NewOutputProperty(t)
|
||||
case *Secret:
|
||||
return NewSecretProperty(t)
|
||||
case Resource:
|
||||
return NewResourceProperty(t)
|
||||
}
|
||||
|
||||
// Next, see if it's an array, slice, pointer or struct, and handle each accordingly.
|
||||
|
@ -382,6 +393,9 @@ func (v PropertyValue) OutputValue() Output { return v.V.(Output) }
|
|||
// SecretValue fetches the underlying secret value (panicking if it isn't a secret).
|
||||
func (v PropertyValue) SecretValue() *Secret { return v.V.(*Secret) }
|
||||
|
||||
// ResourceValue fetches the underlying resource value (panicking if it isn't a resource).
|
||||
func (v PropertyValue) ResourceValue() Resource { return v.V.(Resource) }
|
||||
|
||||
// IsNull returns true if the underlying value is a null.
|
||||
func (v PropertyValue) IsNull() bool {
|
||||
return v.V == nil
|
||||
|
@ -447,6 +461,12 @@ func (v PropertyValue) IsSecret() bool {
|
|||
return is
|
||||
}
|
||||
|
||||
// IsResource returns true if the underlying value is a resource value.
|
||||
func (v PropertyValue) IsResource() bool {
|
||||
_, is := v.V.(Resource)
|
||||
return is
|
||||
}
|
||||
|
||||
// TypeString returns a type representation of the property value's holder type.
|
||||
func (v PropertyValue) TypeString() string {
|
||||
if v.IsNull() {
|
||||
|
@ -471,6 +491,8 @@ func (v PropertyValue) TypeString() string {
|
|||
return "output<" + v.OutputValue().Element.TypeString() + ">"
|
||||
} else if v.IsSecret() {
|
||||
return "secret<" + v.SecretValue().Element.TypeString() + ">"
|
||||
} else if v.IsResource() {
|
||||
return "resource"
|
||||
}
|
||||
contract.Failf("Unrecognized PropertyValue type")
|
||||
return ""
|
||||
|
@ -514,6 +536,8 @@ func (v PropertyValue) MapRepl(replk func(string) (string, bool),
|
|||
return v.OutputValue()
|
||||
} else if v.IsSecret() {
|
||||
return v.SecretValue()
|
||||
} else if v.IsResource() {
|
||||
return v.ResourceValue()
|
||||
}
|
||||
contract.Assertf(v.IsObject(), "v is not Object '%v' instead", v.TypeString())
|
||||
return v.ObjectValue().MapRepl(replk, replv)
|
||||
|
@ -550,6 +574,9 @@ func HasSig(obj PropertyMap, match string) bool {
|
|||
// SecretSig is the unique secret signature.
|
||||
const SecretSig = "1b47061264138c4ac30d75fd1eb44270"
|
||||
|
||||
// ResourceSig is the unique resource signature.
|
||||
const ResourceSig = "5cf8f73096256a8f31e491e813e4eb8e"
|
||||
|
||||
// IsInternalPropertyKey returns true if the given property key is an internal key that should not be displayed to
|
||||
// users.
|
||||
func IsInternalPropertyKey(key PropertyKey) bool {
|
||||
|
|
|
@ -29,9 +29,10 @@ import * as asset from "./asset";
|
|||
import * as dynamic from "./dynamic";
|
||||
import * as iterable from "./iterable";
|
||||
import * as log from "./log";
|
||||
import * as remote from "./remote";
|
||||
import * as runtime from "./runtime";
|
||||
import * as utils from "./utils";
|
||||
export { asset, dynamic, iterable, log, runtime, utils };
|
||||
export { asset, dynamic, iterable, log, remote, runtime, utils };
|
||||
|
||||
// @pulumi is a deployment-only module. If someone tries to capture it, and we fail for some reason
|
||||
// we want to give a good message about what the problem likely is. Note that capturing a
|
||||
|
|
62
sdk/nodejs/proto/runtime_grpc_pb.js
Normal file
62
sdk/nodejs/proto/runtime_grpc_pb.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
// GENERATED CODE -- DO NOT EDIT!
|
||||
|
||||
// Original file comments:
|
||||
// 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.
|
||||
//
|
||||
'use strict';
|
||||
var grpc = require('@grpc/grpc-js');
|
||||
var runtime_pb = require('./runtime_pb.js');
|
||||
var plugin_pb = require('./plugin_pb.js');
|
||||
var google_protobuf_empty_pb = require('google-protobuf/google/protobuf/empty_pb.js');
|
||||
var google_protobuf_struct_pb = require('google-protobuf/google/protobuf/struct_pb.js');
|
||||
|
||||
function serialize_pulumirpc_ConstructRequest(arg) {
|
||||
if (!(arg instanceof runtime_pb.ConstructRequest)) {
|
||||
throw new Error('Expected argument of type pulumirpc.ConstructRequest');
|
||||
}
|
||||
return Buffer.from(arg.serializeBinary());
|
||||
}
|
||||
|
||||
function deserialize_pulumirpc_ConstructRequest(buffer_arg) {
|
||||
return runtime_pb.ConstructRequest.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_pulumirpc_ConstructResponse(arg) {
|
||||
if (!(arg instanceof runtime_pb.ConstructResponse)) {
|
||||
throw new Error('Expected argument of type pulumirpc.ConstructResponse');
|
||||
}
|
||||
return Buffer.from(arg.serializeBinary());
|
||||
}
|
||||
|
||||
function deserialize_pulumirpc_ConstructResponse(buffer_arg) {
|
||||
return runtime_pb.ConstructResponse.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
|
||||
var RuntimeService = exports.RuntimeService = {
|
||||
construct: {
|
||||
path: '/pulumirpc.Runtime/Construct',
|
||||
requestStream: false,
|
||||
responseStream: false,
|
||||
requestType: runtime_pb.ConstructRequest,
|
||||
responseType: runtime_pb.ConstructResponse,
|
||||
requestSerialize: serialize_pulumirpc_ConstructRequest,
|
||||
requestDeserialize: deserialize_pulumirpc_ConstructRequest,
|
||||
responseSerialize: serialize_pulumirpc_ConstructResponse,
|
||||
responseDeserialize: deserialize_pulumirpc_ConstructResponse,
|
||||
},
|
||||
};
|
||||
|
||||
exports.RuntimeClient = grpc.makeGenericClientConstructor(RuntimeService);
|
508
sdk/nodejs/proto/runtime_pb.js
Normal file
508
sdk/nodejs/proto/runtime_pb.js
Normal file
|
@ -0,0 +1,508 @@
|
|||
// source: runtime.proto
|
||||
/**
|
||||
* @fileoverview
|
||||
* @enhanceable
|
||||
* @suppress {messageConventions} JS Compiler reports an error if a variable or
|
||||
* field starts with 'MSG_' and isn't a translatable message.
|
||||
* @public
|
||||
*/
|
||||
// GENERATED CODE -- DO NOT EDIT!
|
||||
|
||||
var jspb = require('google-protobuf');
|
||||
var goog = jspb;
|
||||
var proto = { pulumirpc: {} }, global = proto;
|
||||
|
||||
var plugin_pb = require('./plugin_pb.js');
|
||||
goog.object.extend(proto, plugin_pb);
|
||||
var google_protobuf_empty_pb = require('google-protobuf/google/protobuf/empty_pb.js');
|
||||
goog.object.extend(proto, google_protobuf_empty_pb);
|
||||
var google_protobuf_struct_pb = require('google-protobuf/google/protobuf/struct_pb.js');
|
||||
goog.object.extend(proto, google_protobuf_struct_pb);
|
||||
goog.exportSymbol('proto.pulumirpc.ConstructRequest', null, global);
|
||||
goog.exportSymbol('proto.pulumirpc.ConstructResponse', null, global);
|
||||
/**
|
||||
* Generated by JsPbCodeGenerator.
|
||||
* @param {Array=} opt_data Optional initial data array, typically from a
|
||||
* server response, or constructed directly in Javascript. The array is used
|
||||
* in place and becomes part of the constructed object. It is not cloned.
|
||||
* If no data is provided, the constructed object will be empty, but still
|
||||
* valid.
|
||||
* @extends {jspb.Message}
|
||||
* @constructor
|
||||
*/
|
||||
proto.pulumirpc.ConstructRequest = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
};
|
||||
goog.inherits(proto.pulumirpc.ConstructRequest, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
/**
|
||||
* @public
|
||||
* @override
|
||||
*/
|
||||
proto.pulumirpc.ConstructRequest.displayName = 'proto.pulumirpc.ConstructRequest';
|
||||
}
|
||||
/**
|
||||
* Generated by JsPbCodeGenerator.
|
||||
* @param {Array=} opt_data Optional initial data array, typically from a
|
||||
* server response, or constructed directly in Javascript. The array is used
|
||||
* in place and becomes part of the constructed object. It is not cloned.
|
||||
* If no data is provided, the constructed object will be empty, but still
|
||||
* valid.
|
||||
* @extends {jspb.Message}
|
||||
* @constructor
|
||||
*/
|
||||
proto.pulumirpc.ConstructResponse = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
};
|
||||
goog.inherits(proto.pulumirpc.ConstructResponse, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
/**
|
||||
* @public
|
||||
* @override
|
||||
*/
|
||||
proto.pulumirpc.ConstructResponse.displayName = 'proto.pulumirpc.ConstructResponse';
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto.
|
||||
* Field names that are reserved in JavaScript and will be renamed to pb_name.
|
||||
* Optional fields that are not set will be set to undefined.
|
||||
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
|
||||
* For the list of reserved names please see:
|
||||
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
|
||||
* @param {boolean=} opt_includeInstance Deprecated. whether to include the
|
||||
* JSPB instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.pulumirpc.ConstructRequest.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.pulumirpc.ConstructRequest.toObject(opt_includeInstance, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Static version of the {@see toObject} method.
|
||||
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
|
||||
* the JSPB instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @param {!proto.pulumirpc.ConstructRequest} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.pulumirpc.ConstructRequest.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
librarypath: jspb.Message.getFieldWithDefault(msg, 1, ""),
|
||||
resource: jspb.Message.getFieldWithDefault(msg, 2, ""),
|
||||
name: jspb.Message.getFieldWithDefault(msg, 3, ""),
|
||||
args: (f = msg.getArgs()) && google_protobuf_struct_pb.Struct.toObject(includeInstance, f),
|
||||
opts: (f = msg.getOpts()) && google_protobuf_struct_pb.Struct.toObject(includeInstance, f)
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
obj.$jspbMessageInstance = msg;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format).
|
||||
* @param {jspb.ByteSource} bytes The bytes to deserialize.
|
||||
* @return {!proto.pulumirpc.ConstructRequest}
|
||||
*/
|
||||
proto.pulumirpc.ConstructRequest.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.pulumirpc.ConstructRequest;
|
||||
return proto.pulumirpc.ConstructRequest.deserializeBinaryFromReader(msg, reader);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format) from the
|
||||
* given reader into the given message object.
|
||||
* @param {!proto.pulumirpc.ConstructRequest} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.pulumirpc.ConstructRequest}
|
||||
*/
|
||||
proto.pulumirpc.ConstructRequest.deserializeBinaryFromReader = function(msg, reader) {
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup()) {
|
||||
break;
|
||||
}
|
||||
var field = reader.getFieldNumber();
|
||||
switch (field) {
|
||||
case 1:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setLibrarypath(value);
|
||||
break;
|
||||
case 2:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setResource(value);
|
||||
break;
|
||||
case 3:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setName(value);
|
||||
break;
|
||||
case 4:
|
||||
var value = new google_protobuf_struct_pb.Struct;
|
||||
reader.readMessage(value,google_protobuf_struct_pb.Struct.deserializeBinaryFromReader);
|
||||
msg.setArgs(value);
|
||||
break;
|
||||
case 5:
|
||||
var value = new google_protobuf_struct_pb.Struct;
|
||||
reader.readMessage(value,google_protobuf_struct_pb.Struct.deserializeBinaryFromReader);
|
||||
msg.setOpts(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format).
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
proto.pulumirpc.ConstructRequest.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
proto.pulumirpc.ConstructRequest.serializeBinaryToWriter(this, writer);
|
||||
return writer.getResultBuffer();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the given message to binary data (in protobuf wire
|
||||
* format), writing to the given BinaryWriter.
|
||||
* @param {!proto.pulumirpc.ConstructRequest} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.pulumirpc.ConstructRequest.serializeBinaryToWriter = function(message, writer) {
|
||||
var f = undefined;
|
||||
f = message.getLibrarypath();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
1,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getResource();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
2,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getName();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
3,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getArgs();
|
||||
if (f != null) {
|
||||
writer.writeMessage(
|
||||
4,
|
||||
f,
|
||||
google_protobuf_struct_pb.Struct.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
f = message.getOpts();
|
||||
if (f != null) {
|
||||
writer.writeMessage(
|
||||
5,
|
||||
f,
|
||||
google_protobuf_struct_pb.Struct.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string libraryPath = 1;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.pulumirpc.ConstructRequest.prototype.getLibrarypath = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.pulumirpc.ConstructRequest} returns this
|
||||
*/
|
||||
proto.pulumirpc.ConstructRequest.prototype.setLibrarypath = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 1, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string resource = 2;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.pulumirpc.ConstructRequest.prototype.getResource = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.pulumirpc.ConstructRequest} returns this
|
||||
*/
|
||||
proto.pulumirpc.ConstructRequest.prototype.setResource = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 2, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string name = 3;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.pulumirpc.ConstructRequest.prototype.getName = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.pulumirpc.ConstructRequest} returns this
|
||||
*/
|
||||
proto.pulumirpc.ConstructRequest.prototype.setName = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 3, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional google.protobuf.Struct args = 4;
|
||||
* @return {?proto.google.protobuf.Struct}
|
||||
*/
|
||||
proto.pulumirpc.ConstructRequest.prototype.getArgs = function() {
|
||||
return /** @type{?proto.google.protobuf.Struct} */ (
|
||||
jspb.Message.getWrapperField(this, google_protobuf_struct_pb.Struct, 4));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {?proto.google.protobuf.Struct|undefined} value
|
||||
* @return {!proto.pulumirpc.ConstructRequest} returns this
|
||||
*/
|
||||
proto.pulumirpc.ConstructRequest.prototype.setArgs = function(value) {
|
||||
return jspb.Message.setWrapperField(this, 4, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears the message field making it undefined.
|
||||
* @return {!proto.pulumirpc.ConstructRequest} returns this
|
||||
*/
|
||||
proto.pulumirpc.ConstructRequest.prototype.clearArgs = function() {
|
||||
return this.setArgs(undefined);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this field is set.
|
||||
* @return {boolean}
|
||||
*/
|
||||
proto.pulumirpc.ConstructRequest.prototype.hasArgs = function() {
|
||||
return jspb.Message.getField(this, 4) != null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional google.protobuf.Struct opts = 5;
|
||||
* @return {?proto.google.protobuf.Struct}
|
||||
*/
|
||||
proto.pulumirpc.ConstructRequest.prototype.getOpts = function() {
|
||||
return /** @type{?proto.google.protobuf.Struct} */ (
|
||||
jspb.Message.getWrapperField(this, google_protobuf_struct_pb.Struct, 5));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {?proto.google.protobuf.Struct|undefined} value
|
||||
* @return {!proto.pulumirpc.ConstructRequest} returns this
|
||||
*/
|
||||
proto.pulumirpc.ConstructRequest.prototype.setOpts = function(value) {
|
||||
return jspb.Message.setWrapperField(this, 5, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears the message field making it undefined.
|
||||
* @return {!proto.pulumirpc.ConstructRequest} returns this
|
||||
*/
|
||||
proto.pulumirpc.ConstructRequest.prototype.clearOpts = function() {
|
||||
return this.setOpts(undefined);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this field is set.
|
||||
* @return {boolean}
|
||||
*/
|
||||
proto.pulumirpc.ConstructRequest.prototype.hasOpts = function() {
|
||||
return jspb.Message.getField(this, 5) != null;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto.
|
||||
* Field names that are reserved in JavaScript and will be renamed to pb_name.
|
||||
* Optional fields that are not set will be set to undefined.
|
||||
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
|
||||
* For the list of reserved names please see:
|
||||
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
|
||||
* @param {boolean=} opt_includeInstance Deprecated. whether to include the
|
||||
* JSPB instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.pulumirpc.ConstructResponse.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.pulumirpc.ConstructResponse.toObject(opt_includeInstance, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Static version of the {@see toObject} method.
|
||||
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
|
||||
* the JSPB instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @param {!proto.pulumirpc.ConstructResponse} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.pulumirpc.ConstructResponse.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
outs: (f = msg.getOuts()) && google_protobuf_struct_pb.Struct.toObject(includeInstance, f)
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
obj.$jspbMessageInstance = msg;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format).
|
||||
* @param {jspb.ByteSource} bytes The bytes to deserialize.
|
||||
* @return {!proto.pulumirpc.ConstructResponse}
|
||||
*/
|
||||
proto.pulumirpc.ConstructResponse.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.pulumirpc.ConstructResponse;
|
||||
return proto.pulumirpc.ConstructResponse.deserializeBinaryFromReader(msg, reader);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format) from the
|
||||
* given reader into the given message object.
|
||||
* @param {!proto.pulumirpc.ConstructResponse} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.pulumirpc.ConstructResponse}
|
||||
*/
|
||||
proto.pulumirpc.ConstructResponse.deserializeBinaryFromReader = function(msg, reader) {
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup()) {
|
||||
break;
|
||||
}
|
||||
var field = reader.getFieldNumber();
|
||||
switch (field) {
|
||||
case 1:
|
||||
var value = new google_protobuf_struct_pb.Struct;
|
||||
reader.readMessage(value,google_protobuf_struct_pb.Struct.deserializeBinaryFromReader);
|
||||
msg.setOuts(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format).
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
proto.pulumirpc.ConstructResponse.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
proto.pulumirpc.ConstructResponse.serializeBinaryToWriter(this, writer);
|
||||
return writer.getResultBuffer();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the given message to binary data (in protobuf wire
|
||||
* format), writing to the given BinaryWriter.
|
||||
* @param {!proto.pulumirpc.ConstructResponse} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.pulumirpc.ConstructResponse.serializeBinaryToWriter = function(message, writer) {
|
||||
var f = undefined;
|
||||
f = message.getOuts();
|
||||
if (f != null) {
|
||||
writer.writeMessage(
|
||||
1,
|
||||
f,
|
||||
google_protobuf_struct_pb.Struct.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional google.protobuf.Struct outs = 1;
|
||||
* @return {?proto.google.protobuf.Struct}
|
||||
*/
|
||||
proto.pulumirpc.ConstructResponse.prototype.getOuts = function() {
|
||||
return /** @type{?proto.google.protobuf.Struct} */ (
|
||||
jspb.Message.getWrapperField(this, google_protobuf_struct_pb.Struct, 1));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {?proto.google.protobuf.Struct|undefined} value
|
||||
* @return {!proto.pulumirpc.ConstructResponse} returns this
|
||||
*/
|
||||
proto.pulumirpc.ConstructResponse.prototype.setOuts = function(value) {
|
||||
return jspb.Message.setWrapperField(this, 1, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears the message field making it undefined.
|
||||
* @return {!proto.pulumirpc.ConstructResponse} returns this
|
||||
*/
|
||||
proto.pulumirpc.ConstructResponse.prototype.clearOuts = function() {
|
||||
return this.setOuts(undefined);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this field is set.
|
||||
* @return {boolean}
|
||||
*/
|
||||
proto.pulumirpc.ConstructResponse.prototype.hasOuts = function() {
|
||||
return jspb.Message.getField(this, 1) != null;
|
||||
};
|
||||
|
||||
|
||||
goog.object.extend(exports, proto.pulumirpc);
|
15
sdk/nodejs/remote/index.ts
Normal file
15
sdk/nodejs/remote/index.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
// 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.
|
||||
|
||||
export * from "./proxy";
|
47
sdk/nodejs/remote/proxy.ts
Normal file
47
sdk/nodejs/remote/proxy.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
// 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 * as pulumi from "../";
|
||||
import { getRemoteServer } from "./remoteServer";
|
||||
|
||||
/**
|
||||
* ProxyComponentResource is the abstract base class for proxies around component resources.
|
||||
*/
|
||||
export abstract class ProxyComponentResource extends pulumi.ComponentResource {
|
||||
constructor(
|
||||
t: string,
|
||||
name: string,
|
||||
libraryPath: string,
|
||||
libraryName: string,
|
||||
inputs: pulumi.Inputs,
|
||||
outputs: Record<string, undefined>,
|
||||
opts: pulumi.ComponentResourceOptions = {}) {
|
||||
// There are two cases:
|
||||
// 1. A URN was provided - in this case we are just going to look up the existing resource
|
||||
// and populate this proxy from that URN.
|
||||
// 2. A URN was not provided - in this case we are going to remotely construct the resource,
|
||||
// get the URN from the newly constructed resource, then look it up and populate this
|
||||
// proxy from that URN.
|
||||
if (!opts.urn) {
|
||||
const p = getRemoteServer().construct(libraryPath, libraryName, name, inputs, opts);
|
||||
const urn = p.then(r => <string>r.urn);
|
||||
opts = pulumi.mergeOptions(opts, { urn });
|
||||
}
|
||||
const props = {
|
||||
...inputs,
|
||||
...outputs,
|
||||
};
|
||||
super(t, name, props, opts);
|
||||
}
|
||||
}
|
99
sdk/nodejs/remote/remoteServer.ts
Normal file
99
sdk/nodejs/remote/remoteServer.ts
Normal file
|
@ -0,0 +1,99 @@
|
|||
// 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 * as grpc from "@grpc/grpc-js";
|
||||
import * as child_process from "child_process";
|
||||
import * as readline from "readline";
|
||||
import * as runtime from "../runtime";
|
||||
import * as settings from "../runtime/settings";
|
||||
|
||||
//tslint:disable
|
||||
const gstruct = require("google-protobuf/google/protobuf/struct_pb.js");
|
||||
const runtimeServiceProto = require("../proto/runtime_grpc_pb.js");
|
||||
const runtimeProto = require("../proto/runtime_pb.js");
|
||||
|
||||
let remoteServer: RemoteServer | undefined;
|
||||
export function getRemoteServer(): RemoteServer {
|
||||
if (!remoteServer) {
|
||||
remoteServer = new RemoteServer();
|
||||
}
|
||||
return remoteServer;
|
||||
}
|
||||
|
||||
export class RemoteServer {
|
||||
private readonly client: Promise<any>;
|
||||
constructor() {
|
||||
// Spawn a Node.js process to run a remote server.
|
||||
const subprocess = child_process.spawn(process.execPath, [require.resolve("./server")], {
|
||||
// Listen to stdout, ignore stdin and stderr.
|
||||
stdio: ["ignore", "pipe", "ignore"],
|
||||
env: {
|
||||
...process.env,
|
||||
// We overwrite what we inherited from our own environment just in case any of these were
|
||||
// programmatically set during execution. This is important for example if test mode was enabled
|
||||
// programatically.
|
||||
'PULUMI_NODEJS_PROJECT': settings.getProject(),
|
||||
'PULUMI_NODEJS_STACK': settings.getStack(),
|
||||
'PULUMI_NODEJS_DRY_RUN': settings.isDryRun() ? "true" : "false",
|
||||
'PULUMI_NODEJS_QUERY_MODE': settings.isQueryMode() ? "true": "false",
|
||||
'PULUMI_TEST_MODE': settings.isTestModeEnabled() ? "true" : "false",
|
||||
}
|
||||
});
|
||||
// Ensure we can exit the current process without waiting on the VM server process to exit.
|
||||
subprocess.unref(); // do not track subprocess on our event loop
|
||||
const reader = readline.createInterface(subprocess.stdout!);
|
||||
this.client = new Promise((resolve, reject) => {
|
||||
reader.once('line', port => {
|
||||
try {
|
||||
// Tear down our piped stdout stream to that we do not hold the event loop open.
|
||||
subprocess.stdout?.destroy();
|
||||
// Connect to the process' gRPC server on the provided port.
|
||||
const client = new runtimeServiceProto.RuntimeClient(
|
||||
`0.0.0.0:${port}`,
|
||||
grpc.credentials.createInsecure()
|
||||
);
|
||||
console.log("created client: " + JSON.stringify(client));
|
||||
resolve(client);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public async construct(libraryPath: string, resource: string, name: string, args: any, opts?: any): Promise<any> {
|
||||
const serializedArgs = await runtime.serializeProperties("construct-args", args, { keepResources: true });
|
||||
const argsStruct = gstruct.Struct.fromJavaScript(serializedArgs);
|
||||
const serializedOpts = await runtime.serializeProperties("construct-opts", opts, { keepResources: true });
|
||||
const optsStruct = gstruct.Struct.fromJavaScript(serializedOpts);
|
||||
const client = await this.client;
|
||||
const constructRequest = new runtimeProto.ConstructRequest();
|
||||
constructRequest.setLibrarypath(libraryPath);
|
||||
constructRequest.setResource(resource);
|
||||
constructRequest.setName(name);
|
||||
constructRequest.setArgs(argsStruct);
|
||||
constructRequest.setOpts(optsStruct);
|
||||
const outsStruct = await new Promise<any>((resolve, reject) => {
|
||||
client.construct(constructRequest, (err: Error, resp: any) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(resp.getOuts());
|
||||
}
|
||||
});
|
||||
});
|
||||
const outs = await runtime.deserializeProperties(outsStruct);
|
||||
return outs;
|
||||
}
|
||||
}
|
52
sdk/nodejs/remote/server/index.ts
Normal file
52
sdk/nodejs/remote/server/index.ts
Normal file
|
@ -0,0 +1,52 @@
|
|||
// 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 * as grpc from "@grpc/grpc-js";
|
||||
import * as runtime from "../../runtime";
|
||||
|
||||
//tslint:disable
|
||||
const runtimeServiceProto = require("../../proto/runtime_grpc_pb.js");
|
||||
const runtimeProto = require("../../proto/runtime_pb.js");
|
||||
const gstruct = require("google-protobuf/google/protobuf/struct_pb.js");
|
||||
|
||||
const server = new grpc.Server();
|
||||
server.addService(runtimeServiceProto.RuntimeService, {
|
||||
construct: construct,
|
||||
});
|
||||
|
||||
const port = server.bind("0.0.0.0:0", grpc.ServerCredentials.createInsecure());
|
||||
server.start();
|
||||
process.stdout.write(`${port}\n`);
|
||||
|
||||
function construct(call: any, callback: (err: any, resp?: any) => void) {
|
||||
try {
|
||||
const library = require(call.request.getLibrarypath())
|
||||
const props = runtime.deserializeProperties(call.request.getArgs());
|
||||
const opts = runtime.deserializeProperties(call.request.getOpts());
|
||||
const resource = call.request.getResource();
|
||||
const name = call.request.getName();
|
||||
const ctor = library[resource];
|
||||
|
||||
const res = new ctor(name, props, opts);
|
||||
|
||||
runtime.serializeProperties("inner-construct", res, { keepResources: true }).then(resolved => {
|
||||
const outStruct = gstruct.Struct.fromJavaScript(resolved);
|
||||
const reply = new runtimeProto.ConstructResponse();
|
||||
reply.setOuts(outStruct);
|
||||
callback(null, reply);
|
||||
}).catch(err => callback(err));
|
||||
} catch (err) {
|
||||
callback(err);
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
import { ResourceError } from "./errors";
|
||||
import { Input, Inputs, interpolate, Output, output } from "./output";
|
||||
import { getStackResource, unknownValue } from "./runtime";
|
||||
import { readResource, registerResource, registerResourceOutputs } from "./runtime/resource";
|
||||
import { getResource, readResource, registerResource, registerResourceOutputs } from "./runtime/resource";
|
||||
import { getProject, getStack } from "./runtime/settings";
|
||||
import * as utils from "./utils";
|
||||
|
||||
|
@ -174,6 +174,12 @@ export abstract class Resource {
|
|||
// tslint:disable-next-line:variable-name
|
||||
private readonly __name?: string;
|
||||
|
||||
/* @internal Whether or not this resources contruction registered a resource with the Pulumi
|
||||
* engine. If false, it is not safe to `registerResourceOutputs` on this resource.
|
||||
*/
|
||||
// tslint:disable-next-line:variable-name
|
||||
readonly __wasRegistered?: boolean;
|
||||
|
||||
/**
|
||||
* The set of providers to use for child resources. Keyed by package name (e.g. "aws").
|
||||
* @internal
|
||||
|
@ -219,6 +225,9 @@ export abstract class Resource {
|
|||
if (!name) {
|
||||
throw new ResourceError("Missing resource name argument (for URN creation)", opts.parent);
|
||||
}
|
||||
if (opts.id && opts.urn) {
|
||||
throw new ResourceError(`Resource options specified mututally exlusive 'id' and 'urn' options`, opts.parent);
|
||||
}
|
||||
|
||||
// Before anything else - if there are transformations registered, invoke them in order to transform the properties and
|
||||
// options assigned to this resource.
|
||||
|
@ -313,19 +322,27 @@ export abstract class Resource {
|
|||
}
|
||||
}
|
||||
|
||||
if (opts.id) {
|
||||
if (opts.urn) {
|
||||
// Assume that the resource has already been registered by another piece of code, and
|
||||
// populate this resource object with the state of that resource as retrieved from the
|
||||
// engine.
|
||||
getResource(this, t, name, custom, props, opts);
|
||||
this.__wasRegistered = false;
|
||||
} else if (opts.id) {
|
||||
// If this resource already exists, read its state rather than registering it anew.
|
||||
if (!custom) {
|
||||
throw new ResourceError(
|
||||
"Cannot read an existing resource unless it has a custom provider", opts.parent);
|
||||
}
|
||||
readResource(this, t, name, props, opts);
|
||||
this.__wasRegistered = false;
|
||||
} else {
|
||||
// Kick off the resource registration. If we are actually performing a deployment, this
|
||||
// resource's properties will be resolved asynchronously after the operation completes, so
|
||||
// that dependent computations resolve normally. If we are just planning, on the other
|
||||
// hand, values will never resolve.
|
||||
registerResource(this, t, name, custom, props, opts);
|
||||
this.__wasRegistered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -458,9 +475,13 @@ export interface ResourceOptions {
|
|||
// that mergeOptions works properly for it.
|
||||
|
||||
/**
|
||||
* An optional existing ID to load, rather than create.
|
||||
* An optional existing ID to load, rather than create. At most one of `id` and `urn` can be set.
|
||||
*/
|
||||
id?: Input<ID>;
|
||||
/**
|
||||
* An optional existing URN to load, rather than create. At most one of `urn` and `id` can be set.
|
||||
*/
|
||||
urn?: Input<URN>;
|
||||
/**
|
||||
* An optional parent resource to which this resource belongs.
|
||||
*/
|
||||
|
@ -757,7 +778,7 @@ export class ComponentResource<TData = any> extends Resource {
|
|||
|
||||
/** @internal */
|
||||
// tslint:disable-next-line:variable-name
|
||||
private __registered = false;
|
||||
private __registeredOutputs = false;
|
||||
|
||||
/**
|
||||
* Returns true if the given object is an instance of CustomResource. This is designed to work even when
|
||||
|
@ -780,22 +801,22 @@ export class ComponentResource<TData = any> extends Resource {
|
|||
* @param opts A bag of options that control this resource's behavior.
|
||||
*/
|
||||
constructor(type: string, name: string, args: Inputs = {}, opts: ComponentResourceOptions = {}) {
|
||||
// Explicitly ignore the props passed in. We allow them for back compat reasons. However,
|
||||
// we explicitly do not want to pass them along to the engine. The ComponentResource acts
|
||||
// only as a container for other resources. Another way to think about this is that a normal
|
||||
// 'custom resource' corresponds to real piece of cloud infrastructure. So, when it changes
|
||||
// in some way, the cloud resource needs to be updated (and vice versa). That is not true
|
||||
// for a component resource. The component is just used for organizational purposes and does
|
||||
// not correspond to a real piece of cloud infrastructure. As such, changes to it *itself*
|
||||
// do not have any effect on the cloud side of things at all.
|
||||
super(type, name, /*custom:*/ false, /*props:*/ {}, opts);
|
||||
super(type, name, /*custom:*/ false, args, opts);
|
||||
this.__data = this.initializeAndRegisterOutputs(args);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
private async initializeAndRegisterOutputs(args: Inputs) {
|
||||
const data = await this.initialize(args);
|
||||
this.registerOutputs();
|
||||
if (this.__wasRegistered) {
|
||||
// If the resource construction was registered with the engine (that is, unless `urn` or
|
||||
// `id` opts were passed), we will automatically "finalize" the resource registration by
|
||||
// calling `registerOutputs`. If the user already called this in the constructor, their
|
||||
// outputs will win (due to the `await` above causing a delay of a turn on the event
|
||||
// loop) and this will be a no-op. If they did not, we will invoke this on the next
|
||||
// turn after the (potentially overriden) `initialize` returns.
|
||||
this.registerOutputs();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -825,11 +846,11 @@ export class ComponentResource<TData = any> extends Resource {
|
|||
* called after the `initialize` method completes.
|
||||
*/
|
||||
protected registerOutputs(outputs?: Inputs | Promise<Inputs> | Output<Inputs>): void {
|
||||
if (this.__registered) {
|
||||
if (this.__registeredOutputs) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.__registered = true;
|
||||
this.__registeredOutputs = true;
|
||||
registerResourceOutputs(this, outputs || {});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,6 +154,50 @@ export function readResource(res: Resource, t: string, name: string, props: Inpu
|
|||
}), label);
|
||||
}
|
||||
|
||||
// The shape of data returned by the built-in invoke to `pulumi:pulumi:readStackResource`.
|
||||
interface StackResourceResult {
|
||||
urn: string;
|
||||
// The output properties of the resource represented by `urn` as registered in the Pulumi
|
||||
// engine.
|
||||
outputs: Record<string, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an existing custom resource's state from the engine. Assumes that the resource has already
|
||||
* been registered by some other code, and looks it up by URN.
|
||||
*/
|
||||
export function getResource(res: Resource, t: string, name: string, custom: boolean, props: Inputs, opts: ResourceOptions): void {
|
||||
const urn: Input<URN> | undefined = opts.urn;
|
||||
if (!urn) {
|
||||
throw new Error("Cannot get resource whose options are lacking a URN value");
|
||||
}
|
||||
|
||||
const label = `resource:${name}[${t}]#...`;
|
||||
log.debug(`Getting resource: id=${Output.isInstance(urn) ? "Output<T>" : urn}, t=${t}, name=${name}`);
|
||||
|
||||
const monitor = getMonitor();
|
||||
const resopAsync = prepareResource(label, res, custom, props, opts);
|
||||
|
||||
debuggablePromise(resopAsync.then(async (resop) => {
|
||||
const resolvedURN = await serializeProperty(label, urn, new Set());
|
||||
log.debug(`GetResource RPC prepared: id=${resolvedURN}, t=${t}, name=${name}` +
|
||||
(excessiveDebugOutput ? `, obj=${JSON.stringify(resop.serializedProps)}` : ``));
|
||||
|
||||
const result: StackResourceResult = await invoke("pulumi:pulumi:readStackResource", {
|
||||
urn: resolvedURN,
|
||||
}, { async: true });
|
||||
|
||||
// Now resolve everything: the URN, the ID (if the resource is a `CustomResource`), and the
|
||||
// output properties.
|
||||
resop.resolveURN(resolvedURN);
|
||||
if (custom) {
|
||||
resop.resolveID!(result.outputs.id, result.outputs.id !== undefined);
|
||||
}
|
||||
resolveProperties(res, resop.resolvers, t, name, result.outputs);
|
||||
}), label);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* registerResource registers a new resource object with a given type t and name. It returns the auto-generated
|
||||
* URN and the ID that will resolve after the deployment has completed. All properties will be initialized to property
|
||||
|
@ -514,7 +558,7 @@ export function registerResourceOutputs(res: Resource, outputs: Inputs | Promise
|
|||
// The registration could very well still be taking place, so we will need to wait for its URN.
|
||||
// Additionally, the output properties might have come from other resources, so we must await those too.
|
||||
const urn = await res.urn.promise();
|
||||
const resolved = await serializeProperties(opLabel, { outputs });
|
||||
const resolved = await serializeProperties(opLabel, { outputs }, { keepResources: true });
|
||||
const outputsObj = gstruct.Struct.fromJavaScript(resolved.outputs);
|
||||
log.debug(`RegisterResourceOutputs RPC prepared: urn=${urn}` +
|
||||
(excessiveDebugOutput ? `, outputs=${JSON.stringify(outputsObj)}` : ``));
|
||||
|
|
|
@ -82,7 +82,11 @@ export function transferProperties(onto: Resource, label: string, props: Inputs)
|
|||
* be remoted over to registerResource.
|
||||
*/
|
||||
async function serializeFilteredProperties(
|
||||
label: string, props: Inputs, acceptKey: (k: string) => boolean): Promise<[Record<string, any>, Map<string, Set<Resource>>]> {
|
||||
label: string,
|
||||
props: Inputs,
|
||||
acceptKey: (k: string) => boolean,
|
||||
opts?: SerializeOptions,
|
||||
): Promise<[Record<string, any>, Map<string, Set<Resource>>]> {
|
||||
|
||||
const propertyToDependentResources = new Map<string, Set<Resource>>();
|
||||
|
||||
|
@ -91,7 +95,7 @@ async function serializeFilteredProperties(
|
|||
if (acceptKey(k)) {
|
||||
// We treat properties with undefined values as if they do not exist.
|
||||
const dependentResources = new Set<Resource>();
|
||||
const v = await serializeProperty(`${label}.${k}`, props[k], dependentResources);
|
||||
const v = await serializeProperty(`${label}.${k}`, props[k], dependentResources, opts);
|
||||
if (v !== undefined) {
|
||||
result[k] = v;
|
||||
propertyToDependentResources.set(k, dependentResources);
|
||||
|
@ -110,12 +114,24 @@ export async function serializeResourceProperties(label: string, props: Inputs)
|
|||
return serializeFilteredProperties(label, props, key => key !== "id" && key !== "urn");
|
||||
}
|
||||
|
||||
/**
|
||||
* SerializeOptions represents options that can be passed to a property serialization function.
|
||||
*/
|
||||
export interface SerializeOptions {
|
||||
/**
|
||||
* keepResources indicates that resource references should be maintained as strongly typed
|
||||
* `Resource` instances instead of erased down to `id` (for CustomResources) or `urn` (for
|
||||
* `ComponetResources`).
|
||||
*/
|
||||
keepResources?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* serializeProperties walks the props object passed in, awaiting all interior promises, creating a reasonable
|
||||
* POJO object that can be remoted over to registerResource.
|
||||
*/
|
||||
export async function serializeProperties(label: string, props: Inputs) {
|
||||
const [result] = await serializeFilteredProperties(label, props, _ => true);
|
||||
export async function serializeProperties(label: string, props: Inputs, opts?: SerializeOptions) {
|
||||
const [result] = await serializeFilteredProperties(label, props, _ => true, opts);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -222,14 +238,18 @@ export const specialArchiveSig = "0def7320c3a5731c473e5ecbe6d01bc7";
|
|||
* specialSecretSig is a randomly assigned hash used to identify secrets in maps. See pkg/resource/properties.go.
|
||||
*/
|
||||
export const specialSecretSig = "1b47061264138c4ac30d75fd1eb44270";
|
||||
/**
|
||||
* specialResourceSig is a randomly assigned hash used to identify resources in maps. See pkg/resource/properties.go.
|
||||
*/
|
||||
export const specialResourceSig = "5cf8f73096256a8f31e491e813e4eb8e";
|
||||
|
||||
/**
|
||||
* serializeProperty serializes properties deeply. This understands how to wait on any unresolved promises, as
|
||||
* appropriate, in addition to translating certain "special" values so that they are ready to go on the wire.
|
||||
*/
|
||||
export async function serializeProperty(ctx: string, prop: Input<any>, dependentResources: Set<Resource>): Promise<any> {
|
||||
export async function serializeProperty(ctx: string, prop: Input<any>, dependentResources: Set<Resource>, opts: SerializeOptions = {}): Promise<any> {
|
||||
// IMPORTANT:
|
||||
// IMPORTANT: Keep this in sync with serializesPropertiesSync in invoke.ts
|
||||
// IMPORTANT: Keep this in sync with serializePropertiesSync in invoke.ts
|
||||
// IMPORTANT:
|
||||
|
||||
if (prop === undefined ||
|
||||
|
@ -262,7 +282,7 @@ export async function serializeProperty(ctx: string, prop: Input<any>, dependent
|
|||
|
||||
const subctx = `Promise<${ctx}>`;
|
||||
return serializeProperty(subctx,
|
||||
await debuggablePromise(prop, `serializeProperty.await(${subctx})`), dependentResources);
|
||||
await debuggablePromise(prop, `serializeProperty.await(${subctx})`), dependentResources, opts);
|
||||
}
|
||||
|
||||
if (Output.isInstance(prop)) {
|
||||
|
@ -289,7 +309,7 @@ export async function serializeProperty(ctx: string, prop: Input<any>, dependent
|
|||
// which will wrap undefined, if it were to be resolved (since `Output` has no member named .isSecret).
|
||||
// so we must compare to the literal true instead of just doing await prop.isSecret.
|
||||
const isSecret = await prop.isSecret === true;
|
||||
const value = await serializeProperty(`${ctx}.id`, prop.promise(), dependentResources);
|
||||
const value = await serializeProperty(`${ctx}.id`, prop.promise(), dependentResources, opts);
|
||||
|
||||
if (!isKnown) {
|
||||
return unknownValue;
|
||||
|
@ -309,13 +329,21 @@ export async function serializeProperty(ctx: string, prop: Input<any>, dependent
|
|||
}
|
||||
|
||||
if (CustomResource.isInstance(prop)) {
|
||||
// Resources aren't serializable; instead, we serialize them as references to the ID property.
|
||||
if (excessiveDebugOutput) {
|
||||
log.debug(`Serialize property [${ctx}]: custom resource id`);
|
||||
log.debug(`Serialize property [${ctx}]: custom resource urn`);
|
||||
}
|
||||
|
||||
dependentResources.add(prop);
|
||||
return serializeProperty(`${ctx}.id`, prop.id, dependentResources);
|
||||
if (opts.keepResources) {
|
||||
// If we are keeping resources, emit a stronly typed wrapper over the URN
|
||||
const urn = await serializeProperty(`${ctx}.urn`, prop.urn, dependentResources, opts);
|
||||
return {
|
||||
[specialSigKey]: specialResourceSig,
|
||||
urn: urn,
|
||||
};
|
||||
}
|
||||
// Else, return the id for backward compatibility.
|
||||
return serializeProperty(`${ctx}.id`, prop.id, dependentResources, opts);
|
||||
}
|
||||
|
||||
if (ComponentResource.isInstance(prop)) {
|
||||
|
@ -334,10 +362,19 @@ export async function serializeProperty(ctx: string, prop: Input<any>, dependent
|
|||
// and tracked in a reasonable manner, while not causing us to compute or embed information
|
||||
// about it that is not needed, and which can lead to deadlocks.
|
||||
if (excessiveDebugOutput) {
|
||||
log.debug(`Serialize property [${ctx}]: component resource urnid`);
|
||||
log.debug(`Serialize property [${ctx}]: component resource urn`);
|
||||
}
|
||||
|
||||
return serializeProperty(`${ctx}.urn`, prop.urn, dependentResources);
|
||||
if (opts.keepResources) {
|
||||
// If we are keeping resources, emit a stronly typed wrapper over the URN
|
||||
const urn = await serializeProperty(`${ctx}.urn`, prop.urn, dependentResources, opts);
|
||||
return {
|
||||
[specialSigKey]: specialResourceSig,
|
||||
urn: urn,
|
||||
};
|
||||
}
|
||||
// Else, return the urn for backward compatibility.
|
||||
return serializeProperty(`${ctx}.urn`, prop.urn, dependentResources, opts);
|
||||
}
|
||||
|
||||
if (prop instanceof Array) {
|
||||
|
@ -347,7 +384,7 @@ export async function serializeProperty(ctx: string, prop: Input<any>, dependent
|
|||
log.debug(`Serialize property [${ctx}]: array[${i}] element`);
|
||||
}
|
||||
// When serializing arrays, we serialize any undefined values as `null`. This matches JSON semantics.
|
||||
const elem = await serializeProperty(`${ctx}[${i}]`, prop[i], dependentResources);
|
||||
const elem = await serializeProperty(`${ctx}[${i}]`, prop[i], dependentResources, opts);
|
||||
result.push(elem === undefined ? null : elem);
|
||||
}
|
||||
return result;
|
||||
|
@ -362,7 +399,7 @@ export async function serializeProperty(ctx: string, prop: Input<any>, dependent
|
|||
}
|
||||
|
||||
// When serializing an object, we omit any keys with undefined values. This matches JSON semantics.
|
||||
const v = await serializeProperty(`${ctx}.${k}`, innerProp[k], dependentResources);
|
||||
const v = await serializeProperty(`${ctx}.${k}`, innerProp[k], dependentResources, opts);
|
||||
if (v !== undefined) {
|
||||
obj[k] = v;
|
||||
}
|
||||
|
@ -468,6 +505,18 @@ export function deserializeProperty(prop: any): any {
|
|||
[specialSigKey]: specialSecretSig,
|
||||
value: deserializeProperty(prop["value"]),
|
||||
};
|
||||
case specialResourceSig:
|
||||
// Deserialize the resource into a live Resource reference
|
||||
const urn = prop["urn"];
|
||||
const urnParts = urn.split("::");
|
||||
const qualifiedType = urnParts[2];
|
||||
const type = qualifiedType.split("$").pop()!;
|
||||
const proxyConstructor = proxyConstructors.get(type);
|
||||
if (!proxyConstructor) {
|
||||
throw new Error(`Unable to deserialize resource URN ${urn}, no proxy constructor is registered for type ${type}.`);
|
||||
}
|
||||
const urnName = urnParts[3];
|
||||
return new proxyConstructor(urnName, {}, { urn });
|
||||
default:
|
||||
throw new Error(`Unrecognized signature '${sig}' when unmarshaling resource property`);
|
||||
}
|
||||
|
@ -494,3 +543,20 @@ export function deserializeProperty(prop: any): any {
|
|||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
type ProxyConstructor = {
|
||||
new(name: string, args: any, opts: { urn: string }): Resource;
|
||||
};
|
||||
|
||||
const proxyConstructors = new Map<string, ProxyConstructor>();
|
||||
/**
|
||||
* registerProxyConstructor registers a constructor to be used as a proxy for any URNs matching the
|
||||
* given type that are deserialized by the current instance of the Pulumi JavaScript SDK.
|
||||
*/
|
||||
export function registerProxyConstructor(type: string, constructor: ProxyConstructor) {
|
||||
const existing = proxyConstructors.get(type);
|
||||
if (existing) {
|
||||
throw new Error(`Cannot re-register type ${type} as a proxy. Previous registration was ${existing}, new registration was ${constructor}.`);
|
||||
}
|
||||
proxyConstructors.set(type, constructor);
|
||||
}
|
||||
|
|
|
@ -55,10 +55,13 @@ class Stack extends ComponentResource<Inputs> {
|
|||
*/
|
||||
public readonly outputs: Output<Inputs>;
|
||||
|
||||
private _init: () => Promise<Inputs>;
|
||||
|
||||
constructor(init: () => Promise<Inputs>) {
|
||||
super(rootPulumiStackTypeName, `${getProject()}-${getStack()}`, { init });
|
||||
super(rootPulumiStackTypeName, `${getProject()}-${getStack()}`);
|
||||
const data = this.getData();
|
||||
this.outputs = output(data);
|
||||
this._init = init;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,7 +70,7 @@ class Stack extends ComponentResource<Inputs> {
|
|||
*
|
||||
* @param init The callback to run in the context of this Pulumi stack
|
||||
*/
|
||||
async initialize(args: { init: () => Promise<Inputs> }): Promise<Inputs> {
|
||||
async initialize(): Promise<Inputs> {
|
||||
const parent = await getRootResource();
|
||||
if (parent) {
|
||||
throw new Error("Only one root Pulumi Stack may be active at once");
|
||||
|
@ -79,7 +82,7 @@ class Stack extends ComponentResource<Inputs> {
|
|||
|
||||
let outputs: Inputs | undefined;
|
||||
try {
|
||||
const inputs = await args.init();
|
||||
const inputs = await this._init();
|
||||
outputs = await massage(inputs, []);
|
||||
} finally {
|
||||
// We want to expose stack outputs as simple pojo objects (including Resources). This
|
||||
|
|
26
sdk/nodejs/tests/remote/component.ts
Normal file
26
sdk/nodejs/tests/remote/component.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
// Copyright 2016-2020, 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 { ComponentResource, ComponentResourceOptions, Output, output } from "../../";
|
||||
|
||||
export class MyComponent extends ComponentResource {
|
||||
output: Output<string>;
|
||||
constructor(name: string, args: any, opts: ComponentResourceOptions) {
|
||||
super("my:mod:MyComponent", name, {}, opts);
|
||||
this.output = output(args.input as string);
|
||||
this.registerOutputs({
|
||||
output: this.output,
|
||||
});
|
||||
}
|
||||
}
|
44
sdk/nodejs/tests/remote/proxycomponentresource.spec.ts
Normal file
44
sdk/nodejs/tests/remote/proxycomponentresource.spec.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2016-2020, 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 * as assert from "assert";
|
||||
// import { ComponentResourceOptions, Output } from "../../";
|
||||
// import { ProxyComponentResource } from "../../remote";
|
||||
|
||||
import * as assert from "assert";
|
||||
import { getRemoteServer, RemoteServer } from "../../remote/remoteServer";
|
||||
import * as runtime from "../../runtime";
|
||||
|
||||
describe("remote invocation", () => {
|
||||
before(() => {
|
||||
runtime._setTestModeEnabled(true);
|
||||
runtime._setProject("myproject");
|
||||
runtime._setStack("mystack");
|
||||
});
|
||||
|
||||
after(() => {
|
||||
runtime._setTestModeEnabled(false);
|
||||
runtime._setProject(undefined);
|
||||
runtime._setStack(undefined);
|
||||
});
|
||||
|
||||
it("can construct a simple component", async () => {
|
||||
const server = getRemoteServer();
|
||||
const path = require.resolve("./component");
|
||||
console.log(path);
|
||||
const res = await server.construct(require.resolve("./component"), "MyComponent", "res", { input: "hello", output: undefined }, {});
|
||||
assert.strictEqual("hello", res.output);
|
||||
});
|
||||
});
|
|
@ -35,6 +35,11 @@
|
|||
|
||||
"log/index.ts",
|
||||
|
||||
"remote/index.ts",
|
||||
"remote/proxy.ts",
|
||||
"remote/remoteServer.ts",
|
||||
"remote/server/index.ts",
|
||||
|
||||
"runtime/index.ts",
|
||||
|
||||
"runtime/closure/codePaths.ts",
|
||||
|
@ -76,7 +81,9 @@
|
|||
"tests/runtime/closureLoader.spec.ts",
|
||||
"tests/runtime/tsClosureCases.ts",
|
||||
"tests/runtime/props.spec.ts",
|
||||
"tests/runtime/langhost/run.spec.ts"
|
||||
"tests/runtime/langhost/run.spec.ts",
|
||||
"tests/remote/proxycomponentresource.spec.ts",
|
||||
"tests/remote/component.ts"
|
||||
]
|
||||
}
|
||||
|
||||
|
|
244
sdk/proto/go/runtime.pb.go
Normal file
244
sdk/proto/go/runtime.pb.go
Normal file
|
@ -0,0 +1,244 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: runtime.proto
|
||||
|
||||
package pulumirpc
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
_ "github.com/golang/protobuf/ptypes/empty"
|
||||
_struct "github.com/golang/protobuf/ptypes/struct"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type ConstructRequest struct {
|
||||
LibraryPath string `protobuf:"bytes,1,opt,name=libraryPath,proto3" json:"libraryPath,omitempty"`
|
||||
Resource string `protobuf:"bytes,2,opt,name=resource,proto3" json:"resource,omitempty"`
|
||||
Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Args *_struct.Struct `protobuf:"bytes,4,opt,name=args,proto3" json:"args,omitempty"`
|
||||
Opts *_struct.Struct `protobuf:"bytes,5,opt,name=opts,proto3" json:"opts,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *ConstructRequest) Reset() { *m = ConstructRequest{} }
|
||||
func (m *ConstructRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ConstructRequest) ProtoMessage() {}
|
||||
func (*ConstructRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_86e2dd377c869464, []int{0}
|
||||
}
|
||||
|
||||
func (m *ConstructRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ConstructRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ConstructRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ConstructRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *ConstructRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ConstructRequest.Merge(m, src)
|
||||
}
|
||||
func (m *ConstructRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_ConstructRequest.Size(m)
|
||||
}
|
||||
func (m *ConstructRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ConstructRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ConstructRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *ConstructRequest) GetLibraryPath() string {
|
||||
if m != nil {
|
||||
return m.LibraryPath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *ConstructRequest) GetResource() string {
|
||||
if m != nil {
|
||||
return m.Resource
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *ConstructRequest) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *ConstructRequest) GetArgs() *_struct.Struct {
|
||||
if m != nil {
|
||||
return m.Args
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ConstructRequest) GetOpts() *_struct.Struct {
|
||||
if m != nil {
|
||||
return m.Opts
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ConstructResponse struct {
|
||||
Outs *_struct.Struct `protobuf:"bytes,1,opt,name=outs,proto3" json:"outs,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *ConstructResponse) Reset() { *m = ConstructResponse{} }
|
||||
func (m *ConstructResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*ConstructResponse) ProtoMessage() {}
|
||||
func (*ConstructResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_86e2dd377c869464, []int{1}
|
||||
}
|
||||
|
||||
func (m *ConstructResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ConstructResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ConstructResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ConstructResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *ConstructResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ConstructResponse.Merge(m, src)
|
||||
}
|
||||
func (m *ConstructResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_ConstructResponse.Size(m)
|
||||
}
|
||||
func (m *ConstructResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ConstructResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ConstructResponse proto.InternalMessageInfo
|
||||
|
||||
func (m *ConstructResponse) GetOuts() *_struct.Struct {
|
||||
if m != nil {
|
||||
return m.Outs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*ConstructRequest)(nil), "pulumirpc.ConstructRequest")
|
||||
proto.RegisterType((*ConstructResponse)(nil), "pulumirpc.ConstructResponse")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("runtime.proto", fileDescriptor_86e2dd377c869464) }
|
||||
|
||||
var fileDescriptor_86e2dd377c869464 = []byte{
|
||||
// 256 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x90, 0xcd, 0x4a, 0xc4, 0x30,
|
||||
0x1c, 0xc4, 0xad, 0xd6, 0x8f, 0x66, 0x15, 0x34, 0x17, 0x43, 0x77, 0x0f, 0xa5, 0xa7, 0x05, 0x21,
|
||||
0x0b, 0xeb, 0x0b, 0x08, 0x5e, 0x3c, 0x4a, 0xf7, 0x09, 0xda, 0x12, 0x6b, 0xa1, 0x4d, 0xe2, 0xff,
|
||||
0xe3, 0xb0, 0x2f, 0xe7, 0xb3, 0xc9, 0x26, 0x5a, 0x96, 0x22, 0x7a, 0x4b, 0xe6, 0x37, 0xfc, 0x67,
|
||||
0x18, 0x71, 0x03, 0x6c, 0xa9, 0x1f, 0x8d, 0xf6, 0xe0, 0xc8, 0xc9, 0xcc, 0xf3, 0xc0, 0x63, 0x0f,
|
||||
0xbe, 0xcd, 0xaf, 0xfd, 0xc0, 0x5d, 0x6f, 0x23, 0xc8, 0x97, 0x9d, 0x73, 0xdd, 0x60, 0x36, 0xe1,
|
||||
0xd7, 0xf0, 0xdb, 0xc6, 0x8c, 0x9e, 0xf6, 0xdf, 0x70, 0x35, 0x87, 0x48, 0xc0, 0x2d, 0x45, 0x5a,
|
||||
0x7e, 0x26, 0xe2, 0xf6, 0xd9, 0xd9, 0xa8, 0x55, 0xe6, 0x83, 0x0d, 0x92, 0x2c, 0xc4, 0x62, 0xe8,
|
||||
0x1b, 0xa8, 0x61, 0xff, 0x5a, 0xd3, 0xbb, 0x4a, 0x8a, 0x64, 0x9d, 0x55, 0xc7, 0x92, 0xcc, 0xc5,
|
||||
0x15, 0x18, 0x74, 0x0c, 0xad, 0x51, 0xa7, 0x01, 0x4f, 0x7f, 0x29, 0x45, 0x6a, 0xeb, 0xd1, 0xa8,
|
||||
0xb3, 0xa0, 0x87, 0xb7, 0x7c, 0x10, 0x69, 0x0d, 0x1d, 0xaa, 0xb4, 0x48, 0xd6, 0x8b, 0xed, 0xbd,
|
||||
0x8e, 0x9d, 0xf4, 0x4f, 0x27, 0xbd, 0x8b, 0xf9, 0xc1, 0x74, 0x30, 0x3b, 0x4f, 0xa8, 0xce, 0xff,
|
||||
0x31, 0x1f, 0x4c, 0xe5, 0x93, 0xb8, 0x3b, 0xea, 0x8f, 0xde, 0x59, 0x0c, 0x71, 0x8e, 0x09, 0x43,
|
||||
0xf3, 0x3f, 0x2f, 0x30, 0xe1, 0x76, 0x27, 0x2e, 0xab, 0xb8, 0xb3, 0x7c, 0x11, 0xd9, 0x74, 0x4c,
|
||||
0x2e, 0xf5, 0xb4, 0xb7, 0x9e, 0x4f, 0x94, 0xaf, 0x7e, 0x87, 0x31, 0xbf, 0x3c, 0x69, 0x2e, 0x42,
|
||||
0xd6, 0xe3, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0x96, 0x81, 0xd7, 0xc9, 0xc3, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConnInterface
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion6
|
||||
|
||||
// RuntimeClient is the client API for Runtime service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type RuntimeClient interface {
|
||||
Construct(ctx context.Context, in *ConstructRequest, opts ...grpc.CallOption) (*ConstructResponse, error)
|
||||
}
|
||||
|
||||
type runtimeClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewRuntimeClient(cc grpc.ClientConnInterface) RuntimeClient {
|
||||
return &runtimeClient{cc}
|
||||
}
|
||||
|
||||
func (c *runtimeClient) Construct(ctx context.Context, in *ConstructRequest, opts ...grpc.CallOption) (*ConstructResponse, error) {
|
||||
out := new(ConstructResponse)
|
||||
err := c.cc.Invoke(ctx, "/pulumirpc.Runtime/Construct", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// RuntimeServer is the server API for Runtime service.
|
||||
type RuntimeServer interface {
|
||||
Construct(context.Context, *ConstructRequest) (*ConstructResponse, error)
|
||||
}
|
||||
|
||||
// UnimplementedRuntimeServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedRuntimeServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedRuntimeServer) Construct(ctx context.Context, req *ConstructRequest) (*ConstructResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Construct not implemented")
|
||||
}
|
||||
|
||||
func RegisterRuntimeServer(s *grpc.Server, srv RuntimeServer) {
|
||||
s.RegisterService(&_Runtime_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Runtime_Construct_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ConstructRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RuntimeServer).Construct(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/pulumirpc.Runtime/Construct",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RuntimeServer).Construct(ctx, req.(*ConstructRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Runtime_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "pulumirpc.Runtime",
|
||||
HandlerType: (*RuntimeServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Construct",
|
||||
Handler: _Runtime_Construct_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "runtime.proto",
|
||||
}
|
38
sdk/proto/runtime.proto
Normal file
38
sdk/proto/runtime.proto
Normal file
|
@ -0,0 +1,38 @@
|
|||
// 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.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
import "plugin.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
import "google/protobuf/struct.proto";
|
||||
|
||||
package pulumirpc;
|
||||
|
||||
service Runtime {
|
||||
rpc Construct(ConstructRequest) returns (ConstructResponse) {}
|
||||
// TODO: Add an Invoke to allow arbitary function execution
|
||||
}
|
||||
|
||||
message ConstructRequest {
|
||||
string libraryPath = 1;
|
||||
string resource = 2;
|
||||
string name = 3;
|
||||
google.protobuf.Struct args = 4;
|
||||
google.protobuf.Struct opts = 5;
|
||||
}
|
||||
|
||||
message ConstructResponse {
|
||||
google.protobuf.Struct outs = 1;
|
||||
}
|
21
sdk/python/lib/pulumi/remote/__init__.py
Normal file
21
sdk/python/lib/pulumi/remote/__init__.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# 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.
|
||||
|
||||
"""
|
||||
The remote proxy implementation of the Pulumi Python SDK.
|
||||
"""
|
||||
|
||||
from .remote import (
|
||||
ProxyComponentResource,
|
||||
)
|
105
sdk/python/lib/pulumi/remote/remote.py
Normal file
105
sdk/python/lib/pulumi/remote/remote.py
Normal file
|
@ -0,0 +1,105 @@
|
|||
# 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 asyncio
|
||||
import os
|
||||
from subprocess import Popen, PIPE
|
||||
import time
|
||||
from typing import Callable, Any, Dict, List, Optional
|
||||
import grpc
|
||||
from .. import ComponentResource, CustomResource, Output, InvokeOptions, ResourceOptions, log, Input, Inputs, Resource
|
||||
from ..runtime.proto import runtime_pb2, runtime_pb2_grpc
|
||||
from ..runtime.rpc import deserialize_properties, serialize_properties
|
||||
from ..runtime.settings import SETTINGS
|
||||
|
||||
def spawnServer(library_path: str):
|
||||
def setting_to_string(setting: Optional[str]) -> str:
|
||||
return "" if setting is None else setting
|
||||
proc = Popen(["node", "-e", "require('@pulumi/pulumi/remote/server')"], cwd=library_path, stdout=PIPE, env={
|
||||
**os.environ,
|
||||
'PULUMI_NODEJS_PROJECT': setting_to_string(SETTINGS.project),
|
||||
'PULUMI_NODEJS_STACK': setting_to_string(SETTINGS.stack),
|
||||
'PULUMI_NODEJS_DRY_RUN': "true" if SETTINGS.dry_run else "false",
|
||||
'PULUMI_NODEJS_QUERY_MODE': "false",
|
||||
'PULUMI_NODEJS_MONITOR': setting_to_string(SETTINGS.monitor_addr),
|
||||
'PULUMI_NODEJS_ENGINE': setting_to_string(SETTINGS.engine_addr),
|
||||
'PULUMI_TEST_MODE': "true" if SETTINGS.test_mode_enabled else "false",
|
||||
'PULUMI_ENABLE_LEGACY_APPLY': "false",
|
||||
'PULUMI_NODEJS_SYNC': "false",
|
||||
'PULUMI_NODEJS_PARALLEL': "true",
|
||||
})
|
||||
port = proc.stdout.readline().decode()[:-1]
|
||||
proc.stdout.close()
|
||||
channel = grpc.insecure_channel(f'0.0.0.0:{port}')
|
||||
stub = runtime_pb2_grpc.RuntimeStub(channel)
|
||||
return stub
|
||||
|
||||
stubs: Dict[str, runtime_pb2_grpc.RuntimeStub] = dict()
|
||||
|
||||
def get_server(library_path: str) -> runtime_pb2_grpc.RuntimeStub:
|
||||
stub = stubs.get(library_path, None)
|
||||
if stub is None:
|
||||
stub = spawnServer(library_path)
|
||||
stubs[library_path] = stub
|
||||
return stub
|
||||
|
||||
# def resource_options_to_dict(opts: ResourceOptions) -> Inputs:
|
||||
# d = vars(opts)
|
||||
# d.pop("merge", None)
|
||||
# return d
|
||||
|
||||
async def construct(
|
||||
libraryPath: str,
|
||||
resource: str,
|
||||
name: str,
|
||||
args: Any,
|
||||
_opts: ResourceOptions) -> Any:
|
||||
property_dependencies_resources: Dict[str, List[Resource]] = {}
|
||||
args_struct = await serialize_properties(args, property_dependencies_resources)
|
||||
# TODO - support opts serialization
|
||||
opts_struct = await serialize_properties({}, property_dependencies_resources)
|
||||
req = runtime_pb2.ConstructRequest(
|
||||
libraryPath=libraryPath,
|
||||
resource=resource,
|
||||
name=name,
|
||||
args=args_struct,
|
||||
opts=opts_struct,
|
||||
)
|
||||
resp = get_server(libraryPath).Construct(req)
|
||||
outs = deserialize_properties(resp.outs)
|
||||
return outs
|
||||
|
||||
class ProxyComponentResource(ComponentResource):
|
||||
"""
|
||||
Abstract base class for proxies around component resources.
|
||||
"""
|
||||
def __init__(self,
|
||||
t: str,
|
||||
name: str,
|
||||
library_path: str,
|
||||
library_name: str,
|
||||
inputs: Inputs,
|
||||
outputs: Dict[str, None],
|
||||
opts: Optional[ResourceOptions] = None) -> None:
|
||||
if opts is None or opts.urn is None:
|
||||
async def do_construct():
|
||||
r = await construct(library_path, library_name, name, inputs, opts)
|
||||
return r["urn"]
|
||||
urn = asyncio.ensure_future(do_construct())
|
||||
opts = ResourceOptions.merge(opts, ResourceOptions(urn=urn))
|
||||
props = {
|
||||
**inputs,
|
||||
**outputs,
|
||||
}
|
||||
super().__init__(t, name, props, opts)
|
|
@ -18,7 +18,7 @@ from typing import Optional, List, Any, Mapping, Union, Callable, TYPE_CHECKING,
|
|||
import copy
|
||||
|
||||
from .runtime import known_types
|
||||
from .runtime.resource import _register_resource, register_resource_outputs, _read_resource
|
||||
from .runtime.resource import _register_resource, register_resource_outputs, _read_resource, _get_resource
|
||||
from .runtime.settings import get_root_resource
|
||||
|
||||
from .metadata import get_project, get_stack
|
||||
|
@ -365,6 +365,11 @@ class ResourceOptions:
|
|||
An optional existing ID to load, rather than create.
|
||||
"""
|
||||
|
||||
urn: Optional['Input[str]']
|
||||
"""
|
||||
An optional existing URN to load, rather than create.
|
||||
"""
|
||||
|
||||
import_: Optional[str]
|
||||
"""
|
||||
When provided with a resource ID, import indicates that this resource's provider should import
|
||||
|
@ -386,6 +391,7 @@ class ResourceOptions:
|
|||
aliases: Optional[List['Input[Union[str, Alias]]']] = None,
|
||||
additional_secret_outputs: Optional[List[str]] = None,
|
||||
id: Optional['Input[str]'] = None,
|
||||
urn: Optional['Input[str]'] = None,
|
||||
import_: Optional[str] = None,
|
||||
custom_timeouts: Optional['CustomTimeouts'] = None,
|
||||
transformations: Optional[List[ResourceTransformation]] = None) -> None:
|
||||
|
@ -432,6 +438,7 @@ class ResourceOptions:
|
|||
self.additional_secret_outputs = additional_secret_outputs
|
||||
self.custom_timeouts = custom_timeouts
|
||||
self.id = id
|
||||
self.urn = urn
|
||||
self.import_ = import_
|
||||
self.transformations = transformations
|
||||
|
||||
|
@ -503,6 +510,7 @@ class ResourceOptions:
|
|||
dest.version = dest.version if source.version is None else source.version
|
||||
dest.custom_timeouts = dest.custom_timeouts if source.custom_timeouts is None else source.custom_timeouts
|
||||
dest.id = dest.id if source.id is None else source.id
|
||||
dest.urn = dest.urn if source.urn is None else source.urn
|
||||
dest.import_ = dest.import_ if source.import_ is None else source.import_
|
||||
|
||||
# Now, if we are left with a .providers that is just a single key/value pair, then
|
||||
|
@ -704,7 +712,17 @@ class Resource:
|
|||
self._aliases.append(collapse_alias_to_urn(
|
||||
alias, name, t, opts.parent))
|
||||
|
||||
if opts.id is not None:
|
||||
if opts.urn is not None:
|
||||
# Assume that the resource has already been registered by another piece of code, and
|
||||
# populate this resource object with the state of that resource as retrieved from the
|
||||
# engine.
|
||||
result = _get_resource(self, t, name, custom, props, opts)
|
||||
res.urn = result.urn
|
||||
if custom:
|
||||
assert result.id is not None
|
||||
res = cast('CustomResource', res)
|
||||
res.id = result.id
|
||||
elif opts.id is not None:
|
||||
# If this resource already exists, read its state rather than registering it anew.
|
||||
if not custom:
|
||||
raise Exception(
|
||||
|
|
|
@ -44,3 +44,7 @@ from .stack import (
|
|||
from .invoke import (
|
||||
invoke,
|
||||
)
|
||||
|
||||
from .rpc import (
|
||||
register_proxy_constructor,
|
||||
)
|
||||
|
|
166
sdk/python/lib/pulumi/runtime/proto/runtime_pb2.py
Normal file
166
sdk/python/lib/pulumi/runtime/proto/runtime_pb2.py
Normal file
|
@ -0,0 +1,166 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: runtime.proto
|
||||
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from google.protobuf import reflection as _reflection
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
from . import plugin_pb2 as plugin__pb2
|
||||
from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2
|
||||
from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
||||
name='runtime.proto',
|
||||
package='pulumirpc',
|
||||
syntax='proto3',
|
||||
serialized_options=None,
|
||||
serialized_pb=b'\n\rruntime.proto\x12\tpulumirpc\x1a\x0cplugin.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1cgoogle/protobuf/struct.proto\"\x95\x01\n\x10\x43onstructRequest\x12\x13\n\x0blibraryPath\x18\x01 \x01(\t\x12\x10\n\x08resource\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12%\n\x04\x61rgs\x18\x04 \x01(\x0b\x32\x17.google.protobuf.Struct\x12%\n\x04opts\x18\x05 \x01(\x0b\x32\x17.google.protobuf.Struct\":\n\x11\x43onstructResponse\x12%\n\x04outs\x18\x01 \x01(\x0b\x32\x17.google.protobuf.Struct2S\n\x07Runtime\x12H\n\tConstruct\x12\x1b.pulumirpc.ConstructRequest\x1a\x1c.pulumirpc.ConstructResponse\"\x00\x62\x06proto3'
|
||||
,
|
||||
dependencies=[plugin__pb2.DESCRIPTOR,google_dot_protobuf_dot_empty__pb2.DESCRIPTOR,google_dot_protobuf_dot_struct__pb2.DESCRIPTOR,])
|
||||
|
||||
|
||||
|
||||
|
||||
_CONSTRUCTREQUEST = _descriptor.Descriptor(
|
||||
name='ConstructRequest',
|
||||
full_name='pulumirpc.ConstructRequest',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='libraryPath', full_name='pulumirpc.ConstructRequest.libraryPath', index=0,
|
||||
number=1, type=9, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"".decode('utf-8'),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='resource', full_name='pulumirpc.ConstructRequest.resource', index=1,
|
||||
number=2, type=9, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"".decode('utf-8'),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='name', full_name='pulumirpc.ConstructRequest.name', index=2,
|
||||
number=3, type=9, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"".decode('utf-8'),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='args', full_name='pulumirpc.ConstructRequest.args', index=3,
|
||||
number=4, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='opts', full_name='pulumirpc.ConstructRequest.opts', index=4,
|
||||
number=5, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=102,
|
||||
serialized_end=251,
|
||||
)
|
||||
|
||||
|
||||
_CONSTRUCTRESPONSE = _descriptor.Descriptor(
|
||||
name='ConstructResponse',
|
||||
full_name='pulumirpc.ConstructResponse',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='outs', full_name='pulumirpc.ConstructResponse.outs', index=0,
|
||||
number=1, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=253,
|
||||
serialized_end=311,
|
||||
)
|
||||
|
||||
_CONSTRUCTREQUEST.fields_by_name['args'].message_type = google_dot_protobuf_dot_struct__pb2._STRUCT
|
||||
_CONSTRUCTREQUEST.fields_by_name['opts'].message_type = google_dot_protobuf_dot_struct__pb2._STRUCT
|
||||
_CONSTRUCTRESPONSE.fields_by_name['outs'].message_type = google_dot_protobuf_dot_struct__pb2._STRUCT
|
||||
DESCRIPTOR.message_types_by_name['ConstructRequest'] = _CONSTRUCTREQUEST
|
||||
DESCRIPTOR.message_types_by_name['ConstructResponse'] = _CONSTRUCTRESPONSE
|
||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
||||
|
||||
ConstructRequest = _reflection.GeneratedProtocolMessageType('ConstructRequest', (_message.Message,), {
|
||||
'DESCRIPTOR' : _CONSTRUCTREQUEST,
|
||||
'__module__' : 'runtime_pb2'
|
||||
# @@protoc_insertion_point(class_scope:pulumirpc.ConstructRequest)
|
||||
})
|
||||
_sym_db.RegisterMessage(ConstructRequest)
|
||||
|
||||
ConstructResponse = _reflection.GeneratedProtocolMessageType('ConstructResponse', (_message.Message,), {
|
||||
'DESCRIPTOR' : _CONSTRUCTRESPONSE,
|
||||
'__module__' : 'runtime_pb2'
|
||||
# @@protoc_insertion_point(class_scope:pulumirpc.ConstructResponse)
|
||||
})
|
||||
_sym_db.RegisterMessage(ConstructResponse)
|
||||
|
||||
|
||||
|
||||
_RUNTIME = _descriptor.ServiceDescriptor(
|
||||
name='Runtime',
|
||||
full_name='pulumirpc.Runtime',
|
||||
file=DESCRIPTOR,
|
||||
index=0,
|
||||
serialized_options=None,
|
||||
serialized_start=313,
|
||||
serialized_end=396,
|
||||
methods=[
|
||||
_descriptor.MethodDescriptor(
|
||||
name='Construct',
|
||||
full_name='pulumirpc.Runtime.Construct',
|
||||
index=0,
|
||||
containing_service=None,
|
||||
input_type=_CONSTRUCTREQUEST,
|
||||
output_type=_CONSTRUCTRESPONSE,
|
||||
serialized_options=None,
|
||||
),
|
||||
])
|
||||
_sym_db.RegisterServiceDescriptor(_RUNTIME)
|
||||
|
||||
DESCRIPTOR.services_by_name['Runtime'] = _RUNTIME
|
||||
|
||||
# @@protoc_insertion_point(module_scope)
|
46
sdk/python/lib/pulumi/runtime/proto/runtime_pb2_grpc.py
Normal file
46
sdk/python/lib/pulumi/runtime/proto/runtime_pb2_grpc.py
Normal file
|
@ -0,0 +1,46 @@
|
|||
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
|
||||
import grpc
|
||||
|
||||
from . import runtime_pb2 as runtime__pb2
|
||||
|
||||
|
||||
class RuntimeStub(object):
|
||||
# missing associated documentation comment in .proto file
|
||||
pass
|
||||
|
||||
def __init__(self, channel):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
channel: A grpc.Channel.
|
||||
"""
|
||||
self.Construct = channel.unary_unary(
|
||||
'/pulumirpc.Runtime/Construct',
|
||||
request_serializer=runtime__pb2.ConstructRequest.SerializeToString,
|
||||
response_deserializer=runtime__pb2.ConstructResponse.FromString,
|
||||
)
|
||||
|
||||
|
||||
class RuntimeServicer(object):
|
||||
# missing associated documentation comment in .proto file
|
||||
pass
|
||||
|
||||
def Construct(self, request, context):
|
||||
# missing associated documentation comment in .proto file
|
||||
pass
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
|
||||
def add_RuntimeServicer_to_server(servicer, server):
|
||||
rpc_method_handlers = {
|
||||
'Construct': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.Construct,
|
||||
request_deserializer=runtime__pb2.ConstructRequest.FromString,
|
||||
response_serializer=runtime__pb2.ConstructResponse.SerializeToString,
|
||||
),
|
||||
}
|
||||
generic_handler = grpc.method_handlers_generic_handler(
|
||||
'pulumirpc.Runtime', rpc_method_handlers)
|
||||
server.add_generic_rpc_handlers((generic_handler,))
|
|
@ -24,6 +24,7 @@ from .. import log
|
|||
from ..runtime.proto import resource_pb2
|
||||
from .rpc_manager import RPC_MANAGER
|
||||
from ..metadata import get_project, get_stack
|
||||
from .invoke import invoke
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .. import Resource, ResourceOptions, CustomResource, Inputs, Output
|
||||
|
@ -282,6 +283,102 @@ def read_resource(res: 'CustomResource', ty: str, name: str, props: 'Inputs', op
|
|||
res.id = result.id
|
||||
|
||||
|
||||
def _get_resource(res: 'Resource', ty: str, name: str, custom: bool, props: 'Inputs', opts: 'ResourceOptions') -> _ResourceResult:
|
||||
"""
|
||||
TODO
|
||||
"""
|
||||
urn = opts.urn
|
||||
if urn is None:
|
||||
raise Exception(
|
||||
"Cannot get resource whose options are lacking a URN value")
|
||||
|
||||
log.debug(f"registering resource: ty={ty}, name={name}, custom={custom}")
|
||||
|
||||
# Prepare the resource.
|
||||
|
||||
# Simply initialize the URN property and get prepared to resolve it later on.
|
||||
# Note: a resource urn will always get a value, and thus the output property
|
||||
# for it can always run .apply calls.
|
||||
log.debug(f"preparing resource for RPC")
|
||||
urn_future: asyncio.Future[Any] = asyncio.Future()
|
||||
urn_known: asyncio.Future[bool] = asyncio.Future()
|
||||
urn_secret: asyncio.Future[bool] = asyncio.Future()
|
||||
urn_known.set_result(True)
|
||||
urn_secret.set_result(False)
|
||||
resolve_urn = urn_future.set_result
|
||||
resolve_urn_exn = urn_future.set_exception
|
||||
res.urn = known_types.new_output({res}, urn_future, urn_known, urn_secret)
|
||||
|
||||
# If a custom resource, make room for the ID property.
|
||||
resolve_id: Optional[Callable[[
|
||||
Any, bool, Optional[Exception]], None]] = None
|
||||
if custom:
|
||||
res = cast('CustomResource', res)
|
||||
resolve_value: asyncio.Future[Any] = asyncio.Future()
|
||||
resolve_perform_apply: asyncio.Future[bool] = asyncio.Future()
|
||||
resolve_secret: asyncio.Future[Any] = asyncio.Future()
|
||||
res.id = known_types.new_output(
|
||||
{res}, resolve_value, resolve_perform_apply, resolve_secret)
|
||||
|
||||
def do_resolve(value: Any, perform_apply: bool, exn: Optional[Exception]):
|
||||
if exn is not None:
|
||||
resolve_value.set_exception(exn)
|
||||
resolve_perform_apply.set_exception(exn)
|
||||
resolve_secret.set_exception(exn)
|
||||
else:
|
||||
resolve_value.set_result(value)
|
||||
resolve_perform_apply.set_result(perform_apply)
|
||||
resolve_secret.set_result(False)
|
||||
|
||||
resolve_id = do_resolve
|
||||
|
||||
# Now "transfer" all input properties into unresolved futures on res. This way,
|
||||
# this resource will look like it has all its output properties to anyone it is
|
||||
# passed to. However, those futures won't actually resolve until the RPC returns
|
||||
resolvers = rpc.transfer_properties(res, props)
|
||||
|
||||
async def do_get():
|
||||
try:
|
||||
log.debug(f"preparing get: ty={ty}, name={name}, urn={urn}")
|
||||
_ = await prepare_resource(res, ty, custom, props, opts)
|
||||
|
||||
# Resolve the URN that we were given. Note that we are explicitly discarding the list of
|
||||
# dependencies returned to us from "serialize_property" (the second argument). This is
|
||||
# because a "get" resource does not actually have any dependencies at all in the cloud
|
||||
# provider sense, because a read resource already exists. We do not need to track this
|
||||
# dependency. TODO: Is this actually true for "get"?
|
||||
resolved_urn = await rpc.serialize_property(urn, [])
|
||||
log.debug(f"get prepared: ty={ty}, name={name}, urn={urn}")
|
||||
resp = await invoke("pulumi:pulumi:readStackResource", {"urn": resolved_urn})
|
||||
|
||||
except Exception as exn:
|
||||
log.debug(
|
||||
f"exception when preparing or executing rpc: {traceback.format_exc()}")
|
||||
rpc.resolve_outputs_due_to_exception(resolvers, exn)
|
||||
resolve_urn_exn(exn)
|
||||
if resolve_id:
|
||||
resolve_id(None, False, exn)
|
||||
raise
|
||||
|
||||
resp_urn = resp["urn"]
|
||||
log.debug(f"resource get successful: ty={ty}, urn={resp_urn}")
|
||||
resolve_urn(resp_urn)
|
||||
if resolve_id:
|
||||
id_known = bool(resp["id"])
|
||||
resolve_id(resp["id"], id_known, None)
|
||||
await rpc.resolve_properties(resolvers, resp["outputs"])
|
||||
|
||||
asyncio.ensure_future(RPC_MANAGER.do_rpc("get resource", do_get)())
|
||||
|
||||
def get_resource(res: 'Resource', ty: str, name: str, custom: bool, props: 'Inputs', opts: 'ResourceOptions') -> None:
|
||||
result = _get_resource(res, ty, name, custom, props, opts)
|
||||
res.urn = result.urn
|
||||
if custom:
|
||||
assert result.id is not None
|
||||
res = cast('CustomResource', res)
|
||||
res.id = result.id
|
||||
|
||||
|
||||
# pylint: disable=too-many-locals,too-many-statements
|
||||
|
||||
def _register_resource(res: 'Resource',
|
||||
|
|
|
@ -45,6 +45,9 @@ _special_archive_sig = "0def7320c3a5731c473e5ecbe6d01bc7"
|
|||
_special_secret_sig = "1b47061264138c4ac30d75fd1eb44270"
|
||||
"""special_secret_sig is a randomly assigned hash used to identify secrets in maps. See pkg/resource/properties.go"""
|
||||
|
||||
_special_resource_sig = "5cf8f73096256a8f31e491e813e4eb8e"
|
||||
"""special_resource_sig is a randomly assigned hash used to identify resources in maps. See pkg/resource/properties.go"""
|
||||
|
||||
_INT_OR_FLOAT = six.integer_types + (float,)
|
||||
|
||||
def isLegalProtobufValue(value: Any) -> bool:
|
||||
|
@ -102,7 +105,11 @@ async def serialize_property(value: 'Input[Any]',
|
|||
if known_types.is_custom_resource(value):
|
||||
resource = cast('CustomResource', value)
|
||||
deps.append(resource)
|
||||
return await serialize_property(resource.id, deps, input_transformer)
|
||||
urn = await serialize_property(value.urn, deps, input_transformer)
|
||||
return {
|
||||
_special_sig_key: _special_resource_sig,
|
||||
"urn": urn,
|
||||
}
|
||||
|
||||
if known_types.is_asset(value):
|
||||
# Serializing an asset requires the use of a magical signature key, since otherwise it would
|
||||
|
@ -235,6 +242,17 @@ def deserialize_properties(props_struct: struct_pb2.Struct, keep_unknowns: Optio
|
|||
_special_sig_key: _special_secret_sig,
|
||||
"value": deserialize_property(props_struct["value"])
|
||||
}
|
||||
if props_struct[_special_sig_key] == _special_resource_sig:
|
||||
urn = props_struct["urn"]
|
||||
urn_parts = urn.split("::")
|
||||
qualified_type = urn_parts[2]
|
||||
typ = qualified_type.split("$")[-1]
|
||||
proxy_constructor = PROXY_CONSTRUCTORS.get(typ, None)
|
||||
if proxy_constructor is not None:
|
||||
urn_name = urn_parts[3]
|
||||
return proxy_constructor(urn_name, {"urn": urn})
|
||||
print(f"Saw valid URN {urn} during deserialization, but no proxy constructor is registered for type {typ}.")
|
||||
return urn
|
||||
|
||||
raise AssertionError("Unrecognized signature when unmarshalling resource property")
|
||||
|
||||
|
@ -423,6 +441,10 @@ async def resolve_outputs(res: 'Resource',
|
|||
# the user.
|
||||
all_properties[translated_key] = translate_output_properties(res, deserialize_property(value))
|
||||
|
||||
await resolve_properties(resolvers, all_properties)
|
||||
|
||||
async def resolve_properties(resolvers: Dict[str, Resolver], all_properties: Dict[str, Any]):
|
||||
|
||||
for key, value in all_properties.items():
|
||||
# Skip "id" and "urn", since we handle those specially.
|
||||
if key in ["id", "urn"]:
|
||||
|
@ -486,3 +508,11 @@ def resolve_outputs_due_to_exception(resolvers: Dict[str, Resolver], exn: Except
|
|||
for key, resolve in resolvers.items():
|
||||
log.debug(f"sending exception to resolver for {key}")
|
||||
resolve(None, False, False, exn)
|
||||
|
||||
PROXY_CONSTRUCTORS: Dict[str, Any] = dict()
|
||||
|
||||
def register_proxy_constructor(typ: str, constructor):
|
||||
existing = PROXY_CONSTRUCTORS.get(typ, None)
|
||||
if existing is not None:
|
||||
raise ValueError(f"Cannot re-register type {typ} as a proxy. Previous registration was {existing}, new registration was {constructor}.")
|
||||
PROXY_CONSTRUCTORS[typ] = constructor
|
||||
|
|
|
@ -60,6 +60,8 @@ class Settings:
|
|||
self.dry_run = dry_run
|
||||
self.test_mode_enabled = test_mode_enabled
|
||||
self.legacy_apply_enabled = legacy_apply_enabled
|
||||
self.monitor_addr = monitor
|
||||
self.engine_addr = engine
|
||||
|
||||
if self.test_mode_enabled is None:
|
||||
self.test_mode_enabled = os.getenv("PULUMI_TEST_MODE", "false") == "true"
|
||||
|
|
|
@ -34,8 +34,8 @@ class FakeCustomResource:
|
|||
Fake CustomResource class that duck-types to the real CustomResource.
|
||||
This class is substituted for the real CustomResource for the below test.
|
||||
"""
|
||||
def __init__(self, id):
|
||||
self.id = id
|
||||
def __init__(self, urn):
|
||||
self.urn = Output.from_input(urn)
|
||||
|
||||
|
||||
def async_test(coro):
|
||||
|
@ -85,11 +85,13 @@ class NextSerializationTests(unittest.TestCase):
|
|||
|
||||
@async_test
|
||||
async def test_custom_resource(self):
|
||||
res = FakeCustomResource("some-id")
|
||||
fake_urn = "urn:pulumi:mystack::myproject::my:mod:Fake::fake"
|
||||
res = FakeCustomResource(fake_urn)
|
||||
deps = []
|
||||
prop = await rpc.serialize_property(res, deps)
|
||||
self.assertListEqual([res], deps)
|
||||
self.assertEqual("some-id", prop)
|
||||
self.assertEqual(rpc._special_resource_sig, prop[rpc._special_sig_key])
|
||||
self.assertEqual(fake_urn, prop["urn"])
|
||||
|
||||
@async_test
|
||||
async def test_string_asset(self):
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"bytes"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -323,6 +324,48 @@ func TestAccNodeCompatTests(t *testing.T) {
|
|||
integration.ProgramTest(t, &test)
|
||||
}
|
||||
|
||||
func TestNodeJSMultilang(t *testing.T) {
|
||||
dir := path.Join(getCwd(t), "multilang", "nodejs")
|
||||
test := getBaseOptions().
|
||||
With(integration.ProgramTestOptions{
|
||||
Dir: dir,
|
||||
Config: map[string]string{
|
||||
"aws:region": "us-west-2",
|
||||
},
|
||||
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
||||
assert.Equal(t, "foo", stackInfo.Outputs["id2"])
|
||||
assert.Equal(t, "mydata", stackInfo.Outputs["innerComponent"])
|
||||
assert.Equal(t, "sg-", stackInfo.Outputs["nodeSecurityGroupId"].(string)[0:3])
|
||||
assert.Equal(t, 42.0, stackInfo.Outputs["output1"])
|
||||
},
|
||||
})
|
||||
|
||||
integration.ProgramTest(t, &test)
|
||||
}
|
||||
|
||||
func TestPythonMultilang(t *testing.T) {
|
||||
dir := path.Join(getCwd(t), "multilang", "python")
|
||||
test := getPythonBaseOptions().
|
||||
With(integration.ProgramTestOptions{
|
||||
Dir: dir,
|
||||
Dependencies: []string{
|
||||
filepath.Join("..", "sdk", "python", "env", "src"),
|
||||
filepath.Join(dir, "mycomponent"),
|
||||
},
|
||||
Config: map[string]string{
|
||||
"aws:region": "us-west-2",
|
||||
},
|
||||
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
||||
assert.Equal(t, "foo", stackInfo.Outputs["id2"])
|
||||
assert.Equal(t, "mydata", stackInfo.Outputs["innerComponent"])
|
||||
assert.Equal(t, "sg-", stackInfo.Outputs["nodeSecurityGroupId"].(string)[0:3])
|
||||
assert.Equal(t, 42.0, stackInfo.Outputs["output1"])
|
||||
},
|
||||
})
|
||||
|
||||
integration.ProgramTest(t, &test)
|
||||
}
|
||||
|
||||
func getCwd(t *testing.T) string {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
|
@ -337,6 +380,14 @@ func getBaseOptions() integration.ProgramTestOptions {
|
|||
}
|
||||
}
|
||||
|
||||
func getPythonBaseOptions() integration.ProgramTestOptions {
|
||||
return integration.ProgramTestOptions{
|
||||
Dependencies: []string{
|
||||
filepath.Join("..", "sdk", "python", "env", "src"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func skipIfNotNode610(t *testing.T) {
|
||||
nodeVer, err := getNodeVersion()
|
||||
if err != nil && nodeVer.Major == 6 && nodeVer.Minor == 10 {
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
"align": [
|
||||
true,
|
||||
"parameters",
|
||||
"arguments",
|
||||
"statements"
|
||||
],
|
||||
"ban": false,
|
||||
|
|
Loading…
Reference in a new issue