Merge branch 'master' of github.com:pulumi/pulumi

This commit is contained in:
stack72 2021-07-07 16:21:55 +01:00
commit bf886cd53a
16 changed files with 888 additions and 28 deletions

View file

@ -19,6 +19,7 @@ include:
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
* Contribute in a positive and constructive way
Examples of unacceptable behavior by participants include:
@ -30,6 +31,12 @@ Examples of unacceptable behavior by participants include:
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Community Guidelines
* Be clear and stay on topic. Communicating with strangers on the Internet can make it hard to convey or read tone, and sarcasm is frequently misunderstood. Try to use clear language, and think about how the other person will receive it.
* Dont cross-post the same thing in multiple GitHub Discussion topics or multiple Slack channels. This can make it difficult for people answering your questions and creates "scrollback spam".
* Public discussion is preferred to private. Avoid using Slack DMs for questions, and instead share them in public Slack channels or GitHub Discussion threads. This allows a larger audience to both share their knowledge as well as learn from your question or issue. If you're having a problem, chances are someone else is having a similar problem. Learning in public is a community contribution.
* Minimize notifications to other community members. Avoid tagging other community members in Slack messages or Discussion threads, unless you are replying to something specific. Community members are here to help each other, but are not "on call" for support, and we expect everyone to try to minimize "notification fatigue". If your issue is time-sensitive or critical, use methods like support@pulumi.com instead.
## Our Responsibilities

View file

@ -53,6 +53,11 @@ var sdkTests = []sdkTest{
Directory: "simple-resource-schema-custom-pypackage-name",
Description: "Simple schema with local resource properties and custom Python package name",
},
// TODO[pulumi/pulumi#7072]: Enable test after codegen support for all languages lands.
// {
// Directory: "simple-methods-schema",
// Description: "Simple schema with methods",
// },
}
// TestSDKCodegen runs the complete set of SDK code generation tests against a particular language's code generator.

View file

@ -0,0 +1,40 @@
# coding=utf-8
# *** WARNING: this file was generated by test. ***
# *** Do not edit by hand unless you're certain you know what you are doing! ***
from . import _utilities
import typing
# Export this package's modules as members:
from .foo import *
from .provider import *
# Make subpackages available:
if typing.TYPE_CHECKING:
import pulumi_example.nested as nested
else:
nested = _utilities.lazy_import('pulumi_example.nested')
_utilities.register(
resource_modules="""
[
{
"pkg": "example",
"mod": "",
"fqn": "pulumi_example",
"classes": {
"example::Foo": "Foo"
}
}
]
""",
resource_packages="""
[
{
"pkg": "example",
"token": "pulumi:providers:example",
"fqn": "pulumi_example",
"class": "Provider"
}
]
"""
)

View file

@ -0,0 +1,211 @@
# coding=utf-8
# *** WARNING: this file was generated by test. ***
# *** Do not edit by hand unless you're certain you know what you are doing! ***
import json
import os
import sys
import importlib.util
import pkg_resources
import pulumi
import pulumi.runtime
from semver import VersionInfo as SemverVersion
from parver import Version as PEP440Version
def get_env(*args):
for v in args:
value = os.getenv(v)
if value is not None:
return value
return None
def get_env_bool(*args):
str = get_env(*args)
if str is not None:
# NOTE: these values are taken from https://golang.org/src/strconv/atob.go?s=351:391#L1, which is what
# Terraform uses internally when parsing boolean values.
if str in ["1", "t", "T", "true", "TRUE", "True"]:
return True
if str in ["0", "f", "F", "false", "FALSE", "False"]:
return False
return None
def get_env_int(*args):
str = get_env(*args)
if str is not None:
try:
return int(str)
except:
return None
return None
def get_env_float(*args):
str = get_env(*args)
if str is not None:
try:
return float(str)
except:
return None
return None
def _get_semver_version():
# __name__ is set to the fully-qualified name of the current module, In our case, it will be
# <some module>._utilities. <some module> is the module we want to query the version for.
root_package, *rest = __name__.split('.')
# pkg_resources uses setuptools to inspect the set of installed packages. We use it here to ask
# for the currently installed version of the root package (i.e. us) and get its version.
# Unfortunately, PEP440 and semver differ slightly in incompatible ways. The Pulumi engine expects
# to receive a valid semver string when receiving requests from the language host, so it's our
# responsibility as the library to convert our own PEP440 version into a valid semver string.
pep440_version_string = pkg_resources.require(root_package)[0].version
pep440_version = PEP440Version.parse(pep440_version_string)
(major, minor, patch) = pep440_version.release
prerelease = None
if pep440_version.pre_tag == 'a':
prerelease = f"alpha.{pep440_version.pre}"
elif pep440_version.pre_tag == 'b':
prerelease = f"beta.{pep440_version.pre}"
elif pep440_version.pre_tag == 'rc':
prerelease = f"rc.{pep440_version.pre}"
elif pep440_version.dev is not None:
prerelease = f"dev.{pep440_version.dev}"
# The only significant difference between PEP440 and semver as it pertains to us is that PEP440 has explicit support
# for dev builds, while semver encodes them as "prerelease" versions. In order to bridge between the two, we convert
# our dev build version into a prerelease tag. This matches what all of our other packages do when constructing
# their own semver string.
return SemverVersion(major=major, minor=minor, patch=patch, prerelease=prerelease)
# Determine the version once and cache the value, which measurably improves program performance.
_version = _get_semver_version()
_version_str = str(_version)
def get_version():
return _version_str
def get_resource_args_opts(resource_args_type, resource_options_type, *args, **kwargs):
"""
Return the resource args and options given the *args and **kwargs of a resource's
__init__ method.
"""
resource_args, opts = None, None
# If the first item is the resource args type, save it and remove it from the args list.
if args and isinstance(args[0], resource_args_type):
resource_args, args = args[0], args[1:]
# Now look at the first item in the args list again.
# If the first item is the resource options class, save it.
if args and isinstance(args[0], resource_options_type):
opts = args[0]
# If resource_args is None, see if "args" is in kwargs, and, if so, if it's typed as the
# the resource args type.
if resource_args is None:
a = kwargs.get("args")
if isinstance(a, resource_args_type):
resource_args = a
# If opts is None, look it up in kwargs.
if opts is None:
opts = kwargs.get("opts")
return resource_args, opts
# Temporary: just use pulumi._utils.lazy_import once everyone upgrades.
def lazy_import(fullname):
import pulumi._utils as u
f = getattr(u, 'lazy_import', None)
if f is None:
f = _lazy_import_temp
return f(fullname)
# Copied from pulumi._utils.lazy_import, see comments there.
def _lazy_import_temp(fullname):
m = sys.modules.get(fullname, None)
if m is not None:
return m
spec = importlib.util.find_spec(fullname)
m = sys.modules.get(fullname, None)
if m is not None:
return m
loader = importlib.util.LazyLoader(spec.loader)
spec.loader = loader
module = importlib.util.module_from_spec(spec)
m = sys.modules.get(fullname, None)
if m is not None:
return m
sys.modules[fullname] = module
loader.exec_module(module)
return module
class Package(pulumi.runtime.ResourcePackage):
def __init__(self, pkg_info):
super().__init__()
self.pkg_info = pkg_info
def version(self):
return _version
def construct_provider(self, name: str, typ: str, urn: str) -> pulumi.ProviderResource:
if typ != self.pkg_info['token']:
raise Exception(f"unknown provider type {typ}")
Provider = getattr(lazy_import(self.pkg_info['fqn']), self.pkg_info['class'])
return Provider(name, pulumi.ResourceOptions(urn=urn))
class Module(pulumi.runtime.ResourceModule):
def __init__(self, mod_info):
super().__init__()
self.mod_info = mod_info
def version(self):
return _version
def construct(self, name: str, typ: str, urn: str) -> pulumi.Resource:
class_name = self.mod_info['classes'].get(typ, None)
if class_name is None:
raise Exception(f"unknown resource type {typ}")
TheClass = getattr(lazy_import(self.mod_info['fqn']), class_name)
return TheClass(name, pulumi.ResourceOptions(urn=urn))
def register(resource_modules, resource_packages):
resource_modules = json.loads(resource_modules)
resource_packages = json.loads(resource_packages)
for pkg_info in resource_packages:
pulumi.runtime.register_resource_package(pkg_info['pkg'], Package(pkg_info))
for mod_info in resource_modules:
pulumi.runtime.register_resource_module(
mod_info['pkg'],
mod_info['mod'],
Module(mod_info))

View file

@ -0,0 +1,127 @@
# coding=utf-8
# *** WARNING: this file was generated by test. ***
# *** Do not edit by hand unless you're certain you know what you are doing! ***
import warnings
import pulumi
import pulumi.runtime
from typing import Any, Mapping, Optional, Sequence, Union, overload
from . import _utilities
from . import nested as _nested
import pulumi_random
__all__ = ['FooArgs', 'Foo']
@pulumi.input_type
class FooArgs:
def __init__(__self__):
"""
The set of arguments for constructing a Foo resource.
"""
pass
class Foo(pulumi.ComponentResource):
@overload
def __init__(__self__,
resource_name: str,
opts: Optional[pulumi.ResourceOptions] = None,
__props__=None):
"""
Create a Foo resource with the given unique name, props, and options.
:param str resource_name: The name of the resource.
:param pulumi.ResourceOptions opts: Options for the resource.
"""
...
@overload
def __init__(__self__,
resource_name: str,
args: Optional[FooArgs] = None,
opts: Optional[pulumi.ResourceOptions] = None):
"""
Create a Foo resource with the given unique name, props, and options.
:param str resource_name: The name of the resource.
:param FooArgs args: The arguments to use to populate this resource's properties.
:param pulumi.ResourceOptions opts: Options for the resource.
"""
...
def __init__(__self__, resource_name: str, *args, **kwargs):
resource_args, opts = _utilities.get_resource_args_opts(FooArgs, pulumi.ResourceOptions, *args, **kwargs)
if resource_args is not None:
__self__._internal_init(resource_name, opts, **resource_args.__dict__)
else:
__self__._internal_init(resource_name, *args, **kwargs)
def _internal_init(__self__,
resource_name: str,
opts: Optional[pulumi.ResourceOptions] = None,
__props__=None):
if opts is None:
opts = pulumi.ResourceOptions()
if not isinstance(opts, pulumi.ResourceOptions):
raise TypeError('Expected resource options to be a ResourceOptions instance')
if opts.version is None:
opts.version = _utilities.get_version()
if opts.id is not None:
raise ValueError('ComponentResource classes do not support opts.id')
else:
if __props__ is not None:
raise TypeError('__props__ is only valid when passed in combination with a valid opts.id to get an existing resource')
__props__ = FooArgs.__new__(FooArgs)
super(Foo, __self__).__init__(
'example::Foo',
resource_name,
__props__,
opts,
remote=True)
@pulumi.output_type
class BarResult:
def __init__(__self__, some_value=None):
if some_value and not isinstance(some_value, str):
raise TypeError("Expected argument 'some_value' to be a str")
pulumi.set(__self__, "some_value", some_value)
@property
@pulumi.getter(name="someValue")
def some_value(self) -> str:
return pulumi.get(self, "some_value")
def bar(__self__, *,
baz_required: pulumi.Input['_nested.BazArgs'],
bool_value_required: pulumi.Input[bool],
name_required: pulumi.Input['pulumi_random.RandomPet'],
string_value_required: pulumi.Input[str],
baz: Optional[pulumi.Input['_nested.BazArgs']] = None,
baz_plain: Optional['_nested.BazArgs'] = None,
bool_value: Optional[pulumi.Input[bool]] = None,
bool_value_plain: Optional[bool] = None,
name: Optional[pulumi.Input['pulumi_random.RandomPet']] = None,
name_plain: Optional['pulumi_random.RandomPet'] = None,
string_value: Optional[pulumi.Input[str]] = None,
string_value_plain: Optional[str] = None) -> pulumi.Output['Foo.BarResult']:
"""
A description of bar.
"""
__args__ = dict()
__args__['__self__'] = __self__
__args__['bazRequired'] = baz_required
__args__['boolValueRequired'] = bool_value_required
__args__['nameRequired'] = name_required
__args__['stringValueRequired'] = string_value_required
__args__['baz'] = baz
__args__['bazPlain'] = baz_plain
__args__['boolValue'] = bool_value
__args__['boolValuePlain'] = bool_value_plain
__args__['name'] = name
__args__['namePlain'] = name_plain
__args__['stringValue'] = string_value
__args__['stringValuePlain'] = string_value_plain
return pulumi.runtime.call('example::Foo/bar', __args__, res=__self__, typ=Foo.BarResult)
def baz(__self__) -> None:
__args__ = dict()
__args__['__self__'] = __self__
pulumi.runtime.call('example::Foo/baz', __args__, res=__self__)

View file

@ -0,0 +1,7 @@
# coding=utf-8
# *** WARNING: this file was generated by test. ***
# *** Do not edit by hand unless you're certain you know what you are doing! ***
from .. import _utilities
import typing
from ._inputs import *

View file

@ -0,0 +1,43 @@
# coding=utf-8
# *** WARNING: this file was generated by test. ***
# *** Do not edit by hand unless you're certain you know what you are doing! ***
import warnings
import pulumi
import pulumi.runtime
from typing import Any, Mapping, Optional, Sequence, Union, overload
from .. import _utilities
__all__ = [
'Baz',
]
@pulumi.input_type
class Baz:
def __init__(__self__, *,
hello: Optional[str] = None,
world: Optional[str] = None):
if hello is not None:
pulumi.set(__self__, "hello", hello)
if world is not None:
pulumi.set(__self__, "world", world)
@property
@pulumi.getter
def hello(self) -> Optional[str]:
return pulumi.get(self, "hello")
@hello.setter
def hello(self, value: Optional[str]):
pulumi.set(self, "hello", value)
@property
@pulumi.getter
def world(self) -> Optional[str]:
return pulumi.get(self, "world")
@world.setter
def world(self, value: Optional[str]):
pulumi.set(self, "world", value)

View file

@ -0,0 +1,73 @@
# coding=utf-8
# *** WARNING: this file was generated by test. ***
# *** Do not edit by hand unless you're certain you know what you are doing! ***
import warnings
import pulumi
import pulumi.runtime
from typing import Any, Mapping, Optional, Sequence, Union, overload
from . import _utilities
__all__ = ['ProviderArgs', 'Provider']
@pulumi.input_type
class ProviderArgs:
def __init__(__self__):
"""
The set of arguments for constructing a Provider resource.
"""
pass
class Provider(pulumi.ProviderResource):
@overload
def __init__(__self__,
resource_name: str,
opts: Optional[pulumi.ResourceOptions] = None,
__props__=None):
"""
Create a Example resource with the given unique name, props, and options.
:param str resource_name: The name of the resource.
:param pulumi.ResourceOptions opts: Options for the resource.
"""
...
@overload
def __init__(__self__,
resource_name: str,
args: Optional[ProviderArgs] = None,
opts: Optional[pulumi.ResourceOptions] = None):
"""
Create a Example resource with the given unique name, props, and options.
:param str resource_name: The name of the resource.
:param ProviderArgs args: The arguments to use to populate this resource's properties.
:param pulumi.ResourceOptions opts: Options for the resource.
"""
...
def __init__(__self__, resource_name: str, *args, **kwargs):
resource_args, opts = _utilities.get_resource_args_opts(ProviderArgs, pulumi.ResourceOptions, *args, **kwargs)
if resource_args is not None:
__self__._internal_init(resource_name, opts, **resource_args.__dict__)
else:
__self__._internal_init(resource_name, *args, **kwargs)
def _internal_init(__self__,
resource_name: str,
opts: Optional[pulumi.ResourceOptions] = None,
__props__=None):
if opts is None:
opts = pulumi.ResourceOptions()
if not isinstance(opts, pulumi.ResourceOptions):
raise TypeError('Expected resource options to be a ResourceOptions instance')
if opts.version is None:
opts.version = _utilities.get_version()
if opts.id is None:
if __props__ is not None:
raise TypeError('__props__ is only valid when passed in combination with a valid opts.id to get an existing resource')
__props__ = ProviderArgs.__new__(ProviderArgs)
super(Provider, __self__).__init__(
'example',
resource_name,
__props__,
opts)

View file

@ -0,0 +1,55 @@
# coding=utf-8
# *** WARNING: this file was generated by test. ***
# *** Do not edit by hand unless you're certain you know what you are doing! ***
import errno
from setuptools import setup, find_packages
from setuptools.command.install import install
from subprocess import check_call
class InstallPluginCommand(install):
def run(self):
install.run(self)
try:
check_call(['pulumi', 'plugin', 'install', 'resource', 'example', '${PLUGIN_VERSION}'])
except OSError as error:
if error.errno == errno.ENOENT:
print("""
There was an error installing the example resource provider plugin.
It looks like `pulumi` is not installed on your system.
Please visit https://pulumi.com/ to install the Pulumi CLI.
You may try manually installing the plugin by running
`pulumi plugin install resource example ${PLUGIN_VERSION}`
""")
else:
raise
def readme():
try:
with open('README.md', encoding='utf-8') as f:
return f.read()
except FileNotFoundError:
return "example Pulumi Package - Development Version"
setup(name='pulumi_example',
version='${VERSION}',
long_description=readme(),
long_description_content_type='text/markdown',
cmdclass={
'install': InstallPluginCommand,
},
packages=find_packages(),
package_data={
'pulumi_example': [
'py.typed',
]
},
install_requires=[
'parver>=0.2.1',
'pulumi',
'semver>=2.8.1'
],
zip_safe=False)

View file

@ -0,0 +1,115 @@
{
"version": "0.0.1",
"name": "example",
"types": {
"example:nested:Baz": {
"properties": {
"hello": {
"type": "string"
},
"world": {
"type": "string"
}
},
"type": "object"
}
},
"resources": {
"example::Foo": {
"isComponent": true,
"methods": {
"bar": "example::Foo/bar",
"baz": "example::Foo/baz"
}
}
},
"functions": {
"example::Foo/bar": {
"description": "A description of bar.",
"inputs": {
"properties": {
"__self__": {
"$ref": "#/resources/example::Foo"
},
"boolValue": {
"type": "boolean"
},
"boolValueRequired": {
"type": "boolean"
},
"boolValuePlain": {
"type": "boolean",
"plain": true
},
"stringValue": {
"type": "string"
},
"stringValueRequired": {
"type": "string"
},
"stringValuePlain": {
"type": "string",
"plain": true
},
"name": {
"$ref": "/random/v2.3.1/schema.json#/resources/random:index%2FrandomPet:RandomPet"
},
"nameRequired": {
"$ref": "/random/v2.3.1/schema.json#/resources/random:index%2FrandomPet:RandomPet"
},
"namePlain": {
"$ref": "/random/v2.3.1/schema.json#/resources/random:index%2FrandomPet:RandomPet",
"plain": true
},
"baz": {
"$ref": "#/types/example:nested:Baz"
},
"bazRequired": {
"$ref": "#/types/example:nested:Baz"
},
"bazPlain": {
"$ref": "#/types/example:nested:Baz",
"plain": true
}
},
"required": [
"__self__",
"boolValueRequired",
"stringValueRequired",
"nameRequired",
"bazRequired"
]
},
"outputs": {
"properties": {
"someValue": {
"type": "string"
}
},
"required": [
"someValue"
]
}
},
"example::Foo/baz": {
"inputs": {
"properties": {
"__self__": {
"$ref": "#/resources/example::Foo"
}
},
"required": [
"__self__"
]
}
}
},
"language": {
"csharp": {},
"go": {
"importBasePath": "github.com/pulumi/pulumi/pkg/v3/codegen/internal/test/testdata/simple-methods-schema/go/example"
},
"nodejs": {},
"python": {}
}
}

View file

@ -877,7 +877,7 @@ func (mod *modContext) genAwaitableType(w io.Writer, obj *schema.ObjectType) str
fmt.Fprintf(w, "\n")
// Write out Python property getters for each property.
mod.genProperties(w, obj.Properties, false /*setters*/, func(prop *schema.Property) string {
mod.genProperties(w, obj.Properties, false /*setters*/, "", func(prop *schema.Property) string {
return mod.typeString(prop.Type, false /*input*/, false /*acceptMapping*/)
})
@ -909,22 +909,35 @@ func (mod *modContext) genAwaitableType(w io.Writer, obj *schema.ObjectType) str
return awaitableName
}
func (mod *modContext) genResource(res *schema.Resource) (string, error) {
w := &bytes.Buffer{}
imports := imports{}
mod.collectImports(res.Properties, imports, false /*input*/)
mod.collectImports(res.InputProperties, imports, true /*input*/)
if res.StateInputs != nil {
mod.collectImports(res.StateInputs.Properties, imports, true /*input*/)
}
mod.genHeader(w, true /*needsSDK*/, imports)
func resourceName(res *schema.Resource) string {
name := pyClassName(tokenToName(res.Token))
if res.IsProvider {
name = "Provider"
}
return name
}
func (mod *modContext) genResource(res *schema.Resource) (string, error) {
w := &bytes.Buffer{}
imports := imports{}
mod.collectImportsForResource(res.Properties, imports, false /*input*/, res)
mod.collectImportsForResource(res.InputProperties, imports, true /*input*/, res)
if res.StateInputs != nil {
mod.collectImportsForResource(res.StateInputs.Properties, imports, true /*input*/, res)
}
for _, method := range res.Methods {
if method.Function.Inputs != nil {
mod.collectImportsForResource(method.Function.Inputs.Properties, imports, true /*input*/, res)
}
if method.Function.Outputs != nil {
mod.collectImportsForResource(method.Function.Outputs.Properties, imports, false /*input*/, res)
}
}
mod.genHeader(w, true /*needsSDK*/, imports)
name := resourceName(res)
resourceArgsName := fmt.Sprintf("%sArgs", name)
// Some providers (e.g. Kubernetes) have types with the same name as resources (e.g. StorageClass in Kubernetes).
@ -1226,15 +1239,18 @@ func (mod *modContext) genResource(res *schema.Resource) (string, error) {
}
// Write out Python property getters for each of the resource's properties.
mod.genProperties(w, res.Properties, false /*setters*/, func(prop *schema.Property) string {
mod.genProperties(w, res.Properties, false /*setters*/, "", func(prop *schema.Property) string {
ty := mod.typeString(prop.Type, false /*input*/, false /*acceptMapping*/)
return fmt.Sprintf("pulumi.Output[%s]", ty)
})
// Write out methods.
mod.genMethods(w, res)
return w.String(), nil
}
func (mod *modContext) genProperties(w io.Writer, properties []*schema.Property, setters bool,
func (mod *modContext) genProperties(w io.Writer, properties []*schema.Property, setters bool, indent string,
propType func(prop *schema.Property) string) {
// Write out Python properties for each property. If there is a property named "property", it will
// be emitted last to avoid conflicting with the built-in `@property` decorator function. We do
@ -1242,22 +1258,22 @@ func (mod *modContext) genProperties(w io.Writer, properties []*schema.Property,
// because that wouldn't address the problem if there was a property named "builtins".
emitProp := func(pname string, prop *schema.Property) {
ty := propType(prop)
fmt.Fprintf(w, " @property\n")
fmt.Fprintf(w, "%s @property\n", indent)
if pname == prop.Name {
fmt.Fprintf(w, " @pulumi.getter\n")
fmt.Fprintf(w, "%s @pulumi.getter\n", indent)
} else {
fmt.Fprintf(w, " @pulumi.getter(name=%q)\n", prop.Name)
fmt.Fprintf(w, "%s @pulumi.getter(name=%q)\n", indent, prop.Name)
}
fmt.Fprintf(w, " def %s(self) -> %s:\n", pname, ty)
fmt.Fprintf(w, "%s def %s(self) -> %s:\n", indent, pname, ty)
if prop.Comment != "" {
printComment(w, prop.Comment, " ")
printComment(w, prop.Comment, indent+" ")
}
fmt.Fprintf(w, " return pulumi.get(self, %q)\n\n", pname)
fmt.Fprintf(w, "%s return pulumi.get(self, %q)\n\n", indent, pname)
if setters {
fmt.Fprintf(w, " @%s.setter\n", pname)
fmt.Fprintf(w, " def %s(self, value: %s):\n", pname, ty)
fmt.Fprintf(w, " pulumi.set(self, %q, value)\n\n", pname)
fmt.Fprintf(w, "%s @%s.setter\n", indent, pname)
fmt.Fprintf(w, "%s def %s(self, value: %s):\n", indent, pname, ty)
fmt.Fprintf(w, "%s pulumi.set(self, %q, value)\n\n", indent, pname)
}
}
var propNamedProperty *schema.Property
@ -1275,6 +1291,152 @@ func (mod *modContext) genProperties(w io.Writer, properties []*schema.Property,
}
}
func (mod *modContext) genMethods(w io.Writer, res *schema.Resource) {
genReturnType := func(method *schema.Method) string {
obj := method.Function.Outputs
name := pyClassName(title(method.Name)) + "Result"
// Produce a class definition with optional """ comment.
fmt.Fprintf(w, " @pulumi.output_type\n")
fmt.Fprintf(w, " class %s:\n", name)
printComment(w, obj.Comment, " ")
// Now generate an initializer with properties for all inputs.
fmt.Fprintf(w, " def __init__(__self__")
for _, prop := range obj.Properties {
fmt.Fprintf(w, ", %s=None", PyName(prop.Name))
}
fmt.Fprintf(w, "):\n")
for _, prop := range obj.Properties {
// Check that required arguments are present. Also check that types are as expected.
pname := PyName(prop.Name)
ptype := mod.pyType(prop.Type)
fmt.Fprintf(w, " if %s and not isinstance(%s, %s):\n", pname, pname, ptype)
fmt.Fprintf(w, " raise TypeError(\"Expected argument '%s' to be a %s\")\n", pname, ptype)
if prop.DeprecationMessage != "" {
escaped := strings.ReplaceAll(prop.DeprecationMessage, `"`, `\"`)
fmt.Fprintf(w, " if %s is not None:\n", pname)
fmt.Fprintf(w, " warnings.warn(\"\"\"%s\"\"\", DeprecationWarning)\n", escaped)
fmt.Fprintf(w, " pulumi.log.warn(\"\"\"%s is deprecated: %s\"\"\")\n\n", pname, escaped)
}
// Now perform the assignment.
fmt.Fprintf(w, " pulumi.set(__self__, \"%[1]s\", %[1]s)\n", pname)
}
fmt.Fprintf(w, "\n")
// Write out Python property getters for each property.
mod.genProperties(w, obj.Properties, false /*setters*/, " ", func(prop *schema.Property) string {
return mod.typeString(prop.Type, false /*input*/, false /*acceptMapping*/)
})
return name
}
genMethod := func(method *schema.Method) {
methodName := PyName(method.Name)
fun := method.Function
// If there is a return type, emit it.
var retTypeName, retTypeNameQualified, retTypeNameQualifiedOutput string
if fun.Outputs != nil {
retTypeName = genReturnType(method)
retTypeNameQualified = fmt.Sprintf("%s.%s", resourceName(res), retTypeName)
retTypeNameQualifiedOutput = fmt.Sprintf("pulumi.Output['%s']", retTypeNameQualified)
}
var args []*schema.Property
if fun.Inputs != nil {
// Filter out the __self__ argument from the inputs.
args = make([]*schema.Property, 0, len(fun.Inputs.InputShape.Properties)-1)
for _, arg := range fun.Inputs.InputShape.Properties {
if arg.Name == "__self__" {
continue
}
args = append(args, arg)
}
// Sort required args first.
sort.Slice(args, func(i, j int) bool {
pi, pj := args[i], args[j]
switch {
case pi.IsRequired() != pj.IsRequired():
return pi.IsRequired() && !pj.IsRequired()
default:
return pi.Name < pj.Name
}
})
}
// Write out the function signature.
def := fmt.Sprintf(" def %s(", methodName)
var indent string
if len(args) > 0 {
indent = strings.Repeat(" ", len(def))
}
fmt.Fprintf(w, "%s__self__", def)
// Bare `*` argument to force callers to use named arguments.
if len(args) > 0 {
fmt.Fprintf(w, ", *")
}
for _, arg := range args {
pname := PyName(arg.Name)
ty := mod.typeString(arg.Type, true, false /*acceptMapping*/)
var defaultValue string
if !arg.IsRequired() {
defaultValue = " = None"
}
fmt.Fprintf(w, ",\n%s%s: %s%s", indent, pname, ty, defaultValue)
}
if retTypeNameQualifiedOutput != "" {
fmt.Fprintf(w, ") -> %s:\n", retTypeNameQualifiedOutput)
} else {
fmt.Fprintf(w, ") -> None:\n")
}
// If this func has documentation, write it at the top of the docstring, otherwise use a generic comment.
docs := &bytes.Buffer{}
if fun.Comment != "" {
fmt.Fprintln(docs, codegen.FilterExamples(fun.Comment, "python"))
}
if len(args) > 0 {
fmt.Fprintln(docs, "")
for _, arg := range args {
mod.genPropDocstring(docs, PyName(arg.Name), arg, false /*acceptMapping*/)
}
}
printComment(w, docs.String(), " ")
if fun.DeprecationMessage != "" {
fmt.Fprintf(w, " pulumi.log.warn(\"\"\"%s is deprecated: %s\"\"\")\n", methodName,
fun.DeprecationMessage)
}
// Copy the function arguments into a dictionary.
fmt.Fprintf(w, " __args__ = dict()\n")
fmt.Fprintf(w, " __args__['__self__'] = __self__\n")
for _, arg := range args {
pname := PyName(arg.Name)
fmt.Fprintf(w, " __args__['%s'] = %s\n", arg.Name, pname)
}
// Now simply call the function with the arguments.
var typ, ret string
if retTypeNameQualified != "" {
// Pass along the private output_type we generated, so any nested output classes are instantiated by
// the call.
typ = fmt.Sprintf(", typ=%s", retTypeNameQualified)
ret = "return "
}
fmt.Fprintf(w, " %spulumi.runtime.call('%s', __args__, res=__self__%s)\n", ret, fun.Token, typ)
fmt.Fprintf(w, "\n")
}
for _, method := range res.Methods {
genMethod(method)
}
}
func (mod *modContext) writeAlias(w io.Writer, alias *schema.Alias) {
fmt.Fprint(w, "pulumi.Alias(")
parts := []string{}
@ -1500,6 +1662,11 @@ func visitObjectTypes(properties []*schema.Property, visitor func(objectOrResour
}
func (mod *modContext) collectImports(properties []*schema.Property, imports imports, input bool) {
mod.collectImportsForResource(properties, imports, input, nil)
}
func (mod *modContext) collectImportsForResource(properties []*schema.Property, imports imports, input bool,
res *schema.Resource) {
codegen.VisitTypeClosure(properties, func(t schema.Type) {
switch t := t.(type) {
case *schema.ObjectType:
@ -1507,7 +1674,10 @@ func (mod *modContext) collectImports(properties []*schema.Property, imports imp
case *schema.EnumType:
imports.addEnum(mod, t.Token)
case *schema.ResourceType:
imports.addResource(mod, t)
// Don't import itself.
if t.Resource != res {
imports.addResource(mod, t)
}
}
})
}
@ -2164,7 +2334,7 @@ func (mod *modContext) genType(w io.Writer, name, comment string, properties []*
fmt.Fprintf(w, "\n")
// Generate properties. Input types have getters and setters, output types only have getters.
mod.genProperties(w, props, input /*setters*/, func(prop *schema.Property) string {
mod.genProperties(w, props, input /*setters*/, "", func(prop *schema.Property) string {
return mod.typeString(prop.Type, input, false /*acceptMapping*/)
})
@ -2342,7 +2512,9 @@ func generateModuleContextMap(tool string, pkg *schema.Package, info PackageInfo
// Find input and output types referenced by functions.
for _, f := range pkg.Functions {
mod := getModFromToken(f.Token, f.Package)
mod.functions = append(mod.functions, f)
if !f.IsMethod {
mod.functions = append(mod.functions, f)
}
if f.Inputs != nil {
visitObjectTypes(f.Inputs.Properties, func(t schema.Type) {
switch T := t.(type) {

View file

@ -280,6 +280,7 @@ func newPlugin(ctx *Context, pwd, bin, prefix string, args, env []string, option
// execPlugin starts the plugin executable.
func execPlugin(bin string, pluginArgs []string, pwd string, env []string) (*plugin, error) {
var args []string
args = append(args, pluginArgs...)
// Flow the logging information if set.
if logging.LogFlow {
if logging.LogToStderr {
@ -293,7 +294,6 @@ func execPlugin(bin string, pluginArgs []string, pwd string, env []string) (*plu
if cmdutil.TracingEndpoint != "" && !cmdutil.TracingToFile {
args = append(args, "--tracing", cmdutil.TracingEndpoint)
}
args = append(args, pluginArgs...)
cmd := exec.Command(bin, args...)
cmdutil.RegisterProcessGroup(cmd)

View file

@ -189,6 +189,9 @@ def main(provider: Provider, args: List[str]) -> None: # args not in use?
argp = argparse.ArgumentParser(description='Pulumi provider plugin (gRPC server)')
argp.add_argument('engine', help='Pulumi engine address')
argp.add_argument('--logflow', action='store_true', help='Currently ignored')
argp.add_argument('--logtostderr', action='store_true', help='Currently ignored')
engine_address: str = argp.parse_args().engine
async def serve() -> None:

View file

@ -892,6 +892,8 @@ func optsForConstructNode(t *testing.T, expectedResourceCount int, env ...string
},
Quick: true,
NoParallel: true,
// verify that additional flags don't cause the component provider hang
UpdateCommandlineFlags: []string{"--logflow", "--logtostderr"},
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
assert.NotNil(t, stackInfo.Deployment)
if assert.Equal(t, expectedResourceCount, len(stackInfo.Deployment.Resources)) {