diff --git a/sdk/go/digitalocean/internal/pulumiUtilities.go b/sdk/go/digitalocean/internal/pulumiUtilities.go index 7df9378d..6550de83 100644 --- a/sdk/go/digitalocean/internal/pulumiUtilities.go +++ b/sdk/go/digitalocean/internal/pulumiUtilities.go @@ -15,6 +15,10 @@ import ( "github.com/pulumi/pulumi/sdk/v3/go/pulumi" ) +import ( + "github.com/pulumi/pulumi/sdk/v3/go/pulumi/internals" +) + type envParser func(v string) interface{} func ParseEnvBool(v string) interface{} { @@ -90,6 +94,73 @@ func IsZero(v interface{}) bool { return reflect.ValueOf(v).IsZero() } +func CallPlain( + ctx *pulumi.Context, + tok string, + args pulumi.Input, + output pulumi.Output, + self pulumi.Resource, + property string, + resultPtr reflect.Value, + errorPtr *error, + opts ...pulumi.InvokeOption, +) { + res, err := callPlainInner(ctx, tok, args, output, self, opts...) + if err != nil { + *errorPtr = err + return + } + + v := reflect.ValueOf(res) + + // extract res.property field if asked to do so + if property != "" { + v = v.FieldByName("Res") + } + + // return by setting the result pointer; this style of returns shortens the generated code without generics + resultPtr.Elem().Set(v) +} + +func callPlainInner( + ctx *pulumi.Context, + tok string, + args pulumi.Input, + output pulumi.Output, + self pulumi.Resource, + opts ...pulumi.InvokeOption, +) (any, error) { + o, err := ctx.Call(tok, args, output, self, opts...) + if err != nil { + return nil, err + } + + outputData, err := internals.UnsafeAwaitOutput(ctx.Context(), o) + if err != nil { + return nil, err + } + + // Ingoring deps silently. They are typically non-empty, r.f() calls include r as a dependency. + known := outputData.Known + value := outputData.Value + secret := outputData.Secret + + problem := "" + if !known { + problem = "an unknown value" + } else if secret { + problem = "a secret value" + } + + if problem != "" { + return nil, fmt.Errorf("Plain resource method %q incorrectly returned %s. "+ + "This is an error in the provider, please report this to the provider developer.", + tok, problem) + } + + return value, nil +} + // PkgResourceDefaultOpts provides package level defaults to pulumi.OptionResource. func PkgResourceDefaultOpts(opts []pulumi.ResourceOption) []pulumi.ResourceOption { defaults := []pulumi.ResourceOption{} diff --git a/sdk/nodejs/certificate.ts b/sdk/nodejs/certificate.ts index 956a4646..782810e0 100644 --- a/sdk/nodejs/certificate.ts +++ b/sdk/nodejs/certificate.ts @@ -25,9 +25,9 @@ import * as utilities from "./utilities"; * * const cert = new digitalocean.Certificate("cert", { * type: "custom", - * privateKey: fs.readFileSync("/Users/myuser/certs/privkey.pem"), - * leafCertificate: fs.readFileSync("/Users/myuser/certs/cert.pem"), - * certificateChain: fs.readFileSync("/Users/myuser/certs/fullchain.pem"), + * privateKey: fs.readFileSync("/Users/myuser/certs/privkey.pem", "utf8"), + * leafCertificate: fs.readFileSync("/Users/myuser/certs/cert.pem", "utf8"), + * certificateChain: fs.readFileSync("/Users/myuser/certs/fullchain.pem", "utf8"), * }); * ``` * ### Let's Encrypt Certificate diff --git a/sdk/nodejs/sshKey.ts b/sdk/nodejs/sshKey.ts index 6e968bf3..d3fa1639 100644 --- a/sdk/nodejs/sshKey.ts +++ b/sdk/nodejs/sshKey.ts @@ -18,7 +18,7 @@ import * as utilities from "./utilities"; * import * as fs from "fs"; * * // Create a new SSH key - * const _default = new digitalocean.SshKey("default", {publicKey: fs.readFileSync("/Users/myuser/.ssh/id_rsa.pub")}); + * const _default = new digitalocean.SshKey("default", {publicKey: fs.readFileSync("/Users/myuser/.ssh/id_rsa.pub", "utf8")}); * // Create a new Droplet using the SSH key * const web = new digitalocean.Droplet("web", { * image: "ubuntu-18-04-x64", diff --git a/sdk/nodejs/utilities.ts b/sdk/nodejs/utilities.ts index 2580a644..080cc828 100644 --- a/sdk/nodejs/utilities.ts +++ b/sdk/nodejs/utilities.ts @@ -2,6 +2,9 @@ // *** Do not edit by hand unless you're certain you know what you are doing! *** +import * as runtime from "@pulumi/pulumi/runtime"; +import * as pulumi from "@pulumi/pulumi"; + export function getEnv(...vars: string[]): string | undefined { for (const v of vars) { const value = process.env[v]; @@ -64,3 +67,29 @@ export function lazyLoad(exports: any, props: string[], loadModule: any) { }); } } + +export async function callAsync( + tok: string, + props: pulumi.Inputs, + res?: pulumi.Resource, + opts?: {property?: string}, +): Promise { + const o: any = runtime.call(tok, props, res); + const value = await o.promise(true /*withUnknowns*/); + const isKnown = await o.isKnown; + const isSecret = await o.isSecret; + const problem: string|undefined = + !isKnown ? "an unknown value" + : isSecret ? "a secret value" + : undefined; + // Ingoring o.resources silently. They are typically non-empty, r.f() calls include r as a dependency. + if (problem) { + throw new Error(`Plain resource method "${tok}" incorrectly returned ${problem}. ` + + "This is an error in the provider, please report this to the provider developer."); + } + // Extract a single property if requested. + if (opts && opts.property) { + return value[opts.property]; + } + return value; +} diff --git a/sdk/python/pulumi_digitalocean/_utilities.py b/sdk/python/pulumi_digitalocean/_utilities.py index 2d9a3c84..968f8f56 100644 --- a/sdk/python/pulumi_digitalocean/_utilities.py +++ b/sdk/python/pulumi_digitalocean/_utilities.py @@ -3,6 +3,7 @@ # *** Do not edit by hand unless you're certain you know what you are doing! *** +import asyncio import importlib.util import inspect import json @@ -13,6 +14,7 @@ import pulumi import pulumi.runtime +from pulumi.runtime.sync_await import _sync_await from semver import VersionInfo as SemverVersion from parver import Version as PEP440Version @@ -246,5 +248,44 @@ def lifted_func(*args, opts=None, **kwargs): return (lambda _: lifted_func) + +def call_plain( + tok: str, + props: pulumi.Inputs, + res: typing.Optional[pulumi.Resource] = None, + typ: typing.Optional[type] = None, +) -> typing.Any: + """ + Wraps pulumi.runtime.plain to force the output and return it plainly. + """ + + output = pulumi.runtime.call(tok, props, res, typ) + + # Ingoring deps silently. They are typically non-empty, r.f() calls include r as a dependency. + result, known, secret, _ = _sync_await(asyncio.ensure_future(_await_output(output))) + + problem = None + if not known: + problem = ' an unknown value' + elif secret: + problem = ' a secret value' + + if problem: + raise AssertionError( + f"Plain resource method '{tok}' incorrectly returned {problem}. " + + "This is an error in the provider, please report this to the provider developer." + ) + + return result + + +async def _await_output(o: pulumi.Output[typing.Any]) -> typing.Tuple[object, bool, bool, set]: + return ( + await o._future, + await o._is_known, + await o._is_secret, + await o._resources, + ) + def get_plugin_download_url(): return None