diff --git a/.github/workflows/ci-run-test.yml b/.github/workflows/ci-run-test.yml index 9ebd6b4ddc61..1de0e63373fd 100644 --- a/.github/workflows/ci-run-test.yml +++ b/.github/workflows/ci-run-test.yml @@ -50,6 +50,11 @@ on: "nodejs": "14.x", "python": "3.9.x" } + continue-on-error: + description: "Whether to continue running the job if the step fails" + required: false + default: false + type: boolean defaults: @@ -84,6 +89,8 @@ jobs: runs-on: ${{ inputs.platform }} + timeout-minutes: 60 + continue-on-error: ${{ inputs.continue-on-error }} steps: - name: "Windows cache workaround" # https://github.com/actions/cache/issues/752#issuecomment-1222415717 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4914670397b7..68530abe8e14 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -293,6 +293,7 @@ jobs: name: Smoke Test${{ matrix.platform && '' }} needs: [matrix, build-binaries, build-sdks] if: ${{ needs.matrix.outputs.smoke-test-matrix != '{}' }} + # alow jobs to fail if the platform contains windows strategy: fail-fast: ${{ contains(needs.matrix.outputs.smoke-test-matrix, 'macos') }} matrix: ${{ fromJson(needs.matrix.outputs.smoke-test-matrix) }} @@ -309,6 +310,7 @@ jobs: # require-build: false # TODO, remove ${{ matrix.require-build || false }} version-set: ${{ toJson(matrix.version-set) }} + continue-on-error: ${{ contains(matrix.platform, 'windows') }} secrets: inherit test-collect-reports: diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index 5c6647b2ff99..804c332fcb48 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -71,7 +71,7 @@ jobs: prepare-release: name: prepare - if: | + if: >- # No newlines or trailing newline. ${{ github.event.pull_request.head.repo.full_name == github.repository && contains(github.event.pull_request.labels.*.name, 'ci/test') diff --git a/.version b/.version index ffa235181eba..04da28a74b0c 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -3.48.1 +3.49.1 diff --git a/CHANGELOG.md b/CHANGELOG.md index abfa2904bd37..5f7f0cc2268a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,67 @@ # Changelog +## 3.49.0 (2022-12-08) + + +### Features + +- [sdk] Add methods to cast pointer types to corresponding Pulumi Ptr types + [#11539](https://github.com/pulumi/pulumi/pull/11539) + +- [yaml] [Updates Pulumi YAML to v1.0.4](https://github.com/pulumi/pulumi-yaml/releases/tag/v1.0.4) unblocking Docker Image resource support in a future Docker provider release. + [#11583](https://github.com/pulumi/pulumi/pull/11583) + +- [backend/service] Allows the service to opt into a bandwidth-optimized DIFF protocol for storing checkpoints. Previously this required setting the PULUMI_OPTIMIZED_CHECKPOINT_PATCH env variable on the client. This env variable is now deprecated. + [#11421](https://github.com/pulumi/pulumi/pull/11421) + +- [cli/about] Add fully qualified stack name to current stack. + [#11387](https://github.com/pulumi/pulumi/pull/11387) + +- [sdk/{dotnet,nodejs}] Add InvokeSingle variants to dotnet and nodejs SDKs + [#11564](https://github.com/pulumi/pulumi/pull/11564) + + +### Bug Fixes + +- [docs] Exclude id output property for component resources + [#11469](https://github.com/pulumi/pulumi/pull/11469) + +- [engine] Fix an assert for resources being replaced but also pending deletion. + [#11475](https://github.com/pulumi/pulumi/pull/11475) + +- [pkg] Fixes codegen/python generation of non-string secrets in provider properties + [#11494](https://github.com/pulumi/pulumi/pull/11494) + +- [pkg/testing] Optionally caches python venvs for testing + [#11532](https://github.com/pulumi/pulumi/pull/11532) + +- [programgen] Improve error message for invalid enum values on `pulumi convert`. + [#11019](https://github.com/pulumi/pulumi/pull/11019) + +- [programgen] Interpret schema.Asset as pcl.AssetOrArchive. + [#11593](https://github.com/pulumi/pulumi/pull/11593) + +- [programgen/go] Convert the result of immediate invokes to ouputs when necessary. + [#11480](https://github.com/pulumi/pulumi/pull/11480) + +- [programgen/nodejs] Add `.` between `?` and `[`. + [#11477](https://github.com/pulumi/pulumi/pull/11477) + +- [programgen/nodejs] Fix capitalization when generating `fs.readdirSync`. + [#11478](https://github.com/pulumi/pulumi/pull/11478) + +- [sdk/nodejs] Fix regression when passing a provider to a MLC + [#11509](https://github.com/pulumi/pulumi/pull/11509) + +- [sdk/python] Allows for duplicate output values in python + [#11559](https://github.com/pulumi/pulumi/pull/11559) + +- [sdkgen/go] Fixes superfluous newline being added between documentation comment and package statement in doc.go + [#11492](https://github.com/pulumi/pulumi/pull/11492) + +- [sdkgen/nodejs] Generate JS doc comments for output-versioned invokes and use explicit any type. + [#11511](https://github.com/pulumi/pulumi/pull/11511) + ## 3.48.0 (2022-11-23) diff --git a/changelog/pending/20221111--sdkgen-go--fixes-superfluous-newline-being-added-between-documentation-comment-and-package-statement-in-doc-go.yaml b/changelog/pending/20221111--sdkgen-go--fixes-superfluous-newline-being-added-between-documentation-comment-and-package-statement-in-doc-go.yaml deleted file mode 100644 index 928e08367fa5..000000000000 --- a/changelog/pending/20221111--sdkgen-go--fixes-superfluous-newline-being-added-between-documentation-comment-and-package-statement-in-doc-go.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: sdkgen/go - description: Fixes superfluous newline being added between documentation comment and package statement in doc.go diff --git a/changelog/pending/20221118--cli-about--add-fully-qualified-stack-name-to-current-stack.yaml b/changelog/pending/20221118--cli-about--add-fully-qualified-stack-name-to-current-stack.yaml deleted file mode 100644 index 0eb5cbba99a0..000000000000 --- a/changelog/pending/20221118--cli-about--add-fully-qualified-stack-name-to-current-stack.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: feat - scope: cli/about - description: Add fully qualified stack name to current stack. diff --git a/changelog/pending/20221121--backend-service--allows-the-service-to-opt-into-bandwidth-optimized-diff-protocol.yaml b/changelog/pending/20221121--backend-service--allows-the-service-to-opt-into-bandwidth-optimized-diff-protocol.yaml deleted file mode 100644 index f929c8632edf..000000000000 --- a/changelog/pending/20221121--backend-service--allows-the-service-to-opt-into-bandwidth-optimized-diff-protocol.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: feat - scope: backend/service - description: Allows the service to opt into a bandwidth-optimized DIFF protocol for storing checkpoints. Previously this required setting the PULUMI_OPTIMIZED_CHECKPOINT_PATCH env variable on the client. This env variable is now deprecated. diff --git a/changelog/pending/20221125--docs--exclude-id-output-property-for-component-resources.yaml b/changelog/pending/20221125--docs--exclude-id-output-property-for-component-resources.yaml deleted file mode 100644 index 21406387e3c7..000000000000 --- a/changelog/pending/20221125--docs--exclude-id-output-property-for-component-resources.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: docs - description: Exclude id output property for component resources diff --git a/changelog/pending/20221125--sdkgen-nodejs--fix-nodejs-sdk-when-a-component-is-using-another-component-from-the-same-schema-as-a-property.yaml b/changelog/pending/20221125--sdkgen-nodejs--fix-nodejs-sdk-when-a-component-is-using-another-component-from-the-same-schema-as-a-property.yaml new file mode 100644 index 000000000000..0f9f65a08e99 --- /dev/null +++ b/changelog/pending/20221125--sdkgen-nodejs--fix-nodejs-sdk-when-a-component-is-using-another-component-from-the-same-schema-as-a-property.yaml @@ -0,0 +1,4 @@ +changes: + - type: fix + scope: sdkgen/nodejs,dotnet + description: Fix imports when a component is using another component from the same schema as a property diff --git a/changelog/pending/20221128--programgen-nodejs--add-between-and.yaml b/changelog/pending/20221128--programgen-nodejs--add-between-and.yaml deleted file mode 100644 index 912903f06b7a..000000000000 --- a/changelog/pending/20221128--programgen-nodejs--add-between-and.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: programgen/nodejs - description: Add `.` between `?` and `[`. diff --git a/changelog/pending/20221128--programgen-nodejs--fix-capitalization-when-generating-fs-readdirsync.yaml b/changelog/pending/20221128--programgen-nodejs--fix-capitalization-when-generating-fs-readdirsync.yaml deleted file mode 100644 index 94dafdbf717c..000000000000 --- a/changelog/pending/20221128--programgen-nodejs--fix-capitalization-when-generating-fs-readdirsync.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: programgen/nodejs - description: Fix capitalization when generating `fs.readdirSync`. diff --git a/changelog/pending/20221129--engine--fix-an-assert-for-resources-being-replaced-but-also-pending-deletion.yaml b/changelog/pending/20221129--engine--fix-an-assert-for-resources-being-replaced-but-also-pending-deletion.yaml deleted file mode 100644 index fc9b0531a32f..000000000000 --- a/changelog/pending/20221129--engine--fix-an-assert-for-resources-being-replaced-but-also-pending-deletion.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: engine - description: Fix an assert for resources being replaced but also pending deletion. diff --git a/changelog/pending/20221129--programgen-go--convert-the-result-of-immediate-invokes-to-ouputs-when-necessary.yaml b/changelog/pending/20221129--programgen-go--convert-the-result-of-immediate-invokes-to-ouputs-when-necessary.yaml deleted file mode 100644 index 9985d3106368..000000000000 --- a/changelog/pending/20221129--programgen-go--convert-the-result-of-immediate-invokes-to-ouputs-when-necessary.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: programgen/go - description: Convert the result of immediate invokes to ouputs when necessary. diff --git a/changelog/pending/20221129--sdk-nodejs--emit-closure-requires-in-global-scope-for-improved-cold-start-on-lambda.yaml b/changelog/pending/20221129--sdk-nodejs--emit-closure-requires-in-global-scope-for-improved-cold-start-on-lambda.yaml new file mode 100644 index 000000000000..6e1500681064 --- /dev/null +++ b/changelog/pending/20221129--sdk-nodejs--emit-closure-requires-in-global-scope-for-improved-cold-start-on-lambda.yaml @@ -0,0 +1,4 @@ +changes: +- type: feat + scope: sdk/nodejs + description: Emit closure requires in global scope for improved cold start on Lambda diff --git a/changelog/pending/20221201--programgen--improve-error-message-for-invalid-enum-values-on-pulumi-convert.yaml b/changelog/pending/20221201--programgen--improve-error-message-for-invalid-enum-values-on-pulumi-convert.yaml deleted file mode 100644 index 1b8b163cc48a..000000000000 --- a/changelog/pending/20221201--programgen--improve-error-message-for-invalid-enum-values-on-pulumi-convert.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: programgen - description: Improve error message for invalid enum values on `pulumi convert`. diff --git a/changelog/pending/20221201--sdk-nodejs--fix-regression-when-passing-a-provider-to-a-mlc.yaml b/changelog/pending/20221201--sdk-nodejs--fix-regression-when-passing-a-provider-to-a-mlc.yaml deleted file mode 100644 index 510d02e39772..000000000000 --- a/changelog/pending/20221201--sdk-nodejs--fix-regression-when-passing-a-provider-to-a-mlc.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: sdk/nodejs - description: Fix regression when passing a provider to a MLC diff --git a/changelog/pending/20221202--sdkgen-nodejs--generate-js-doc-comments-for-output-versioned-invokes-and-use-explicit-any-type.yaml b/changelog/pending/20221202--sdkgen-nodejs--generate-js-doc-comments-for-output-versioned-invokes-and-use-explicit-any-type.yaml deleted file mode 100644 index 1178adfe4c09..000000000000 --- a/changelog/pending/20221202--sdkgen-nodejs--generate-js-doc-comments-for-output-versioned-invokes-and-use-explicit-any-type.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: sdkgen/nodejs - description: Generate JS doc comments for output-versioned invokes and use explicit any type. diff --git a/changelog/pending/20221205--pkg-testing--optionally-caches-python-venvs-for-testing.yaml b/changelog/pending/20221205--pkg-testing--optionally-caches-python-venvs-for-testing.yaml deleted file mode 100644 index 374d3cff10d3..000000000000 --- a/changelog/pending/20221205--pkg-testing--optionally-caches-python-venvs-for-testing.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: pkg/testing - description: Optionally caches python venvs for testing diff --git a/changelog/pending/20221205--sdk--add-methods-to-cast-pointer-types-to-corresponding-pulumi-ptr-types.yaml b/changelog/pending/20221205--sdk--add-methods-to-cast-pointer-types-to-corresponding-pulumi-ptr-types.yaml deleted file mode 100644 index 98064d91c897..000000000000 --- a/changelog/pending/20221205--sdk--add-methods-to-cast-pointer-types-to-corresponding-pulumi-ptr-types.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: feat - scope: sdk - description: Add methods to cast pointer types to corresponding Pulumi Ptr types diff --git a/changelog/pending/20221206--pkg--fixes-codegen-python-nonstring-secrets.yaml b/changelog/pending/20221206--pkg--fixes-codegen-python-nonstring-secrets.yaml deleted file mode 100644 index 2faf39de7052..000000000000 --- a/changelog/pending/20221206--pkg--fixes-codegen-python-nonstring-secrets.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: pkg - description: Fixes codegen/python generation of non-string secrets in provider properties diff --git a/changelog/pending/20221206--sdk-dotnet--add-output-jsonserialize-using-system-text-json.yaml b/changelog/pending/20221206--sdk-dotnet--add-output-jsonserialize-using-system-text-json.yaml new file mode 100644 index 000000000000..c4047b5713fb --- /dev/null +++ b/changelog/pending/20221206--sdk-dotnet--add-output-jsonserialize-using-system-text-json.yaml @@ -0,0 +1,4 @@ +changes: +- type: feat + scope: sdk/dotnet + description: Add Output.JsonSerialize using System.Text.Json. diff --git a/changelog/pending/20221206--sdk-dotnet-nodejs--add-inokesingle-variants-to-dotnet-and-nodejs-sdks.yaml b/changelog/pending/20221206--sdk-dotnet-nodejs--add-inokesingle-variants-to-dotnet-and-nodejs-sdks.yaml deleted file mode 100644 index cb47bcc7621f..000000000000 --- a/changelog/pending/20221206--sdk-dotnet-nodejs--add-inokesingle-variants-to-dotnet-and-nodejs-sdks.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: feat - scope: sdk/dotnet,nodejs - description: Add InvokeSingle variants to dotnet and nodejs SDKs diff --git a/changelog/pending/20221206--sdk-nodejs--remove-function-serialization-code-for-out-of-suppport-nodejs-versions.yaml b/changelog/pending/20221206--sdk-nodejs--remove-function-serialization-code-for-out-of-suppport-nodejs-versions.yaml new file mode 100644 index 000000000000..c5b849eace19 --- /dev/null +++ b/changelog/pending/20221206--sdk-nodejs--remove-function-serialization-code-for-out-of-suppport-nodejs-versions.yaml @@ -0,0 +1,4 @@ +changes: +- type: chore + scope: sdk/nodejs + description: Remove function serialization code for out of suppport NodeJS versions. diff --git a/changelog/pending/20221207--yaml--updates-yaml-to-1-0-4.yaml b/changelog/pending/20221207--yaml--updates-yaml-to-1-0-4.yaml deleted file mode 100644 index 2dad8f34c03b..000000000000 --- a/changelog/pending/20221207--yaml--updates-yaml-to-1-0-4.yaml +++ /dev/null @@ -1,6 +0,0 @@ -changes: -- type: feat - scope: yaml - description: > - [Updates Pulumi YAML to v1.0.4](https://github.com/pulumi/pulumi-yaml/releases/tag/v1.0.4) - unblocking Docker Image resource support in a future Docker provider release. diff --git a/changelog/pending/20221208--sdk-python--fix-a-deadlock-on-provider-side-error-with-automation-api.yaml b/changelog/pending/20221208--sdk-python--fix-a-deadlock-on-provider-side-error-with-automation-api.yaml new file mode 100644 index 000000000000..94f14ac1dd86 --- /dev/null +++ b/changelog/pending/20221208--sdk-python--fix-a-deadlock-on-provider-side-error-with-automation-api.yaml @@ -0,0 +1,4 @@ +changes: +- type: fix + scope: sdk/python + description: Fix a deadlock on provider-side error with automation api diff --git a/changelog/pending/20221208--sdkgen-dotnet-go-nodejs-python--do-not-generate-result-types-for-functions-with-empty-outputs.yaml b/changelog/pending/20221208--sdkgen-dotnet-go-nodejs-python--do-not-generate-result-types-for-functions-with-empty-outputs.yaml new file mode 100644 index 000000000000..3478fa51ab5c --- /dev/null +++ b/changelog/pending/20221208--sdkgen-dotnet-go-nodejs-python--do-not-generate-result-types-for-functions-with-empty-outputs.yaml @@ -0,0 +1,4 @@ +changes: +- type: fix + scope: sdkgen/dotnet,go,nodejs,python + description: Do not generate Result types for functions with empty outputs diff --git a/changelog/pending/20221209--sdk-go--add-jsonmarshal-to-go-sdk.yaml b/changelog/pending/20221209--sdk-go--add-jsonmarshal-to-go-sdk.yaml new file mode 100644 index 000000000000..cccb0544fcdb --- /dev/null +++ b/changelog/pending/20221209--sdk-go--add-jsonmarshal-to-go-sdk.yaml @@ -0,0 +1,4 @@ +changes: +- type: feat + scope: sdk/go + description: Add JSONMarshal to go sdk. diff --git a/changelog/pending/20221209--sdk-nodejs--add-output-jsonstringify-using-json-stringify.yaml b/changelog/pending/20221209--sdk-nodejs--add-output-jsonstringify-using-json-stringify.yaml new file mode 100644 index 000000000000..f7feb962f356 --- /dev/null +++ b/changelog/pending/20221209--sdk-nodejs--add-output-jsonstringify-using-json-stringify.yaml @@ -0,0 +1,4 @@ +changes: +- type: feat + scope: sdk/nodejs + description: Add output jsonStringify using JSON.stringify. diff --git a/developer-docs/providers/metaschema.md b/developer-docs/providers/metaschema.md index 8b00d2e17541..fa4744e73e5b 100644 --- a/developer-docs/providers/metaschema.md +++ b/developer-docs/providers/metaschema.md @@ -618,6 +618,14 @@ Specifies whether a change to the property causes its containing resource to be --- +#### `willReplaceOnChanges` + +Indicates that the provider will replace the resource when this property is changed. + +`boolean` + +--- + #### `secret` Specifies whether the property is secret (default false). diff --git a/pkg/backend/filestate/backend_test.go b/pkg/backend/filestate/backend_test.go index 80a9e36baf66..0a58501e3a17 100644 --- a/pkg/backend/filestate/backend_test.go +++ b/pkg/backend/filestate/backend_test.go @@ -3,7 +3,6 @@ package filestate import ( "context" "encoding/json" - "io/ioutil" "os" "path" "path/filepath" @@ -143,8 +142,7 @@ func makeUntypedDeployment(name tokens.QName, phrase, state string) (*apitype.Un //nolint:paralleltest // mutates environment variables func TestListStacksWithMultiplePassphrases(t *testing.T) { // Login to a temp dir filestate backend - tmpDir, err := ioutil.TempDir("", "filestatebackend") - assert.NoError(t, err) + tmpDir := t.TempDir() b, err := New(cmdutil.Diag(), "file://"+filepath.ToSlash(tmpDir)) assert.NoError(t, err) ctx := context.Background() @@ -205,8 +203,7 @@ func TestDrillError(t *testing.T) { t.Parallel() // Login to a temp dir filestate backend - tmpDir, err := ioutil.TempDir("", "filestatebackend") - assert.NoError(t, err) + tmpDir := t.TempDir() b, err := New(cmdutil.Diag(), "file://"+filepath.ToSlash(tmpDir)) assert.NoError(t, err) ctx := context.Background() @@ -224,8 +221,7 @@ func TestCancel(t *testing.T) { t.Parallel() // Login to a temp dir filestate backend - tmpDir, err := ioutil.TempDir("", "filestatebackend") - assert.NoError(t, err) + tmpDir := t.TempDir() b, err := New(cmdutil.Diag(), "file://"+filepath.ToSlash(tmpDir)) assert.NoError(t, err) ctx := context.Background() @@ -286,8 +282,7 @@ func TestRemoveMakesBackups(t *testing.T) { t.Parallel() // Login to a temp dir filestate backend - tmpDir, err := ioutil.TempDir("", "filestatebackend") - assert.NoError(t, err) + tmpDir := t.TempDir() b, err := New(cmdutil.Diag(), "file://"+filepath.ToSlash(tmpDir)) assert.NoError(t, err) ctx := context.Background() @@ -330,8 +325,7 @@ func TestRenameWorks(t *testing.T) { t.Parallel() // Login to a temp dir filestate backend - tmpDir, err := ioutil.TempDir("", "filestatebackend") - assert.NoError(t, err) + tmpDir := t.TempDir() b, err := New(cmdutil.Diag(), "file://"+filepath.ToSlash(tmpDir)) assert.NoError(t, err) ctx := context.Background() @@ -445,8 +439,7 @@ func TestHtmlEscaping(t *testing.T) { } // Login to a temp dir filestate backend - tmpDir, err := ioutil.TempDir("", "filestatebackend") - assert.NoError(t, err) + tmpDir := t.TempDir() b, err := New(cmdutil.Diag(), "file://"+filepath.ToSlash(tmpDir)) assert.NoError(t, err) ctx := context.Background() diff --git a/pkg/cmd/pulumi/convert.go b/pkg/cmd/pulumi/convert.go index 7fc2db623601..7345f2432b8b 100644 --- a/pkg/cmd/pulumi/convert.go +++ b/pkg/cmd/pulumi/convert.go @@ -178,6 +178,8 @@ func runConvert( projectGenerator = yamlgen.GenerateProject case "pulumi", "pcl": if cmdutil.IsTruthy(os.Getenv("PULUMI_DEV")) { + // No plugin for PCL to install dependencies with + generateOnly = true projectGenerator = pclGenerateProject break } @@ -214,8 +216,6 @@ func runConvert( } } else if from == "pcl" { if cmdutil.IsTruthy(os.Getenv("PULUMI_DEV")) { - // No plugin for PCL to generate with - generateOnly = true proj, program, err = pclEject(cwd, loader) if err != nil { return result.FromError(fmt.Errorf("could not load pcl program: %w", err)) diff --git a/pkg/cmd/pulumi/convert_test.go b/pkg/cmd/pulumi/convert_test.go index c424e5f9d648..bb738bc516d2 100644 --- a/pkg/cmd/pulumi/convert_test.go +++ b/pkg/cmd/pulumi/convert_test.go @@ -41,8 +41,7 @@ func TestPclConvert(t *testing.T) { t.Setenv("PULUMI_DEV", "TRUE") // Check that we can run convert from PCL to PCL - tmp, err := os.MkdirTemp("", "pulumi-convert-test") - assert.NoError(t, err) + tmp := t.TempDir() result := runConvert("pcl_convert_testdata", []string{}, "pcl", "pcl", tmp, true) assert.Nil(t, result) diff --git a/pkg/cmd/pulumi/new.go b/pkg/cmd/pulumi/new.go index 4112fe05a0f9..6391a443588b 100644 --- a/pkg/cmd/pulumi/new.go +++ b/pkg/cmd/pulumi/new.go @@ -435,8 +435,9 @@ func newNewCmd() *cobra.Command { Long: "Create a new Pulumi project and stack from a template.\n" + "\n" + "To create a project from a specific template, pass the template name (such as `aws-typescript`\n" + - "or `azure-python`). If no template name is provided, a list of suggested templates will be presented\n" + + "or `azure-python`). If no template name is provided, a list of suggested templates will be presented\n" + "which can be selected interactively.\n" + + "For testing, a path to a local template may be passed instead (such as `~/templates/aws-typescript`)\n" + "\n" + "By default, a stack created using the pulumi.com backend will use the pulumi.com secrets\n" + "provider and a stack created using the local or cloud object storage backend will use the\n" + diff --git a/pkg/cmd/pulumi/policy_new_smoke_test.go b/pkg/cmd/pulumi/policy_new_smoke_test.go index f2e362f0bc9f..770428cf6042 100644 --- a/pkg/cmd/pulumi/policy_new_smoke_test.go +++ b/pkg/cmd/pulumi/policy_new_smoke_test.go @@ -16,8 +16,6 @@ package main import ( "context" - "io/ioutil" - "os" "path/filepath" "strings" "testing" @@ -29,8 +27,7 @@ import ( func TestCreatingPolicyPackWithArgsSpecifiedName(t *testing.T) { skipIfShortOrNoPulumiAccessToken(t) - tempdir, _ := ioutil.TempDir("", "test-env") - defer os.RemoveAll(tempdir) + tempdir := t.TempDir() chdir(t, tempdir) var args = newPolicyArgs{ diff --git a/pkg/cmd/pulumi/policy_new_test.go b/pkg/cmd/pulumi/policy_new_test.go index 904328c5d4d3..c8d94db0edd7 100644 --- a/pkg/cmd/pulumi/policy_new_test.go +++ b/pkg/cmd/pulumi/policy_new_test.go @@ -18,8 +18,6 @@ package main import ( "context" - "io/ioutil" - "os" "path/filepath" "testing" @@ -30,8 +28,7 @@ import ( func TestCreatingPolicyPackWithPromptedName(t *testing.T) { skipIfShortOrNoPulumiAccessToken(t) - tempdir, _ := ioutil.TempDir("", "test-env") - defer os.RemoveAll(tempdir) + tempdir := t.TempDir() chdir(t, tempdir) var args = newPolicyArgs{ @@ -54,9 +51,7 @@ func TestInvalidPolicyPackTemplateName(t *testing.T) { const nonExistantTemplate = "this-is-not-the-template-youre-looking-for" t.Run("RemoteTemplateNotFound", func(t *testing.T) { - tempdir, _ := ioutil.TempDir("", "test-env") - defer os.RemoveAll(tempdir) - assert.DirExists(t, tempdir) + tempdir := t.TempDir() chdir(t, tempdir) var args = newPolicyArgs{ @@ -71,8 +66,7 @@ func TestInvalidPolicyPackTemplateName(t *testing.T) { }) t.Run("LocalTemplateNotFound", func(t *testing.T) { - tempdir, _ := ioutil.TempDir("", "test-env") - defer os.RemoveAll(tempdir) + tempdir := t.TempDir() chdir(t, tempdir) var args = newPolicyArgs{ diff --git a/pkg/codegen/docs/gen.go b/pkg/codegen/docs/gen.go index 795f1a9ec3f8..63ca6d2134db 100644 --- a/pkg/codegen/docs/gen.go +++ b/pkg/codegen/docs/gen.go @@ -1849,7 +1849,7 @@ func (mod *modContext) genIndex() indexData { for _, r := range mod.resources { name := resourceName(r) resources = append(resources, indexEntry{ - Link: getResourceLink(name), + Link: getResourceLink(name) + "/", DisplayName: name, }) } @@ -1859,7 +1859,7 @@ func (mod *modContext) genIndex() indexData { for _, f := range mod.functions { name := tokenToName(f.Token) functions = append(functions, indexEntry{ - Link: getFunctionLink(name), + Link: getFunctionLink(name) + "/", DisplayName: strings.Title(name), }) } diff --git a/pkg/codegen/dotnet/doc.go b/pkg/codegen/dotnet/doc.go index 6a2a727e308b..2aed2dd4124e 100644 --- a/pkg/codegen/dotnet/doc.go +++ b/pkg/codegen/dotnet/doc.go @@ -79,7 +79,7 @@ func (d DocLanguageHelper) GetLanguageTypeString(pkg *schema.Package, moduleName } typeDetails := map[*schema.ObjectType]*typeDetails{} mod := &modContext{ - pkg: pkg, + pkg: pkg.Reference(), mod: moduleName, typeDetails: typeDetails, namespaces: d.Namespaces, @@ -130,7 +130,7 @@ func (d DocLanguageHelper) GetMethodResultName(pkg *schema.Package, modName stri if info.LiftSingleValueMethodReturns && returnType != nil && len(returnType.Properties) == 1 { typeDetails := map[*schema.ObjectType]*typeDetails{} mod := &modContext{ - pkg: pkg, + pkg: pkg.Reference(), mod: modName, typeDetails: typeDetails, namespaces: d.Namespaces, diff --git a/pkg/codegen/dotnet/gen.go b/pkg/codegen/dotnet/gen.go index c52375f7aa90..ce318c882b82 100644 --- a/pkg/codegen/dotnet/gen.go +++ b/pkg/codegen/dotnet/gen.go @@ -27,7 +27,6 @@ import ( "path" "path/filepath" "reflect" - "sort" "strconv" "strings" "unicode" @@ -125,7 +124,7 @@ func namespaceName(namespaces map[string]string, name string) string { } type modContext struct { - pkg *schema.Package + pkg schema.PackageReference mod string propertyNames map[*schema.Property]string types []*schema.ObjectType @@ -372,13 +371,15 @@ func (mod *modContext) typeString(t schema.Type, qualifier string, input, state, return fmt.Sprintf("%v", mapType, mod.typeString(t.ElementType, qualifier, input, state, false)) case *schema.ObjectType: namingCtx := mod - if t.Package != mod.pkg { + if !codegen.PkgEquals(t.PackageReference, mod.pkg) { // If object type belongs to another package, we apply naming conventions from that package, // including namespace naming and compatibility mode. - extPkg := t.Package + extPkg := t.PackageReference var info CSharpPackageInfo - contract.AssertNoError(extPkg.ImportLanguages(map[string]schema.Language{"csharp": Importer})) - if v, ok := t.Package.Language["csharp"].(CSharpPackageInfo); ok { + def, err := extPkg.Definition() + contract.AssertNoError(err) + contract.AssertNoError(def.ImportLanguages(map[string]schema.Language{"csharp": Importer})) + if v, ok := def.Language["csharp"].(CSharpPackageInfo); ok { info = v } namingCtx = &modContext{ @@ -406,13 +407,15 @@ func (mod *modContext) typeString(t schema.Type, qualifier string, input, state, } namingCtx := mod - if t.Resource != nil && t.Resource.Package != mod.pkg { + if t.Resource != nil && !codegen.PkgEquals(t.Resource.PackageReference, mod.pkg) { // If resource type belongs to another package, we apply naming conventions from that package, // including namespace naming and compatibility mode. - extPkg := t.Resource.Package + extPkg := t.Resource.PackageReference var info CSharpPackageInfo - contract.AssertNoError(extPkg.ImportLanguages(map[string]schema.Language{"csharp": Importer})) - if v, ok := t.Resource.Package.Language["csharp"].(CSharpPackageInfo); ok { + def, err := extPkg.Definition() + contract.AssertNoError(err) + contract.AssertNoError(def.ImportLanguages(map[string]schema.Language{"csharp": Importer})) + if v, ok := def.Language["csharp"].(CSharpPackageInfo); ok { info = v } namingCtx = &modContext{ @@ -906,7 +909,7 @@ func (mod *modContext) genResource(w io.Writer, r *schema.Resource) error { if r.DeprecationMessage != "" { fmt.Fprintf(w, " [Obsolete(@\"%s\")]\n", strings.Replace(r.DeprecationMessage, `"`, `""`, -1)) } - fmt.Fprintf(w, " [%sResourceType(\"%s\")]\n", namespaceName(mod.namespaces, mod.pkg.Name), r.Token) + fmt.Fprintf(w, " [%sResourceType(\"%s\")]\n", namespaceName(mod.namespaces, mod.pkg.Name()), r.Token) fmt.Fprintf(w, " public partial class %s : %s\n", className, baseType) fmt.Fprintf(w, " {\n") @@ -967,7 +970,7 @@ func (mod *modContext) genResource(w io.Writer, r *schema.Resource) error { tok := r.Token if r.IsProvider { - tok = mod.pkg.Name + tok = mod.pkg.Name() } argsOverride := fmt.Sprintf("args ?? new %sArgs()", className) @@ -1046,7 +1049,11 @@ func (mod *modContext) genResource(w io.Writer, r *schema.Resource) error { fmt.Fprintf(w, " var defaultOptions = new %s\n", optionsType) fmt.Fprintf(w, " {\n") fmt.Fprintf(w, " Version = Utilities.Version,\n") - if url := mod.pkg.PluginDownloadURL; url != "" { + def, err := mod.pkg.Definition() + if err != nil { + return err + } + if url := def.PluginDownloadURL; url != "" { fmt.Fprintf(w, " PluginDownloadURL = %q,\n", url) } @@ -1311,8 +1318,6 @@ func (mod *modContext) genResource(w io.Writer, r *schema.Resource) error { } func (mod *modContext) genFunctionFileCode(f *schema.Function) (string, error) { - imports := map[string]codegen.StringSet{} - mod.getImports(f, imports) buffer := &bytes.Buffer{} importStrings := mod.pulumiImports() @@ -1322,9 +1327,6 @@ func (mod *modContext) genFunctionFileCode(f *schema.Function) (string, error) { if nonStandardNamespace { importStrings = append(importStrings, mod.namespaceName) } - for _, i := range imports { - importStrings = append(importStrings, i.SortedValues()...) - } // We need to qualify input types when we are not in the same module as them. if nonStandardNamespace { @@ -1823,132 +1825,6 @@ func (mod *modContext) pulumiImports() []string { return pulumiImports } -func (mod *modContext) getTypeImports(t schema.Type, recurse bool, imports map[string]codegen.StringSet, seen codegen.Set) { - mod.getTypeImportsForResource(t, recurse, imports, seen, nil) -} - -func (mod *modContext) getTypeImportsForResource(t schema.Type, recurse bool, imports map[string]codegen.StringSet, seen codegen.Set, res *schema.Resource) { - if seen.Has(t) { - return - } - seen.Add(t) - - switch t := t.(type) { - case *schema.OptionalType: - mod.getTypeImports(t.ElementType, recurse, imports, seen) - return - case *schema.InputType: - mod.getTypeImports(t.ElementType, recurse, imports, seen) - return - case *schema.ArrayType: - mod.getTypeImports(t.ElementType, recurse, imports, seen) - return - case *schema.MapType: - mod.getTypeImports(t.ElementType, recurse, imports, seen) - return - case *schema.ObjectType: - if t != nil { - for _, p := range t.Properties { - mod.getTypeImports(p.Type, recurse, imports, seen) - } - } - return - case *schema.ResourceType: - // If it's an external resource, we'll be using fully-qualified type names, so there's no need - // for an import. - if t.Resource != nil && t.Resource.Package != mod.pkg { - return - } - - // Don't import itself. - if t.Resource == res { - return - } - - modName, name, modPath := mod.pkg.TokenToModule(t.Token), tokenToName(t.Token), "" - if modName != mod.mod { - mp, err := filepath.Rel(mod.mod, modName) - contract.Assert(err == nil) - if path.Base(mp) == "." { - mp = path.Dir(mp) - } - modPath = filepath.ToSlash(mp) - } - if len(modPath) == 0 { - return - } - if imports[modPath] == nil { - imports[modPath] = codegen.NewStringSet() - } - imports[modPath].Add(name) - return - case *schema.TokenType: - return - case *schema.UnionType: - for _, e := range t.ElementTypes { - mod.getTypeImports(e, recurse, imports, seen) - } - return - default: - return - } -} - -func (mod *modContext) getImports(member interface{}, imports map[string]codegen.StringSet) { - mod.getImportsForResource(member, imports, nil) -} - -func (mod *modContext) getImportsForResource(member interface{}, imports map[string]codegen.StringSet, res *schema.Resource) { - seen := codegen.Set{} - switch member := member.(type) { - case *schema.ObjectType: - for _, p := range member.Properties { - mod.getTypeImports(p.Type, true, imports, seen) - } - return - case *schema.ResourceType: - mod.getTypeImports(member, true, imports, seen) - return - case *schema.Resource: - for _, p := range member.Properties { - mod.getTypeImportsForResource(p.Type, false, imports, seen, res) - } - for _, p := range member.InputProperties { - mod.getTypeImportsForResource(p.Type, false, imports, seen, res) - } - for _, method := range member.Methods { - if method.Function.Inputs != nil { - for _, p := range method.Function.Inputs.Properties { - mod.getTypeImportsForResource(p.Type, false, imports, seen, res) - } - } - if method.Function.ReturnType != nil { - if objectType, ok := method.Function.ReturnType.(*schema.ObjectType); ok && objectType != nil { - for _, p := range objectType.Properties { - mod.getTypeImportsForResource(p.Type, false, imports, seen, res) - } - } - } - } - return - case *schema.Function: - if member.Inputs != nil { - mod.getTypeImports(member.Inputs, false, imports, seen) - } - if member.ReturnType != nil { - mod.getTypeImports(member.ReturnType, false, imports, seen) - } - return - case []*schema.Property: - for _, p := range member { - mod.getTypeImports(p.Type, false, imports, seen) - } - return - default: - return - } -} - func (mod *modContext) genHeader(w io.Writer, using []string) { fmt.Fprintf(w, "// *** WARNING: this file was generated by %v. ***\n", mod.tool) fmt.Fprintf(w, "// *** Do not edit by hand unless you're certain you know what you are doing! ***\n") @@ -2031,7 +1907,7 @@ func (mod *modContext) genConfig(variables []*schema.Property) (string, error) { fmt.Fprintf(w, "\n") // Create a config bag for the variables to pull from. - fmt.Fprintf(w, " private static readonly global::Pulumi.Config __config = new global::Pulumi.Config(\"%v\");\n", mod.pkg.Name) + fmt.Fprintf(w, " private static readonly global::Pulumi.Config __config = new global::Pulumi.Config(\"%v\");\n", mod.pkg.Name()) fmt.Fprintf(w, "\n") // Emit an entry for all config variables. @@ -2109,12 +1985,16 @@ func (mod *modContext) genConfig(variables []*schema.Property) (string, error) { func (mod *modContext) genUtilities() (string, error) { // Strip any 'v' off of the version. w := &bytes.Buffer{} - err := csharpUtilitiesTemplate.Execute(w, csharpUtilitiesTemplateContext{ - Name: namespaceName(mod.namespaces, mod.pkg.Name), + def, err := mod.pkg.Definition() + if err != nil { + return "", err + } + err = csharpUtilitiesTemplate.Execute(w, csharpUtilitiesTemplateContext{ + Name: namespaceName(mod.namespaces, mod.pkg.Name()), Namespace: mod.namespaceName, ClassName: "Utilities", Tool: mod.tool, - PluginDownloadURL: mod.pkg.PluginDownloadURL, + PluginDownloadURL: def.PluginDownloadURL, }) if err != nil { return "", err @@ -2153,7 +2033,7 @@ func (mod *modContext) gen(fs codegen.Fs) error { } // Ensure that the target module directory contains a README.md file. - readme := mod.pkg.Description + readme := mod.pkg.Description() if readme != "" && readme[len(readme)-1] != '\n' { readme += "\n" } @@ -2168,8 +2048,12 @@ func (mod *modContext) gen(fs codegen.Fs) error { } fs.Add("Utilities.cs", []byte(utilities)) case "config": - if len(mod.pkg.Config) > 0 { - config, err := mod.genConfig(mod.pkg.Config) + config, err := mod.pkg.Config() + if err != nil { + return err + } + if len(config) > 0 { + config, err := mod.genConfig(config) if err != nil { return err } @@ -2185,17 +2069,8 @@ func (mod *modContext) gen(fs codegen.Fs) error { continue } - imports := map[string]codegen.StringSet{} - mod.getImportsForResource(r, imports, r) - buffer := &bytes.Buffer{} - var additionalImports []string - for _, i := range imports { - additionalImports = append(additionalImports, i.SortedValues()...) - } - sort.Strings(additionalImports) importStrings := mod.pulumiImports() - importStrings = append(importStrings, additionalImports...) mod.genHeader(buffer, importStrings) if err := mod.genResource(buffer, r); err != nil { @@ -2408,19 +2283,20 @@ type LanguageResource struct { func generateModuleContextMap(tool string, pkg *schema.Package) (map[string]*modContext, *CSharpPackageInfo, error) { // Decode .NET-specific info for each package as we discover them. infos := map[*schema.Package]*CSharpPackageInfo{} - var getPackageInfo = func(p *schema.Package) *CSharpPackageInfo { - info, ok := infos[p] + var getPackageInfo = func(p schema.PackageReference) *CSharpPackageInfo { + def, err := p.Definition() + contract.AssertNoError(err) + info, ok := infos[def] if !ok { - if err := p.ImportLanguages(map[string]schema.Language{"csharp": Importer}); err != nil { - panic(err) - } + err := def.ImportLanguages(map[string]schema.Language{"csharp": Importer}) + contract.AssertNoError(err) csharpInfo, _ := pkg.Language["csharp"].(CSharpPackageInfo) info = &csharpInfo - infos[p] = info + infos[def] = info } return info } - infos[pkg] = getPackageInfo(pkg) + infos[pkg] = getPackageInfo(pkg.Reference()) propertyNames := map[*schema.Property]string{} computePropertyNames(pkg.Config, propertyNames) @@ -2463,8 +2339,8 @@ func generateModuleContextMap(tool string, pkg *schema.Package) (map[string]*mod modules := map[string]*modContext{} details := map[*schema.ObjectType]*typeDetails{} - var getMod func(modName string, p *schema.Package) *modContext - getMod = func(modName string, p *schema.Package) *modContext { + var getMod func(modName string, p schema.PackageReference) *modContext + getMod = func(modName string, p schema.PackageReference) *modContext { mod, ok := modules[modName] if !ok { info := getPackageInfo(p) @@ -2497,41 +2373,41 @@ func generateModuleContextMap(tool string, pkg *schema.Package) (map[string]*mod // Save the module only if it's for the current package. // This way, modules for external packages are not saved. - if p == pkg { + if codegen.PkgEquals(p, pkg.Reference()) { modules[modName] = mod } } return mod } - getModFromToken := func(token string, p *schema.Package) *modContext { + getModFromToken := func(token string, p schema.PackageReference) *modContext { return getMod(p.TokenToModule(token), p) } // Create the config module if necessary. if len(pkg.Config) > 0 { - cfg := getMod("config", pkg) + cfg := getMod("config", pkg.Reference()) cfg.namespaceName = fmt.Sprintf("%s.%s", cfg.RootNamespace(), namespaceName(infos[pkg].Namespaces, pkg.Name)) } visitObjectTypes(pkg.Config, func(t *schema.ObjectType) { - getModFromToken(t.Token, pkg).details(t).outputType = true + getModFromToken(t.Token, pkg.Reference()).details(t).outputType = true }) // Find input and output types referenced by resources. scanResource := func(r *schema.Resource) { - mod := getModFromToken(r.Token, pkg) + mod := getModFromToken(r.Token, pkg.Reference()) mod.resources = append(mod.resources, r) visitObjectTypes(r.Properties, func(t *schema.ObjectType) { - getModFromToken(t.Token, t.Package).details(t).outputType = true + getModFromToken(t.Token, t.PackageReference).details(t).outputType = true }) visitObjectTypes(r.InputProperties, func(t *schema.ObjectType) { - getModFromToken(t.Token, t.Package).details(t).inputType = true + getModFromToken(t.Token, t.PackageReference).details(t).inputType = true }) if r.StateInputs != nil { visitObjectTypes(r.StateInputs.Properties, func(t *schema.ObjectType) { - getModFromToken(t.Token, t.Package).details(t).inputType = true - getModFromToken(t.Token, t.Package).details(t).stateType = true + getModFromToken(t.Token, t.PackageReference).details(t).inputType = true + getModFromToken(t.Token, t.PackageReference).details(t).stateType = true }) } } @@ -2548,19 +2424,19 @@ func generateModuleContextMap(tool string, pkg *schema.Package) (map[string]*mod continue } - mod := getModFromToken(f.Token, pkg) + mod := getModFromToken(f.Token, pkg.Reference()) if !f.IsMethod { mod.functions = append(mod.functions, f) } if f.Inputs != nil { visitObjectTypes(f.Inputs.Properties, func(t *schema.ObjectType) { - details := getModFromToken(t.Token, t.Package).details(t) + details := getModFromToken(t.Token, t.PackageReference).details(t) details.inputType = true details.plainType = true }) if f.NeedsOutputVersion() { visitObjectTypes(f.Inputs.InputShape.Properties, func(t *schema.ObjectType) { - details := getModFromToken(t.Token, t.Package).details(t) + details := getModFromToken(t.Token, t.PackageReference).details(t) details.inputType = true details.usedInFunctionOutputVersionInputs = true }) @@ -2591,11 +2467,11 @@ func generateModuleContextMap(tool string, pkg *schema.Package) (map[string]*mod for _, t := range pkg.Types { switch typ := t.(type) { case *schema.ObjectType: - mod := getModFromToken(typ.Token, pkg) + mod := getModFromToken(typ.Token, pkg.Reference()) mod.types = append(mod.types, typ) case *schema.EnumType: if !typ.IsOverlay { - mod := getModFromToken(typ.Token, pkg) + mod := getModFromToken(typ.Token, pkg.Reference()) mod.enums = append(mod.enums, typ) } default: diff --git a/pkg/codegen/dotnet/gen_program.go b/pkg/codegen/dotnet/gen_program.go index cac2587b0ba3..e5e6f832989e 100644 --- a/pkg/codegen/dotnet/gen_program.go +++ b/pkg/codegen/dotnet/gen_program.go @@ -316,8 +316,10 @@ func (g *generator) genPreamble(w io.Writer, program *pcl.Program) { if pkg != pulumiPackage { namespace := namespaceName(g.namespaces[pkg], pkg) var info CSharpPackageInfo - if r.Schema != nil && r.Schema.Package != nil { - if csharpinfo, ok := r.Schema.Package.Language["csharp"].(CSharpPackageInfo); ok { + if r.Schema != nil && r.Schema.PackageReference != nil { + def, err := r.Schema.PackageReference.Definition() + contract.AssertNoError(err) + if csharpinfo, ok := def.Language["csharp"].(CSharpPackageInfo); ok { info = csharpinfo } } diff --git a/pkg/codegen/dotnet/gen_program_expressions.go b/pkg/codegen/dotnet/gen_program_expressions.go index 4e19eb0b47df..07dd5cd623d9 100644 --- a/pkg/codegen/dotnet/gen_program_expressions.go +++ b/pkg/codegen/dotnet/gen_program_expressions.go @@ -301,7 +301,9 @@ func enumName(enum *model.EnumType) (string, string) { if !ok { return "", "" } - namespaceMap := e.(*schema.EnumType).Package.Language["csharp"].(CSharpPackageInfo).Namespaces + def, err := e.(*schema.EnumType).PackageReference.Definition() + contract.AssertNoError(err) + namespaceMap := def.Language["csharp"].(CSharpPackageInfo).Namespaces namespace := namespaceName(namespaceMap, components[0]) if components[1] != "" && components[1] != "index" { namespace += "." + namespaceName(namespaceMap, components[1]) diff --git a/pkg/codegen/go/doc.go b/pkg/codegen/go/doc.go index d26ad3eb814a..3d02c4682c2a 100644 --- a/pkg/codegen/go/doc.go +++ b/pkg/codegen/go/doc.go @@ -26,6 +26,7 @@ import ( "github.com/golang/glog" "github.com/pulumi/pulumi/pkg/v3/codegen" "github.com/pulumi/pulumi/pkg/v3/codegen/schema" + "github.com/pulumi/pulumi/sdk/v3/go/common/util/contract" ) const pulumiSDKVersion = "v3" @@ -98,7 +99,9 @@ func (d DocLanguageHelper) GetLanguageTypeString(pkg *schema.Package, moduleName // GeneratePackagesMap generates a map of Go packages for resources, functions and types. func (d *DocLanguageHelper) GeneratePackagesMap(pkg *schema.Package, tool string, goInfo GoPackageInfo) { - d.packages = generatePackageContextMap(tool, pkg, goInfo, nil) + var err error + d.packages, err = generatePackageContextMap(tool, pkg.Reference(), goInfo, nil) + contract.AssertNoError(err) } // GetPropertyName returns the property name specific to Go. diff --git a/pkg/codegen/go/gen.go b/pkg/codegen/go/gen.go index 17a78ed77277..b17c651fd7ef 100644 --- a/pkg/codegen/go/gen.go +++ b/pkg/codegen/go/gen.go @@ -103,7 +103,7 @@ func Title(s string) string { return s } -func tokenToPackage(pkg *schema.Package, overrides map[string]string, tok string) string { +func tokenToPackage(pkg schema.PackageReference, overrides map[string]string, tok string) string { mod := pkg.TokenToModule(tok) if override, ok := overrides[mod]; ok { mod = override @@ -140,7 +140,7 @@ func (c *Cache) setContextMap(pkg *schema.Package, m map[string]*pkgContext) { } type pkgContext struct { - pkg *schema.Package + pkg schema.PackageReference mod string importBasePath string rootPackageName string @@ -226,7 +226,9 @@ func (pkg *pkgContext) tokenToType(tok string) string { return name } if mod == "" { - mod = packageRoot(pkg.pkg) + var err error + mod, err = packageRoot(pkg.pkg) + contract.AssertNoError(err) } var importPath string @@ -624,30 +626,26 @@ func (pkg *pkgContext) isExternalReference(t schema.Type) bool { // Return if `t` is external to `pkg`. If so, the associated foreign schema.Package is returned. func (pkg *pkgContext) isExternalReferenceWithPackage(t schema.Type) ( - isExternal bool, extPkg *schema.Package, token string) { - var err error + isExternal bool, extPkg schema.PackageReference, token string) { switch typ := t.(type) { case *schema.ObjectType: - isExternal = typ.Package != nil && pkg.pkg != nil && typ.Package != pkg.pkg + isExternal = typ.PackageReference != nil && !codegen.PkgEquals(typ.PackageReference, pkg.pkg) if isExternal { - extPkg, err = typ.PackageReference.Definition() - contract.AssertNoError(err) + extPkg = typ.PackageReference token = typ.Token } return case *schema.ResourceType: - isExternal = typ.Resource != nil && pkg.pkg != nil && typ.Resource.Package != pkg.pkg + isExternal = typ.Resource != nil && pkg.pkg != nil && !codegen.PkgEquals(typ.Resource.PackageReference, pkg.pkg) if isExternal { - extPkg, err = typ.Resource.PackageReference.Definition() - contract.AssertNoError(err) + extPkg = typ.Resource.PackageReference token = typ.Token } return case *schema.EnumType: - isExternal = pkg.pkg != nil && typ.Package != pkg.pkg + isExternal = pkg.pkg != nil && !codegen.PkgEquals(typ.PackageReference, pkg.pkg) if isExternal { - extPkg, err = typ.PackageReference.Definition() - contract.AssertNoError(err) + extPkg = typ.PackageReference token = typ.Token } return @@ -666,7 +664,7 @@ func (pkg *pkgContext) resolveResourceType(t *schema.ResourceType) string { extPkgCtx, _ := pkg.contextForExternalReference(t) resType := extPkgCtx.tokenToResource(t.Token) if !strings.Contains(resType, ".") { - resType = fmt.Sprintf("%s.%s", extPkgCtx.pkg.Name, resType) + resType = fmt.Sprintf("%s.%s", extPkgCtx.pkg.Name(), resType) } return resType } @@ -694,8 +692,10 @@ func (pkg *pkgContext) contextForExternalReference(t schema.Type) (*pkgContext, contract.Assert(isExternal) var goInfo GoPackageInfo - contract.AssertNoError(extPkg.ImportLanguages(map[string]schema.Language{"go": Importer})) - if info, ok := extPkg.Language["go"].(GoPackageInfo); ok { + extDef, err := extPkg.Definition() + contract.AssertNoError(err) + contract.AssertNoError(extDef.ImportLanguages(map[string]schema.Language{"go": Importer})) + if info, ok := extDef.Language["go"].(GoPackageInfo); ok { goInfo = info } else { goInfo.ImportBasePath = extractImportBasePath(extPkg) @@ -705,7 +705,9 @@ func (pkg *pkgContext) contextForExternalReference(t schema.Type) (*pkgContext, // Ensure that any package import aliases we have specified locally take precedence over those // specified in the remote package. - if ourPkgGoInfoI, has := pkg.pkg.Language["go"]; has { + def, err := pkg.pkg.Definition() + contract.AssertNoError(err) + if ourPkgGoInfoI, has := def.Language["go"]; has { ourPkgGoInfo := ourPkgGoInfoI.(GoPackageInfo) if len(ourPkgGoInfo.PackageImportAliases) > 0 { pkgImportAliases = make(map[string]string) @@ -722,11 +724,12 @@ func (pkg *pkgContext) contextForExternalReference(t schema.Type) (*pkgContext, var maps map[string]*pkgContext - if extMap, ok := pkg.externalPackages.lookupContextMap(extPkg); ok { + if extMap, ok := pkg.externalPackages.lookupContextMap(extDef); ok { maps = extMap } else { - maps = generatePackageContextMap(pkg.tool, extPkg, goInfo, pkg.externalPackages) - pkg.externalPackages.setContextMap(extPkg, maps) + maps, err = generatePackageContextMap(pkg.tool, extPkg, goInfo, pkg.externalPackages) + contract.AssertNoError(err) + pkg.externalPackages.setContextMap(extDef, maps) } extPkgCtx := maps[""] extPkgCtx.pkgImportAliases = pkgImportAliases @@ -1792,7 +1795,10 @@ func (pkg *pkgContext) genResource(w io.Writer, r *schema.Resource, generateReso fmt.Fprint(w, "\topts = append(opts, replaceOnChanges)\n") } - pkg.GenPkgDefaultsOptsCall(w, false /*invoke*/) + err := pkg.GenPkgDefaultsOptsCall(w, false /*invoke*/) + if err != nil { + return err + } // Finally make the call to registration. fmt.Fprintf(w, "\tvar resource %s\n", name) @@ -2110,7 +2116,7 @@ func (pkg *pkgContext) genFunction(w io.Writer, f *schema.Function) error { var objectReturnType *schema.ObjectType if f.ReturnType != nil { - if objectType, ok := f.ReturnType.(*schema.ObjectType); ok && objectType != nil { + if objectType, ok := f.ReturnType.(*schema.ObjectType); ok { objectReturnType = objectType } else { // TODO: remove when we add support for generalized return type for go @@ -2151,7 +2157,10 @@ func (pkg *pkgContext) genFunction(w io.Writer, f *schema.Function) error { outputsType = name + "Result" } - pkg.GenPkgDefaultsOptsCall(w, true /*invoke*/) + err := pkg.GenPkgDefaultsOptsCall(w, true /*invoke*/) + if err != nil { + return err + } fmt.Fprintf(w, "\tvar rv %s\n", outputsType) fmt.Fprintf(w, "\terr := ctx.Invoke(\"%s\", %s, &rv, opts...)\n", f.Token, inputsVar) @@ -2769,13 +2778,13 @@ func (pkg *pkgContext) getTypeImports(t schema.Type, recurse bool, importsAndAli } } -func extractImportBasePath(extPkg *schema.Package) string { - version := extPkg.Version.Major +func extractImportBasePath(extPkg schema.PackageReference) string { + version := extPkg.Version().Major var vPath string if version > 1 { vPath = fmt.Sprintf("/v%d", version) } - return fmt.Sprintf("github.com/pulumi/pulumi-%s/sdk%s/go/%s", extPkg.Name, vPath, extPkg.Name) + return fmt.Sprintf("github.com/pulumi/pulumi-%s/sdk%s/go/%s", extPkg.Name(), vPath, extPkg.Name()) } func (pkg *pkgContext) getImports(member interface{}, importsAndAliases map[string]string) { @@ -2849,7 +2858,9 @@ func (pkg *pkgContext) genHeader(w io.Writer, goImports []string, importsAndAlia var pkgName string if pkg.mod == "" { - pkgName = packageName(pkg.pkg) + def, err := pkg.pkg.Definition() + contract.AssertNoError(err) + pkgName = packageName(def) } else { pkgName = path.Base(pkg.mod) } @@ -2919,7 +2930,7 @@ func (pkg *pkgContext) genConfig(w io.Writer, variables []*schema.Property) erro } printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, false) - configKey := fmt.Sprintf("\"%s:%s\"", pkg.pkg.Name, cgstrings.Camel(p.Name)) + configKey := fmt.Sprintf("\"%s:%s\"", pkg.pkg.Name(), cgstrings.Camel(p.Name)) fmt.Fprintf(w, "func Get%s(ctx *pulumi.Context) %s {\n", Title(p.Name), getType) if p.DefaultValue != nil { @@ -2946,7 +2957,7 @@ func (pkg *pkgContext) genConfig(w io.Writer, variables []*schema.Property) erro // Pulumi runtime. The generated ResourceModule supports the deserialization of resource references into fully- // hydrated Resource instances. If this is the root module, this function also generates a ResourcePackage // definition and its registration to support rehydrating providers. -func (pkg *pkgContext) genResourceModule(w io.Writer) { +func (pkg *pkgContext) genResourceModule(w io.Writer) error { contract.Assert(len(pkg.resources) != 0) allResourcesAreOverlays := true for _, r := range pkg.resources { @@ -2957,7 +2968,7 @@ func (pkg *pkgContext) genResourceModule(w io.Writer) { } if allResourcesAreOverlays { // If all resources in this module are overlays, skip further code generation. - return + return nil } basePath := pkg.importBasePath @@ -2978,7 +2989,11 @@ func (pkg *pkgContext) genResourceModule(w io.Writer) { // If there are any internal dependencies, include them as blank imports. if topLevelModule { - if goInfo, ok := pkg.pkg.Language["go"].(GoPackageInfo); ok { + def, err := pkg.pkg.Definition() + if err != nil { + return err + } + if goInfo, ok := def.Language["go"].(GoPackageInfo); ok { for _, dep := range goInfo.InternalDependencies { imports[dep] = "_" } @@ -3035,7 +3050,7 @@ func (pkg *pkgContext) genResourceModule(w io.Writer) { fmt.Fprintf(w, "}\n\n") fmt.Fprintf(w, "func (p *pkg) ConstructProvider(ctx *pulumi.Context, name, typ, urn string) (pulumi.ProviderResource, error) {\n") - fmt.Fprintf(w, "\tif typ != \"pulumi:providers:%s\" {\n", pkg.pkg.Name) + fmt.Fprintf(w, "\tif typ != \"pulumi:providers:%s\" {\n", pkg.pkg.Name()) fmt.Fprintf(w, "\t\treturn nil, fmt.Errorf(\"unknown provider type: %%s\", typ)\n") fmt.Fprintf(w, "\t}\n\n") fmt.Fprintf(w, "\tr := &Provider{}\n") @@ -3067,7 +3082,7 @@ func (pkg *pkgContext) genResourceModule(w io.Writer) { if len(registrations) > 0 { for _, mod := range registrations.SortedValues() { fmt.Fprintf(w, "\tpulumi.RegisterResourceModule(\n") - fmt.Fprintf(w, "\t\t%q,\n", pkg.pkg.Name) + fmt.Fprintf(w, "\t\t%q,\n", pkg.pkg.Name()) fmt.Fprintf(w, "\t\t%q,\n", mod) fmt.Fprintf(w, "\t\t&module{version},\n") fmt.Fprintf(w, "\t)\n") @@ -3075,15 +3090,16 @@ func (pkg *pkgContext) genResourceModule(w io.Writer) { } if provider != nil { fmt.Fprintf(w, "\tpulumi.RegisterResourcePackage(\n") - fmt.Fprintf(w, "\t\t%q,\n", pkg.pkg.Name) + fmt.Fprintf(w, "\t\t%q,\n", pkg.pkg.Name()) fmt.Fprintf(w, "\t\t&pkg{version},\n") fmt.Fprintf(w, "\t)\n") } - fmt.Fprintf(w, "}\n") + _, err := fmt.Fprintf(w, "}\n") + return err } // generatePackageContextMap groups resources, types, and functions into Go packages. -func generatePackageContextMap(tool string, pkg *schema.Package, goInfo GoPackageInfo, externalPkgs *Cache) map[string]*pkgContext { +func generatePackageContextMap(tool string, pkg schema.PackageReference, goInfo GoPackageInfo, externalPkgs *Cache) (map[string]*pkgContext, error) { packages := map[string]*pkgContext{} // Share the cache @@ -3135,7 +3151,11 @@ func generatePackageContextMap(tool string, pkg *schema.Package, goInfo GoPackag } } - if len(pkg.Config) > 0 { + config, err := pkg.Config() + if err != nil { + return nil, err + } + if len(config) > 0 { _ = getPkg("config") } @@ -3226,13 +3246,17 @@ func generatePackageContextMap(tool string, pkg *schema.Package, goInfo GoPackag } // Rewrite cyclic types. See the docs on rewriteCyclicFields for the motivation. - rewriteCyclicObjectFields(pkg) + def, err := pkg.Definition() + if err != nil { + return nil, err + } + rewriteCyclicObjectFields(def) // Use a string set to track object types that have already been processed. // This avoids recursively processing the same type. For example, in the // Kubernetes package, JSONSchemaProps have properties whose type is itself. seenMap := codegen.NewStringSet() - for _, t := range pkg.Types { + for _, t := range def.Types { switch typ := t.(type) { case *schema.ArrayType: details := getPkgFromType(typ.ElementType).detailsForType(codegen.UnwrapType(typ.ElementType)) @@ -3345,8 +3369,8 @@ func generatePackageContextMap(tool string, pkg *schema.Package, goInfo GoPackag } } - scanResource(pkg.Provider) - for _, r := range pkg.Resources { + scanResource(def.Provider) + for _, r := range def.Resources { scanResource(r) } @@ -3454,7 +3478,7 @@ func generatePackageContextMap(tool string, pkg *schema.Package, goInfo GoPackag } } - for _, t := range pkg.Types { + for _, t := range def.Types { scanType(t) } @@ -3462,7 +3486,7 @@ func generatePackageContextMap(tool string, pkg *schema.Package, goInfo GoPackag // input or output property type metadata, in case they have // types used in array or pointer element positions. if !goInfo.DisableFunctionOutputVersions || goInfo.GenerateExtraInputTypes { - for _, f := range pkg.Functions { + for _, f := range def.Functions { if f.NeedsOutputVersion() || goInfo.GenerateExtraInputTypes { optional := false if f.Inputs != nil { @@ -3475,7 +3499,7 @@ func generatePackageContextMap(tool string, pkg *schema.Package, goInfo GoPackag } } - for _, f := range pkg.Functions { + for _, f := range def.Functions { if f.IsMethod { continue } @@ -3509,7 +3533,7 @@ func generatePackageContextMap(tool string, pkg *schema.Package, goInfo GoPackag } } - return packages + return packages, nil } // LanguageResource is derived from the schema and can be used by downstream codegen. @@ -3534,7 +3558,10 @@ func LanguageResources(tool string, pkg *schema.Package) (map[string]LanguageRes if goInfo, ok := pkg.Language["go"].(GoPackageInfo); ok { goPkgInfo = goInfo } - packages := generatePackageContextMap(tool, pkg, goPkgInfo, globalCache) + packages, err := generatePackageContextMap(tool, pkg.Reference(), goPkgInfo, globalCache) + if err != nil { + return nil, err + } // emit each package var pkgMods []string @@ -3572,19 +3599,23 @@ func LanguageResources(tool string, pkg *schema.Package) (map[string]LanguageRes // source file should be under this root. For example: // // root = aws => sdk/go/aws/*.go -func packageRoot(pkg *schema.Package) string { +func packageRoot(pkg schema.PackageReference) (string, error) { + def, err := pkg.Definition() + if err != nil { + return "", err + } var info GoPackageInfo - if goInfo, ok := pkg.Language["go"].(GoPackageInfo); ok { + if goInfo, ok := def.Language["go"].(GoPackageInfo); ok { info = goInfo } if info.RootPackageName != "" { // package structure is flat - return "" + return "", nil } if info.ImportBasePath != "" { - return path.Base(info.ImportBasePath) + return path.Base(info.ImportBasePath), nil } - return goPackage(pkg.Name) + return goPackage(pkg.Name()), nil } // packageName is the go package name for the generated package. @@ -3596,7 +3627,9 @@ func packageName(pkg *schema.Package) string { if info.RootPackageName != "" { return info.RootPackageName } - return goPackage(packageRoot(pkg)) + root, err := packageRoot(pkg.Reference()) + contract.AssertNoErrorf(err, "We generated the ref from a pkg, so we know its a valid ref") + return goPackage(root) } func GeneratePackage(tool string, pkg *schema.Package) (map[string][]byte, error) { @@ -3608,7 +3641,10 @@ func GeneratePackage(tool string, pkg *schema.Package) (map[string][]byte, error if goInfo, ok := pkg.Language["go"].(GoPackageInfo); ok { goPkgInfo = goInfo } - packages := generatePackageContextMap(tool, pkg, goPkgInfo, NewCache()) + packages, err := generatePackageContextMap(tool, pkg.Reference(), goPkgInfo, NewCache()) + if err != nil { + return nil, err + } // emit each package var pkgMods []string @@ -3618,7 +3654,10 @@ func GeneratePackage(tool string, pkg *schema.Package) (map[string][]byte, error sort.Strings(pkgMods) name := packageName(pkg) - pathPrefix := packageRoot(pkg) + pathPrefix, err := packageRoot(pkg.Reference()) + if err != nil { + return nil, err + } files := codegen.Fs{} @@ -3657,8 +3696,8 @@ func GeneratePackage(tool string, pkg *schema.Package) (map[string][]byte, error switch mod { case "": buffer := &bytes.Buffer{} - if pkg.pkg.Description != "" { - printComment(buffer, pkg.pkg.Description, false) + if pkg.pkg.Description() != "" { + printComment(buffer, pkg.pkg.Description(), false) } else { fmt.Fprintf(buffer, "// Package %[1]s exports types, functions, subpackages for provisioning %[1]s resources.\n", name) } @@ -3667,9 +3706,13 @@ func GeneratePackage(tool string, pkg *schema.Package) (map[string][]byte, error setFile(path.Join(mod, "doc.go"), buffer.String()) case "config": - if len(pkg.pkg.Config) > 0 { + config, err := pkg.pkg.Config() + if err != nil { + return nil, err + } + if len(config) > 0 { buffer := &bytes.Buffer{} - if err := pkg.genConfig(buffer, pkg.pkg.Config); err != nil { + if err := pkg.genConfig(buffer, config); err != nil { return nil, err } @@ -3784,12 +3827,15 @@ func GeneratePackage(tool string, pkg *schema.Package) (map[string][]byte, error } pkg.genHeader(buffer, []string{"fmt", "os", "reflect", "regexp", "strconv", "strings"}, importsAndAliases) - packageRegex := fmt.Sprintf("^.*/pulumi-%s/sdk(/v\\d+)?", pkg.pkg.Name) + packageRegex := fmt.Sprintf("^.*/pulumi-%s/sdk(/v\\d+)?", pkg.pkg.Name()) if pkg.rootPackageName != "" { packageRegex = fmt.Sprintf("^%s(/v\\d+)?", pkg.importBasePath) } - pkg.GenUtilitiesFile(buffer, packageRegex) + err := pkg.GenUtilitiesFile(buffer, packageRegex) + if err != nil { + return nil, err + } setFile(path.Join(mod, "pulumiUtilities.go"), buffer.String()) } @@ -3797,7 +3843,10 @@ func GeneratePackage(tool string, pkg *schema.Package) (map[string][]byte, error // If there are resources in this module, register the module with the runtime. if len(pkg.resources) != 0 && !allResourcesAreOverlays(pkg.resources) { buffer := &bytes.Buffer{} - pkg.genResourceModule(buffer) + err := pkg.genResourceModule(buffer) + if err != nil { + return nil, err + } setFile(path.Join(mod, "init.go"), buffer.String()) } @@ -3857,7 +3906,7 @@ func goPackage(name string) string { return strings.ReplaceAll(name, "-", "") } -func (pkg *pkgContext) GenUtilitiesFile(w io.Writer, packageRegex string) { +func (pkg *pkgContext) GenUtilitiesFile(w io.Writer, packageRegex string) error { const utilitiesFile = ` type envParser func(v string) interface{} @@ -3931,14 +3980,20 @@ func isZero(v interface{}) bool { } ` _, err := fmt.Fprintf(w, utilitiesFile, packageRegex) - contract.AssertNoError(err) - pkg.GenPkgDefaultOpts(w) + if err != nil { + return err + } + return pkg.GenPkgDefaultOpts(w) } -func (pkg *pkgContext) GenPkgDefaultOpts(w io.Writer) { - url := pkg.pkg.PluginDownloadURL +func (pkg *pkgContext) GenPkgDefaultOpts(w io.Writer) error { + p, err := pkg.pkg.Definition() + if err != nil { + return err + } + url := p.PluginDownloadURL if url == "" { - return + return nil } const template string = ` // pkg%[1]sDefaultOpts provides package level defaults to pulumi.Option%[1]s. @@ -3950,28 +4005,35 @@ func pkg%[1]sDefaultOpts(opts []pulumi.%[1]sOption) []pulumi.%[1]sOption { ` pluginDownloadURL := fmt.Sprintf("pulumi.PluginDownloadURL(%q)", url) version := "" - if info := pkg.pkg.Language["go"]; info != nil { - if info.(GoPackageInfo).RespectSchemaVersion && pkg.pkg.Version != nil { - version = fmt.Sprintf(", pulumi.Version(%q)", pkg.pkg.Version.String()) + if info := p.Language["go"]; info != nil { + if info.(GoPackageInfo).RespectSchemaVersion && pkg.pkg.Version() != nil { + version = fmt.Sprintf(", pulumi.Version(%q)", p.Version.String()) } } for _, typ := range []string{"Resource", "Invoke"} { _, err := fmt.Fprintf(w, template, typ, pluginDownloadURL, version) - contract.AssertNoError(err) + if err != nil { + return err + } } + return nil } // GenPkgDefaultsOptsCall generates a call to Pkg{TYPE}DefaultsOpts. -func (pkg *pkgContext) GenPkgDefaultsOptsCall(w io.Writer, invoke bool) { +func (pkg *pkgContext) GenPkgDefaultsOptsCall(w io.Writer, invoke bool) error { // The `pkg%sDefaultOpts` call won't do anything, so we don't insert it. - if pkg.pkg.PluginDownloadURL == "" { - return + p, err := pkg.pkg.Definition() + if err != nil { + return err + } + if p.PluginDownloadURL == "" { + return nil } pkg.needsUtils = true typ := "Resource" if invoke { typ = "Invoke" } - _, err := fmt.Fprintf(w, "\topts = pkg%sDefaultOpts(opts)\n", typ) - contract.AssertNoError(err) + _, err = fmt.Fprintf(w, "\topts = pkg%sDefaultOpts(opts)\n", typ) + return err } diff --git a/pkg/codegen/go/gen_crd2pulumi.go b/pkg/codegen/go/gen_crd2pulumi.go index cd4c86e90df4..28808c867556 100644 --- a/pkg/codegen/go/gen_crd2pulumi.go +++ b/pkg/codegen/go/gen_crd2pulumi.go @@ -18,7 +18,10 @@ func CRDTypes(tool string, pkg *schema.Package) (map[string]*bytes.Buffer, error if goInfo, ok := pkg.Language["go"].(GoPackageInfo); ok { goPkgInfo = goInfo } - packages := generatePackageContextMap(tool, pkg, goPkgInfo, nil) + packages, err := generatePackageContextMap(tool, pkg.Reference(), goPkgInfo, nil) + if err != nil { + return nil, err + } var pkgMods []string for mod := range packages { diff --git a/pkg/codegen/go/gen_program.go b/pkg/codegen/go/gen_program.go index edbbec1d1cf8..fb6fd53b738b 100644 --- a/pkg/codegen/go/gen_program.go +++ b/pkg/codegen/go/gen_program.go @@ -272,7 +272,8 @@ func getPackages(tool string, pkg *schema.Package, cache *Cache) map[string]*pkg if goInfo, ok := pkg.Language["go"].(GoPackageInfo); ok { goPkgInfo = goInfo } - v := generatePackageContextMap(tool, pkg, goPkgInfo, cache) + v, err := generatePackageContextMap(tool, pkg.Reference(), goPkgInfo, cache) + contract.AssertNoError(err) packageContexts.Store(pkg, v) return v } diff --git a/pkg/codegen/go/gen_program_expressions.go b/pkg/codegen/go/gen_program_expressions.go index fa57ac94509e..296db4edfab9 100644 --- a/pkg/codegen/go/gen_program_expressions.go +++ b/pkg/codegen/go/gen_program_expressions.go @@ -375,7 +375,10 @@ func outputVersionFunctionArgTypeName(t model.Type, cache *Cache) (string, error return "", fmt.Errorf("Expected a schema.ObjectType, got %s", schemaType.String()) } - pkg := &pkgContext{pkg: &schema.Package{Name: "main"}, externalPackages: cache} + pkg := &pkgContext{ + pkg: (&schema.Package{Name: "main"}).Reference(), + externalPackages: cache, + } var ty string if pkg.isExternalReference(objType) { @@ -785,8 +788,10 @@ func (g *generator) argumentTypeName(expr model.Expression, destType model.Type, } if schemaType, ok := pcl.GetSchemaForType(destType); ok { - pkg := &pkgContext{pkg: &schema.Package{Name: "main"}, externalPackages: g.externalCache} - return pkg.argsType(schemaType) + return (&pkgContext{ + pkg: (&schema.Package{Name: "main"}).Reference(), + externalPackages: g.externalCache, + }).argsType(schemaType) } switch destType := destType.(type) { diff --git a/pkg/codegen/go/gen_test.go b/pkg/codegen/go/gen_test.go index 3f94022ab2d1..2acecde4d190 100644 --- a/pkg/codegen/go/gen_test.go +++ b/pkg/codegen/go/gen_test.go @@ -135,7 +135,8 @@ func TestGenerateTypeNames(t *testing.T) { if goInfo, ok := pkg.Language["go"].(GoPackageInfo); ok { goPkgInfo = goInfo } - packages := generatePackageContextMap("test", pkg, goPkgInfo, nil) + packages, err := generatePackageContextMap("test", pkg.Reference(), goPkgInfo, nil) + require.NoError(t, err) root, ok := packages[""] require.True(t, ok) @@ -252,7 +253,7 @@ func TestTokenToType(t *testing.T) { }{ { pkg: &pkgContext{ - pkg: importSpec(t, awsSpec), + pkg: importSpec(t, awsSpec).Reference(), importBasePath: awsImportBasePath, }, token: "aws:s3/BucketWebsite:BucketWebsite", @@ -260,7 +261,7 @@ func TestTokenToType(t *testing.T) { }, { pkg: &pkgContext{ - pkg: importSpec(t, awsSpec), + pkg: importSpec(t, awsSpec).Reference(), importBasePath: awsImportBasePath, pkgImportAliases: map[string]string{ "github.com/pulumi/pulumi-aws/sdk/v4/go/aws/s3": "awss3", @@ -271,7 +272,7 @@ func TestTokenToType(t *testing.T) { }, { pkg: &pkgContext{ - pkg: importSpec(t, googleNativeSpec), + pkg: importSpec(t, googleNativeSpec).Reference(), importBasePath: googleNativeImportBasePath, pkgImportAliases: map[string]string{ "github.com/pulumi/pulumi-google-native/sdk/go/google/dns/v1": "dns", @@ -316,7 +317,7 @@ func TestTokenToResource(t *testing.T) { }{ { pkg: &pkgContext{ - pkg: importSpec(t, awsSpec), + pkg: importSpec(t, awsSpec).Reference(), importBasePath: awsImportBasePath, }, token: "aws:s3/Bucket:Bucket", @@ -324,7 +325,7 @@ func TestTokenToResource(t *testing.T) { }, { pkg: &pkgContext{ - pkg: importSpec(t, awsSpec), + pkg: importSpec(t, awsSpec).Reference(), importBasePath: awsImportBasePath, pkgImportAliases: map[string]string{ "github.com/pulumi/pulumi-aws/sdk/v4/go/aws/s3": "awss3", @@ -335,7 +336,7 @@ func TestTokenToResource(t *testing.T) { }, { pkg: &pkgContext{ - pkg: importSpec(t, googleNativeSpec), + pkg: importSpec(t, googleNativeSpec).Reference(), importBasePath: googleNativeImportBasePath, pkgImportAliases: map[string]string{ "github.com/pulumi/pulumi-google-native/sdk/go/google/dns/v1": "dns", @@ -368,7 +369,7 @@ func TestGenHeader(t *testing.T) { pkg := &pkgContext{ tool: "a tool", - pkg: &schema.Package{Name: "test-pkg"}, + pkg: (&schema.Package{Name: "test-pkg"}).Reference(), } s := func() string { diff --git a/pkg/codegen/hcl2/model/expression.go b/pkg/codegen/hcl2/model/expression.go index 62b31800442a..58ed16503cad 100644 --- a/pkg/codegen/hcl2/model/expression.go +++ b/pkg/codegen/hcl2/model/expression.go @@ -1281,9 +1281,9 @@ func literalText(value cty.Value, rawBytes []byte, escaped, quoted bool) string if !escaped { return value.AsString() } - s := strconv.Quote(value.AsString()) - if !quoted { - return s[1 : len(s)-1] + s := escapeString(value.AsString()) + if quoted { + return fmt.Sprintf(`"%s"`, s) } return s default: @@ -1291,6 +1291,31 @@ func literalText(value cty.Value, rawBytes []byte, escaped, quoted bool) string } } +func escapeString(s string) string { + // escape special characters + s = strconv.Quote(s) + s = s[1 : len(s)-1] // Remove surrounding double quote (`"`) + + // Escape `${` + runes := []rune(s) + out := make([]rune, 0, len(runes)) + for i, r := range runes { + next := func() rune { + if i >= len(runes)-1 { + return 0 + } + return runes[i+1] + } + if r == '$' && next() == '{' { + out = append(out, '$') + } else if r == '%' && next() == '{' { + out = append(out, '%') + } + out = append(out, r) + } + return string(out) +} + // LiteralValueExpression represents a semantically-analyzed literal value expression. type LiteralValueExpression struct { // The syntax node associated with the literal value expression. diff --git a/pkg/codegen/hcl2/model/print_test.go b/pkg/codegen/hcl2/model/print_test.go index 172ee1a6f1a6..8615887b407b 100644 --- a/pkg/codegen/hcl2/model/print_test.go +++ b/pkg/codegen/hcl2/model/print_test.go @@ -20,9 +20,28 @@ func TestPrintNoTokens(t *testing.T) { Value: cty.True, }, }, + &Attribute{ + Name: "literal", + Value: &TemplateExpression{ + Parts: []Expression{ + &LiteralValueExpression{ + Value: cty.StringVal("foo${bar} %{"), + }, + &LiteralValueExpression{ + Value: cty.StringVal("$"), + }, + &LiteralValueExpression{ + Value: cty.StringVal("%{"), + }, + }, + }, + }, }, }, } - expected := "block {\n attribute = true\n}" + expected := `block { + attribute = true + literal = "foo$${bar} %%{$%%{" +}` assert.Equal(t, expected, fmt.Sprintf("%v", b)) } diff --git a/pkg/codegen/importer/hcl2_test.go b/pkg/codegen/importer/hcl2_test.go index a6fd817265cb..79a5fae48a31 100644 --- a/pkg/codegen/importer/hcl2_test.go +++ b/pkg/codegen/importer/hcl2_test.go @@ -89,10 +89,14 @@ func renderLiteralValue(t *testing.T, x *model.LiteralValueExpression) resource. } func renderTemplate(t *testing.T, x *model.TemplateExpression) resource.PropertyValue { - if !assert.Len(t, x.Parts, 1) { - return resource.NewStringProperty("") + if len(x.Parts) == 1 { + return renderLiteralValue(t, x.Parts[0].(*model.LiteralValueExpression)) } - return renderLiteralValue(t, x.Parts[0].(*model.LiteralValueExpression)) + b := "" + for _, p := range x.Parts { + b += p.(*model.LiteralValueExpression).Value.AsString() + } + return resource.NewStringProperty(b) } func renderObjectCons(t *testing.T, x *model.ObjectConsExpression) resource.PropertyValue { diff --git a/pkg/codegen/importer/language.go b/pkg/codegen/importer/language.go index 90fb8c953a85..0e838e8c42a3 100644 --- a/pkg/codegen/importer/language.go +++ b/pkg/codegen/importer/language.go @@ -80,7 +80,7 @@ func GenerateLanguageDefinitions(w io.Writer, loader schema.Loader, gen Language } parser := syntax.NewParser() - if err := parser.ParseFile(&hcl2Text, string("anonymous.pp")); err != nil { + if err := parser.ParseFile(&hcl2Text, "anonymous.pp"); err != nil { return err } if parser.Diagnostics.HasErrors() { diff --git a/pkg/codegen/importer/testdata/cases.json b/pkg/codegen/importer/testdata/cases.json index 05965f277808..aa301b908e71 100644 --- a/pkg/codegen/importer/testdata/cases.json +++ b/pkg/codegen/importer/testdata/cases.json @@ -1556,7 +1556,7 @@ "type": "random:index/randomId:RandomId", "inputs": { "byteLength": 42, - "prefix": "foobar" + "prefix": "f${oo}ba%{}r" } } ] diff --git a/pkg/codegen/nodejs/doc.go b/pkg/codegen/nodejs/doc.go index e5e782ce4404..cf5c6a5d509e 100644 --- a/pkg/codegen/nodejs/doc.go +++ b/pkg/codegen/nodejs/doc.go @@ -77,7 +77,7 @@ func (d DocLanguageHelper) GetLanguageTypeString(pkg *schema.Package, moduleName } modCtx := &modContext{ - pkg: pkg, + pkg: pkg.Reference(), mod: moduleName, } typeName := modCtx.typeString(t, input, nil) @@ -127,7 +127,7 @@ func (d DocLanguageHelper) GetMethodResultName(pkg *schema.Package, modName stri if info, ok := pkg.Language["nodejs"].(NodePackageInfo); ok { if info.LiftSingleValueMethodReturns && objectReturnType != nil && len(objectReturnType.Properties) == 1 { modCtx := &modContext{ - pkg: pkg, + pkg: pkg.Reference(), mod: modName, } return modCtx.typeString(objectReturnType.Properties[0].Type, false, nil) diff --git a/pkg/codegen/nodejs/gen.go b/pkg/codegen/nodejs/gen.go index b858c7545e8c..f83b7e28f219 100644 --- a/pkg/codegen/nodejs/gen.go +++ b/pkg/codegen/nodejs/gen.go @@ -128,7 +128,7 @@ func externalModuleName(s string) string { } type modContext struct { - pkg *schema.Package + pkg schema.PackageReference mod string types []*schema.ObjectType enums []*schema.EnumType @@ -180,15 +180,17 @@ func (mod *modContext) tokenToModName(tok string) string { return modName } -func (mod *modContext) namingContext(pkg *schema.Package) (namingCtx *modContext, pkgName string, external bool) { +func (mod *modContext) namingContext(pkg schema.PackageReference) (namingCtx *modContext, pkgName string, external bool) { namingCtx = mod - if pkg != nil && pkg != mod.pkg { + if pkg != nil && !codegen.PkgEquals(pkg, mod.pkg) { external = true - pkgName = pkg.Name + "." + pkgName = pkg.Name() + "." var info NodePackageInfo - contract.AssertNoError(pkg.ImportLanguages(map[string]schema.Language{"nodejs": Importer})) - if v, ok := pkg.Language["nodejs"].(NodePackageInfo); ok { + def, err := pkg.Definition() + contract.AssertNoError(err) + contract.AssertNoError(def.ImportLanguages(map[string]schema.Language{"nodejs": Importer})) + if v, ok := def.Language["nodejs"].(NodePackageInfo); ok { info = v } namingCtx = &modContext{ @@ -200,7 +202,7 @@ func (mod *modContext) namingContext(pkg *schema.Package) (namingCtx *modContext return } -func (mod *modContext) objectType(pkg *schema.Package, details *typeDetails, tok string, input, args, enum bool) string { +func (mod *modContext) objectType(pkg schema.PackageReference, details *typeDetails, tok string, input, args, enum bool) string { root := "outputs." if input { @@ -238,7 +240,7 @@ func (mod *modContext) objectType(pkg *schema.Package, details *typeDetails, tok func (mod *modContext) resourceType(r *schema.ResourceType) string { if strings.HasPrefix(r.Token, "pulumi:providers:") { pkgName := strings.TrimPrefix(r.Token, "pulumi:providers:") - if pkgName != mod.pkg.Name { + if pkgName != mod.pkg.Name() { pkgName = externalModuleName(pkgName) } @@ -247,13 +249,15 @@ func (mod *modContext) resourceType(r *schema.ResourceType) string { pkg := mod.pkg if r.Resource != nil { - pkg = r.Resource.Package + pkg = r.Resource.PackageReference } namingCtx, pkgName, external := mod.namingContext(pkg) - if external { - pkgName = externalModuleName(pkgName) + if !external { + name := tokenToName(r.Token) + return title(name) } + pkgName = externalModuleName(pkgName) modName, name := namingCtx.tokenToModName(r.Token), tokenToName(r.Token) return pkgName + modName + title(name) @@ -298,14 +302,14 @@ func (mod *modContext) typeAst(t schema.Type, input bool, constValue interface{} } return tstypes.Identifier(fmt.Sprintf("pulumi.Input<%s>", typ)) case *schema.EnumType: - return tstypes.Identifier(mod.objectType(t.Package, nil, t.Token, input, false, true)) + return tstypes.Identifier(mod.objectType(t.PackageReference, nil, t.Token, input, false, true)) case *schema.ArrayType: return tstypes.Array(mod.typeAst(t.ElementType, input, constValue)) case *schema.MapType: return tstypes.StringMap(mod.typeAst(t.ElementType, input, constValue)) case *schema.ObjectType: details := mod.details(t) - return tstypes.Identifier(mod.objectType(t.Package, details, t.Token, input, t.IsInputShape(), false)) + return tstypes.Identifier(mod.objectType(t.PackageReference, details, t.Token, input, t.IsInputShape(), false)) case *schema.ResourceType: return tstypes.Identifier(mod.resourceType(t)) case *schema.TokenType: @@ -670,7 +674,7 @@ func (mod *modContext) genResource(w io.Writer, r *schema.Resource) (resourceFil pulumiType := r.Token if r.IsProvider { - pulumiType = mod.pkg.Name + pulumiType = mod.pkg.Name() } fmt.Fprintf(w, " /** @internal */\n") @@ -1427,7 +1431,9 @@ func (mod *modContext) getTypeImportsForResource(t schema.Type, recurse bool, ex } var nodePackageInfo NodePackageInfo - if languageInfo, hasLanguageInfo := mod.pkg.Language["nodejs"]; hasLanguageInfo { + def, err := mod.pkg.Definition() + contract.AssertNoError(err) + if languageInfo, hasLanguageInfo := def.Language["nodejs"]; hasLanguageInfo { nodePackageInfo = languageInfo.(NodePackageInfo) } @@ -1450,16 +1456,16 @@ func (mod *modContext) getTypeImportsForResource(t schema.Type, recurse bool, ex return mod.getTypeImports(t.ElementType, recurse, externalImports, imports, seen) case *schema.EnumType: // If the enum is from another package, add an import for the external package. - if t.Package != nil && t.Package != mod.pkg { - pkg := t.Package.Name + if t.PackageReference != nil && !codegen.PkgEquals(t.PackageReference, mod.pkg) { + pkg := t.PackageReference.Name() writeImports(pkg) return false } return true case *schema.ObjectType: // If it's from another package, add an import for the external package. - if t.Package != nil && t.Package != mod.pkg { - pkg := t.Package.Name + if t.PackageReference != nil && !codegen.PkgEquals(t.PackageReference, mod.pkg) { + pkg := t.PackageReference.Name() writeImports(pkg) return false } @@ -1470,8 +1476,8 @@ func (mod *modContext) getTypeImportsForResource(t schema.Type, recurse bool, ex return true case *schema.ResourceType: // If it's from another package, add an import for the external package. - if t.Resource != nil && t.Resource.Package != mod.pkg { - pkg := t.Resource.Package.Name + if t.Resource != nil && !codegen.PkgEquals(t.Resource.PackageReference, mod.pkg) { + pkg := t.Resource.PackageReference.Name() writeImports(pkg) return false } @@ -1633,7 +1639,7 @@ func (mod *modContext) genConfig(w io.Writer, variables []*schema.Property) erro fmt.Fprintf(w, "declare var exports: any;\n") // Create a config bag for the variables to pull from. - fmt.Fprintf(w, "const __config = new pulumi.Config(\"%v\");\n", mod.pkg.Name) + fmt.Fprintf(w, "const __config = new pulumi.Config(\"%v\");\n", mod.pkg.Name()) fmt.Fprintf(w, "\n") // Emit an entry for all config variables. @@ -1684,9 +1690,11 @@ func (mod *modContext) sdkImports(nested, utilities bool) []string { fmt.Sprintf(`import * as outputs from "%s/types/output";`, relRoot), }...) - if mod.pkg.Language["nodejs"].(NodePackageInfo).ContainsEnums { + def, err := mod.pkg.Definition() + contract.AssertNoError(err) + if def.Language["nodejs"].(NodePackageInfo).ContainsEnums { code := `import * as enums from "%s/types/enums";` - if lookupNodePackageInfo(mod.pkg).UseTypeOnlyReferences { + if lookupNodePackageInfo(def).UseTypeOnlyReferences { code = `import type * as enums from "%s/types/enums";` } imports = append(imports, fmt.Sprintf(code, relRoot)) @@ -1877,7 +1885,9 @@ func (mod *modContext) isReservedSourceFileName(name string) bool { case "utilities.ts": return mod.mod == "" case "vars.ts": - return len(mod.pkg.Config) > 0 + config, err := mod.pkg.Config() + contract.AssertNoError(err) + return len(config) > 0 default: return false } @@ -1923,26 +1933,34 @@ func (mod *modContext) gen(fs codegen.Fs) error { fs.Add(p, []byte(contents)) } + def, err := mod.pkg.Definition() + if err != nil { + return err + } + // Utilities, config, readme switch mod.mod { case "": buffer := &bytes.Buffer{} mod.genHeader(buffer, nil, nil, nil) - mod.genUtilitiesFile(buffer) + err := mod.genUtilitiesFile(buffer) + if err != nil { + return err + } fs.Add(path.Join(modDir, "utilities.ts"), buffer.Bytes()) // Ensure that the top-level (provider) module directory contains a README.md file. - readme := mod.pkg.Language["nodejs"].(NodePackageInfo).Readme + readme := def.Language["nodejs"].(NodePackageInfo).Readme if readme == "" { - readme = mod.pkg.Description + readme = def.Description if readme != "" && readme[len(readme)-1] != '\n' { readme += "\n" } - if mod.pkg.Attribution != "" { + if def.Attribution != "" { if len(readme) != 0 { readme += "\n" } - readme += mod.pkg.Attribution + readme += def.Attribution } } if readme != "" && readme[len(readme)-1] != '\n' { @@ -1950,9 +1968,9 @@ func (mod *modContext) gen(fs codegen.Fs) error { } fs.Add(path.Join(modDir, "README.md"), []byte(readme)) case "config": - if len(mod.pkg.Config) > 0 { + if len(def.Config) > 0 { buffer := &bytes.Buffer{} - if err := mod.genConfig(buffer, mod.pkg.Config); err != nil { + if err := mod.genConfig(buffer, def.Config); err != nil { return err } addFile(otherFileType, "vars.ts", buffer.String()) @@ -2027,7 +2045,7 @@ func (mod *modContext) gen(fs codegen.Fs) error { // Nested types // Importing enums always imports inputs and outputs, so if we have enums we generate inputs and outputs - if len(mod.types) > 0 || (mod.pkg.Language["nodejs"].(NodePackageInfo).ContainsEnums && mod.mod == "types") { + if len(mod.types) > 0 || (def.Language["nodejs"].(NodePackageInfo).ContainsEnums && mod.mod == "types") { input, output, err := mod.genTypes() if err != nil { return err @@ -2098,7 +2116,10 @@ func (mod *modContext) genIndex(exports []fileInfo) string { } } - info, _ := mod.pkg.Language["nodejs"].(NodePackageInfo) + def, err := mod.pkg.Definition() + contract.AssertNoError(err) + + info, _ := def.Language["nodejs"].(NodePackageInfo) if info.ContainsEnums { if mod.mod == "types" { children.Add("enums") @@ -2173,7 +2194,7 @@ func (mod *modContext) genResourceModule(w io.Writer) { continue } - registrations.Add(mod.pkg.TokenToRuntimeModule(r.Token)) + registrations.Add(schema.TokenToRuntimeModule(r.Token)) } fmt.Fprintf(w, "\nconst _module = {\n") @@ -2201,12 +2222,12 @@ func (mod *modContext) genResourceModule(w io.Writer) { fmt.Fprintf(w, " },\n") fmt.Fprintf(w, "};\n") for _, name := range registrations.SortedValues() { - fmt.Fprintf(w, "pulumi.runtime.registerResourceModule(\"%v\", \"%v\", _module)\n", mod.pkg.Name, name) + fmt.Fprintf(w, "pulumi.runtime.registerResourceModule(\"%v\", \"%v\", _module)\n", mod.pkg.Name(), name) } } if provider != nil { - fmt.Fprintf(w, "pulumi.runtime.registerResourcePackage(\"%v\", {\n", mod.pkg.Name) + fmt.Fprintf(w, "pulumi.runtime.registerResourcePackage(\"%v\", {\n", mod.pkg.Name()) fmt.Fprintf(w, " version: utilities.getVersion(),\n") fmt.Fprintf(w, " constructProvider: (name: string, type: string, urn: string): pulumi.ProviderResource => {\n") fmt.Fprintf(w, " if (type !== \"%v\") {\n", provider.Token) @@ -2475,7 +2496,7 @@ func generateModuleContextMap(tool string, pkg *schema.Package, extraFiles map[s mod, ok := modules[modName] if !ok { mod = &modContext{ - pkg: pkg, + pkg: pkg.Reference(), mod: modName, tool: tool, compatibility: info.Compatibility, @@ -2704,7 +2725,7 @@ func GeneratePackage(tool string, pkg *schema.Package, extraFiles map[string][]b return files, nil } -func (mod *modContext) genUtilitiesFile(w io.Writer) { +func (mod *modContext) genUtilitiesFile(w io.Writer) error { const body = ` export function getEnv(...vars: string[]): string | undefined { for (const v of vars) { @@ -2769,12 +2790,16 @@ export function lazyLoad(exports: any, props: string[], loadModule: any) { } } ` + def, err := mod.pkg.Definition() + if err != nil { + return err + } var pluginDownloadURL string - if url := mod.pkg.PluginDownloadURL; url != "" { + if url := def.PluginDownloadURL; url != "" { pluginDownloadURL = fmt.Sprintf(", pluginDownloadURL: %q", url) } - _, err := fmt.Fprintf(w, body, pluginDownloadURL) - contract.AssertNoError(err) + _, err = fmt.Fprintf(w, body, pluginDownloadURL) + return err } func genInstallScript(pluginDownloadURL string) string { diff --git a/pkg/codegen/nodejs/gen_program.go b/pkg/codegen/nodejs/gen_program.go index e8caac5626ee..ec2dae39ff2b 100644 --- a/pkg/codegen/nodejs/gen_program.go +++ b/pkg/codegen/nodejs/gen_program.go @@ -73,7 +73,10 @@ func GenerateProgram(program *pcl.Program) (map[string][]byte, hcl.Diagnostics, } var index bytes.Buffer - g.genPreamble(&index, program, preambleHelperMethods) + err = g.genPreamble(&index, program, preambleHelperMethods) + if err != nil { + return nil, nil, err + } for _, n := range nodes { if g.asyncMain { break @@ -281,7 +284,7 @@ func (g *generator) genComment(w io.Writer, comment syntax.Comment) { } } -func (g *generator) genPreamble(w io.Writer, program *pcl.Program, preambleHelperMethods codegen.StringSet) { +func (g *generator) genPreamble(w io.Writer, program *pcl.Program, preambleHelperMethods codegen.StringSet) error { // Print the @pulumi/pulumi import at the top. g.Fprintln(w, `import * as pulumi from "@pulumi/pulumi";`) @@ -296,8 +299,12 @@ func (g *generator) genPreamble(w io.Writer, program *pcl.Program, preambleHelpe continue } pkgName := "@pulumi/" + pkg - if r.Schema != nil && r.Schema.Package != nil { - if info, ok := r.Schema.Package.Language["nodejs"].(NodePackageInfo); ok && info.PackageName != "" { + if r.Schema != nil && r.Schema.PackageReference != nil { + def, err := r.Schema.PackageReference.Definition() + if err != nil { + return err + } + if info, ok := def.Language["nodejs"].(NodePackageInfo); ok && info.PackageName != "" { pkgName = info.PackageName } npmToPuPkgName[pkgName] = pkg @@ -345,6 +352,7 @@ func (g *generator) genPreamble(w io.Writer, program *pcl.Program, preambleHelpe for _, preambleHelperMethodBody := range preambleHelperMethods.SortedValues() { g.Fprintf(w, "%s\n\n", preambleHelperMethodBody) } + return nil } func (g *generator) genNode(w io.Writer, n pcl.Node) { @@ -384,18 +392,20 @@ func resourceTypeName(r *pcl.Resource) (string, string, string, hcl.Diagnostics) pkg, module, member, diagnostics := r.DecomposeToken() if r.Schema != nil { - module = moduleName(module, r.Schema.Package) + module = moduleName(module, r.Schema.PackageReference) } return makeValidIdentifier(pkg), module, title(member), diagnostics } -func moduleName(module string, pkg *schema.Package) string { +func moduleName(module string, pkg schema.PackageReference) string { // Normalize module. if pkg != nil { - err := pkg.ImportLanguages(map[string]schema.Language{"nodejs": Importer}) + def, err := pkg.Definition() + contract.AssertNoError(err) + err = def.ImportLanguages(map[string]schema.Language{"nodejs": Importer}) contract.AssertNoError(err) - if lang, ok := pkg.Language["nodejs"]; ok { + if lang, ok := def.Language["nodejs"]; ok { pkgInfo := lang.(NodePackageInfo) if m, ok := pkgInfo.ModuleToPackage[module]; ok { module = m diff --git a/pkg/codegen/nodejs/gen_program_expressions.go b/pkg/codegen/nodejs/gen_program_expressions.go index b6cfabd101d4..ad848e6df262 100644 --- a/pkg/codegen/nodejs/gen_program_expressions.go +++ b/pkg/codegen/nodejs/gen_program_expressions.go @@ -309,11 +309,15 @@ func enumName(enum *model.EnumType) (string, error) { if !ok { return "", fmt.Errorf("Could not get associated enum") } - if name := e.(*schema.EnumType).Package.Language["nodejs"].(NodePackageInfo).PackageName; name != "" { + def, err := e.(*schema.EnumType).PackageReference.Definition() + if err != nil { + return "", err + } + if name := def.Language["nodejs"].(NodePackageInfo).PackageName; name != "" { pkg = name } if mod := components[1]; mod != "" && mod != "index" { - if pkg := e.(*schema.EnumType).Package; pkg != nil { + if pkg := e.(*schema.EnumType).PackageReference; pkg != nil { mod = moduleName(mod, pkg) } pkg += "." + mod diff --git a/pkg/codegen/pcl/binder_schema.go b/pkg/codegen/pcl/binder_schema.go index 5c88a4e01eda..8ed904939bd1 100644 --- a/pkg/codegen/pcl/binder_schema.go +++ b/pkg/codegen/pcl/binder_schema.go @@ -389,7 +389,11 @@ func (b *binder) schemaTypeToType(src schema.Type) model.Type { case schema.ArchiveType: return ArchiveType case schema.AssetType: - return AssetType + // Generated SDK code accepts assets or archives when schema.AssetType is + // specified. In an effort to keep PCL type checking in sync with our + // generated SDKs, we match the SDKs behavior when translating schema types to + // PCL types. + return AssetOrArchiveType case schema.JSONType: fallthrough case schema.AnyType: diff --git a/pkg/codegen/python/doc.go b/pkg/codegen/python/doc.go index 86fe71b0e301..82f7ef45bd5b 100644 --- a/pkg/codegen/python/doc.go +++ b/pkg/codegen/python/doc.go @@ -76,7 +76,7 @@ func (d DocLanguageHelper) GetDocLinkForFunctionInputOrOutputType(pkg *schema.Pa func (d DocLanguageHelper) GetLanguageTypeString(pkg *schema.Package, moduleName string, t schema.Type, input bool) string { typeDetails := map[*schema.ObjectType]*typeDetails{} mod := &modContext{ - pkg: pkg, + pkg: pkg.Reference(), mod: moduleName, typeDetails: typeDetails, } @@ -121,7 +121,7 @@ func (d DocLanguageHelper) GetMethodResultName(pkg *schema.Package, modName stri if info.LiftSingleValueMethodReturns && returnType != nil && len(returnType.Properties) == 1 { typeDetails := map[*schema.ObjectType]*typeDetails{} mod := &modContext{ - pkg: pkg, + pkg: pkg.Reference(), mod: modName, typeDetails: typeDetails, } diff --git a/pkg/codegen/python/gen.go b/pkg/codegen/python/gen.go index 8099d6adbd72..de9c7e475ce5 100644 --- a/pkg/codegen/python/gen.go +++ b/pkg/codegen/python/gen.go @@ -97,7 +97,7 @@ type modLocator struct { } type modContext struct { - pkg *schema.Package + pkg schema.PackageReference modLocator *modLocator mod string pyPkgName string @@ -158,10 +158,12 @@ func (mod *modContext) details(t *schema.ObjectType) *typeDetails { return details } -func (mod *modContext) modNameAndName(pkg *schema.Package, t schema.Type, input bool) (modName string, name string) { +func (mod *modContext) modNameAndName(pkg schema.PackageReference, t schema.Type, input bool) (modName string, name string) { var info PackageInfo - contract.AssertNoError(pkg.ImportLanguages(map[string]schema.Language{"python": Importer})) - if v, ok := pkg.Language["python"].(PackageInfo); ok { + p, err := pkg.Definition() + contract.AssertNoError(err) + contract.AssertNoError(p.ImportLanguages(map[string]schema.Language{"python": Importer})) + if v, ok := p.Language["python"].(PackageInfo); ok { info = v } @@ -213,9 +215,10 @@ func (mod *modContext) objectType(t *schema.ObjectType, input bool) string { } // If it's an external type, reference it via fully qualified name. - if t.Package != mod.pkg { - modName, name := mod.modNameAndName(t.Package, t, input) - return fmt.Sprintf("'%s.%s%s%s'", pyPack(t.Package.Name), modName, prefix, name) + + if !codegen.PkgEquals(t.PackageReference, mod.pkg) { + modName, name := mod.modNameAndName(t.PackageReference, t, input) + return fmt.Sprintf("'%s.%s%s%s'", pyPack(t.PackageReference.Name()), modName, prefix, name) } modName, name := mod.tokenToModule(t.Token), mod.unqualifiedObjectTypeName(t, input) @@ -255,7 +258,8 @@ func (mod *modContext) tokenToEnum(tok string) string { } func (mod *modContext) resourceType(r *schema.ResourceType) string { - if r.Resource == nil || r.Resource.Package == mod.pkg { + + if r.Resource == nil || codegen.PkgEquals(r.Resource.PackageReference, mod.pkg) { return mod.tokenToResource(r.Token) } @@ -265,9 +269,9 @@ func (mod *modContext) resourceType(r *schema.ResourceType) string { return fmt.Sprintf("pulumi_%s.Provider", pkgName) } - pkg := r.Resource.Package + pkg := r.Resource.PackageReference modName, name := mod.modNameAndName(pkg, r, false) - return fmt.Sprintf("%s.%s%s", pyPack(pkg.Name), modName, name) + return fmt.Sprintf("%s.%s%s", pyPack(pkg.Name()), modName, name) } func (mod *modContext) tokenToResource(tok string) string { @@ -304,8 +308,12 @@ func tokenToName(tok string) string { return title(components[2]) } -func tokenToModule(tok string, pkg *schema.Package, moduleNameOverrides map[string]string) string { +func tokenToModule(tok string, pkg schema.PackageReference, moduleNameOverrides map[string]string) string { // See if there's a manually-overridden module name. + if pkg == nil { + // If pkg is nil, we use the default `TokenToModule` scheme. + pkg = (&schema.Package{}).Reference() + } canonicalModName := pkg.TokenToModule(tok) if override, ok := moduleNameOverrides[canonicalModName]; ok { return override @@ -357,27 +365,52 @@ func genStandardHeader(w io.Writer, tool string) { fmt.Fprintf(w, "# *** Do not edit by hand unless you're certain you know what you are doing! ***\n\n") } +func typingImports() []string { + return []string{ + "Any", + "Mapping", + "Optional", + "Sequence", + "Union", + "overload", + } +} + +func (mod *modContext) generateCommonImports(w io.Writer, imports imports, typingImports []string) { + rel, err := filepath.Rel(mod.mod, "") + contract.Assert(err == nil) + relRoot := path.Dir(rel) + relImport := relPathToRelImport(relRoot) + + fmt.Fprintf(w, "import copy\n") + fmt.Fprintf(w, "import warnings\n") + fmt.Fprintf(w, "import pulumi\n") + fmt.Fprintf(w, "import pulumi.runtime\n") + fmt.Fprintf(w, "from typing import %s\n", strings.Join(typingImports, ", ")) + fmt.Fprintf(w, "from %s import _utilities\n", relImport) + for _, imp := range imports.strings() { + fmt.Fprintf(w, "%s\n", imp) + } + fmt.Fprintf(w, "\n") +} + func (mod *modContext) genHeader(w io.Writer, needsSDK bool, imports imports) { genStandardHeader(w, mod.tool) // If needed, emit the standard Pulumi SDK import statement. if needsSDK { - rel, err := filepath.Rel(mod.mod, "") - contract.Assert(err == nil) - relRoot := path.Dir(rel) - relImport := relPathToRelImport(relRoot) - - fmt.Fprintf(w, "import copy\n") - fmt.Fprintf(w, "import warnings\n") - fmt.Fprintf(w, "import pulumi\n") - fmt.Fprintf(w, "import pulumi.runtime\n") - fmt.Fprintf(w, "from typing import Any, Mapping, Optional, Sequence, Union, overload\n") - fmt.Fprintf(w, "from %s import _utilities\n", relImport) - for _, imp := range imports.strings() { - fmt.Fprintf(w, "%s\n", imp) - } - fmt.Fprintf(w, "\n") + typings := typingImports() + mod.generateCommonImports(w, imports, typings) + } +} + +func (mod *modContext) genFunctionHeader(w io.Writer, function *schema.Function, imports imports) { + genStandardHeader(w, mod.tool) + typings := typingImports() + if function.Outputs == nil || len(function.Outputs.Properties) == 0 { + typings = append(typings, "Awaitable") } + mod.generateCommonImports(w, imports, typings) } func relPathToRelImport(relPath string) string { @@ -397,20 +430,23 @@ func relPathToRelImport(relPath string) string { return relImport } -func (mod *modContext) genUtilitiesFile() []byte { +func (mod *modContext) genUtilitiesFile() ([]byte, error) { buffer := &bytes.Buffer{} genStandardHeader(buffer, mod.tool) fmt.Fprintf(buffer, utilitiesFile) optionalURL := "None" - if url := mod.pkg.PluginDownloadURL; url != "" { + pkg, err := mod.pkg.Definition() + if err != nil { + return nil, err + } + if url := pkg.PluginDownloadURL; url != "" { optionalURL = fmt.Sprintf("%q", url) } - _, err := fmt.Fprintf(buffer, ` + _, err = fmt.Fprintf(buffer, ` def get_plugin_download_url(): return %s `, optionalURL) - contract.AssertNoError(err) - return buffer.Bytes() + return buffer.Bytes(), err } func (mod *modContext) gen(fs codegen.Fs) error { @@ -438,28 +474,37 @@ func (mod *modContext) gen(fs codegen.Fs) error { // Utilities, config, readme switch mod.mod { case "": - fs.Add(filepath.Join(dir, "_utilities.py"), mod.genUtilitiesFile()) + utils, err := mod.genUtilitiesFile() + if err != nil { + return err + } + fs.Add(filepath.Join(dir, "_utilities.py"), utils) fs.Add(filepath.Join(dir, "py.typed"), []byte{}) // Ensure that the top-level (provider) module directory contains a README.md file. + pkg, err := mod.pkg.Definition() + if err != nil { + return err + } + var readme string - if pythonInfo, ok := mod.pkg.Language["python"]; ok { + if pythonInfo, ok := pkg.Language["python"]; ok { if typedInfo, ok := pythonInfo.(PackageInfo); ok { readme = typedInfo.Readme } } if readme == "" { - readme = mod.pkg.Description + readme = mod.pkg.Description() if readme != "" && readme[len(readme)-1] != '\n' { readme += "\n" } - if mod.pkg.Attribution != "" { + if pkg.Attribution != "" { if len(readme) != 0 { readme += "\n" } - readme += mod.pkg.Attribution + readme += pkg.Attribution } if readme != "" && readme[len(readme)-1] != '\n' { readme += "\n" @@ -468,13 +513,17 @@ func (mod *modContext) gen(fs codegen.Fs) error { fs.Add(filepath.Join(dir, "README.md"), []byte(readme)) case "config": - if len(mod.pkg.Config) > 0 { - vars, err := mod.genConfig(mod.pkg.Config) + config, err := mod.pkg.Config() + if err != nil { + return err + } + if len(config) > 0 { + vars, err := mod.genConfig(config) if err != nil { return err } addFile("vars.py", vars) - typeStubs, err := mod.genConfigStubs(mod.pkg.Config) + typeStubs, err := mod.genConfigStubs(config) if err != nil { return err } @@ -605,7 +654,7 @@ func (mod *modContext) fullyQualifiedImportName() string { return mod.pyPkgName } if mod.parent == nil { - return fmt.Sprintf("%s.%s", pyPack(mod.pkg.Name), name) + return fmt.Sprintf("%s.%s", pyPack(mod.pkg.Name()), name) } return fmt.Sprintf("%s.%s", mod.parent.fullyQualifiedImportName(), name) } @@ -717,8 +766,8 @@ func (mod *modContext) genUtilitiesImport() string { } func (mod *modContext) importObjectType(t *schema.ObjectType, input bool) string { - if t.Package != mod.pkg { - return fmt.Sprintf("import %s", pyPack(t.Package.Name)) + if !codegen.PkgEquals(t.PackageReference, mod.pkg) { + return fmt.Sprintf("import %s", pyPack(t.PackageReference.Name())) } tok := t.Token @@ -735,7 +784,7 @@ func (mod *modContext) importObjectType(t *schema.ObjectType, input bool) string } importPath := mod.getRelImportFromRoot() - if mod.pkg.Name != parts[0] { + if mod.pkg.Name() != parts[0] { importPath = fmt.Sprintf("pulumi_%s", refPkgName) } @@ -752,8 +801,8 @@ func (mod *modContext) importObjectType(t *schema.ObjectType, input bool) string } func (mod *modContext) importEnumType(e *schema.EnumType) string { - if e.Package != mod.pkg { - return fmt.Sprintf("import %s", pyPack(e.Package.Name)) + if !codegen.PkgEquals(e.PackageReference, mod.pkg) { + return fmt.Sprintf("import %s", pyPack(e.PackageReference.Name())) } modName := mod.tokenToModule(e.Token) @@ -772,8 +821,9 @@ func (mod *modContext) importEnumType(e *schema.EnumType) string { } func (mod *modContext) importResourceType(r *schema.ResourceType) string { - if r.Resource != nil && r.Resource.Package != mod.pkg { - return fmt.Sprintf("import %s", pyPack(r.Resource.Package.Name)) + + if r.Resource != nil && !codegen.PkgEquals(r.Resource.PackageReference, mod.pkg) { + return fmt.Sprintf("import %s", pyPack(r.Resource.PackageReference.Name())) } tok := r.Token @@ -790,7 +840,7 @@ func (mod *modContext) importResourceType(r *schema.ResourceType) string { modName := mod.tokenToResource(tok) importPath := mod.getRelImportFromRoot() - if mod.pkg.Name != parts[0] { + if mod.pkg.Name() != parts[0] { importPath = fmt.Sprintf("pulumi_%s", refPkgName) } @@ -820,7 +870,7 @@ func (mod *modContext) genConfig(variables []*schema.Property) (string, error) { fmt.Fprintf(w, "\n") // Create a config bag for the variables to pull from. - fmt.Fprintf(w, "__config__ = pulumi.Config('%s')\n", mod.pkg.Name) + fmt.Fprintf(w, "__config__ = pulumi.Config('%s')\n", mod.pkg.Name()) fmt.Fprintf(w, "\n\n") // To avoid a breaking change to the existing config getters, we define a class that extends @@ -1371,7 +1421,7 @@ func (mod *modContext) genResource(res *schema.Resource) (string, error) { // Finally, chain to the base constructor, which will actually register the resource. tok := res.Token if res.IsProvider { - tok = mod.pkg.Name + tok = mod.pkg.Name() } fmt.Fprintf(w, " super(%s, __self__).__init__(\n", name) fmt.Fprintf(w, " '%s',\n", tok) @@ -1677,7 +1727,7 @@ func (mod *modContext) genFunction(fun *schema.Function) (string, error) { var returnType *schema.ObjectType if fun.ReturnType != nil { - if objectType, ok := fun.ReturnType.(*schema.ObjectType); ok && objectType != nil { + if objectType, ok := fun.ReturnType.(*schema.ObjectType); ok { returnType = objectType } else { // TODO: remove when we add support for generalized return type for python @@ -1689,7 +1739,7 @@ func (mod *modContext) genFunction(fun *schema.Function) (string, error) { mod.collectImports(returnType.Properties, imports, false) } - mod.genHeader(w, true /*needsSDK*/, imports) + mod.genFunctionHeader(w, fun, imports) var baseName, awaitableName string if returnType != nil { @@ -1720,6 +1770,8 @@ func (mod *modContext) genFunction(fun *schema.Function) (string, error) { if returnType != nil { retTypeName, rets = mod.genAwaitableType(w, returnType), returnType.Properties fmt.Fprintf(w, "\n\n") + } else { + retTypeName = "Awaitable[None]" } var args []*schema.Property @@ -2663,8 +2715,8 @@ func generateModuleContextMap(tool string, pkg *schema.Package, info PackageInfo // modules map will contain modContext entries for all modules in current package (pkg) modules := map[string]*modContext{} - var getMod func(modName string, p *schema.Package) *modContext - getMod = func(modName string, p *schema.Package) *modContext { + var getMod func(modName string, p schema.PackageReference) *modContext + getMod = func(modName string, p schema.PackageReference) *modContext { mod, ok := modules[modName] if !ok { mod = &modContext{ @@ -2677,7 +2729,7 @@ func generateModuleContextMap(tool string, pkg *schema.Package, info PackageInfo liftSingleValueMethodReturns: info.LiftSingleValueMethodReturns, } - if modName != "" && p == pkg { + if modName != "" && codegen.PkgEquals(p, pkg.Reference()) { parentName := path.Dir(modName) if parentName == "." { parentName = "" @@ -2688,14 +2740,15 @@ func generateModuleContextMap(tool string, pkg *schema.Package, info PackageInfo // Save the module only if it's for the current package. // This way, modules for external packages are not saved. - if p == pkg { + + if codegen.PkgEquals(p, pkg.Reference()) { modules[modName] = mod } } return mod } - getModFromToken := func(tok string, p *schema.Package) *modContext { + getModFromToken := func(tok string, p schema.PackageReference) *modContext { modName := tokenToModule(tok, p, info.ModuleNameOverrides) return getMod(modName, p) } @@ -2703,40 +2756,40 @@ func generateModuleContextMap(tool string, pkg *schema.Package, info PackageInfo // Create the config module if necessary. if len(pkg.Config) > 0 && info.Compatibility != kubernetes20 { // k8s SDK doesn't use config. - configMod := getMod("config", pkg) + configMod := getMod("config", pkg.Reference()) configMod.isConfig = true } visitObjectTypes(pkg.Config, func(t schema.Type) { if t, ok := t.(*schema.ObjectType); ok { - getModFromToken(t.Token, t.Package).details(t).outputType = true + getModFromToken(t.Token, t.PackageReference).details(t).outputType = true } }) // Find input and output types referenced by resources. scanResource := func(r *schema.Resource) { - mod := getModFromToken(r.Token, pkg) + mod := getModFromToken(r.Token, pkg.Reference()) mod.resources = append(mod.resources, r) visitObjectTypes(r.Properties, func(t schema.Type) { switch T := t.(type) { case *schema.ObjectType: - getModFromToken(T.Token, T.Package).details(T).outputType = true - getModFromToken(T.Token, T.Package).details(T).resourceOutputType = true + getModFromToken(T.Token, T.PackageReference).details(T).outputType = true + getModFromToken(T.Token, T.PackageReference).details(T).resourceOutputType = true } }) visitObjectTypes(r.InputProperties, func(t schema.Type) { switch T := t.(type) { case *schema.ObjectType: - getModFromToken(T.Token, T.Package).details(T).inputType = true + getModFromToken(T.Token, T.PackageReference).details(T).inputType = true } }) if r.StateInputs != nil { visitObjectTypes(r.StateInputs.Properties, func(t schema.Type) { switch T := t.(type) { case *schema.ObjectType: - getModFromToken(T.Token, T.Package).details(T).inputType = true + getModFromToken(T.Token, T.PackageReference).details(T).inputType = true case *schema.ResourceType: - getModFromToken(T.Token, T.Resource.Package) + getModFromToken(T.Token, T.Resource.PackageReference) } }) } @@ -2749,7 +2802,7 @@ 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 := getModFromToken(f.Token, f.PackageReference) if !f.IsMethod { mod.functions = append(mod.functions, f) } @@ -2757,10 +2810,10 @@ func generateModuleContextMap(tool string, pkg *schema.Package, info PackageInfo visitObjectTypes(f.Inputs.Properties, func(t schema.Type) { switch T := t.(type) { case *schema.ObjectType: - getModFromToken(T.Token, T.Package).details(T).inputType = true - getModFromToken(T.Token, T.Package).details(T).plainType = true + getModFromToken(T.Token, T.PackageReference).details(T).inputType = true + getModFromToken(T.Token, T.PackageReference).details(T).plainType = true case *schema.ResourceType: - getModFromToken(T.Token, T.Resource.Package) + getModFromToken(T.Token, T.Resource.PackageReference) } }) } @@ -2776,10 +2829,10 @@ func generateModuleContextMap(tool string, pkg *schema.Package, info PackageInfo visitObjectTypes(returnType.Properties, func(t schema.Type) { switch T := t.(type) { case *schema.ObjectType: - getModFromToken(T.Token, T.Package).details(T).outputType = true - getModFromToken(T.Token, T.Package).details(T).plainType = true + getModFromToken(T.Token, T.PackageReference).details(T).outputType = true + getModFromToken(T.Token, T.PackageReference).details(T).plainType = true case *schema.ResourceType: - getModFromToken(T.Token, T.Resource.Package) + getModFromToken(T.Token, T.Resource.PackageReference) } }) } @@ -2789,14 +2842,14 @@ func generateModuleContextMap(tool string, pkg *schema.Package, info PackageInfo for _, t := range pkg.Types { switch typ := t.(type) { case *schema.ObjectType: - mod := getModFromToken(typ.Token, typ.Package) + mod := getModFromToken(typ.Token, typ.PackageReference) d := mod.details(typ) if d.inputType || d.outputType { mod.types = append(mod.types, typ) } case *schema.EnumType: if !typ.IsOverlay { - mod := getModFromToken(typ.Token, pkg) + mod := getModFromToken(typ.Token, pkg.Reference()) mod.enums = append(mod.enums, typ) } default: @@ -2815,7 +2868,7 @@ func generateModuleContextMap(tool string, pkg *schema.Package, info PackageInfo if modName == "/" || modName == "." { modName = "" } - mod := getMod(modName, pkg) + mod := getMod(modName, pkg.Reference()) mod.extraSourceFiles = append(mod.extraSourceFiles, p) } @@ -2823,11 +2876,11 @@ func generateModuleContextMap(tool string, pkg *schema.Package, info PackageInfo // modContext for every ObjectType. modLocator := &modLocator{ objectTypeMod: func(t *schema.ObjectType) *modContext { - if t.Package != pkg { + if !codegen.PkgEquals(t.PackageReference, pkg.Reference()) { return nil } - return getModFromToken(t.Token, t.Package) + return getModFromToken(t.Token, t.PackageReference) }, } diff --git a/pkg/codegen/python/gen_program.go b/pkg/codegen/python/gen_program.go index c9dfe4f8e547..f1f9531fcb92 100644 --- a/pkg/codegen/python/gen_program.go +++ b/pkg/codegen/python/gen_program.go @@ -211,9 +211,12 @@ func (g *generator) genPreamble(w io.Writer, program *pcl.Program, preambleHelpe continue } packageName := "pulumi_" + makeValidIdentifier(pkg) - if r.Schema != nil && r.Schema.Package != nil { - if info, ok := r.Schema.Package.Language["python"].(PackageInfo); ok && info.PackageName != "" { - packageName = info.PackageName + if r.Schema != nil && r.Schema.PackageReference != nil { + pkg, err := r.Schema.PackageReference.Definition() + if err == nil { + if info, ok := pkg.Language["python"].(PackageInfo); ok && info.PackageName != "" { + packageName = info.PackageName + } } } importSet[packageName] = Import{ImportAs: true, Pkg: makeValidIdentifier(pkg)} @@ -306,13 +309,22 @@ func resourceTypeName(r *pcl.Resource) (string, hcl.Diagnostics) { // Normalize module. if r.Schema != nil { - pkg := r.Schema.Package - err := pkg.ImportLanguages(map[string]schema.Language{"python": Importer}) - contract.AssertNoError(err) - if lang, ok := pkg.Language["python"]; ok { - pkgInfo := lang.(PackageInfo) - if m, ok := pkgInfo.ModuleNameOverrides[module]; ok { - module = m + pkg, err := r.Schema.PackageReference.Definition() + if err != nil { + diagnostics = append(diagnostics, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "unable to bind schema for resource", + Detail: err.Error(), + Subject: r.Definition.Syntax.DefRange().Ptr(), + }) + } else { + err = pkg.ImportLanguages(map[string]schema.Language{"python": Importer}) + contract.AssertNoError(err) + if lang, ok := pkg.Language["python"]; ok { + pkgInfo := lang.(PackageInfo) + if m, ok := pkgInfo.ModuleNameOverrides[module]; ok { + module = m + } } } } @@ -341,10 +353,11 @@ func (g *generator) argumentTypeName(expr model.Expression, destType model.Type) pkgName, module, member, diagnostics := pcl.DecomposeToken(token, tokenRange) contract.Assert(len(diagnostics) == 0) - modName := objType.Package.TokenToModule(token) + modName := objType.PackageReference.TokenToModule(token) // Normalize module. - pkg := objType.Package + pkg, err := objType.PackageReference.Definition() + contract.AssertNoError(err) if lang, ok := pkg.Language["python"]; ok { pkgInfo := lang.(PackageInfo) if m, ok := pkgInfo.ModuleNameOverrides[module]; ok { diff --git a/pkg/codegen/python/gen_program_expressions.go b/pkg/codegen/python/gen_program_expressions.go index cd23aaf984fd..dd9befc70845 100644 --- a/pkg/codegen/python/gen_program_expressions.go +++ b/pkg/codegen/python/gen_program_expressions.go @@ -237,9 +237,12 @@ func (g *generator) GenFunctionCallExpression(w io.Writer, expr *model.FunctionC g.Fgenf(w, "%.v", expr.Args[0]) return } - moduleNameOverrides := enum.(*schema.EnumType).Package.Language["python"].(PackageInfo).ModuleNameOverrides + var moduleNameOverrides map[string]string + if pkg, err := enum.(*schema.EnumType).PackageReference.Definition(); err == nil { + moduleNameOverrides = pkg.Language["python"].(PackageInfo).ModuleNameOverrides + } pkg := strings.ReplaceAll(components[0], "-", "_") - if m := tokenToModule(to.Token, &schema.Package{}, moduleNameOverrides); m != "" { + if m := tokenToModule(to.Token, nil, moduleNameOverrides); m != "" { pkg += "." + m } enumName := tokenToName(to.Token) diff --git a/pkg/codegen/python/gen_resource_mappings.go b/pkg/codegen/python/gen_resource_mappings.go index 61758de18c2e..b879734af938 100644 --- a/pkg/codegen/python/gen_resource_mappings.go +++ b/pkg/codegen/python/gen_resource_mappings.go @@ -19,6 +19,8 @@ import ( "fmt" "io" "sort" + + "github.com/pulumi/pulumi/pkg/v3/codegen/schema" ) // Generates code to build and regsiter ResourceModule and @@ -109,8 +111,8 @@ func collectResourceModuleInfos(mctx *modContext) []resourceModuleInfo { } if !res.IsProvider { - pkg := mctx.pkg.Name - mod := mctx.pkg.TokenToRuntimeModule(res.Token) + pkg := mctx.pkg.Name() + mod := schema.TokenToRuntimeModule(res.Token) fqn := mctx.fullyQualifiedImportName() rmi, found := byMod[mod] @@ -169,7 +171,7 @@ func collectResourcePackageInfos(mctx *modContext) []resourcePackageInfo { } if res.IsProvider { - pkg := mctx.pkg.Name + pkg := mctx.pkg.Name() token := res.Token fqn := mctx.fullyQualifiedImportName() class := "Provider" diff --git a/pkg/codegen/schema/bind.go b/pkg/codegen/schema/bind.go index 2916c15cf3b8..a171bad42ba9 100644 --- a/pkg/codegen/schema/bind.go +++ b/pkg/codegen/schema/bind.go @@ -1579,6 +1579,7 @@ func (t *types) bindFunctionDef(token string) (*Function, hcl.Diagnostics, error fn := &Function{ Package: t.pkg, + PackageReference: t.externalPackage(), Token: token, Comment: spec.Description, Inputs: inputs, diff --git a/pkg/codegen/schema/schema.go b/pkg/codegen/schema/schema.go index 4a09210d076a..b61fb1ae053c 100644 --- a/pkg/codegen/schema/schema.go +++ b/pkg/codegen/schema/schema.go @@ -530,8 +530,11 @@ type Method struct { // Function describes a Pulumi function. type Function struct { - // Package is the package that defines the function. + // Package is the package that defines the function. Package will not be accurate for + // types loaded by reference. In that case, use PackageReference instead. Package *Package + // PackageReference is the PackageReference that defines the function. + PackageReference PackageReference // Token is the function's Pulumi type token. Token string // Comment is the description of the function, if any. @@ -904,7 +907,7 @@ func (pkg *Package) TokenToModule(tok string) string { } } -func (pkg *Package) TokenToRuntimeModule(tok string) string { +func TokenToRuntimeModule(tok string) string { // token := pkg ":" module ":" member components := strings.Split(tok, ":") @@ -914,6 +917,10 @@ func (pkg *Package) TokenToRuntimeModule(tok string) string { return components[1] } +func (pkg *Package) TokenToRuntimeModule(tok string) string { + return TokenToRuntimeModule(tok) +} + func (pkg *Package) GetResource(token string) (*Resource, bool) { r, ok := pkg.resourceTable[token] return r, ok diff --git a/pkg/codegen/testing/test/program_driver.go b/pkg/codegen/testing/test/program_driver.go index 54125c3f5032..bd9a8b5660c3 100644 --- a/pkg/codegen/testing/test/program_driver.go +++ b/pkg/codegen/testing/test/program_driver.go @@ -255,7 +255,6 @@ var PulumiPulumiYAMLProgramTests = []ProgramTest{ Directory: transpiled("azure-app-service"), Description: "Azure App Service", Skip: codegen.NewStringSet("go", "dotnet"), - BindOptions: []pcl.BindOption{pcl.SkipResourceTypechecking}, }, { Directory: transpiled("azure-container-apps"), diff --git a/pkg/codegen/testing/test/sdk_driver.go b/pkg/codegen/testing/test/sdk_driver.go index 33951e8d0ec0..af8987716ff3 100644 --- a/pkg/codegen/testing/test/sdk_driver.go +++ b/pkg/codegen/testing/test/sdk_driver.go @@ -84,7 +84,7 @@ var allLanguages = codegen.NewStringSet("python/any", "nodejs/any", "dotnet/any" var PulumiPulumiSDKTests = []*SDKTest{ { Directory: "naming-collisions", - Description: "Schema with types that could potentially produce collisions (go).", + Description: "Schema with types that could potentially produce collisions.", }, { Directory: "dash-named-schema", diff --git a/pkg/codegen/testing/test/testdata/azure-native-nested-types/docs/_index.md b/pkg/codegen/testing/test/testdata/azure-native-nested-types/docs/_index.md index 60a15484b671..4f34b3890b86 100644 --- a/pkg/codegen/testing/test/testdata/azure-native-nested-types/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/azure-native-nested-types/docs/_index.md @@ -18,7 +18,7 @@ A native Pulumi package for creating and managing Azure resources.

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/azure-native-nested-types/docs/documentdb/_index.md b/pkg/codegen/testing/test/testdata/azure-native-nested-types/docs/documentdb/_index.md index d5d1cea18210..0a32a2a4c23d 100644 --- a/pkg/codegen/testing/test/testdata/azure-native-nested-types/docs/documentdb/_index.md +++ b/pkg/codegen/testing/test/testdata/azure-native-nested-types/docs/documentdb/_index.md @@ -13,7 +13,7 @@ Explore the resources and functions of the azure-native.documentdb module.

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/cyclic-types/docs/_index.md b/pkg/codegen/testing/test/testdata/cyclic-types/docs/_index.md index dd1672a8feab..80f112d1c313 100644 --- a/pkg/codegen/testing/test/testdata/cyclic-types/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/cyclic-types/docs/_index.md @@ -13,7 +13,7 @@ no_edit_this_page: true

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/dash-named-schema/docs/_index.md b/pkg/codegen/testing/test/testdata/dash-named-schema/docs/_index.md index 01e7507bd903..b4b57bd9c2bf 100644 --- a/pkg/codegen/testing/test/testdata/dash-named-schema/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/dash-named-schema/docs/_index.md @@ -18,7 +18,7 @@ no_edit_this_page: true

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/dash-named-schema/docs/submodule1/_index.md b/pkg/codegen/testing/test/testdata/dash-named-schema/docs/submodule1/_index.md index e6d6d5c115c8..c8cb736cd0bd 100644 --- a/pkg/codegen/testing/test/testdata/dash-named-schema/docs/submodule1/_index.md +++ b/pkg/codegen/testing/test/testdata/dash-named-schema/docs/submodule1/_index.md @@ -13,8 +13,8 @@ Explore the resources and functions of the foo-bar.submodule1 module.

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/dashed-import-schema/docs/_index.md b/pkg/codegen/testing/test/testdata/dashed-import-schema/docs/_index.md index 2a20f7e4e1e9..fe858ba6562a 100644 --- a/pkg/codegen/testing/test/testdata/dashed-import-schema/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/dashed-import-schema/docs/_index.md @@ -18,7 +18,7 @@ no_edit_this_page: true

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/dashed-import-schema/docs/tree/v1/_index.md b/pkg/codegen/testing/test/testdata/dashed-import-schema/docs/tree/v1/_index.md index 1a7e0192b0db..681bbdb8d26c 100644 --- a/pkg/codegen/testing/test/testdata/dashed-import-schema/docs/tree/v1/_index.md +++ b/pkg/codegen/testing/test/testdata/dashed-import-schema/docs/tree/v1/_index.md @@ -13,8 +13,8 @@ Explore the resources and functions of the plant.tree/v1 module.

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/different-enum/docs/_index.md b/pkg/codegen/testing/test/testdata/different-enum/docs/_index.md index 2a20f7e4e1e9..fe858ba6562a 100644 --- a/pkg/codegen/testing/test/testdata/different-enum/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/different-enum/docs/_index.md @@ -18,7 +18,7 @@ no_edit_this_page: true

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/different-enum/docs/tree/v1/_index.md b/pkg/codegen/testing/test/testdata/different-enum/docs/tree/v1/_index.md index 1a7e0192b0db..681bbdb8d26c 100644 --- a/pkg/codegen/testing/test/testdata/different-enum/docs/tree/v1/_index.md +++ b/pkg/codegen/testing/test/testdata/different-enum/docs/tree/v1/_index.md @@ -13,8 +13,8 @@ Explore the resources and functions of the plant.tree/v1 module.

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/enum-reference/docs/_index.md b/pkg/codegen/testing/test/testdata/enum-reference/docs/_index.md index edd463473d43..6b7d5642153f 100644 --- a/pkg/codegen/testing/test/testdata/enum-reference/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/enum-reference/docs/_index.md @@ -18,7 +18,7 @@ no_edit_this_page: true

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/enum-reference/docs/myModule/_index.md b/pkg/codegen/testing/test/testdata/enum-reference/docs/myModule/_index.md index ca67b4e655a9..61a202d702d9 100644 --- a/pkg/codegen/testing/test/testdata/enum-reference/docs/myModule/_index.md +++ b/pkg/codegen/testing/test/testdata/enum-reference/docs/myModule/_index.md @@ -13,7 +13,7 @@ Explore the resources and functions of the example.myModule module.

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/external-enum/docs/_index.md b/pkg/codegen/testing/test/testdata/external-enum/docs/_index.md index 2e538a3da820..7f4e098c74af 100644 --- a/pkg/codegen/testing/test/testdata/external-enum/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/external-enum/docs/_index.md @@ -13,8 +13,8 @@ no_edit_this_page: true

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/external-resource-schema/docs/_index.md b/pkg/codegen/testing/test/testdata/external-resource-schema/docs/_index.md index b6717385592e..e7957fa81c8f 100644 --- a/pkg/codegen/testing/test/testdata/external-resource-schema/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/external-resource-schema/docs/_index.md @@ -13,15 +13,15 @@ no_edit_this_page: true

Resources

Functions

Package Details

diff --git a/pkg/codegen/testing/test/testdata/functions-secrets/docs/_index.md b/pkg/codegen/testing/test/testdata/functions-secrets/docs/_index.md index ae05ca39eeea..d365d1835157 100644 --- a/pkg/codegen/testing/test/testdata/functions-secrets/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/functions-secrets/docs/_index.md @@ -13,12 +13,12 @@ no_edit_this_page: true

Resources

Functions

Package Details

diff --git a/pkg/codegen/testing/test/testdata/hyphen-url/docs/_index.md b/pkg/codegen/testing/test/testdata/hyphen-url/docs/_index.md index 0ac4b178b9a0..5fe0c9378e85 100644 --- a/pkg/codegen/testing/test/testdata/hyphen-url/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/hyphen-url/docs/_index.md @@ -13,8 +13,8 @@ no_edit_this_page: true

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/docs/_index.md b/pkg/codegen/testing/test/testdata/naming-collisions/docs/_index.md index 91ec072b77e0..1805a60c7b23 100644 --- a/pkg/codegen/testing/test/testdata/naming-collisions/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/naming-collisions/docs/_index.md @@ -1,6 +1,6 @@ --- title: "example" -title_tag: "example.example" +title_tag: "example Package" meta_desc: "" layout: api no_edit_this_page: true @@ -11,11 +11,17 @@ no_edit_this_page: true +

Modules

+ +

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/docs/codegen-manifest.json b/pkg/codegen/testing/test/testdata/naming-collisions/docs/codegen-manifest.json index 8f38aed747b4..06272297720a 100644 --- a/pkg/codegen/testing/test/testdata/naming-collisions/docs/codegen-manifest.json +++ b/pkg/codegen/testing/test/testdata/naming-collisions/docs/codegen-manifest.json @@ -1,6 +1,10 @@ { "emittedFiles": [ "_index.md", + "maincomponent/_index.md", + "mod/_index.md", + "mod/component/_index.md", + "mod/component2/_index.md", "provider/_index.md", "resource/_index.md", "resourceinput/_index.md" diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/docs/maincomponent/_index.md b/pkg/codegen/testing/test/testdata/naming-collisions/docs/maincomponent/_index.md new file mode 100644 index 000000000000..3db224a80cc0 --- /dev/null +++ b/pkg/codegen/testing/test/testdata/naming-collisions/docs/maincomponent/_index.md @@ -0,0 +1,366 @@ + +--- +title: "MainComponent" +title_tag: "example.MainComponent" +meta_desc: "Documentation for the example.MainComponent resource with examples, input properties, output properties, lookup functions, and supporting types." +layout: api +no_edit_this_page: true +--- + + + + + + + + + +## Create MainComponent Resource {#create} +
+ +
+ + +
+ +
new MainComponent(name: string, args?: MainComponentArgs, opts?: CustomResourceOptions);
+
+
+ +
+ +
@overload
+def MainComponent(resource_name: str,
+                  opts: Optional[ResourceOptions] = None)
+@overload
+def MainComponent(resource_name: str,
+                  args: Optional[MainComponentArgs] = None,
+                  opts: Optional[ResourceOptions] = None)
+
+
+ +
+ +
func NewMainComponent(ctx *Context, name string, args *MainComponentArgs, opts ...ResourceOption) (*MainComponent, error)
+
+
+ +
+ +
public MainComponent(string name, MainComponentArgs? args = null, CustomResourceOptions? opts = null)
+
+
+ +
+ +
+public MainComponent(String name, MainComponentArgs args)
+public MainComponent(String name, MainComponentArgs args, CustomResourceOptions options)
+
+
+
+ +
+ +
type: example:MainComponent
+properties: # The arguments to resource properties.
+options: # Bag of options to control resource's behavior.
+
+
+
+
+ +
+ + +
+ name + + string +
+
The unique name of the resource.
+ args + + MainComponentArgs +
+
The arguments to resource properties.
+ opts + + CustomResourceOptions +
+
Bag of options to control resource's behavior.
+ +
+
+ +
+ + +
+ resource_name + + str +
+
The unique name of the resource.
+ args + + MainComponentArgs +
+
The arguments to resource properties.
+ opts + + ResourceOptions +
+
Bag of options to control resource's behavior.
+ +
+
+ +
+ + +
+ ctx + + Context +
+
Context object for the current deployment.
+ name + + string +
+
The unique name of the resource.
+ args + + MainComponentArgs +
+
The arguments to resource properties.
+ opts + + ResourceOption +
+
Bag of options to control resource's behavior.
+ +
+
+ +
+ + +
+ name + + string +
+
The unique name of the resource.
+ args + + MainComponentArgs +
+
The arguments to resource properties.
+ opts + + CustomResourceOptions +
+
Bag of options to control resource's behavior.
+ +
+
+ +
+ + +
+ name + + String +
+
The unique name of the resource.
+ args + + MainComponentArgs +
+
The arguments to resource properties.
+ options + + CustomResourceOptions +
+
Bag of options to control resource's behavior.
+ +
+
+ +## MainComponent Resource Properties {#properties} + +To learn more about resource properties and how to use them, see [Inputs and Outputs](/docs/intro/concepts/inputs-outputs) in the Architecture and Concepts docs. + +### Inputs + +The MainComponent resource accepts the following [input](/docs/intro/concepts/inputs-outputs) properties: + + + +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ + +### Outputs + +All [input](#inputs) properties are implicitly available as output properties. Additionally, the MainComponent resource produces the following output properties: + + + +
+ +
+ +Id + + + string +
+

The provider-assigned unique ID for this managed resource.

+
+
+
+ +
+ +
+ +Id + + + string +
+

The provider-assigned unique ID for this managed resource.

+
+
+
+ +
+ +
+ +id + + + String +
+

The provider-assigned unique ID for this managed resource.

+
+
+
+ +
+ +
+ +id + + + string +
+

The provider-assigned unique ID for this managed resource.

+
+
+
+ +
+ +
+ +id + + + str +
+

The provider-assigned unique ID for this managed resource.

+
+
+
+ +
+ +
+ +id + + + String +
+

The provider-assigned unique ID for this managed resource.

+
+
+
+ + + + + + + + +

Package Details

+
+
Repository
+
+
License
+
+
+ diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/docs/mod/_index.md b/pkg/codegen/testing/test/testdata/naming-collisions/docs/mod/_index.md new file mode 100644 index 000000000000..8538946db9ea --- /dev/null +++ b/pkg/codegen/testing/test/testdata/naming-collisions/docs/mod/_index.md @@ -0,0 +1,29 @@ +--- +title: "mod" +title_tag: "example.mod" +meta_desc: "Explore the resources and functions of the example.mod module." +layout: api +no_edit_this_page: true +--- + + + + +Explore the resources and functions of the example.mod module. + +

Resources

+ + +

Package Details

+
+
Repository
+
+
License
+
+
Version
+
0.0.1
+
+ diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/docs/mod/component/_index.md b/pkg/codegen/testing/test/testdata/naming-collisions/docs/mod/component/_index.md new file mode 100644 index 000000000000..83affe1480d4 --- /dev/null +++ b/pkg/codegen/testing/test/testdata/naming-collisions/docs/mod/component/_index.md @@ -0,0 +1,464 @@ + +--- +title: "Component" +title_tag: "example.mod.Component" +meta_desc: "Documentation for the example.mod.Component resource with examples, input properties, output properties, lookup functions, and supporting types." +layout: api +no_edit_this_page: true +--- + + + + + + + + + +## Create Component Resource {#create} +
+ +
+ + +
+ +
new Component(name: string, args?: ComponentArgs, opts?: CustomResourceOptions);
+
+
+ +
+ +
@overload
+def Component(resource_name: str,
+              opts: Optional[ResourceOptions] = None,
+              local: Optional[Component2] = None,
+              main: Optional[MainComponent] = None)
+@overload
+def Component(resource_name: str,
+              args: Optional[ComponentArgs] = None,
+              opts: Optional[ResourceOptions] = None)
+
+
+ +
+ +
func NewComponent(ctx *Context, name string, args *ComponentArgs, opts ...ResourceOption) (*Component, error)
+
+
+ +
+ +
public Component(string name, ComponentArgs? args = null, CustomResourceOptions? opts = null)
+
+
+ +
+ +
+public Component(String name, ComponentArgs args)
+public Component(String name, ComponentArgs args, CustomResourceOptions options)
+
+
+
+ +
+ +
type: example:mod:Component
+properties: # The arguments to resource properties.
+options: # Bag of options to control resource's behavior.
+
+
+
+
+ +
+ + +
+ name + + string +
+
The unique name of the resource.
+ args + + ComponentArgs +
+
The arguments to resource properties.
+ opts + + CustomResourceOptions +
+
Bag of options to control resource's behavior.
+ +
+
+ +
+ + +
+ resource_name + + str +
+
The unique name of the resource.
+ args + + ComponentArgs +
+
The arguments to resource properties.
+ opts + + ResourceOptions +
+
Bag of options to control resource's behavior.
+ +
+
+ +
+ + +
+ ctx + + Context +
+
Context object for the current deployment.
+ name + + string +
+
The unique name of the resource.
+ args + + ComponentArgs +
+
The arguments to resource properties.
+ opts + + ResourceOption +
+
Bag of options to control resource's behavior.
+ +
+
+ +
+ + +
+ name + + string +
+
The unique name of the resource.
+ args + + ComponentArgs +
+
The arguments to resource properties.
+ opts + + CustomResourceOptions +
+
Bag of options to control resource's behavior.
+ +
+
+ +
+ + +
+ name + + String +
+
The unique name of the resource.
+ args + + ComponentArgs +
+
The arguments to resource properties.
+ options + + CustomResourceOptions +
+
Bag of options to control resource's behavior.
+ +
+
+ +## Component Resource Properties {#properties} + +To learn more about resource properties and how to use them, see [Inputs and Outputs](/docs/intro/concepts/inputs-outputs) in the Architecture and Concepts docs. + +### Inputs + +The Component resource accepts the following [input](/docs/intro/concepts/inputs-outputs) properties: + + + +
+ +
+ +Local + + + Pulumi.Example.Mod.Component2 +
+
+ +Main + + + Pulumi.Example.MainComponent +
+
+
+
+ +
+ +
+ +Local + + + Component2 +
+
+ +Main + + + MainComponent +
+
+
+
+ +
+ +
+ +local + + + Component2 +
+
+ +main + + + MainComponent +
+
+
+
+ +
+ +
+ +local + + + Component2 +
+
+ +main + + + MainComponent +
+
+
+
+ +
+ +
+ +local + + + Component2 +
+
+ +main + + + MainComponent +
+
+
+
+ +
+ +
+ +local + + + example::Component2 +
+
+ +main + + + example:MainComponent +
+
+
+
+ + +### Outputs + +All [input](#inputs) properties are implicitly available as output properties. Additionally, the Component resource produces the following output properties: + + + +
+ +
+ +Id + + + string +
+

The provider-assigned unique ID for this managed resource.

+
+
+
+ +
+ +
+ +Id + + + string +
+

The provider-assigned unique ID for this managed resource.

+
+
+
+ +
+ +
+ +id + + + String +
+

The provider-assigned unique ID for this managed resource.

+
+
+
+ +
+ +
+ +id + + + string +
+

The provider-assigned unique ID for this managed resource.

+
+
+
+ +
+ +
+ +id + + + str +
+

The provider-assigned unique ID for this managed resource.

+
+
+
+ +
+ +
+ +id + + + String +
+

The provider-assigned unique ID for this managed resource.

+
+
+
+ + + + + + + + +

Package Details

+
+
Repository
+
+
License
+
+
+ diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/docs/mod/component2/_index.md b/pkg/codegen/testing/test/testdata/naming-collisions/docs/mod/component2/_index.md new file mode 100644 index 000000000000..dc6ff8de1e9f --- /dev/null +++ b/pkg/codegen/testing/test/testdata/naming-collisions/docs/mod/component2/_index.md @@ -0,0 +1,366 @@ + +--- +title: "Component2" +title_tag: "example.mod.Component2" +meta_desc: "Documentation for the example.mod.Component2 resource with examples, input properties, output properties, lookup functions, and supporting types." +layout: api +no_edit_this_page: true +--- + + + + + + + + + +## Create Component2 Resource {#create} +
+ +
+ + +
+ +
new Component2(name: string, args?: Component2Args, opts?: CustomResourceOptions);
+
+
+ +
+ +
@overload
+def Component2(resource_name: str,
+               opts: Optional[ResourceOptions] = None)
+@overload
+def Component2(resource_name: str,
+               args: Optional[Component2Args] = None,
+               opts: Optional[ResourceOptions] = None)
+
+
+ +
+ +
func NewComponent2(ctx *Context, name string, args *Component2Args, opts ...ResourceOption) (*Component2, error)
+
+
+ +
+ +
public Component2(string name, Component2Args? args = null, CustomResourceOptions? opts = null)
+
+
+ +
+ +
+public Component2(String name, Component2Args args)
+public Component2(String name, Component2Args args, CustomResourceOptions options)
+
+
+
+ +
+ +
type: example:mod:Component2
+properties: # The arguments to resource properties.
+options: # Bag of options to control resource's behavior.
+
+
+
+
+ +
+ + +
+ name + + string +
+
The unique name of the resource.
+ args + + Component2Args +
+
The arguments to resource properties.
+ opts + + CustomResourceOptions +
+
Bag of options to control resource's behavior.
+ +
+
+ +
+ + +
+ resource_name + + str +
+
The unique name of the resource.
+ args + + Component2Args +
+
The arguments to resource properties.
+ opts + + ResourceOptions +
+
Bag of options to control resource's behavior.
+ +
+
+ +
+ + +
+ ctx + + Context +
+
Context object for the current deployment.
+ name + + string +
+
The unique name of the resource.
+ args + + Component2Args +
+
The arguments to resource properties.
+ opts + + ResourceOption +
+
Bag of options to control resource's behavior.
+ +
+
+ +
+ + +
+ name + + string +
+
The unique name of the resource.
+ args + + Component2Args +
+
The arguments to resource properties.
+ opts + + CustomResourceOptions +
+
Bag of options to control resource's behavior.
+ +
+
+ +
+ + +
+ name + + String +
+
The unique name of the resource.
+ args + + Component2Args +
+
The arguments to resource properties.
+ options + + CustomResourceOptions +
+
Bag of options to control resource's behavior.
+ +
+
+ +## Component2 Resource Properties {#properties} + +To learn more about resource properties and how to use them, see [Inputs and Outputs](/docs/intro/concepts/inputs-outputs) in the Architecture and Concepts docs. + +### Inputs + +The Component2 resource accepts the following [input](/docs/intro/concepts/inputs-outputs) properties: + + + +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ + +### Outputs + +All [input](#inputs) properties are implicitly available as output properties. Additionally, the Component2 resource produces the following output properties: + + + +
+ +
+ +Id + + + string +
+

The provider-assigned unique ID for this managed resource.

+
+
+
+ +
+ +
+ +Id + + + string +
+

The provider-assigned unique ID for this managed resource.

+
+
+
+ +
+ +
+ +id + + + String +
+

The provider-assigned unique ID for this managed resource.

+
+
+
+ +
+ +
+ +id + + + string +
+

The provider-assigned unique ID for this managed resource.

+
+
+
+ +
+ +
+ +id + + + str +
+

The provider-assigned unique ID for this managed resource.

+
+
+
+ +
+ +
+ +id + + + String +
+

The provider-assigned unique ID for this managed resource.

+
+
+
+ + + + + + + + +

Package Details

+
+
Repository
+
+
License
+
+
+ diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/dotnet/MainComponent.cs b/pkg/codegen/testing/test/testdata/naming-collisions/dotnet/MainComponent.cs new file mode 100644 index 000000000000..3e8cfe27fec9 --- /dev/null +++ b/pkg/codegen/testing/test/testdata/naming-collisions/dotnet/MainComponent.cs @@ -0,0 +1,64 @@ +// *** WARNING: this file was generated by test. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Pulumi.Serialization; + +namespace Pulumi.Example +{ + [ExampleResourceType("example::MainComponent")] + public partial class MainComponent : global::Pulumi.CustomResource + { + /// + /// Create a MainComponent resource with the given unique name, arguments, and options. + /// + /// + /// The unique name of the resource + /// The arguments used to populate this resource's properties + /// A bag of options that control this resource's behavior + public MainComponent(string name, MainComponentArgs? args = null, CustomResourceOptions? options = null) + : base("example::MainComponent", name, args ?? new MainComponentArgs(), MakeResourceOptions(options, "")) + { + } + + private MainComponent(string name, Input id, CustomResourceOptions? options = null) + : base("example::MainComponent", name, null, MakeResourceOptions(options, id)) + { + } + + private static CustomResourceOptions MakeResourceOptions(CustomResourceOptions? options, Input? id) + { + var defaultOptions = new CustomResourceOptions + { + Version = Utilities.Version, + }; + var merged = CustomResourceOptions.Merge(defaultOptions, options); + // Override the ID if one was specified for consistency with other language SDKs. + merged.Id = id ?? merged.Id; + return merged; + } + /// + /// Get an existing MainComponent resource's state with the given name, ID, and optional extra + /// properties used to qualify the lookup. + /// + /// + /// The unique name of the resulting resource. + /// The unique provider ID of the resource to lookup. + /// A bag of options that control this resource's behavior + public static MainComponent Get(string name, Input id, CustomResourceOptions? options = null) + { + return new MainComponent(name, id, options); + } + } + + public sealed class MainComponentArgs : global::Pulumi.ResourceArgs + { + public MainComponentArgs() + { + } + public static new MainComponentArgs Empty => new MainComponentArgs(); + } +} diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/dotnet/Mod/Component.cs b/pkg/codegen/testing/test/testdata/naming-collisions/dotnet/Mod/Component.cs new file mode 100644 index 000000000000..cbc5b0458082 --- /dev/null +++ b/pkg/codegen/testing/test/testdata/naming-collisions/dotnet/Mod/Component.cs @@ -0,0 +1,70 @@ +// *** WARNING: this file was generated by test. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Pulumi.Serialization; + +namespace Pulumi.Example.Mod +{ + [ExampleResourceType("example:mod:Component")] + public partial class Component : global::Pulumi.CustomResource + { + /// + /// Create a Component resource with the given unique name, arguments, and options. + /// + /// + /// The unique name of the resource + /// The arguments used to populate this resource's properties + /// A bag of options that control this resource's behavior + public Component(string name, ComponentArgs? args = null, CustomResourceOptions? options = null) + : base("example:mod:Component", name, args ?? new ComponentArgs(), MakeResourceOptions(options, "")) + { + } + + private Component(string name, Input id, CustomResourceOptions? options = null) + : base("example:mod:Component", name, null, MakeResourceOptions(options, id)) + { + } + + private static CustomResourceOptions MakeResourceOptions(CustomResourceOptions? options, Input? id) + { + var defaultOptions = new CustomResourceOptions + { + Version = Utilities.Version, + }; + var merged = CustomResourceOptions.Merge(defaultOptions, options); + // Override the ID if one was specified for consistency with other language SDKs. + merged.Id = id ?? merged.Id; + return merged; + } + /// + /// Get an existing Component resource's state with the given name, ID, and optional extra + /// properties used to qualify the lookup. + /// + /// + /// The unique name of the resulting resource. + /// The unique provider ID of the resource to lookup. + /// A bag of options that control this resource's behavior + public static Component Get(string name, Input id, CustomResourceOptions? options = null) + { + return new Component(name, id, options); + } + } + + public sealed class ComponentArgs : global::Pulumi.ResourceArgs + { + [Input("local")] + public Input? Local { get; set; } + + [Input("main")] + public Input? Main { get; set; } + + public ComponentArgs() + { + } + public static new ComponentArgs Empty => new ComponentArgs(); + } +} diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/dotnet/Mod/Component2.cs b/pkg/codegen/testing/test/testdata/naming-collisions/dotnet/Mod/Component2.cs new file mode 100644 index 000000000000..9eeb2ea30909 --- /dev/null +++ b/pkg/codegen/testing/test/testdata/naming-collisions/dotnet/Mod/Component2.cs @@ -0,0 +1,64 @@ +// *** WARNING: this file was generated by test. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Pulumi.Serialization; + +namespace Pulumi.Example.Mod +{ + [ExampleResourceType("example:mod:Component2")] + public partial class Component2 : global::Pulumi.CustomResource + { + /// + /// Create a Component2 resource with the given unique name, arguments, and options. + /// + /// + /// The unique name of the resource + /// The arguments used to populate this resource's properties + /// A bag of options that control this resource's behavior + public Component2(string name, Component2Args? args = null, CustomResourceOptions? options = null) + : base("example:mod:Component2", name, args ?? new Component2Args(), MakeResourceOptions(options, "")) + { + } + + private Component2(string name, Input id, CustomResourceOptions? options = null) + : base("example:mod:Component2", name, null, MakeResourceOptions(options, id)) + { + } + + private static CustomResourceOptions MakeResourceOptions(CustomResourceOptions? options, Input? id) + { + var defaultOptions = new CustomResourceOptions + { + Version = Utilities.Version, + }; + var merged = CustomResourceOptions.Merge(defaultOptions, options); + // Override the ID if one was specified for consistency with other language SDKs. + merged.Id = id ?? merged.Id; + return merged; + } + /// + /// Get an existing Component2 resource's state with the given name, ID, and optional extra + /// properties used to qualify the lookup. + /// + /// + /// The unique name of the resulting resource. + /// The unique provider ID of the resource to lookup. + /// A bag of options that control this resource's behavior + public static Component2 Get(string name, Input id, CustomResourceOptions? options = null) + { + return new Component2(name, id, options); + } + } + + public sealed class Component2Args : global::Pulumi.ResourceArgs + { + public Component2Args() + { + } + public static new Component2Args Empty => new Component2Args(); + } +} diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/dotnet/Mod/README.md b/pkg/codegen/testing/test/testdata/naming-collisions/dotnet/Mod/README.md new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/dotnet/codegen-manifest.json b/pkg/codegen/testing/test/testdata/naming-collisions/dotnet/codegen-manifest.json index 1518ba188758..0a5b5b680de2 100644 --- a/pkg/codegen/testing/test/testdata/naming-collisions/dotnet/codegen-manifest.json +++ b/pkg/codegen/testing/test/testdata/naming-collisions/dotnet/codegen-manifest.json @@ -1,6 +1,10 @@ { "emittedFiles": [ "Enums.cs", + "MainComponent.cs", + "Mod/Component.cs", + "Mod/Component2.cs", + "Mod/README.md", "Provider.cs", "Pulumi.Example.csproj", "README.md", diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/go/codegen-manifest.json b/pkg/codegen/testing/test/testdata/naming-collisions/go/codegen-manifest.json index 265b5ccfe435..b92de6f0e9ab 100644 --- a/pkg/codegen/testing/test/testdata/naming-collisions/go/codegen-manifest.json +++ b/pkg/codegen/testing/test/testdata/naming-collisions/go/codegen-manifest.json @@ -2,6 +2,10 @@ "emittedFiles": [ "example/doc.go", "example/init.go", + "example/mainComponent.go", + "example/mod/component.go", + "example/mod/component2.go", + "example/mod/init.go", "example/provider.go", "example/pulumi-plugin.json", "example/pulumiEnums.go", diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/go/example/init.go b/pkg/codegen/testing/test/testdata/naming-collisions/go/example/init.go index 718b035d0a1f..f2c78c82eb99 100644 --- a/pkg/codegen/testing/test/testdata/naming-collisions/go/example/init.go +++ b/pkg/codegen/testing/test/testdata/naming-collisions/go/example/init.go @@ -20,6 +20,8 @@ func (m *module) Version() semver.Version { func (m *module) Construct(ctx *pulumi.Context, name, typ, urn string) (r pulumi.Resource, err error) { switch typ { + case "example::MainComponent": + r = &MainComponent{} case "example::Resource": r = &Resource{} case "example::ResourceInput": diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/go/example/mainComponent.go b/pkg/codegen/testing/test/testdata/naming-collisions/go/example/mainComponent.go new file mode 100644 index 000000000000..08092beb6752 --- /dev/null +++ b/pkg/codegen/testing/test/testdata/naming-collisions/go/example/mainComponent.go @@ -0,0 +1,102 @@ +// Code generated by test DO NOT EDIT. +// *** WARNING: Do not edit by hand unless you're certain you know what you are doing! *** + +package example + +import ( + "context" + "reflect" + + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +type MainComponent struct { + pulumi.CustomResourceState +} + +// NewMainComponent registers a new resource with the given unique name, arguments, and options. +func NewMainComponent(ctx *pulumi.Context, + name string, args *MainComponentArgs, opts ...pulumi.ResourceOption) (*MainComponent, error) { + if args == nil { + args = &MainComponentArgs{} + } + + var resource MainComponent + err := ctx.RegisterResource("example::MainComponent", name, args, &resource, opts...) + if err != nil { + return nil, err + } + return &resource, nil +} + +// GetMainComponent gets an existing MainComponent resource's state with the given name, ID, and optional +// state properties that are used to uniquely qualify the lookup (nil if not required). +func GetMainComponent(ctx *pulumi.Context, + name string, id pulumi.IDInput, state *MainComponentState, opts ...pulumi.ResourceOption) (*MainComponent, error) { + var resource MainComponent + err := ctx.ReadResource("example::MainComponent", name, id, state, &resource, opts...) + if err != nil { + return nil, err + } + return &resource, nil +} + +// Input properties used for looking up and filtering MainComponent resources. +type mainComponentState struct { +} + +type MainComponentState struct { +} + +func (MainComponentState) ElementType() reflect.Type { + return reflect.TypeOf((*mainComponentState)(nil)).Elem() +} + +type mainComponentArgs struct { +} + +// The set of arguments for constructing a MainComponent resource. +type MainComponentArgs struct { +} + +func (MainComponentArgs) ElementType() reflect.Type { + return reflect.TypeOf((*mainComponentArgs)(nil)).Elem() +} + +type MainComponentInput interface { + pulumi.Input + + ToMainComponentOutput() MainComponentOutput + ToMainComponentOutputWithContext(ctx context.Context) MainComponentOutput +} + +func (*MainComponent) ElementType() reflect.Type { + return reflect.TypeOf((**MainComponent)(nil)).Elem() +} + +func (i *MainComponent) ToMainComponentOutput() MainComponentOutput { + return i.ToMainComponentOutputWithContext(context.Background()) +} + +func (i *MainComponent) ToMainComponentOutputWithContext(ctx context.Context) MainComponentOutput { + return pulumi.ToOutputWithContext(ctx, i).(MainComponentOutput) +} + +type MainComponentOutput struct{ *pulumi.OutputState } + +func (MainComponentOutput) ElementType() reflect.Type { + return reflect.TypeOf((**MainComponent)(nil)).Elem() +} + +func (o MainComponentOutput) ToMainComponentOutput() MainComponentOutput { + return o +} + +func (o MainComponentOutput) ToMainComponentOutputWithContext(ctx context.Context) MainComponentOutput { + return o +} + +func init() { + pulumi.RegisterInputType(reflect.TypeOf((*MainComponentInput)(nil)).Elem(), &MainComponent{}) + pulumi.RegisterOutputType(MainComponentOutput{}) +} diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/go/example/mod/component.go b/pkg/codegen/testing/test/testdata/naming-collisions/go/example/mod/component.go new file mode 100644 index 000000000000..ad5677ea10e8 --- /dev/null +++ b/pkg/codegen/testing/test/testdata/naming-collisions/go/example/mod/component.go @@ -0,0 +1,107 @@ +// Code generated by test DO NOT EDIT. +// *** WARNING: Do not edit by hand unless you're certain you know what you are doing! *** + +package mod + +import ( + "context" + "reflect" + + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" + "naming-collisions/example" +) + +type Component struct { + pulumi.CustomResourceState +} + +// NewComponent registers a new resource with the given unique name, arguments, and options. +func NewComponent(ctx *pulumi.Context, + name string, args *ComponentArgs, opts ...pulumi.ResourceOption) (*Component, error) { + if args == nil { + args = &ComponentArgs{} + } + + var resource Component + err := ctx.RegisterResource("example:mod:Component", name, args, &resource, opts...) + if err != nil { + return nil, err + } + return &resource, nil +} + +// GetComponent gets an existing Component resource's state with the given name, ID, and optional +// state properties that are used to uniquely qualify the lookup (nil if not required). +func GetComponent(ctx *pulumi.Context, + name string, id pulumi.IDInput, state *ComponentState, opts ...pulumi.ResourceOption) (*Component, error) { + var resource Component + err := ctx.ReadResource("example:mod:Component", name, id, state, &resource, opts...) + if err != nil { + return nil, err + } + return &resource, nil +} + +// Input properties used for looking up and filtering Component resources. +type componentState struct { +} + +type ComponentState struct { +} + +func (ComponentState) ElementType() reflect.Type { + return reflect.TypeOf((*componentState)(nil)).Elem() +} + +type componentArgs struct { + Local *Component2 `pulumi:"local"` + Main *example.MainComponent `pulumi:"main"` +} + +// The set of arguments for constructing a Component resource. +type ComponentArgs struct { + Local Component2Input + Main example.MainComponentInput +} + +func (ComponentArgs) ElementType() reflect.Type { + return reflect.TypeOf((*componentArgs)(nil)).Elem() +} + +type ComponentInput interface { + pulumi.Input + + ToComponentOutput() ComponentOutput + ToComponentOutputWithContext(ctx context.Context) ComponentOutput +} + +func (*Component) ElementType() reflect.Type { + return reflect.TypeOf((**Component)(nil)).Elem() +} + +func (i *Component) ToComponentOutput() ComponentOutput { + return i.ToComponentOutputWithContext(context.Background()) +} + +func (i *Component) ToComponentOutputWithContext(ctx context.Context) ComponentOutput { + return pulumi.ToOutputWithContext(ctx, i).(ComponentOutput) +} + +type ComponentOutput struct{ *pulumi.OutputState } + +func (ComponentOutput) ElementType() reflect.Type { + return reflect.TypeOf((**Component)(nil)).Elem() +} + +func (o ComponentOutput) ToComponentOutput() ComponentOutput { + return o +} + +func (o ComponentOutput) ToComponentOutputWithContext(ctx context.Context) ComponentOutput { + return o +} + +func init() { + pulumi.RegisterInputType(reflect.TypeOf((*ComponentInput)(nil)).Elem(), &Component{}) + pulumi.RegisterOutputType(ComponentOutput{}) +} diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/go/example/mod/component2.go b/pkg/codegen/testing/test/testdata/naming-collisions/go/example/mod/component2.go new file mode 100644 index 000000000000..d021635682b2 --- /dev/null +++ b/pkg/codegen/testing/test/testdata/naming-collisions/go/example/mod/component2.go @@ -0,0 +1,102 @@ +// Code generated by test DO NOT EDIT. +// *** WARNING: Do not edit by hand unless you're certain you know what you are doing! *** + +package mod + +import ( + "context" + "reflect" + + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +type Component2 struct { + pulumi.CustomResourceState +} + +// NewComponent2 registers a new resource with the given unique name, arguments, and options. +func NewComponent2(ctx *pulumi.Context, + name string, args *Component2Args, opts ...pulumi.ResourceOption) (*Component2, error) { + if args == nil { + args = &Component2Args{} + } + + var resource Component2 + err := ctx.RegisterResource("example:mod:Component2", name, args, &resource, opts...) + if err != nil { + return nil, err + } + return &resource, nil +} + +// GetComponent2 gets an existing Component2 resource's state with the given name, ID, and optional +// state properties that are used to uniquely qualify the lookup (nil if not required). +func GetComponent2(ctx *pulumi.Context, + name string, id pulumi.IDInput, state *Component2State, opts ...pulumi.ResourceOption) (*Component2, error) { + var resource Component2 + err := ctx.ReadResource("example:mod:Component2", name, id, state, &resource, opts...) + if err != nil { + return nil, err + } + return &resource, nil +} + +// Input properties used for looking up and filtering Component2 resources. +type component2State struct { +} + +type Component2State struct { +} + +func (Component2State) ElementType() reflect.Type { + return reflect.TypeOf((*component2State)(nil)).Elem() +} + +type component2Args struct { +} + +// The set of arguments for constructing a Component2 resource. +type Component2Args struct { +} + +func (Component2Args) ElementType() reflect.Type { + return reflect.TypeOf((*component2Args)(nil)).Elem() +} + +type Component2Input interface { + pulumi.Input + + ToComponent2Output() Component2Output + ToComponent2OutputWithContext(ctx context.Context) Component2Output +} + +func (*Component2) ElementType() reflect.Type { + return reflect.TypeOf((**Component2)(nil)).Elem() +} + +func (i *Component2) ToComponent2Output() Component2Output { + return i.ToComponent2OutputWithContext(context.Background()) +} + +func (i *Component2) ToComponent2OutputWithContext(ctx context.Context) Component2Output { + return pulumi.ToOutputWithContext(ctx, i).(Component2Output) +} + +type Component2Output struct{ *pulumi.OutputState } + +func (Component2Output) ElementType() reflect.Type { + return reflect.TypeOf((**Component2)(nil)).Elem() +} + +func (o Component2Output) ToComponent2Output() Component2Output { + return o +} + +func (o Component2Output) ToComponent2OutputWithContext(ctx context.Context) Component2Output { + return o +} + +func init() { + pulumi.RegisterInputType(reflect.TypeOf((*Component2Input)(nil)).Elem(), &Component2{}) + pulumi.RegisterOutputType(Component2Output{}) +} diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/go/example/mod/init.go b/pkg/codegen/testing/test/testdata/naming-collisions/go/example/mod/init.go new file mode 100644 index 000000000000..b41a5e52629b --- /dev/null +++ b/pkg/codegen/testing/test/testdata/naming-collisions/go/example/mod/init.go @@ -0,0 +1,46 @@ +// Code generated by test DO NOT EDIT. +// *** WARNING: Do not edit by hand unless you're certain you know what you are doing! *** + +package mod + +import ( + "fmt" + + "github.com/blang/semver" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" + "naming-collisions/example" +) + +type module struct { + version semver.Version +} + +func (m *module) Version() semver.Version { + return m.version +} + +func (m *module) Construct(ctx *pulumi.Context, name, typ, urn string) (r pulumi.Resource, err error) { + switch typ { + case "example:mod:Component": + r = &Component{} + case "example:mod:Component2": + r = &Component2{} + default: + return nil, fmt.Errorf("unknown resource type: %s", typ) + } + + err = ctx.RegisterResource(typ, name, nil, r, pulumi.URN_(urn)) + return +} + +func init() { + version, err := example.PkgVersion() + if err != nil { + version = semver.Version{Major: 1} + } + pulumi.RegisterResourceModule( + "example", + "mod", + &module{version}, + ) +} diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/codegen-manifest.json b/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/codegen-manifest.json index 8742bace2e66..be6e08fb76b8 100644 --- a/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/codegen-manifest.json +++ b/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/codegen-manifest.json @@ -2,6 +2,10 @@ "emittedFiles": [ "README.md", "index.ts", + "mainComponent.ts", + "mod/component.ts", + "mod/component2.ts", + "mod/index.ts", "package.json", "provider.ts", "resource.ts", diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/index.ts b/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/index.ts index 3800243e08d5..649f54b62aaa 100644 --- a/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/index.ts +++ b/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/index.ts @@ -5,6 +5,11 @@ import * as pulumi from "@pulumi/pulumi"; import * as utilities from "./utilities"; // Export members: +export { MainComponentArgs } from "./mainComponent"; +export type MainComponent = import("./mainComponent").MainComponent; +export const MainComponent: typeof import("./mainComponent").MainComponent = null as any; +utilities.lazyLoad(exports, ["MainComponent"], () => require("./mainComponent")); + export { ProviderArgs } from "./provider"; export type Provider = import("./provider").Provider; export const Provider: typeof import("./provider").Provider = null as any; @@ -25,9 +30,11 @@ utilities.lazyLoad(exports, ["ResourceInput"], () => require("./resourceInput")) export * from "./types/enums"; // Export sub-modules: +import * as mod from "./mod"; import * as types from "./types"; export { + mod, types, }; @@ -35,6 +42,8 @@ const _module = { version: utilities.getVersion(), construct: (name: string, type: string, urn: string): pulumi.Resource => { switch (type) { + case "example::MainComponent": + return new MainComponent(name, undefined, { urn }) case "example::Resource": return new Resource(name, undefined, { urn }) case "example::ResourceInput": diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/mainComponent.ts b/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/mainComponent.ts new file mode 100644 index 000000000000..857380e01098 --- /dev/null +++ b/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/mainComponent.ts @@ -0,0 +1,57 @@ +// *** WARNING: this file was generated by test. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +import * as pulumi from "@pulumi/pulumi"; +import * as utilities from "./utilities"; + +export class MainComponent extends pulumi.CustomResource { + /** + * Get an existing MainComponent resource's state with the given name, ID, and optional extra + * properties used to qualify the lookup. + * + * @param name The _unique_ name of the resulting resource. + * @param id The _unique_ provider ID of the resource to lookup. + * @param opts Optional settings to control the behavior of the CustomResource. + */ + public static get(name: string, id: pulumi.Input, opts?: pulumi.CustomResourceOptions): MainComponent { + return new MainComponent(name, undefined as any, { ...opts, id: id }); + } + + /** @internal */ + public static readonly __pulumiType = 'example::MainComponent'; + + /** + * Returns true if the given object is an instance of MainComponent. This is designed to work even + * when multiple copies of the Pulumi SDK have been loaded into the same process. + */ + public static isInstance(obj: any): obj is MainComponent { + if (obj === undefined || obj === null) { + return false; + } + return obj['__pulumiType'] === MainComponent.__pulumiType; + } + + + /** + * Create a MainComponent resource with the given unique name, arguments, and options. + * + * @param name The _unique_ name of the resource. + * @param args The arguments to use to populate this resource's properties. + * @param opts A bag of options that control this resource's behavior. + */ + constructor(name: string, args?: MainComponentArgs, opts?: pulumi.CustomResourceOptions) { + let resourceInputs: pulumi.Inputs = {}; + opts = opts || {}; + if (!opts.id) { + } else { + } + opts = pulumi.mergeOptions(utilities.resourceOptsDefaults(), opts); + super(MainComponent.__pulumiType, name, resourceInputs, opts); + } +} + +/** + * The set of arguments for constructing a MainComponent resource. + */ +export interface MainComponentArgs { +} diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/mod/component.ts b/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/mod/component.ts new file mode 100644 index 000000000000..b038677d3ddf --- /dev/null +++ b/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/mod/component.ts @@ -0,0 +1,64 @@ +// *** WARNING: this file was generated by test. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +import * as pulumi from "@pulumi/pulumi"; +import * as utilities from "../utilities"; + +import {MainComponent} from ".."; +import {Component2} from "./index"; + +export class Component extends pulumi.CustomResource { + /** + * Get an existing Component resource's state with the given name, ID, and optional extra + * properties used to qualify the lookup. + * + * @param name The _unique_ name of the resulting resource. + * @param id The _unique_ provider ID of the resource to lookup. + * @param opts Optional settings to control the behavior of the CustomResource. + */ + public static get(name: string, id: pulumi.Input, opts?: pulumi.CustomResourceOptions): Component { + return new Component(name, undefined as any, { ...opts, id: id }); + } + + /** @internal */ + public static readonly __pulumiType = 'example:mod:Component'; + + /** + * Returns true if the given object is an instance of Component. This is designed to work even + * when multiple copies of the Pulumi SDK have been loaded into the same process. + */ + public static isInstance(obj: any): obj is Component { + if (obj === undefined || obj === null) { + return false; + } + return obj['__pulumiType'] === Component.__pulumiType; + } + + + /** + * Create a Component resource with the given unique name, arguments, and options. + * + * @param name The _unique_ name of the resource. + * @param args The arguments to use to populate this resource's properties. + * @param opts A bag of options that control this resource's behavior. + */ + constructor(name: string, args?: ComponentArgs, opts?: pulumi.CustomResourceOptions) { + let resourceInputs: pulumi.Inputs = {}; + opts = opts || {}; + if (!opts.id) { + resourceInputs["local"] = args ? args.local : undefined; + resourceInputs["main"] = args ? args.main : undefined; + } else { + } + opts = pulumi.mergeOptions(utilities.resourceOptsDefaults(), opts); + super(Component.__pulumiType, name, resourceInputs, opts); + } +} + +/** + * The set of arguments for constructing a Component resource. + */ +export interface ComponentArgs { + local?: pulumi.Input; + main?: pulumi.Input; +} diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/mod/component2.ts b/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/mod/component2.ts new file mode 100644 index 000000000000..52746711009b --- /dev/null +++ b/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/mod/component2.ts @@ -0,0 +1,57 @@ +// *** WARNING: this file was generated by test. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +import * as pulumi from "@pulumi/pulumi"; +import * as utilities from "../utilities"; + +export class Component2 extends pulumi.CustomResource { + /** + * Get an existing Component2 resource's state with the given name, ID, and optional extra + * properties used to qualify the lookup. + * + * @param name The _unique_ name of the resulting resource. + * @param id The _unique_ provider ID of the resource to lookup. + * @param opts Optional settings to control the behavior of the CustomResource. + */ + public static get(name: string, id: pulumi.Input, opts?: pulumi.CustomResourceOptions): Component2 { + return new Component2(name, undefined as any, { ...opts, id: id }); + } + + /** @internal */ + public static readonly __pulumiType = 'example:mod:Component2'; + + /** + * Returns true if the given object is an instance of Component2. This is designed to work even + * when multiple copies of the Pulumi SDK have been loaded into the same process. + */ + public static isInstance(obj: any): obj is Component2 { + if (obj === undefined || obj === null) { + return false; + } + return obj['__pulumiType'] === Component2.__pulumiType; + } + + + /** + * Create a Component2 resource with the given unique name, arguments, and options. + * + * @param name The _unique_ name of the resource. + * @param args The arguments to use to populate this resource's properties. + * @param opts A bag of options that control this resource's behavior. + */ + constructor(name: string, args?: Component2Args, opts?: pulumi.CustomResourceOptions) { + let resourceInputs: pulumi.Inputs = {}; + opts = opts || {}; + if (!opts.id) { + } else { + } + opts = pulumi.mergeOptions(utilities.resourceOptsDefaults(), opts); + super(Component2.__pulumiType, name, resourceInputs, opts); + } +} + +/** + * The set of arguments for constructing a Component2 resource. + */ +export interface Component2Args { +} diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/mod/index.ts b/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/mod/index.ts new file mode 100644 index 000000000000..ef5ee59ad102 --- /dev/null +++ b/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/mod/index.ts @@ -0,0 +1,32 @@ +// *** WARNING: this file was generated by test. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +import * as pulumi from "@pulumi/pulumi"; +import * as utilities from "../utilities"; + +// Export members: +export { ComponentArgs } from "./component"; +export type Component = import("./component").Component; +export const Component: typeof import("./component").Component = null as any; +utilities.lazyLoad(exports, ["Component"], () => require("./component")); + +export { Component2Args } from "./component2"; +export type Component2 = import("./component2").Component2; +export const Component2: typeof import("./component2").Component2 = null as any; +utilities.lazyLoad(exports, ["Component2"], () => require("./component2")); + + +const _module = { + version: utilities.getVersion(), + construct: (name: string, type: string, urn: string): pulumi.Resource => { + switch (type) { + case "example:mod:Component": + return new Component(name, undefined, { urn }) + case "example:mod:Component2": + return new Component2(name, undefined, { urn }) + default: + throw new Error(`unknown resource type ${type}`); + } + }, +}; +pulumi.runtime.registerResourceModule("example", "mod", _module) diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/tsconfig.json b/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/tsconfig.json index 580c6b03ddba..41516b009096 100644 --- a/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/tsconfig.json +++ b/pkg/codegen/testing/test/testdata/naming-collisions/nodejs/tsconfig.json @@ -14,6 +14,10 @@ }, "files": [ "index.ts", + "mainComponent.ts", + "mod/component.ts", + "mod/component2.ts", + "mod/index.ts", "provider.ts", "resource.ts", "resourceInput.ts", diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/python/codegen-manifest.json b/pkg/codegen/testing/test/testdata/naming-collisions/python/codegen-manifest.json index 128fa05a6af1..c61dc52f7214 100644 --- a/pkg/codegen/testing/test/testdata/naming-collisions/python/codegen-manifest.json +++ b/pkg/codegen/testing/test/testdata/naming-collisions/python/codegen-manifest.json @@ -4,6 +4,10 @@ "pulumi_example/__init__.py", "pulumi_example/_enums.py", "pulumi_example/_utilities.py", + "pulumi_example/main_component.py", + "pulumi_example/mod/__init__.py", + "pulumi_example/mod/component.py", + "pulumi_example/mod/component2.py", "pulumi_example/provider.py", "pulumi_example/pulumi-plugin.json", "pulumi_example/py.typed", diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/python/pulumi_example/__init__.py b/pkg/codegen/testing/test/testdata/naming-collisions/python/pulumi_example/__init__.py index 1ac75b8368a8..159293b6a47c 100644 --- a/pkg/codegen/testing/test/testdata/naming-collisions/python/pulumi_example/__init__.py +++ b/pkg/codegen/testing/test/testdata/naming-collisions/python/pulumi_example/__init__.py @@ -6,9 +6,18 @@ import typing # Export this package's modules as members: from ._enums import * +from .main_component import * from .provider import * from .resource import * from .resource_input import * + +# Make subpackages available: +if typing.TYPE_CHECKING: + import pulumi_example.mod as __mod + mod = __mod +else: + mod = _utilities.lazy_import('pulumi_example.mod') + _utilities.register( resource_modules=""" [ @@ -17,9 +26,19 @@ "mod": "", "fqn": "pulumi_example", "classes": { + "example::MainComponent": "MainComponent", "example::Resource": "Resource", "example::ResourceInput": "ResourceInput" } + }, + { + "pkg": "example", + "mod": "mod", + "fqn": "pulumi_example.mod", + "classes": { + "example:mod:Component": "Component", + "example:mod:Component2": "Component2" + } } ] """, diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/python/pulumi_example/main_component.py b/pkg/codegen/testing/test/testdata/naming-collisions/python/pulumi_example/main_component.py new file mode 100644 index 000000000000..8b043e62a8a7 --- /dev/null +++ b/pkg/codegen/testing/test/testdata/naming-collisions/python/pulumi_example/main_component.py @@ -0,0 +1,89 @@ +# 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 copy +import warnings +import pulumi +import pulumi.runtime +from typing import Any, Mapping, Optional, Sequence, Union, overload +from . import _utilities + +__all__ = ['MainComponentArgs', 'MainComponent'] + +@pulumi.input_type +class MainComponentArgs: + def __init__(__self__): + """ + The set of arguments for constructing a MainComponent resource. + """ + pass + + +class MainComponent(pulumi.CustomResource): + @overload + def __init__(__self__, + resource_name: str, + opts: Optional[pulumi.ResourceOptions] = None, + __props__=None): + """ + Create a MainComponent 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[MainComponentArgs] = None, + opts: Optional[pulumi.ResourceOptions] = None): + """ + Create a MainComponent resource with the given unique name, props, and options. + :param str resource_name: The name of the resource. + :param MainComponentArgs 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(MainComponentArgs, 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): + opts = pulumi.ResourceOptions.merge(_utilities.get_resource_opts_defaults(), opts) + if not isinstance(opts, pulumi.ResourceOptions): + raise TypeError('Expected resource options to be a ResourceOptions instance') + 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__ = MainComponentArgs.__new__(MainComponentArgs) + + super(MainComponent, __self__).__init__( + 'example::MainComponent', + resource_name, + __props__, + opts) + + @staticmethod + def get(resource_name: str, + id: pulumi.Input[str], + opts: Optional[pulumi.ResourceOptions] = None) -> 'MainComponent': + """ + Get an existing MainComponent resource's state with the given name, id, and optional extra + properties used to qualify the lookup. + + :param str resource_name: The unique name of the resulting resource. + :param pulumi.Input[str] id: The unique provider ID of the resource to lookup. + :param pulumi.ResourceOptions opts: Options for the resource. + """ + opts = pulumi.ResourceOptions.merge(opts, pulumi.ResourceOptions(id=id)) + + __props__ = MainComponentArgs.__new__(MainComponentArgs) + + return MainComponent(resource_name, opts=opts, __props__=__props__) + diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/python/pulumi_example/mod/__init__.py b/pkg/codegen/testing/test/testdata/naming-collisions/python/pulumi_example/mod/__init__.py new file mode 100644 index 000000000000..a5ec9ef89732 --- /dev/null +++ b/pkg/codegen/testing/test/testdata/naming-collisions/python/pulumi_example/mod/__init__.py @@ -0,0 +1,9 @@ +# 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 .component import * +from .component2 import * diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/python/pulumi_example/mod/component.py b/pkg/codegen/testing/test/testdata/naming-collisions/python/pulumi_example/mod/component.py new file mode 100644 index 000000000000..4864a4cd76c9 --- /dev/null +++ b/pkg/codegen/testing/test/testdata/naming-collisions/python/pulumi_example/mod/component.py @@ -0,0 +1,120 @@ +# 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 copy +import warnings +import pulumi +import pulumi.runtime +from typing import Any, Mapping, Optional, Sequence, Union, overload +from .. import _utilities +from ..component2 import Component2 +from ..main_component import MainComponent + +__all__ = ['ComponentArgs', 'Component'] + +@pulumi.input_type +class ComponentArgs: + def __init__(__self__, *, + local: Optional[pulumi.Input['Component2']] = None, + main: Optional[pulumi.Input['MainComponent']] = None): + """ + The set of arguments for constructing a Component resource. + """ + if local is not None: + pulumi.set(__self__, "local", local) + if main is not None: + pulumi.set(__self__, "main", main) + + @property + @pulumi.getter + def local(self) -> Optional[pulumi.Input['Component2']]: + return pulumi.get(self, "local") + + @local.setter + def local(self, value: Optional[pulumi.Input['Component2']]): + pulumi.set(self, "local", value) + + @property + @pulumi.getter + def main(self) -> Optional[pulumi.Input['MainComponent']]: + return pulumi.get(self, "main") + + @main.setter + def main(self, value: Optional[pulumi.Input['MainComponent']]): + pulumi.set(self, "main", value) + + +class Component(pulumi.CustomResource): + @overload + def __init__(__self__, + resource_name: str, + opts: Optional[pulumi.ResourceOptions] = None, + local: Optional[pulumi.Input['Component2']] = None, + main: Optional[pulumi.Input['MainComponent']] = None, + __props__=None): + """ + Create a Component 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[ComponentArgs] = None, + opts: Optional[pulumi.ResourceOptions] = None): + """ + Create a Component resource with the given unique name, props, and options. + :param str resource_name: The name of the resource. + :param ComponentArgs 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(ComponentArgs, 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, + local: Optional[pulumi.Input['Component2']] = None, + main: Optional[pulumi.Input['MainComponent']] = None, + __props__=None): + opts = pulumi.ResourceOptions.merge(_utilities.get_resource_opts_defaults(), opts) + if not isinstance(opts, pulumi.ResourceOptions): + raise TypeError('Expected resource options to be a ResourceOptions instance') + 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__ = ComponentArgs.__new__(ComponentArgs) + + __props__.__dict__["local"] = local + __props__.__dict__["main"] = main + super(Component, __self__).__init__( + 'example:mod:Component', + resource_name, + __props__, + opts) + + @staticmethod + def get(resource_name: str, + id: pulumi.Input[str], + opts: Optional[pulumi.ResourceOptions] = None) -> 'Component': + """ + Get an existing Component resource's state with the given name, id, and optional extra + properties used to qualify the lookup. + + :param str resource_name: The unique name of the resulting resource. + :param pulumi.Input[str] id: The unique provider ID of the resource to lookup. + :param pulumi.ResourceOptions opts: Options for the resource. + """ + opts = pulumi.ResourceOptions.merge(opts, pulumi.ResourceOptions(id=id)) + + __props__ = ComponentArgs.__new__(ComponentArgs) + + return Component(resource_name, opts=opts, __props__=__props__) + diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/python/pulumi_example/mod/component2.py b/pkg/codegen/testing/test/testdata/naming-collisions/python/pulumi_example/mod/component2.py new file mode 100644 index 000000000000..882a51cf1a83 --- /dev/null +++ b/pkg/codegen/testing/test/testdata/naming-collisions/python/pulumi_example/mod/component2.py @@ -0,0 +1,89 @@ +# 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 copy +import warnings +import pulumi +import pulumi.runtime +from typing import Any, Mapping, Optional, Sequence, Union, overload +from .. import _utilities + +__all__ = ['Component2Args', 'Component2'] + +@pulumi.input_type +class Component2Args: + def __init__(__self__): + """ + The set of arguments for constructing a Component2 resource. + """ + pass + + +class Component2(pulumi.CustomResource): + @overload + def __init__(__self__, + resource_name: str, + opts: Optional[pulumi.ResourceOptions] = None, + __props__=None): + """ + Create a Component2 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[Component2Args] = None, + opts: Optional[pulumi.ResourceOptions] = None): + """ + Create a Component2 resource with the given unique name, props, and options. + :param str resource_name: The name of the resource. + :param Component2Args 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(Component2Args, 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): + opts = pulumi.ResourceOptions.merge(_utilities.get_resource_opts_defaults(), opts) + if not isinstance(opts, pulumi.ResourceOptions): + raise TypeError('Expected resource options to be a ResourceOptions instance') + 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__ = Component2Args.__new__(Component2Args) + + super(Component2, __self__).__init__( + 'example:mod:Component2', + resource_name, + __props__, + opts) + + @staticmethod + def get(resource_name: str, + id: pulumi.Input[str], + opts: Optional[pulumi.ResourceOptions] = None) -> 'Component2': + """ + Get an existing Component2 resource's state with the given name, id, and optional extra + properties used to qualify the lookup. + + :param str resource_name: The unique name of the resulting resource. + :param pulumi.Input[str] id: The unique provider ID of the resource to lookup. + :param pulumi.ResourceOptions opts: Options for the resource. + """ + opts = pulumi.ResourceOptions.merge(opts, pulumi.ResourceOptions(id=id)) + + __props__ = Component2Args.__new__(Component2Args) + + return Component2(resource_name, opts=opts, __props__=__props__) + diff --git a/pkg/codegen/testing/test/testdata/naming-collisions/schema.json b/pkg/codegen/testing/test/testdata/naming-collisions/schema.json index 7cafda06d866..679f85474b47 100644 --- a/pkg/codegen/testing/test/testdata/naming-collisions/schema.json +++ b/pkg/codegen/testing/test/testdata/naming-collisions/schema.json @@ -56,6 +56,23 @@ } } }, + "example:mod:Component": { + "component": true, + "inputProperties": { + "main": { + "$ref": "#/resources/example::MainComponent" + }, + "local": { + "$ref": "#/resources/example:mod:Component2" + } + } + }, + "example:mod:Component2": { + "component": true + }, + "example::MainComponent": { + "component": true + }, "example::ResourceInput": { "properties": { "bar": { @@ -72,7 +89,7 @@ ] }, "go": { - "importBasePath": "github.com/pulumi/pulumi/pkg/v3/codegen/testing/test/testdata/input-collision/go/example", + "importBasePath": "naming-collisions/example", "generateExtraInputTypes": true }, "nodejs": { diff --git a/pkg/codegen/testing/test/testdata/nested-module-thirdparty/docs/_index.md b/pkg/codegen/testing/test/testdata/nested-module-thirdparty/docs/_index.md index 9ddf5a370f31..4586b089fff6 100644 --- a/pkg/codegen/testing/test/testdata/nested-module-thirdparty/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/nested-module-thirdparty/docs/_index.md @@ -18,7 +18,7 @@ no_edit_this_page: true

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/nested-module-thirdparty/docs/deeply/nested/module/_index.md b/pkg/codegen/testing/test/testdata/nested-module-thirdparty/docs/deeply/nested/module/_index.md index d125d16e6933..015f482c9bfa 100644 --- a/pkg/codegen/testing/test/testdata/nested-module-thirdparty/docs/deeply/nested/module/_index.md +++ b/pkg/codegen/testing/test/testdata/nested-module-thirdparty/docs/deeply/nested/module/_index.md @@ -13,7 +13,7 @@ Explore the resources and functions of the foo-bar.deeply/nested/module module.

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/nested-module/docs/_index.md b/pkg/codegen/testing/test/testdata/nested-module/docs/_index.md index 6cd3ce5d5f0f..223ef2d78490 100644 --- a/pkg/codegen/testing/test/testdata/nested-module/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/nested-module/docs/_index.md @@ -18,7 +18,7 @@ no_edit_this_page: true

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/nested-module/docs/nested/module/_index.md b/pkg/codegen/testing/test/testdata/nested-module/docs/nested/module/_index.md index a6058f6cc14b..2d9904b66179 100644 --- a/pkg/codegen/testing/test/testdata/nested-module/docs/nested/module/_index.md +++ b/pkg/codegen/testing/test/testdata/nested-module/docs/nested/module/_index.md @@ -13,7 +13,7 @@ Explore the resources and functions of the foo.nested/module module.

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/other-owned/docs/_index.md b/pkg/codegen/testing/test/testdata/other-owned/docs/_index.md index 279529a2e380..26c184b07bd6 100644 --- a/pkg/codegen/testing/test/testdata/other-owned/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/other-owned/docs/_index.md @@ -13,19 +13,19 @@ no_edit_this_page: true

Resources

Functions

Package Details

diff --git a/pkg/codegen/testing/test/testdata/output-funcs-edgeorder/docs/_index.md b/pkg/codegen/testing/test/testdata/output-funcs-edgeorder/docs/_index.md index 5df90309888b..7c8e75c630ae 100644 --- a/pkg/codegen/testing/test/testdata/output-funcs-edgeorder/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/output-funcs-edgeorder/docs/_index.md @@ -13,13 +13,13 @@ no_edit_this_page: true

Resources

Functions

Package Details

diff --git a/pkg/codegen/testing/test/testdata/output-funcs-tfbridge20/docs/_index.md b/pkg/codegen/testing/test/testdata/output-funcs-tfbridge20/docs/_index.md index 7e92d1901739..14ae494865e6 100644 --- a/pkg/codegen/testing/test/testdata/output-funcs-tfbridge20/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/output-funcs-tfbridge20/docs/_index.md @@ -13,13 +13,13 @@ no_edit_this_page: true

Resources

Functions

Package Details

diff --git a/pkg/codegen/testing/test/testdata/output-funcs/docs/_index.md b/pkg/codegen/testing/test/testdata/output-funcs/docs/_index.md index 9705dd472306..6ea4468eceea 100644 --- a/pkg/codegen/testing/test/testdata/output-funcs/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/output-funcs/docs/_index.md @@ -13,21 +13,21 @@ no_edit_this_page: true

Resources

Functions

Package Details

diff --git a/pkg/codegen/testing/test/testdata/output-funcs/python/pulumi_mypkg/func_with_const_input.py b/pkg/codegen/testing/test/testdata/output-funcs/python/pulumi_mypkg/func_with_const_input.py index e65015b2dd67..730d3c2fb85b 100644 --- a/pkg/codegen/testing/test/testdata/output-funcs/python/pulumi_mypkg/func_with_const_input.py +++ b/pkg/codegen/testing/test/testdata/output-funcs/python/pulumi_mypkg/func_with_const_input.py @@ -6,7 +6,7 @@ import warnings import pulumi import pulumi.runtime -from typing import Any, Mapping, Optional, Sequence, Union, overload +from typing import Any, Mapping, Optional, Sequence, Union, overload, Awaitable from . import _utilities __all__ = [ @@ -14,7 +14,7 @@ ] def func_with_const_input(plain_input: Optional[str] = None, - opts: Optional[pulumi.InvokeOptions] = None): + opts: Optional[pulumi.InvokeOptions] = None) -> Awaitable[None]: """ Codegen demo with const inputs """ diff --git a/pkg/codegen/testing/test/testdata/output-funcs/python/pulumi_mypkg/func_with_empty_outputs.py b/pkg/codegen/testing/test/testdata/output-funcs/python/pulumi_mypkg/func_with_empty_outputs.py index f1a3a6af86bb..9d0d6c81f9c4 100644 --- a/pkg/codegen/testing/test/testdata/output-funcs/python/pulumi_mypkg/func_with_empty_outputs.py +++ b/pkg/codegen/testing/test/testdata/output-funcs/python/pulumi_mypkg/func_with_empty_outputs.py @@ -6,7 +6,7 @@ import warnings import pulumi import pulumi.runtime -from typing import Any, Mapping, Optional, Sequence, Union, overload +from typing import Any, Mapping, Optional, Sequence, Union, overload, Awaitable from . import _utilities __all__ = [ @@ -14,7 +14,7 @@ ] def func_with_empty_outputs(name: Optional[str] = None, - opts: Optional[pulumi.InvokeOptions] = None): + opts: Optional[pulumi.InvokeOptions] = None) -> Awaitable[None]: """ n/a diff --git a/pkg/codegen/testing/test/testdata/plain-and-default/docs/_index.md b/pkg/codegen/testing/test/testdata/plain-and-default/docs/_index.md index e9faa4521603..1eabeb0318bb 100644 --- a/pkg/codegen/testing/test/testdata/plain-and-default/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/plain-and-default/docs/_index.md @@ -13,8 +13,8 @@ no_edit_this_page: true

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/plain-object-defaults/docs/_index.md b/pkg/codegen/testing/test/testdata/plain-object-defaults/docs/_index.md index 6d0011cb63da..cef4c7af9ad0 100644 --- a/pkg/codegen/testing/test/testdata/plain-object-defaults/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/plain-object-defaults/docs/_index.md @@ -19,14 +19,14 @@ no_edit_this_page: true

Resources

Functions

Package Details

diff --git a/pkg/codegen/testing/test/testdata/plain-object-disable-defaults/docs/_index.md b/pkg/codegen/testing/test/testdata/plain-object-disable-defaults/docs/_index.md index 6d0011cb63da..cef4c7af9ad0 100644 --- a/pkg/codegen/testing/test/testdata/plain-object-disable-defaults/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/plain-object-disable-defaults/docs/_index.md @@ -19,14 +19,14 @@ no_edit_this_page: true

Resources

Functions

Package Details

diff --git a/pkg/codegen/testing/test/testdata/plain-schema-gh6957/docs/_index.md b/pkg/codegen/testing/test/testdata/plain-schema-gh6957/docs/_index.md index 1418b701bd43..656cd1230473 100644 --- a/pkg/codegen/testing/test/testdata/plain-schema-gh6957/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/plain-schema-gh6957/docs/_index.md @@ -13,8 +13,8 @@ no_edit_this_page: true

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/provider-config-schema/docs/_index.md b/pkg/codegen/testing/test/testdata/provider-config-schema/docs/_index.md index 9b2a218610f0..36e0d3327230 100644 --- a/pkg/codegen/testing/test/testdata/provider-config-schema/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/provider-config-schema/docs/_index.md @@ -18,12 +18,12 @@ no_edit_this_page: true

Resources

Functions

Package Details

diff --git a/pkg/codegen/testing/test/testdata/regress-8403/docs/_index.md b/pkg/codegen/testing/test/testdata/regress-8403/docs/_index.md index 20654b6c95ad..9a5ca92adf00 100644 --- a/pkg/codegen/testing/test/testdata/regress-8403/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/regress-8403/docs/_index.md @@ -13,12 +13,12 @@ no_edit_this_page: true

Resources

Functions

Package Details

diff --git a/pkg/codegen/testing/test/testdata/regress-node-8110/docs/_index.md b/pkg/codegen/testing/test/testdata/regress-node-8110/docs/_index.md index 7b7e48c2dfd0..4766a1438c12 100644 --- a/pkg/codegen/testing/test/testdata/regress-node-8110/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/regress-node-8110/docs/_index.md @@ -13,12 +13,12 @@ no_edit_this_page: true

Resources

Functions

Package Details

diff --git a/pkg/codegen/testing/test/testdata/regress-node-8110/python/pulumi_my8110/example_func.py b/pkg/codegen/testing/test/testdata/regress-node-8110/python/pulumi_my8110/example_func.py index eb63bf80019c..f7e64ab757f1 100644 --- a/pkg/codegen/testing/test/testdata/regress-node-8110/python/pulumi_my8110/example_func.py +++ b/pkg/codegen/testing/test/testdata/regress-node-8110/python/pulumi_my8110/example_func.py @@ -6,7 +6,7 @@ import warnings import pulumi import pulumi.runtime -from typing import Any, Mapping, Optional, Sequence, Union, overload +from typing import Any, Mapping, Optional, Sequence, Union, overload, Awaitable from . import _utilities from ._enums import * @@ -15,7 +15,7 @@ ] def example_func(enums: Optional[Sequence[Union[str, 'MyEnum']]] = None, - opts: Optional[pulumi.InvokeOptions] = None): + opts: Optional[pulumi.InvokeOptions] = None) -> Awaitable[None]: """ Use this data source to access information about an existing resource. """ diff --git a/pkg/codegen/testing/test/testdata/replace-on-change/docs/_index.md b/pkg/codegen/testing/test/testdata/replace-on-change/docs/_index.md index 5a9f221065af..e6fdf3c212ac 100644 --- a/pkg/codegen/testing/test/testdata/replace-on-change/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/replace-on-change/docs/_index.md @@ -13,12 +13,12 @@ no_edit_this_page: true

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/resource-args-python-case-insensitive/docs/_index.md b/pkg/codegen/testing/test/testdata/resource-args-python-case-insensitive/docs/_index.md index 6c0e125da670..91ff51991be8 100644 --- a/pkg/codegen/testing/test/testdata/resource-args-python-case-insensitive/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/resource-args-python-case-insensitive/docs/_index.md @@ -13,9 +13,9 @@ no_edit_this_page: true

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/resource-args-python/docs/_index.md b/pkg/codegen/testing/test/testdata/resource-args-python/docs/_index.md index 6c0e125da670..91ff51991be8 100644 --- a/pkg/codegen/testing/test/testdata/resource-args-python/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/resource-args-python/docs/_index.md @@ -13,9 +13,9 @@ no_edit_this_page: true

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/resource-property-overlap/docs/_index.md b/pkg/codegen/testing/test/testdata/resource-property-overlap/docs/_index.md index 155219bea50f..7799816402a9 100644 --- a/pkg/codegen/testing/test/testdata/resource-property-overlap/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/resource-property-overlap/docs/_index.md @@ -13,8 +13,8 @@ no_edit_this_page: true

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/secrets/docs/_index.md b/pkg/codegen/testing/test/testdata/secrets/docs/_index.md index d1ff3210bdeb..2af081f0aa12 100644 --- a/pkg/codegen/testing/test/testdata/secrets/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/secrets/docs/_index.md @@ -13,8 +13,8 @@ no_edit_this_page: true

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/simple-enum-schema/docs/_index.md b/pkg/codegen/testing/test/testdata/simple-enum-schema/docs/_index.md index 2a20f7e4e1e9..fe858ba6562a 100644 --- a/pkg/codegen/testing/test/testdata/simple-enum-schema/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/simple-enum-schema/docs/_index.md @@ -18,7 +18,7 @@ no_edit_this_page: true

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/simple-enum-schema/docs/tree/v1/_index.md b/pkg/codegen/testing/test/testdata/simple-enum-schema/docs/tree/v1/_index.md index 1a7e0192b0db..681bbdb8d26c 100644 --- a/pkg/codegen/testing/test/testdata/simple-enum-schema/docs/tree/v1/_index.md +++ b/pkg/codegen/testing/test/testdata/simple-enum-schema/docs/tree/v1/_index.md @@ -13,8 +13,8 @@ Explore the resources and functions of the plant.tree/v1 module.

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/simple-methods-schema-single-value-returns/docs/_index.md b/pkg/codegen/testing/test/testdata/simple-methods-schema-single-value-returns/docs/_index.md index 7aa38f1d8d12..50fb035417f9 100644 --- a/pkg/codegen/testing/test/testdata/simple-methods-schema-single-value-returns/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/simple-methods-schema-single-value-returns/docs/_index.md @@ -13,8 +13,8 @@ no_edit_this_page: true

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/simple-methods-schema/docs/_index.md b/pkg/codegen/testing/test/testdata/simple-methods-schema/docs/_index.md index 7aa38f1d8d12..50fb035417f9 100644 --- a/pkg/codegen/testing/test/testdata/simple-methods-schema/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/simple-methods-schema/docs/_index.md @@ -13,8 +13,8 @@ no_edit_this_page: true

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/simple-plain-schema-with-root-package/docs/_index.md b/pkg/codegen/testing/test/testdata/simple-plain-schema-with-root-package/docs/_index.md index 2e538a3da820..7f4e098c74af 100644 --- a/pkg/codegen/testing/test/testdata/simple-plain-schema-with-root-package/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/simple-plain-schema-with-root-package/docs/_index.md @@ -13,8 +13,8 @@ no_edit_this_page: true

Resources

Package Details

diff --git a/pkg/codegen/testing/test/testdata/simple-plain-schema/docs/_index.md b/pkg/codegen/testing/test/testdata/simple-plain-schema/docs/_index.md index 6f02cbc4474e..2c32399421f5 100644 --- a/pkg/codegen/testing/test/testdata/simple-plain-schema/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/simple-plain-schema/docs/_index.md @@ -13,13 +13,13 @@ no_edit_this_page: true

Resources

Functions

Package Details

diff --git a/pkg/codegen/testing/test/testdata/simple-plain-schema/python/pulumi_example/do_foo.py b/pkg/codegen/testing/test/testdata/simple-plain-schema/python/pulumi_example/do_foo.py index 3635cb794612..17cf22139d81 100644 --- a/pkg/codegen/testing/test/testdata/simple-plain-schema/python/pulumi_example/do_foo.py +++ b/pkg/codegen/testing/test/testdata/simple-plain-schema/python/pulumi_example/do_foo.py @@ -6,7 +6,7 @@ import warnings import pulumi import pulumi.runtime -from typing import Any, Mapping, Optional, Sequence, Union, overload +from typing import Any, Mapping, Optional, Sequence, Union, overload, Awaitable from . import _utilities from ._inputs import * @@ -15,7 +15,7 @@ ] def do_foo(foo: Optional[pulumi.InputType['Foo']] = None, - opts: Optional[pulumi.InvokeOptions] = None): + opts: Optional[pulumi.InvokeOptions] = None) -> Awaitable[None]: """ Use this data source to access information about an existing resource. """ diff --git a/pkg/codegen/testing/test/testdata/simple-resource-schema-custom-pypackage-name/docs/_index.md b/pkg/codegen/testing/test/testdata/simple-resource-schema-custom-pypackage-name/docs/_index.md index b163d04ae7d8..a5b7fd18e919 100644 --- a/pkg/codegen/testing/test/testdata/simple-resource-schema-custom-pypackage-name/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/simple-resource-schema-custom-pypackage-name/docs/_index.md @@ -13,14 +13,14 @@ no_edit_this_page: true

Resources

Functions

Package Details

diff --git a/pkg/codegen/testing/test/testdata/simple-resource-schema/docs/_index.md b/pkg/codegen/testing/test/testdata/simple-resource-schema/docs/_index.md index 229e42ecdc3d..d763c396f429 100644 --- a/pkg/codegen/testing/test/testdata/simple-resource-schema/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/simple-resource-schema/docs/_index.md @@ -13,19 +13,19 @@ no_edit_this_page: true

Resources

Functions

Package Details

diff --git a/pkg/codegen/testing/test/testdata/simple-yaml-schema/docs/_index.md b/pkg/codegen/testing/test/testdata/simple-yaml-schema/docs/_index.md index ebf7803bc27b..cfb02d6cb7ba 100644 --- a/pkg/codegen/testing/test/testdata/simple-yaml-schema/docs/_index.md +++ b/pkg/codegen/testing/test/testdata/simple-yaml-schema/docs/_index.md @@ -13,15 +13,15 @@ no_edit_this_page: true

Resources

Functions

Package Details

diff --git a/pkg/codegen/utilities.go b/pkg/codegen/utilities.go index 90af78e7ebbc..b2cd08d71fb8 100644 --- a/pkg/codegen/utilities.go +++ b/pkg/codegen/utilities.go @@ -20,6 +20,7 @@ import ( "reflect" "sort" + "github.com/pulumi/pulumi/pkg/v3/codegen/schema" "github.com/pulumi/pulumi/sdk/v3/go/common/util/contract" ) @@ -184,3 +185,24 @@ func (fs Fs) Add(path string, contents []byte) { contract.Assertf(!has, "duplicate file: %s", path) fs[path] = contents } + +// Check if two packages are the same. +func PkgEquals(p1, p2 schema.PackageReference) bool { + if p1 == p2 { + return true + } else if p1 == nil || p2 == nil { + return false + } + + if p1.Name() != p2.Name() { + return false + } + + v1, v2 := p1.Version(), p2.Version() + if v1 == v2 { + return true + } else if v1 == nil || v2 == nil { + return false + } + return v1.Equals(*v2) +} diff --git a/pkg/go.mod b/pkg/go.mod index 18d7290e176c..20403125a8d7 100644 --- a/pkg/go.mod +++ b/pkg/go.mod @@ -36,7 +36,7 @@ require ( github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d github.com/opentracing/opentracing-go v1.2.0 github.com/pgavlin/goldmark v1.1.33-0.20200616210433-b5eb04559386 - github.com/pulumi/pulumi/sdk/v3 v3.48.1-0.20221207010559-e812f69ba562 + github.com/pulumi/pulumi/sdk/v3 v3.49.0 github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 github.com/sergi/go-diff v1.2.0 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 diff --git a/pkg/testing/integration/program.go b/pkg/testing/integration/program.go index bc3bf982bcb3..d7b3925d27f1 100644 --- a/pkg/testing/integration/program.go +++ b/pkg/testing/integration/program.go @@ -844,11 +844,7 @@ func newProgramTester(t *testing.T, opts *ProgramTestOptions) *ProgramTester { // MakeTempBackend creates a temporary backend directory which will clean up on test exit. func MakeTempBackend(t *testing.T) string { - tempDir, err := os.MkdirTemp("", "") - if err != nil { - t.Fatalf("Failed to create temporary directory: %v", err) - } - t.Cleanup(func() { os.RemoveAll(tempDir) }) + tempDir := t.TempDir() return fmt.Sprintf("file://%s", filepath.ToSlash(tempDir)) } diff --git a/pkg/testing/integration/program_test.go b/pkg/testing/integration/program_test.go index 90a3fc1c7dd3..868b7067748b 100644 --- a/pkg/testing/integration/program_test.go +++ b/pkg/testing/integration/program_test.go @@ -22,8 +22,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - - "github.com/pulumi/pulumi/sdk/v3/go/common/util/contract" ) // Test that RunCommand writes the command's output to a log file. @@ -42,9 +40,7 @@ func TestRunCommandLog(t *testing.T) { Stderr: os.Stderr, } - tempdir, err := ioutil.TempDir("", "test") - contract.AssertNoError(err) - defer os.RemoveAll(tempdir) + tempdir := t.TempDir() args := []string{node, "-e", "console.log('output from node');"} err = RunCommand(t, "node", args, tempdir, opts) diff --git a/scripts/get-job-matrix.py b/scripts/get-job-matrix.py index b955505ee8c6..c56411c2beec 100755 --- a/scripts/get-job-matrix.py +++ b/scripts/get-job-matrix.py @@ -391,6 +391,9 @@ def get_matrix( test_suites += run_gotestsum_ci_matrix_single_package(item, pkg_tests, tags) + if kind == JobKind.SMOKE_TEST: + platforms = list(map(lambda p: "windows-8core-2022" if p == "windows-latest" else p, platforms)) + return { "test-suite": test_suites, "platform": platforms, diff --git a/sdk/dotnet/Pulumi.Tests/Core/OutputTests.cs b/sdk/dotnet/Pulumi.Tests/Core/OutputTests.cs index cba9f7cefe72..5de66721f0d4 100644 --- a/sdk/dotnet/Pulumi.Tests/Core/OutputTests.cs +++ b/sdk/dotnet/Pulumi.Tests/Core/OutputTests.cs @@ -1,5 +1,6 @@ // Copyright 2016-2019, Pulumi Corporation +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; @@ -9,12 +10,30 @@ namespace Pulumi.Tests.Core { + // Simple struct used for JSON tests + public struct TestStructure { + public int X { get; set;} + + private int y; + + public string Z => (y+1).ToString(); + + public TestStructure(int x, int y) { + X = x; + this.y = y; + } + } + public class OutputTests : PulumiTest { private static Output CreateOutput(T value, bool isKnown, bool isSecret = false) => new Output(Task.FromResult(OutputData.Create( ImmutableHashSet.Empty, value, isKnown, isSecret))); + private static Output CreateOutput(IEnumerable resources, T value, bool isKnown, bool isSecret = false) + => new Output(Task.FromResult(OutputData.Create( + ImmutableHashSet.CreateRange(resources), value, isKnown, isSecret))); + public class PreviewTests { [Fact] @@ -618,6 +637,122 @@ public Task CreateSecretSetsSecret() Assert.True(data.IsSecret); Assert.Equal(0, data.Value); }); + + [Fact] + public Task JsonSerializeBasic() + => RunInNormal(async () => + { + var o1 = CreateOutput(new int[]{ 0, 1} , true); + var o2 = Output.JsonSerialize(o1); + var data = await o2.DataTask.ConfigureAwait(false); + Assert.True(data.IsKnown); + Assert.False(data.IsSecret); + Assert.Equal("[0,1]", data.Value); + }); + + [Fact] + public Task JsonSerializeNested() + => RunInNormal(async () => + { + var o1 = CreateOutput(new Output[] { + CreateOutput(0, true), + CreateOutput(1, true), + }, true); + var o2 = Output.JsonSerialize(o1); + var data = await o2.DataTask.ConfigureAwait(false); + Assert.True(data.IsKnown); + Assert.False(data.IsSecret); + Assert.Equal("[0,1]", data.Value); + }); + + [Fact] + public Task JsonSerializeNestedUnknown() + => RunInNormal(async () => + { + var o1 = CreateOutput(new Output[] { + CreateOutput(default, false), + CreateOutput(1, true), + }, true); + var o2 = Output.JsonSerialize(o1); + var data = await o2.DataTask.ConfigureAwait(false); + Assert.False(data.IsKnown); + Assert.False(data.IsSecret); + }); + + [Fact] + public Task JsonSerializeNestedSecret() + => RunInNormal(async () => + { + var o1 = CreateOutput(new Output[] { + CreateOutput(0, true, true), + CreateOutput(1, true), + }, true); + var o2 = Output.JsonSerialize(o1); + var data = await o2.DataTask.ConfigureAwait(false); + Assert.True(data.IsKnown); + Assert.True(data.IsSecret); + Assert.Equal("[0,1]", data.Value); + }); + + [Fact] + public Task JsonSerializeWithOptions() + => RunInNormal(async () => + { + var v = new System.Collections.Generic.Dictionary(); + v.Add("a", new TestStructure(1, 2)); + v.Add("b", new TestStructure(int.MinValue, int.MaxValue)); + var o1 = CreateOutput(v, true); + var options = new System.Text.Json.JsonSerializerOptions(); + options.WriteIndented = true; + var o2 = Output.JsonSerialize(o1, options); + var data = await o2.DataTask.ConfigureAwait(false); + Assert.True(data.IsKnown); + Assert.False(data.IsSecret); + var expected = @"{ + ""a"": { + ""X"": 1, + ""Z"": ""3"" + }, + ""b"": { + ""X"": -2147483648, + ""Z"": ""-2147483648"" + } +}"; + Assert.Equal(expected, data.Value); + }); + + [Fact] + public async Task JsonSerializeNestedDependencies() { + // We need a custom mock setup for this because new CustomResource will call into the + // deployment to try and register. + var runner = new Moq.Mock(Moq.MockBehavior.Strict); + runner.Setup(r => r.RegisterTask(Moq.It.IsAny(), Moq.It.IsAny())); + + var logger = new Moq.Mock(Moq.MockBehavior.Strict); + logger.Setup(l => l.DebugAsync(Moq.It.IsAny(), Moq.It.IsAny(), Moq.It.IsAny(), Moq.It.IsAny())).Returns(Task.CompletedTask); + + var mock = new Moq.Mock(Moq.MockBehavior.Strict); + mock.Setup(d => d.IsDryRun).Returns(false); + mock.Setup(d => d.Stack).Returns(() => null!); + mock.Setup(d => d.Runner).Returns(runner.Object); + mock.Setup(d => d.Logger).Returns(logger.Object); + mock.Setup(d => d.ReadOrRegisterResource(Moq.It.IsAny(), Moq.It.IsAny(), Moq.It.IsAny>(), Moq.It.IsAny(), Moq.It.IsAny())); + + Deployment.Instance = new DeploymentInstance(mock.Object); + + var resource = new CustomResource("type", "name", null); + + var o1 = CreateOutput(new Output[] { + CreateOutput(new Resource[] { resource}, 0, true, true), + CreateOutput(1, true), + }, true); + var o2 = Output.JsonSerialize(o1); + var data = await o2.DataTask.ConfigureAwait(false); + Assert.True(data.IsKnown); + Assert.True(data.IsSecret); + Assert.Contains(resource, data.Resources); + Assert.Equal("[0,1]", data.Value); + } } } } diff --git a/sdk/dotnet/Pulumi/Core/Output.cs b/sdk/dotnet/Pulumi/Core/Output.cs index 4523a7faa60f..6e7c5cc6899a 100644 --- a/sdk/dotnet/Pulumi/Core/Output.cs +++ b/sdk/dotnet/Pulumi/Core/Output.cs @@ -5,11 +5,88 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading.Tasks; using Pulumi.Serialization; namespace Pulumi { + /// + /// Internal class used for Output.JsonSerialize. + /// + sealed class OutputJsonConverter : System.Text.Json.Serialization.JsonConverterFactory + { + private sealed class OutputJsonConverterInner : System.Text.Json.Serialization.JsonConverter> + { + readonly OutputJsonConverter Parent; + readonly JsonConverter Converter; + + public OutputJsonConverterInner(OutputJsonConverter parent, JsonSerializerOptions options) { + Parent = parent; + Converter = (JsonConverter)options.GetConverter(typeof(T)); + } + + public override Output Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + throw new NotImplementedException("JsonSerialize only supports writing to JSON"); + } + + public override void Write(Utf8JsonWriter writer, Output value, JsonSerializerOptions options) + { + // Sadly we have to block here as converters aren't async + var result = value.DataTask.Result; + // Add the seen dependencies to the resources set + Parent.Resources.AddRange(result.Resources); + if (!result.IsKnown) + { + // If the result isn't known we can just write a null and flag the parent to reject this whole serialization + writer.WriteNullValue(); + Parent.SeenUnknown = true; + } + else + { + // The result is known we can just serialize the inner value, but flag the parent if we've seen a secret + Converter.Write(writer, result.Value, options); + Parent.SeenSecret |= result.IsSecret; + } + } + } + + public bool SeenUnknown {get; private set;} + public bool SeenSecret {get; private set;} + public ImmutableHashSet SeenResources => Resources.ToImmutableHashSet(); + private readonly HashSet Resources; + + public OutputJsonConverter() + { + Resources = new HashSet(); + } + + public override bool CanConvert(Type typeToConvert) + { + if (typeToConvert.IsGenericType) + { + var genericType = typeToConvert.GetGenericTypeDefinition(); + return genericType == typeof(Output<>); + } + return false; + } + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + Type elementType = typeToConvert.GetGenericArguments()[0]; + JsonConverter converter = (JsonConverter)Activator.CreateInstance( + typeof(OutputJsonConverterInner<>).MakeGenericType( + new Type[] { elementType }), + System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public, + binder: null, + args: new object[] { this, options }, + culture: null)!; + return converter; + } + } + /// /// Useful static utility methods for both creating and working with s. /// @@ -106,6 +183,70 @@ public static Output Format(FormattableString formattableString) internal static Output> Concat(Output> values1, Output> values2) => Tuple(values1, values2).Apply(tuple => tuple.Item1.AddRange(tuple.Item2)); + + /// + /// Uses to serialize the given value into a JSON string. + /// + public static Output JsonSerialize(Output value, System.Text.Json.JsonSerializerOptions? options = null) + { + if (value == null) { + throw new ArgumentNullException("value"); + } + + async Task> GetData() + { + var result = await value.DataTask; + + if (!result.IsKnown) { + return new OutputData(result.Resources, "", false, result.IsSecret); + } + + var utf8 = new System.IO.MemoryStream(); + // This needs to handle nested potentially secret and unknown Output values, we do this by + // hooking options to handle any seen Output values. + + // TODO: This can be simplified in net6.0 to just new System.Text.Json.JsonSerializerOptions(options); + var internalOptions = new System.Text.Json.JsonSerializerOptions(); + internalOptions.AllowTrailingCommas = options?.AllowTrailingCommas ?? internalOptions.AllowTrailingCommas; + if (options != null) + { + foreach(var converter in options.Converters) + { + internalOptions.Converters.Add(converter); + } + } + internalOptions.DefaultBufferSize = options?.DefaultBufferSize ?? internalOptions.DefaultBufferSize; + internalOptions.DictionaryKeyPolicy = options?.DictionaryKeyPolicy ?? internalOptions.DictionaryKeyPolicy; + internalOptions.Encoder = options?.Encoder ?? internalOptions.Encoder; + internalOptions.IgnoreNullValues = options?.IgnoreNullValues ?? internalOptions.IgnoreNullValues; + internalOptions.IgnoreReadOnlyProperties = options?.IgnoreReadOnlyProperties ?? internalOptions.IgnoreReadOnlyProperties; + internalOptions.MaxDepth = options?.MaxDepth ?? internalOptions.MaxDepth; + internalOptions.PropertyNameCaseInsensitive = options?.PropertyNameCaseInsensitive ?? internalOptions.PropertyNameCaseInsensitive; + internalOptions.PropertyNamingPolicy = options?.PropertyNamingPolicy ?? internalOptions.PropertyNamingPolicy; + internalOptions.ReadCommentHandling = options?.ReadCommentHandling ?? internalOptions.ReadCommentHandling; + internalOptions.WriteIndented = options?.WriteIndented ?? internalOptions.WriteIndented; + + // Add the magic converter to allow us to do nested outputs + var outputConverter = new OutputJsonConverter(); + internalOptions.Converters.Add(outputConverter); + + await System.Text.Json.JsonSerializer.SerializeAsync(utf8, result.Value, internalOptions); + + // Check if the result is valid or not, that is if we saw any nulls we can just throw away the json string made and return unknown + if (outputConverter.SeenUnknown) { + return new OutputData(result.Resources.Union(outputConverter.SeenResources), "", false, result.IsSecret | outputConverter.SeenSecret); + } + + // GetBuffer returns the entire byte array backing the MemoryStream, wrapping a span of the + // correct length around that rather than just calling ToArray() saves an array copy. + var json = System.Text.Encoding.UTF8.GetString(new ReadOnlySpan(utf8.GetBuffer(), 0, (int)utf8.Length)); + + return new OutputData(result.Resources.Union(outputConverter.SeenResources), json, true, result.IsSecret | outputConverter.SeenSecret); + } + + return new Output(GetData()); + } } /// @@ -128,7 +269,7 @@ internal interface IOutput /// s are a key part of how Pulumi tracks dependencies between s. Because the values of outputs are not available until resources are /// created, these are represented using the special s type, which - /// internally represents two things: an eventually available value of the output and + /// internally represents two things: an eventually available value of the output and /// the dependency on the source(s) of the output value. /// In fact, s is quite similar to . /// Additionally, they carry along dependency information. diff --git a/sdk/go/auto/git_test.go b/sdk/go/auto/git_test.go index 83f03492048f..19c34362bed5 100644 --- a/sdk/go/auto/git_test.go +++ b/sdk/go/auto/git_test.go @@ -21,10 +21,7 @@ func TestGitClone(t *testing.T) { // This makes a git repo to clone from, so to avoid relying on something at GitHub that could // change or be inaccessible. - tmpDir, err := os.MkdirTemp("", "pulumi-git-test") - assert.NoError(t, err) - assert.True(t, len(tmpDir) > 1) - t.Cleanup(func() { os.RemoveAll(tmpDir) }) + tmpDir := t.TempDir() originDir := filepath.Join(tmpDir, "origin") origin, err := git.PlainInit(originDir, false) diff --git a/sdk/go/common/resource/asset_test.go b/sdk/go/common/resource/asset_test.go index 02657eced118..31a2a8435714 100644 --- a/sdk/go/common/resource/asset_test.go +++ b/sdk/go/common/resource/asset_test.go @@ -444,8 +444,7 @@ func TestNestedArchive(t *testing.T) { t.Parallel() // Create temp dir and place some files. - dirName, err := os.MkdirTemp("", "") - assert.Nil(t, err) + dirName := t.TempDir() assert.NoError(t, os.MkdirAll(filepath.Join(dirName, "foo", "bar"), 0777)) assert.NoError(t, os.WriteFile(filepath.Join(dirName, "foo", "a.txt"), []byte("a"), 0777)) assert.NoError(t, os.WriteFile(filepath.Join(dirName, "foo", "bar", "b.txt"), []byte("b"), 0777)) @@ -487,8 +486,7 @@ func TestFileReferencedThroughMultiplePaths(t *testing.T) { t.Parallel() // Create temp dir and place some files. - dirName, err := os.MkdirTemp("", "") - assert.Nil(t, err) + dirName := t.TempDir() assert.NoError(t, os.MkdirAll(filepath.Join(dirName, "foo", "bar"), 0777)) assert.NoError(t, os.WriteFile(filepath.Join(dirName, "foo", "bar", "b.txt"), []byte("b"), 0777)) diff --git a/sdk/go/common/resource/plugin/provider_test.go b/sdk/go/common/resource/plugin/provider_test.go index 3626db040564..5f9f38bd7224 100644 --- a/sdk/go/common/resource/plugin/provider_test.go +++ b/sdk/go/common/resource/plugin/provider_test.go @@ -128,3 +128,6 @@ func TestNewDetailedDiff(t *testing.T) { }) } } + +// Assert that UnimplementedProvider implements Provider +var _ = Provider((*UnimplementedProvider)(nil)) diff --git a/sdk/go/common/resource/plugin/provider_unimplemented.go b/sdk/go/common/resource/plugin/provider_unimplemented.go new file mode 100644 index 000000000000..b065046d4002 --- /dev/null +++ b/sdk/go/common/resource/plugin/provider_unimplemented.go @@ -0,0 +1,85 @@ +// Copyright 2016-2022, 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. + +// nolint: lll +package plugin + +import ( + "github.com/pulumi/pulumi/sdk/v3/go/common/resource" + "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" + "github.com/pulumi/pulumi/sdk/v3/go/common/workspace" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// UnimplementedProvider can be embedded to have forward compatible implementations. +type UnimplementedProvider struct{} + +func (p *UnimplementedProvider) Close() error { + return status.Error(codes.Unimplemented, "Close is not yet implemented") +} +func (p *UnimplementedProvider) SignalCancellation() error { + return status.Error(codes.Unimplemented, "SignalCancellation is not yet implemented") +} +func (p *UnimplementedProvider) Pkg() tokens.Package { + return tokens.Package("") +} +func (p *UnimplementedProvider) GetSchema(version int) ([]byte, error) { + return nil, status.Error(codes.Unimplemented, "GetSchema is not yet implemented") +} +func (p *UnimplementedProvider) CheckConfig(urn resource.URN, olds resource.PropertyMap, news resource.PropertyMap, allowUnknowns bool) (resource.PropertyMap, []CheckFailure, error) { + return resource.PropertyMap{}, nil, status.Error(codes.Unimplemented, "CheckConfig is not yet implemented") +} +func (p *UnimplementedProvider) DiffConfig(urn resource.URN, olds resource.PropertyMap, news resource.PropertyMap, allowUnknowns bool, ignoreChanges []string) (DiffResult, error) { + return DiffResult{}, status.Error(codes.Unimplemented, "DiffConfig is not yet implemented") +} +func (p *UnimplementedProvider) Configure(inputs resource.PropertyMap) error { + return status.Error(codes.Unimplemented, "Configure is not yet implemented") +} +func (p *UnimplementedProvider) Check(urn resource.URN, olds resource.PropertyMap, news resource.PropertyMap, allowUnknowns bool, randomSeed []byte) (resource.PropertyMap, []CheckFailure, error) { + return resource.PropertyMap{}, nil, status.Error(codes.Unimplemented, "Check is not yet implemented") +} +func (p *UnimplementedProvider) Diff(urn resource.URN, id resource.ID, olds resource.PropertyMap, news resource.PropertyMap, allowUnknowns bool, ignoreChanges []string) (DiffResult, error) { + return DiffResult{}, status.Error(codes.Unimplemented, "Diff is not yet implemented") +} +func (p *UnimplementedProvider) Create(urn resource.URN, news resource.PropertyMap, timeout float64, preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) { + return resource.ID(""), resource.PropertyMap{}, resource.StatusUnknown, status.Error(codes.Unimplemented, "Create is not yet implemented") +} +func (p *UnimplementedProvider) Read(urn resource.URN, id resource.ID, inputs resource.PropertyMap, state resource.PropertyMap) (ReadResult, resource.Status, error) { + return ReadResult{}, resource.StatusUnknown, status.Error(codes.Unimplemented, "Read is not yet implemented") +} +func (p *UnimplementedProvider) Update(urn resource.URN, id resource.ID, olds resource.PropertyMap, news resource.PropertyMap, timeout float64, ignoreChanges []string, preview bool) (resource.PropertyMap, resource.Status, error) { + return resource.PropertyMap{}, resource.StatusUnknown, status.Error(codes.Unimplemented, "Update is not yet implemented") +} +func (p *UnimplementedProvider) Delete(urn resource.URN, id resource.ID, props resource.PropertyMap, timeout float64) (resource.Status, error) { + return resource.StatusUnknown, status.Error(codes.Unimplemented, "Delete is not yet implemented") +} +func (p *UnimplementedProvider) Construct(info ConstructInfo, typ tokens.Type, name tokens.QName, parent resource.URN, inputs resource.PropertyMap, options ConstructOptions) (ConstructResult, error) { + return ConstructResult{}, status.Error(codes.Unimplemented, "Construct is not yet implemented") +} +func (p *UnimplementedProvider) Invoke(tok tokens.ModuleMember, args resource.PropertyMap) (resource.PropertyMap, []CheckFailure, error) { + return resource.PropertyMap{}, nil, status.Error(codes.Unimplemented, "Invoke is not yet implemented") +} +func (p *UnimplementedProvider) StreamInvoke(tok tokens.ModuleMember, args resource.PropertyMap, onNext func(resource.PropertyMap) error) ([]CheckFailure, error) { + return nil, status.Error(codes.Unimplemented, "StreamInvoke is not yet implemented") +} +func (p *UnimplementedProvider) Call(tok tokens.ModuleMember, args resource.PropertyMap, info CallInfo, options CallOptions) (CallResult, error) { + return CallResult{}, status.Error(codes.Unimplemented, "Call is not yet implemented") +} +func (p *UnimplementedProvider) GetPluginInfo() (workspace.PluginInfo, error) { + return workspace.PluginInfo{}, status.Error(codes.Unimplemented, "GetPluginInfo is not yet implemented") +} +func (p *UnimplementedProvider) GetMapping(key string) ([]byte, string, error) { + return nil, "", status.Error(codes.Unimplemented, "GetMapping is not yet implemented") +} diff --git a/sdk/go/common/util/archive/archive_test.go b/sdk/go/common/util/archive/archive_test.go index 187cfe4b86a0..f84d7002ef5d 100644 --- a/sdk/go/common/util/archive/archive_test.go +++ b/sdk/go/common/util/archive/archive_test.go @@ -28,8 +28,6 @@ import ( "strings" "testing" - "github.com/pulumi/pulumi/sdk/v3/go/common/util/contract" - "github.com/stretchr/testify/assert" ) @@ -122,7 +120,7 @@ func TestIgnoreNestedGitignore(t *testing.T) { func doArchiveTest(t *testing.T, path string, files ...fileContents) { doTest := func(prefixPathInsideTar, path string) { - tarball, err := archiveContents(prefixPathInsideTar, path, files...) + tarball, err := archiveContents(t, prefixPathInsideTar, path, files...) assert.NoError(t, err) tarReader := bytes.NewReader(tarball) @@ -137,15 +135,8 @@ func doArchiveTest(t *testing.T, path string, files ...fileContents) { } } -func archiveContents(prefixPathInsideTar, path string, files ...fileContents) ([]byte, error) { - dir, err := os.MkdirTemp("", "archive-test") - if err != nil { - return nil, err - } - - defer func() { - contract.IgnoreError(os.RemoveAll(dir)) - }() +func archiveContents(t *testing.T, prefixPathInsideTar, path string, files ...fileContents) ([]byte, error) { + dir := t.TempDir() for _, file := range files { name := file.name diff --git a/sdk/go/common/workspace/paths_test.go b/sdk/go/common/workspace/paths_test.go index be36e18a64c0..db3e417526be 100644 --- a/sdk/go/common/workspace/paths_test.go +++ b/sdk/go/common/workspace/paths_test.go @@ -15,7 +15,6 @@ package workspace import ( - "io/ioutil" "os" "path/filepath" "testing" @@ -28,9 +27,8 @@ import ( // that directory. However DetectProjectAndPath will do symlink resolution, while ioutil.TempDir normally does // not. This can lead to asserts especially on macos where TmpDir will have returned /var/folders/XX, but // after sym link resolution that is /private/var/folders/XX. -func mkTempDir(t *testing.T, pattern string) string { - tmpDir, err := ioutil.TempDir("", pattern) - assert.NoError(t, err) +func mkTempDir(t *testing.T) string { + tmpDir := t.TempDir() result, err := filepath.EvalSymlinks(tmpDir) assert.NoError(t, err) return result @@ -38,7 +36,7 @@ func mkTempDir(t *testing.T, pattern string) string { //nolint:paralleltest // Theses test use and change the current working directory func TestDetectProjectAndPath(t *testing.T) { - tmpDir := mkTempDir(t, "TestDetectProjectAndPath") + tmpDir := mkTempDir(t) cwd, err := os.Getwd() assert.NoError(t, err) defer func() { err := os.Chdir(cwd); assert.NoError(t, err) }() @@ -99,7 +97,7 @@ func TestProjectStackPath(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - tmpDir := mkTempDir(t, "TestProjectStackPath") + tmpDir := mkTempDir(t) cwd, err := os.Getwd() assert.NoError(t, err) defer func() { err := os.Chdir(cwd); assert.NoError(t, err) }() diff --git a/sdk/go/common/workspace/plugins_install_test.go b/sdk/go/common/workspace/plugins_install_test.go index fb703d40ece2..52d8fa1d8144 100644 --- a/sdk/go/common/workspace/plugins_install_test.go +++ b/sdk/go/common/workspace/plugins_install_test.go @@ -84,8 +84,7 @@ func prepareTestPluginTGZ(t *testing.T, files map[string][]byte) io.ReadCloser { func prepareTestDir(t *testing.T, files map[string][]byte) (string, io.ReadCloser, PluginSpec) { tarball := prepareTestPluginTGZ(t, files) - dir, err := ioutil.TempDir("", "plugins-test-dir") - require.NoError(t, err) + dir := t.TempDir() v1 := semver.MustParse("0.1.0") plugin := PluginSpec{ @@ -163,7 +162,6 @@ func testPluginInstall(t *testing.T, expectedDir string, files map[string][]byte } dir, tarball, plugin := prepareTestDir(t, files) - defer os.RemoveAll(dir) err := plugin.Install(tarball, false) assert.NoError(t, err) @@ -182,7 +180,6 @@ func TestInstallNoDeps(t *testing.T) { content := []byte("hello\n") dir, tarball, plugin := prepareTestDir(t, map[string][]byte{name: content}) - defer os.RemoveAll(dir) err := plugin.Install(tarball, false) require.NoError(t, err) @@ -201,7 +198,6 @@ func TestReinstall(t *testing.T) { content := []byte("hello\n") dir, tarball, plugin := prepareTestDir(t, map[string][]byte{name: content}) - defer os.RemoveAll(dir) err := plugin.Install(tarball, false) require.NoError(t, err) @@ -231,7 +227,6 @@ func TestConcurrentInstalls(t *testing.T) { content := []byte("hello\n") dir, tarball, plugin := prepareTestDir(t, map[string][]byte{name: content}) - defer os.RemoveAll(dir) assertSuccess := func() PluginInfo { pluginInfo := assertPluginInstalled(t, dir, plugin) @@ -266,7 +261,6 @@ func TestConcurrentInstalls(t *testing.T) { func TestInstallCleansOldFiles(t *testing.T) { dir, tarball, plugin := prepareTestDir(t, nil) - defer os.RemoveAll(dir) // Leftover temp dirs. tempDir1, err := ioutil.TempDir(dir, fmt.Sprintf("%s.tmp", plugin.Dir())) @@ -298,7 +292,6 @@ func TestInstallCleansOldFiles(t *testing.T) { func TestGetPluginsSkipsPartial(t *testing.T) { dir, tarball, plugin := prepareTestDir(t, nil) - defer os.RemoveAll(dir) err := plugin.Install(tarball, false) assert.NoError(t, err) diff --git a/sdk/go/common/workspace/plugins_test.go b/sdk/go/common/workspace/plugins_test.go index 51993a79ed80..99b0b8f36ed0 100644 --- a/sdk/go/common/workspace/plugins_test.go +++ b/sdk/go/common/workspace/plugins_test.go @@ -812,7 +812,7 @@ func TestParsePluginDownloadURLOverride(t *testing.T) { //nolint:paralleltest // changes directory for process func TestUnmarshalProjectWithProviderList(t *testing.T) { t.Parallel() - tempdir, _ := ioutil.TempDir("", "test-env") + tempdir := t.TempDir() pyaml := filepath.Join(tempdir, "Pulumi.yaml") //write to pyaml diff --git a/sdk/go/pulumi/generate/templates/types_builtins.go.template b/sdk/go/pulumi/generate/templates/types_builtins.go.template index 5ac68f4f2a83..925b96e08f2c 100644 --- a/sdk/go/pulumi/generate/templates/types_builtins.go.template +++ b/sdk/go/pulumi/generate/templates/types_builtins.go.template @@ -19,6 +19,7 @@ package pulumi import ( "context" + "fmt" "reflect" ) @@ -55,6 +56,7 @@ func {{.Name}}FromPtr(v *{{.ElemElementType}}) {{.Name}}Input { } {{end}} {{if .DefineInputMethods}} + // ElementType returns the element type of this Input ({{.ElementType}}). func ({{.InputType}}) ElementType() reflect.Type { return {{.Name | Unexported}}Type @@ -92,6 +94,10 @@ func (in {{.InputType}}) To{{.Name}}PtrOutputWithContext(ctx context.Context) {{ // {{.Name}}Output is an Output that returns {{.ElementType}} values. type {{.Name}}Output struct { *OutputState } +func ({{.Name}}Output) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ({{.ElementType}}). func ({{.Name}}Output) ElementType() reflect.Type { return {{.Name | Unexported}}Type diff --git a/sdk/go/pulumi/types.go b/sdk/go/pulumi/types.go index 2b8ce0a10234..305c76e8b2f5 100644 --- a/sdk/go/pulumi/types.go +++ b/sdk/go/pulumi/types.go @@ -17,6 +17,7 @@ package pulumi import ( "context" + "encoding/json" "errors" "fmt" "reflect" @@ -602,6 +603,23 @@ func AllWithContext(ctx context.Context, inputs ...interface{}) ArrayOutput { return ToOutputWithContext(ctx, inputs).(ArrayOutput) } +// JSONMarshal uses "encoding/json".Marshal to serialize the given Output value into a JSON string. +func JSONMarshal(v interface{}) StringOutput { + return JSONMarshalWithContext(context.Background(), v) +} + +// JSONMarshalWithContext uses "encoding/json".Marshal to serialize the given Output value into a JSON string. +func JSONMarshalWithContext(ctx context.Context, v interface{}) StringOutput { + o := ToOutputWithContext(ctx, v) + return o.ApplyTWithContext(ctx, func(_ context.Context, v interface{}) (string, error) { + json, err := json.Marshal(v) + if err != nil { + return "", err + } + return string(json), nil + }).(StringOutput) +} + func gatherJoins(v interface{}) workGroups { if v == nil { return nil @@ -1065,6 +1083,10 @@ func anyWithContext(ctx context.Context, join *workGroup, v interface{}) AnyOutp type AnyOutput struct{ *OutputState } +func (AnyOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + func (AnyOutput) ElementType() reflect.Type { return anyType } @@ -1129,6 +1151,10 @@ func convert(v interface{}, to reflect.Type) interface{} { // TODO: ResourceOutput and the init() should probably be code generated. type ResourceOutput struct{ *OutputState } +func (ResourceOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (Resource). func (ResourceOutput) ElementType() reflect.Type { return reflect.TypeOf((*Resource)(nil)).Elem() @@ -1192,6 +1218,10 @@ func (in ResourceArray) ToResourceArrayOutputWithContext(ctx context.Context) Re // ResourceArrayOutput is an Output that returns []Resource values. type ResourceArrayOutput struct{ *OutputState } +func (ResourceArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([]Resource). func (ResourceArrayOutput) ElementType() reflect.Type { return resourceArrayType diff --git a/sdk/go/pulumi/types_builtins.go b/sdk/go/pulumi/types_builtins.go index 868c49c4ecbd..86b4a46648d1 100644 --- a/sdk/go/pulumi/types_builtins.go +++ b/sdk/go/pulumi/types_builtins.go @@ -19,6 +19,7 @@ package pulumi import ( "context" + "fmt" "reflect" ) @@ -56,6 +57,10 @@ func (in *archive) ToAssetOrArchiveOutputWithContext(ctx context.Context) AssetO // ArchiveOutput is an Output that returns Archive values. type ArchiveOutput struct{ *OutputState } +func (ArchiveOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (Archive). func (ArchiveOutput) ElementType() reflect.Type { return archiveType @@ -108,6 +113,10 @@ func (in ArchiveArray) ToArchiveArrayOutputWithContext(ctx context.Context) Arch // ArchiveArrayOutput is an Output that returns []Archive values. type ArchiveArrayOutput struct{ *OutputState } +func (ArchiveArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([]Archive). func (ArchiveArrayOutput) ElementType() reflect.Type { return archiveArrayType @@ -180,6 +189,10 @@ func (in ArchiveMap) ToArchiveMapOutputWithContext(ctx context.Context) ArchiveM // ArchiveMapOutput is an Output that returns map[string]Archive values. type ArchiveMapOutput struct{ *OutputState } +func (ArchiveMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string]Archive). func (ArchiveMapOutput) ElementType() reflect.Type { return archiveMapType @@ -245,6 +258,10 @@ func (in ArchiveArrayMap) ToArchiveArrayMapOutputWithContext(ctx context.Context // ArchiveArrayMapOutput is an Output that returns map[string][]Archive values. type ArchiveArrayMapOutput struct{ *OutputState } +func (ArchiveArrayMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string][]Archive). func (ArchiveArrayMapOutput) ElementType() reflect.Type { return archiveArrayMapType @@ -310,6 +327,10 @@ func (in ArchiveMapArray) ToArchiveMapArrayOutputWithContext(ctx context.Context // ArchiveMapArrayOutput is an Output that returns []map[string]Archive values. type ArchiveMapArrayOutput struct{ *OutputState } +func (ArchiveMapArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([]map[string]Archive). func (ArchiveMapArrayOutput) ElementType() reflect.Type { return archiveMapArrayType @@ -382,6 +403,10 @@ func (in ArchiveMapMap) ToArchiveMapMapOutputWithContext(ctx context.Context) Ar // ArchiveMapMapOutput is an Output that returns map[string]map[string]Archive values. type ArchiveMapMapOutput struct{ *OutputState } +func (ArchiveMapMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string]map[string]Archive). func (ArchiveMapMapOutput) ElementType() reflect.Type { return archiveMapMapType @@ -447,6 +472,10 @@ func (in ArchiveArrayArray) ToArchiveArrayArrayOutputWithContext(ctx context.Con // ArchiveArrayArrayOutput is an Output that returns [][]Archive values. type ArchiveArrayArrayOutput struct{ *OutputState } +func (ArchiveArrayArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([][]Archive). func (ArchiveArrayArrayOutput) ElementType() reflect.Type { return archiveArrayArrayType @@ -524,6 +553,10 @@ func (in *asset) ToAssetOrArchiveOutputWithContext(ctx context.Context) AssetOrA // AssetOutput is an Output that returns Asset values. type AssetOutput struct{ *OutputState } +func (AssetOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (Asset). func (AssetOutput) ElementType() reflect.Type { return assetType @@ -576,6 +609,10 @@ func (in AssetArray) ToAssetArrayOutputWithContext(ctx context.Context) AssetArr // AssetArrayOutput is an Output that returns []Asset values. type AssetArrayOutput struct{ *OutputState } +func (AssetArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([]Asset). func (AssetArrayOutput) ElementType() reflect.Type { return assetArrayType @@ -648,6 +685,10 @@ func (in AssetMap) ToAssetMapOutputWithContext(ctx context.Context) AssetMapOutp // AssetMapOutput is an Output that returns map[string]Asset values. type AssetMapOutput struct{ *OutputState } +func (AssetMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string]Asset). func (AssetMapOutput) ElementType() reflect.Type { return assetMapType @@ -713,6 +754,10 @@ func (in AssetArrayMap) ToAssetArrayMapOutputWithContext(ctx context.Context) As // AssetArrayMapOutput is an Output that returns map[string][]Asset values. type AssetArrayMapOutput struct{ *OutputState } +func (AssetArrayMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string][]Asset). func (AssetArrayMapOutput) ElementType() reflect.Type { return assetArrayMapType @@ -778,6 +823,10 @@ func (in AssetMapArray) ToAssetMapArrayOutputWithContext(ctx context.Context) As // AssetMapArrayOutput is an Output that returns []map[string]Asset values. type AssetMapArrayOutput struct{ *OutputState } +func (AssetMapArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([]map[string]Asset). func (AssetMapArrayOutput) ElementType() reflect.Type { return assetMapArrayType @@ -850,6 +899,10 @@ func (in AssetMapMap) ToAssetMapMapOutputWithContext(ctx context.Context) AssetM // AssetMapMapOutput is an Output that returns map[string]map[string]Asset values. type AssetMapMapOutput struct{ *OutputState } +func (AssetMapMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string]map[string]Asset). func (AssetMapMapOutput) ElementType() reflect.Type { return assetMapMapType @@ -915,6 +968,10 @@ func (in AssetArrayArray) ToAssetArrayArrayOutputWithContext(ctx context.Context // AssetArrayArrayOutput is an Output that returns [][]Asset values. type AssetArrayArrayOutput struct{ *OutputState } +func (AssetArrayArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([][]Asset). func (AssetArrayArrayOutput) ElementType() reflect.Type { return assetArrayArrayType @@ -971,6 +1028,10 @@ type AssetOrArchiveInput interface { // AssetOrArchiveOutput is an Output that returns AssetOrArchive values. type AssetOrArchiveOutput struct{ *OutputState } +func (AssetOrArchiveOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (AssetOrArchive). func (AssetOrArchiveOutput) ElementType() reflect.Type { return assetOrArchiveType @@ -1013,6 +1074,10 @@ func (in AssetOrArchiveArray) ToAssetOrArchiveArrayOutputWithContext(ctx context // AssetOrArchiveArrayOutput is an Output that returns []AssetOrArchive values. type AssetOrArchiveArrayOutput struct{ *OutputState } +func (AssetOrArchiveArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([]AssetOrArchive). func (AssetOrArchiveArrayOutput) ElementType() reflect.Type { return assetOrArchiveArrayType @@ -1069,6 +1134,10 @@ func (in AssetOrArchiveMap) ToAssetOrArchiveMapOutputWithContext(ctx context.Con // AssetOrArchiveMapOutput is an Output that returns map[string]AssetOrArchive values. type AssetOrArchiveMapOutput struct{ *OutputState } +func (AssetOrArchiveMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string]AssetOrArchive). func (AssetOrArchiveMapOutput) ElementType() reflect.Type { return assetOrArchiveMapType @@ -1118,6 +1187,10 @@ func (in AssetOrArchiveArrayMap) ToAssetOrArchiveArrayMapOutputWithContext(ctx c // AssetOrArchiveArrayMapOutput is an Output that returns map[string][]AssetOrArchive values. type AssetOrArchiveArrayMapOutput struct{ *OutputState } +func (AssetOrArchiveArrayMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string][]AssetOrArchive). func (AssetOrArchiveArrayMapOutput) ElementType() reflect.Type { return assetOrArchiveArrayMapType @@ -1167,6 +1240,10 @@ func (in AssetOrArchiveMapArray) ToAssetOrArchiveMapArrayOutputWithContext(ctx c // AssetOrArchiveMapArrayOutput is an Output that returns []map[string]AssetOrArchive values. type AssetOrArchiveMapArrayOutput struct{ *OutputState } +func (AssetOrArchiveMapArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([]map[string]AssetOrArchive). func (AssetOrArchiveMapArrayOutput) ElementType() reflect.Type { return assetOrArchiveMapArrayType @@ -1223,6 +1300,10 @@ func (in AssetOrArchiveMapMap) ToAssetOrArchiveMapMapOutputWithContext(ctx conte // AssetOrArchiveMapMapOutput is an Output that returns map[string]map[string]AssetOrArchive values. type AssetOrArchiveMapMapOutput struct{ *OutputState } +func (AssetOrArchiveMapMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string]map[string]AssetOrArchive). func (AssetOrArchiveMapMapOutput) ElementType() reflect.Type { return assetOrArchiveMapMapType @@ -1272,6 +1353,10 @@ func (in AssetOrArchiveArrayArray) ToAssetOrArchiveArrayArrayOutputWithContext(c // AssetOrArchiveArrayArrayOutput is an Output that returns [][]AssetOrArchive values. type AssetOrArchiveArrayArrayOutput struct{ *OutputState } +func (AssetOrArchiveArrayArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([][]AssetOrArchive). func (AssetOrArchiveArrayArrayOutput) ElementType() reflect.Type { return assetOrArchiveArrayArrayType @@ -1339,6 +1424,10 @@ func (in Bool) ToBoolPtrOutputWithContext(ctx context.Context) BoolPtrOutput { // BoolOutput is an Output that returns bool values. type BoolOutput struct{ *OutputState } +func (BoolOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (bool). func (BoolOutput) ElementType() reflect.Type { return boolType @@ -1401,6 +1490,10 @@ func (in *boolPtr) ToBoolPtrOutputWithContext(ctx context.Context) BoolPtrOutput // BoolPtrOutput is an Output that returns *bool values. type BoolPtrOutput struct{ *OutputState } +func (BoolPtrOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (*bool). func (BoolPtrOutput) ElementType() reflect.Type { return boolPtrType @@ -1454,6 +1547,10 @@ func (in BoolArray) ToBoolArrayOutputWithContext(ctx context.Context) BoolArrayO // BoolArrayOutput is an Output that returns []bool values. type BoolArrayOutput struct{ *OutputState } +func (BoolArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([]bool). func (BoolArrayOutput) ElementType() reflect.Type { return boolArrayType @@ -1526,6 +1623,10 @@ func (in BoolMap) ToBoolMapOutputWithContext(ctx context.Context) BoolMapOutput // BoolMapOutput is an Output that returns map[string]bool values. type BoolMapOutput struct{ *OutputState } +func (BoolMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string]bool). func (BoolMapOutput) ElementType() reflect.Type { return boolMapType @@ -1591,6 +1692,10 @@ func (in BoolArrayMap) ToBoolArrayMapOutputWithContext(ctx context.Context) Bool // BoolArrayMapOutput is an Output that returns map[string][]bool values. type BoolArrayMapOutput struct{ *OutputState } +func (BoolArrayMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string][]bool). func (BoolArrayMapOutput) ElementType() reflect.Type { return boolArrayMapType @@ -1656,6 +1761,10 @@ func (in BoolMapArray) ToBoolMapArrayOutputWithContext(ctx context.Context) Bool // BoolMapArrayOutput is an Output that returns []map[string]bool values. type BoolMapArrayOutput struct{ *OutputState } +func (BoolMapArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([]map[string]bool). func (BoolMapArrayOutput) ElementType() reflect.Type { return boolMapArrayType @@ -1728,6 +1837,10 @@ func (in BoolMapMap) ToBoolMapMapOutputWithContext(ctx context.Context) BoolMapM // BoolMapMapOutput is an Output that returns map[string]map[string]bool values. type BoolMapMapOutput struct{ *OutputState } +func (BoolMapMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string]map[string]bool). func (BoolMapMapOutput) ElementType() reflect.Type { return boolMapMapType @@ -1793,6 +1906,10 @@ func (in BoolArrayArray) ToBoolArrayArrayOutputWithContext(ctx context.Context) // BoolArrayArrayOutput is an Output that returns [][]bool values. type BoolArrayArrayOutput struct{ *OutputState } +func (BoolArrayArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([][]bool). func (BoolArrayArrayOutput) ElementType() reflect.Type { return boolArrayArrayType @@ -1876,6 +1993,10 @@ func (in Float64) ToFloat64PtrOutputWithContext(ctx context.Context) Float64PtrO // Float64Output is an Output that returns float64 values. type Float64Output struct{ *OutputState } +func (Float64Output) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (float64). func (Float64Output) ElementType() reflect.Type { return float64Type @@ -1938,6 +2059,10 @@ func (in *float64Ptr) ToFloat64PtrOutputWithContext(ctx context.Context) Float64 // Float64PtrOutput is an Output that returns *float64 values. type Float64PtrOutput struct{ *OutputState } +func (Float64PtrOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (*float64). func (Float64PtrOutput) ElementType() reflect.Type { return float64PtrType @@ -1991,6 +2116,10 @@ func (in Float64Array) ToFloat64ArrayOutputWithContext(ctx context.Context) Floa // Float64ArrayOutput is an Output that returns []float64 values. type Float64ArrayOutput struct{ *OutputState } +func (Float64ArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([]float64). func (Float64ArrayOutput) ElementType() reflect.Type { return float64ArrayType @@ -2063,6 +2192,10 @@ func (in Float64Map) ToFloat64MapOutputWithContext(ctx context.Context) Float64M // Float64MapOutput is an Output that returns map[string]float64 values. type Float64MapOutput struct{ *OutputState } +func (Float64MapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string]float64). func (Float64MapOutput) ElementType() reflect.Type { return float64MapType @@ -2128,6 +2261,10 @@ func (in Float64ArrayMap) ToFloat64ArrayMapOutputWithContext(ctx context.Context // Float64ArrayMapOutput is an Output that returns map[string][]float64 values. type Float64ArrayMapOutput struct{ *OutputState } +func (Float64ArrayMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string][]float64). func (Float64ArrayMapOutput) ElementType() reflect.Type { return float64ArrayMapType @@ -2193,6 +2330,10 @@ func (in Float64MapArray) ToFloat64MapArrayOutputWithContext(ctx context.Context // Float64MapArrayOutput is an Output that returns []map[string]float64 values. type Float64MapArrayOutput struct{ *OutputState } +func (Float64MapArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([]map[string]float64). func (Float64MapArrayOutput) ElementType() reflect.Type { return float64MapArrayType @@ -2265,6 +2406,10 @@ func (in Float64MapMap) ToFloat64MapMapOutputWithContext(ctx context.Context) Fl // Float64MapMapOutput is an Output that returns map[string]map[string]float64 values. type Float64MapMapOutput struct{ *OutputState } +func (Float64MapMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string]map[string]float64). func (Float64MapMapOutput) ElementType() reflect.Type { return float64MapMapType @@ -2330,6 +2475,10 @@ func (in Float64ArrayArray) ToFloat64ArrayArrayOutputWithContext(ctx context.Con // Float64ArrayArrayOutput is an Output that returns [][]float64 values. type Float64ArrayArrayOutput struct{ *OutputState } +func (Float64ArrayArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([][]float64). func (Float64ArrayArrayOutput) ElementType() reflect.Type { return float64ArrayArrayType @@ -2418,6 +2567,10 @@ func (in ID) ToIDPtrOutputWithContext(ctx context.Context) IDPtrOutput { // IDOutput is an Output that returns ID values. type IDOutput struct{ *OutputState } +func (IDOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (ID). func (IDOutput) ElementType() reflect.Type { return idType @@ -2490,6 +2643,10 @@ func (in *idPtr) ToIDPtrOutputWithContext(ctx context.Context) IDPtrOutput { // IDPtrOutput is an Output that returns *ID values. type IDPtrOutput struct{ *OutputState } +func (IDPtrOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (*ID). func (IDPtrOutput) ElementType() reflect.Type { return iDPtrType @@ -2543,6 +2700,10 @@ func (in IDArray) ToIDArrayOutputWithContext(ctx context.Context) IDArrayOutput // IDArrayOutput is an Output that returns []ID values. type IDArrayOutput struct{ *OutputState } +func (IDArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([]ID). func (IDArrayOutput) ElementType() reflect.Type { return iDArrayType @@ -2615,6 +2776,10 @@ func (in IDMap) ToIDMapOutputWithContext(ctx context.Context) IDMapOutput { // IDMapOutput is an Output that returns map[string]ID values. type IDMapOutput struct{ *OutputState } +func (IDMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string]ID). func (IDMapOutput) ElementType() reflect.Type { return iDMapType @@ -2680,6 +2845,10 @@ func (in IDArrayMap) ToIDArrayMapOutputWithContext(ctx context.Context) IDArrayM // IDArrayMapOutput is an Output that returns map[string][]ID values. type IDArrayMapOutput struct{ *OutputState } +func (IDArrayMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string][]ID). func (IDArrayMapOutput) ElementType() reflect.Type { return iDArrayMapType @@ -2745,6 +2914,10 @@ func (in IDMapArray) ToIDMapArrayOutputWithContext(ctx context.Context) IDMapArr // IDMapArrayOutput is an Output that returns []map[string]ID values. type IDMapArrayOutput struct{ *OutputState } +func (IDMapArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([]map[string]ID). func (IDMapArrayOutput) ElementType() reflect.Type { return iDMapArrayType @@ -2817,6 +2990,10 @@ func (in IDMapMap) ToIDMapMapOutputWithContext(ctx context.Context) IDMapMapOutp // IDMapMapOutput is an Output that returns map[string]map[string]ID values. type IDMapMapOutput struct{ *OutputState } +func (IDMapMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string]map[string]ID). func (IDMapMapOutput) ElementType() reflect.Type { return iDMapMapType @@ -2882,6 +3059,10 @@ func (in IDArrayArray) ToIDArrayArrayOutputWithContext(ctx context.Context) IDAr // IDArrayArrayOutput is an Output that returns [][]ID values. type IDArrayArrayOutput struct{ *OutputState } +func (IDArrayArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([][]ID). func (IDArrayArrayOutput) ElementType() reflect.Type { return iDArrayArrayType @@ -2954,6 +3135,10 @@ func (in Array) ToArrayOutputWithContext(ctx context.Context) ArrayOutput { // ArrayOutput is an Output that returns []interface{} values. type ArrayOutput struct{ *OutputState } +func (ArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([]interface{}). func (ArrayOutput) ElementType() reflect.Type { return arrayType @@ -3026,6 +3211,10 @@ func (in Map) ToMapOutputWithContext(ctx context.Context) MapOutput { // MapOutput is an Output that returns map[string]interface{} values. type MapOutput struct{ *OutputState } +func (MapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string]interface{}). func (MapOutput) ElementType() reflect.Type { return mapType @@ -3091,6 +3280,10 @@ func (in ArrayMap) ToArrayMapOutputWithContext(ctx context.Context) ArrayMapOutp // ArrayMapOutput is an Output that returns map[string][]interface{} values. type ArrayMapOutput struct{ *OutputState } +func (ArrayMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string][]interface{}). func (ArrayMapOutput) ElementType() reflect.Type { return arrayMapType @@ -3156,6 +3349,10 @@ func (in MapArray) ToMapArrayOutputWithContext(ctx context.Context) MapArrayOutp // MapArrayOutput is an Output that returns []map[string]interface{} values. type MapArrayOutput struct{ *OutputState } +func (MapArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([]map[string]interface{}). func (MapArrayOutput) ElementType() reflect.Type { return mapArrayType @@ -3228,6 +3425,10 @@ func (in MapMap) ToMapMapOutputWithContext(ctx context.Context) MapMapOutput { // MapMapOutput is an Output that returns map[string]map[string]interface{} values. type MapMapOutput struct{ *OutputState } +func (MapMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string]map[string]interface{}). func (MapMapOutput) ElementType() reflect.Type { return mapMapType @@ -3293,6 +3494,10 @@ func (in ArrayArray) ToArrayArrayOutputWithContext(ctx context.Context) ArrayArr // ArrayArrayOutput is an Output that returns [][]interface{} values. type ArrayArrayOutput struct{ *OutputState } +func (ArrayArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([][]interface{}). func (ArrayArrayOutput) ElementType() reflect.Type { return arrayArrayType @@ -3365,6 +3570,10 @@ func (in ArrayArrayMap) ToArrayArrayMapOutputWithContext(ctx context.Context) Ar // ArrayArrayMapOutput is an Output that returns map[string][][]interface{} values. type ArrayArrayMapOutput struct{ *OutputState } +func (ArrayArrayMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string][][]interface{}). func (ArrayArrayMapOutput) ElementType() reflect.Type { return arrayArrayMapType @@ -3441,6 +3650,10 @@ func (in Int) ToIntPtrOutputWithContext(ctx context.Context) IntPtrOutput { // IntOutput is an Output that returns int values. type IntOutput struct{ *OutputState } +func (IntOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (int). func (IntOutput) ElementType() reflect.Type { return intType @@ -3503,6 +3716,10 @@ func (in *intPtr) ToIntPtrOutputWithContext(ctx context.Context) IntPtrOutput { // IntPtrOutput is an Output that returns *int values. type IntPtrOutput struct{ *OutputState } +func (IntPtrOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (*int). func (IntPtrOutput) ElementType() reflect.Type { return intPtrType @@ -3556,6 +3773,10 @@ func (in IntArray) ToIntArrayOutputWithContext(ctx context.Context) IntArrayOutp // IntArrayOutput is an Output that returns []int values. type IntArrayOutput struct{ *OutputState } +func (IntArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([]int). func (IntArrayOutput) ElementType() reflect.Type { return intArrayType @@ -3628,6 +3849,10 @@ func (in IntMap) ToIntMapOutputWithContext(ctx context.Context) IntMapOutput { // IntMapOutput is an Output that returns map[string]int values. type IntMapOutput struct{ *OutputState } +func (IntMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string]int). func (IntMapOutput) ElementType() reflect.Type { return intMapType @@ -3693,6 +3918,10 @@ func (in IntArrayMap) ToIntArrayMapOutputWithContext(ctx context.Context) IntArr // IntArrayMapOutput is an Output that returns map[string][]int values. type IntArrayMapOutput struct{ *OutputState } +func (IntArrayMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string][]int). func (IntArrayMapOutput) ElementType() reflect.Type { return intArrayMapType @@ -3758,6 +3987,10 @@ func (in IntMapArray) ToIntMapArrayOutputWithContext(ctx context.Context) IntMap // IntMapArrayOutput is an Output that returns []map[string]int values. type IntMapArrayOutput struct{ *OutputState } +func (IntMapArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([]map[string]int). func (IntMapArrayOutput) ElementType() reflect.Type { return intMapArrayType @@ -3830,6 +4063,10 @@ func (in IntMapMap) ToIntMapMapOutputWithContext(ctx context.Context) IntMapMapO // IntMapMapOutput is an Output that returns map[string]map[string]int values. type IntMapMapOutput struct{ *OutputState } +func (IntMapMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string]map[string]int). func (IntMapMapOutput) ElementType() reflect.Type { return intMapMapType @@ -3895,6 +4132,10 @@ func (in IntArrayArray) ToIntArrayArrayOutputWithContext(ctx context.Context) In // IntArrayArrayOutput is an Output that returns [][]int values. type IntArrayArrayOutput struct{ *OutputState } +func (IntArrayArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([][]int). func (IntArrayArrayOutput) ElementType() reflect.Type { return intArrayArrayType @@ -3978,6 +4219,10 @@ func (in String) ToStringPtrOutputWithContext(ctx context.Context) StringPtrOutp // StringOutput is an Output that returns string values. type StringOutput struct{ *OutputState } +func (StringOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (string). func (StringOutput) ElementType() reflect.Type { return stringType @@ -4040,6 +4285,10 @@ func (in *stringPtr) ToStringPtrOutputWithContext(ctx context.Context) StringPtr // StringPtrOutput is an Output that returns *string values. type StringPtrOutput struct{ *OutputState } +func (StringPtrOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (*string). func (StringPtrOutput) ElementType() reflect.Type { return stringPtrType @@ -4093,6 +4342,10 @@ func (in StringArray) ToStringArrayOutputWithContext(ctx context.Context) String // StringArrayOutput is an Output that returns []string values. type StringArrayOutput struct{ *OutputState } +func (StringArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([]string). func (StringArrayOutput) ElementType() reflect.Type { return stringArrayType @@ -4165,6 +4418,10 @@ func (in StringMap) ToStringMapOutputWithContext(ctx context.Context) StringMapO // StringMapOutput is an Output that returns map[string]string values. type StringMapOutput struct{ *OutputState } +func (StringMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string]string). func (StringMapOutput) ElementType() reflect.Type { return stringMapType @@ -4230,6 +4487,10 @@ func (in StringArrayMap) ToStringArrayMapOutputWithContext(ctx context.Context) // StringArrayMapOutput is an Output that returns map[string][]string values. type StringArrayMapOutput struct{ *OutputState } +func (StringArrayMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string][]string). func (StringArrayMapOutput) ElementType() reflect.Type { return stringArrayMapType @@ -4295,6 +4556,10 @@ func (in StringMapArray) ToStringMapArrayOutputWithContext(ctx context.Context) // StringMapArrayOutput is an Output that returns []map[string]string values. type StringMapArrayOutput struct{ *OutputState } +func (StringMapArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([]map[string]string). func (StringMapArrayOutput) ElementType() reflect.Type { return stringMapArrayType @@ -4367,6 +4632,10 @@ func (in StringMapMap) ToStringMapMapOutputWithContext(ctx context.Context) Stri // StringMapMapOutput is an Output that returns map[string]map[string]string values. type StringMapMapOutput struct{ *OutputState } +func (StringMapMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string]map[string]string). func (StringMapMapOutput) ElementType() reflect.Type { return stringMapMapType @@ -4432,6 +4701,10 @@ func (in StringArrayArray) ToStringArrayArrayOutputWithContext(ctx context.Conte // StringArrayArrayOutput is an Output that returns [][]string values. type StringArrayArrayOutput struct{ *OutputState } +func (StringArrayArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([][]string). func (StringArrayArrayOutput) ElementType() reflect.Type { return stringArrayArrayType @@ -4520,6 +4793,10 @@ func (in URN) ToURNPtrOutputWithContext(ctx context.Context) URNPtrOutput { // URNOutput is an Output that returns URN values. type URNOutput struct{ *OutputState } +func (URNOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (URN). func (URNOutput) ElementType() reflect.Type { return urnType @@ -4592,6 +4869,10 @@ func (in *urnPtr) ToURNPtrOutputWithContext(ctx context.Context) URNPtrOutput { // URNPtrOutput is an Output that returns *URN values. type URNPtrOutput struct{ *OutputState } +func (URNPtrOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (*URN). func (URNPtrOutput) ElementType() reflect.Type { return uRNPtrType @@ -4645,6 +4926,10 @@ func (in URNArray) ToURNArrayOutputWithContext(ctx context.Context) URNArrayOutp // URNArrayOutput is an Output that returns []URN values. type URNArrayOutput struct{ *OutputState } +func (URNArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([]URN). func (URNArrayOutput) ElementType() reflect.Type { return uRNArrayType @@ -4717,6 +5002,10 @@ func (in URNMap) ToURNMapOutputWithContext(ctx context.Context) URNMapOutput { // URNMapOutput is an Output that returns map[string]URN values. type URNMapOutput struct{ *OutputState } +func (URNMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string]URN). func (URNMapOutput) ElementType() reflect.Type { return uRNMapType @@ -4782,6 +5071,10 @@ func (in URNArrayMap) ToURNArrayMapOutputWithContext(ctx context.Context) URNArr // URNArrayMapOutput is an Output that returns map[string][]URN values. type URNArrayMapOutput struct{ *OutputState } +func (URNArrayMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string][]URN). func (URNArrayMapOutput) ElementType() reflect.Type { return uRNArrayMapType @@ -4847,6 +5140,10 @@ func (in URNMapArray) ToURNMapArrayOutputWithContext(ctx context.Context) URNMap // URNMapArrayOutput is an Output that returns []map[string]URN values. type URNMapArrayOutput struct{ *OutputState } +func (URNMapArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([]map[string]URN). func (URNMapArrayOutput) ElementType() reflect.Type { return uRNMapArrayType @@ -4919,6 +5216,10 @@ func (in URNMapMap) ToURNMapMapOutputWithContext(ctx context.Context) URNMapMapO // URNMapMapOutput is an Output that returns map[string]map[string]URN values. type URNMapMapOutput struct{ *OutputState } +func (URNMapMapOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output (map[string]map[string]URN). func (URNMapMapOutput) ElementType() reflect.Type { return uRNMapMapType @@ -4984,6 +5285,10 @@ func (in URNArrayArray) ToURNArrayArrayOutputWithContext(ctx context.Context) UR // URNArrayArrayOutput is an Output that returns [][]URN values. type URNArrayArrayOutput struct{ *OutputState } +func (URNArrayArrayOutput) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("Outputs can not be marshaled to JSON") +} + // ElementType returns the element type of this Output ([][]URN). func (URNArrayArrayOutput) ElementType() reflect.Type { return uRNArrayArrayType diff --git a/sdk/go/pulumi/types_test.go b/sdk/go/pulumi/types_test.go index 43c65771f18d..e7250f13695a 100644 --- a/sdk/go/pulumi/types_test.go +++ b/sdk/go/pulumi/types_test.go @@ -1087,3 +1087,44 @@ func TestTypeCoersion(t *testing.T) { }) } } + +func TestJSONMarshalBasic(t *testing.T) { + t.Parallel() + + out, resolve, _ := NewOutput() + go func() { + resolve([]int{0, 1}) + }() + json := JSONMarshal(out) + v, known, secret, deps, err := await(json) + assert.Nil(t, err) + assert.True(t, known) + assert.False(t, secret) + assert.Nil(t, deps) + assert.NotNil(t, v) + assert.Equal(t, "[0,1]", v.(string)) +} + +func TestJSONMarshalNested(t *testing.T) { + t.Parallel() + + a, resolvea, _ := NewOutput() + go func() { + resolvea(0) + }() + b, resolveb, _ := NewOutput() + go func() { + resolveb(1) + }() + out, resolve, _ := NewOutput() + go func() { + resolve([]Output{a, b}) + }() + json := JSONMarshal(out) + v, known, secret, deps, err := await(json) + assert.Equal(t, "json: error calling MarshalJSON for type pulumi.AnyOutput: Outputs can not be marshaled to JSON", err.Error()) + assert.True(t, known) + assert.False(t, secret) + assert.Nil(t, deps) + assert.Nil(t, v) +} diff --git a/sdk/nodejs/cmd/pulumi-language-nodejs/main_test.go b/sdk/nodejs/cmd/pulumi-language-nodejs/main_test.go index 2cfd35887dfd..39ea506aebaa 100644 --- a/sdk/nodejs/cmd/pulumi-language-nodejs/main_test.go +++ b/sdk/nodejs/cmd/pulumi-language-nodejs/main_test.go @@ -16,7 +16,6 @@ package main import ( "context" - "io/ioutil" "os" "path/filepath" "strings" @@ -123,9 +122,7 @@ func TestCompatibleVersions(t *testing.T) { func TestGetRequiredPlugins(t *testing.T) { t.Parallel() - dir, err := ioutil.TempDir("", "test-dir") - assert.NoError(t, err) - defer os.RemoveAll(dir) + dir := t.TempDir() files := []struct { path string diff --git a/sdk/nodejs/npm/npm_test.go b/sdk/nodejs/npm/npm_test.go index f9fa114233eb..35686be48a36 100644 --- a/sdk/nodejs/npm/npm_test.go +++ b/sdk/nodejs/npm/npm_test.go @@ -57,8 +57,7 @@ func testInstall(t *testing.T, expectedBin string, production bool) { } // Create a new empty test directory and change the current working directory to it. - tempdir, _ := ioutil.TempDir("", "test-env") - t.Cleanup(func() { os.RemoveAll(tempdir) }) + tempdir := t.TempDir() chdir(t, tempdir) // Create a package directory to install dependencies into. diff --git a/sdk/nodejs/output.ts b/sdk/nodejs/output.ts index c73540fb5162..622beac80d44 100644 --- a/sdk/nodejs/output.ts +++ b/sdk/nodejs/output.ts @@ -1013,3 +1013,12 @@ export function interpolate(literals: TemplateStringsArray, ...placeholders: Inp return result; }); } + +/** + * [jsonStringify] Uses JSON.stringify to serialize the given Input value into a JSON string. + */ +export function jsonStringify(obj: Input, replacer?: (this: any, key: string, value: any) => any | (number | string)[], space?: string | number): Output { + return output(obj).apply(o => { + return JSON.stringify(o, replacer, space); + }); +} diff --git a/sdk/nodejs/runtime/closure/serializeClosure.ts b/sdk/nodejs/runtime/closure/serializeClosure.ts index c4ae3f925115..0e8a80ef8699 100644 --- a/sdk/nodejs/runtime/closure/serializeClosure.ts +++ b/sdk/nodejs/runtime/closure/serializeClosure.ts @@ -151,6 +151,7 @@ function serializeJavaScriptText( let environmentText = ""; let functionText = ""; + const emittedRequires = new Set(); const outerFunctionName = emitFunctionAndGetName(outerClosure.func); @@ -202,6 +203,16 @@ function serializeJavaScriptText( const parameters = [...Array(functionInfo.paramCount)].map((_, index) => `__${index}`).join(", "); + for (const [keyEntry, { entry: valEntry }] of functionInfo.capturedValues) { + if (valEntry.module !== undefined) { + if(!emittedRequires.has(keyEntry.json)) { + emittedRequires.add(keyEntry.json); + functionText += `const ${keyEntry.json} = require("${valEntry.module}");\n`; + } + delete capturedValues[keyEntry.json]; + } + } + functionText += "\n" + "function " + varName + "(" + parameters + ") {\n" + " return (function() {\n" + diff --git a/sdk/nodejs/runtime/closure/v8.ts b/sdk/nodejs/runtime/closure/v8.ts index cb52ac616368..3b5f831c81b0 100644 --- a/sdk/nodejs/runtime/closure/v8.ts +++ b/sdk/nodejs/runtime/closure/v8.ts @@ -22,14 +22,39 @@ import * as v8 from "v8"; v8.setFlagsFromString("--allow-natives-syntax"); +import * as inspector from "inspector"; +import * as util from "util"; +import * as vm from "vm"; import * as v8Hooks from "./v8Hooks"; -import * as v8_v10andLower from "./v8_v10andLower"; -import * as v8_v11andHigher from "./v8_v11andHigher"; +/** + * Given a function, returns the file, line and column number in the file where this function was + * defined. Returns { "", 0, 0 } if the location cannot be found or if the given function has no Script. + * @internal + */ +export async function getFunctionLocationAsync(func: Function) { + // First, find the runtime's internal id for this function. + const functionId = await getRuntimeIdForFunctionAsync(func); + + // Now, query for the internal properties the runtime sets up for it. + const { internalProperties } = await runtimeGetPropertiesAsync(functionId, /*ownProperties:*/ false); -// Node majorly changed their introspection apis between 10.0 and 11.0 (removing outright some -// of the APIs we use). Detect if we're before or after this change and delegate to the -const versionSpecificV8Module = v8Hooks.isNodeAtLeastV11 ? v8_v11andHigher : v8_v10andLower; + // There should normally be an internal property called [[FunctionLocation]]: + // https://chromium.googlesource.com/v8/v8.git/+/3f99afc93c9ba1ba5df19f123b93cc3079893c9b/src/inspector/v8-debugger.cc#793 + const functionLocation = internalProperties.find(p => p.name === "[[FunctionLocation]]"); + if (!functionLocation || !functionLocation.value || !functionLocation.value.value) { + return { file: "", line: 0, column: 0 }; + } + + const value = functionLocation.value.value; + + // Map from the scriptId the value has to a file-url. + const file = v8Hooks.getScriptUrl(value.scriptId) || ""; + const line = value.lineNumber || 0; + const column = value.columnNumber || 0; + + return { file, line, column }; +} /** * Given a function and a free variable name, lookupCapturedVariableValue looks up the value of that free variable @@ -42,11 +67,221 @@ const versionSpecificV8Module = v8Hooks.isNodeAtLeastV11 ? v8_v11andHigher : v8 * @returns The value of the free variable. If `throwOnFailure` is false, returns `undefined` if not found. * @internal */ -export const lookupCapturedVariableValueAsync = versionSpecificV8Module.lookupCapturedVariableValueAsync; +export async function lookupCapturedVariableValueAsync( + func: Function, freeVariable: string, throwOnFailure: boolean): Promise { -/** - * Given a function, returns the file, line and column number in the file where this function was - * defined. Returns { "", 0, 0 } if the location cannot be found or if the given function has no Script. - * @internal - */ -export const getFunctionLocationAsync = versionSpecificV8Module.getFunctionLocationAsync; + // First, find the runtime's internal id for this function. + const functionId = await getRuntimeIdForFunctionAsync(func); + + // Now, query for the internal properties the runtime sets up for it. + const { internalProperties } = await runtimeGetPropertiesAsync(functionId, /*ownProperties:*/ false); + + // There should normally be an internal property called [[Scopes]]: + // https://chromium.googlesource.com/v8/v8.git/+/3f99afc93c9ba1ba5df19f123b93cc3079893c9b/src/inspector/v8-debugger.cc#820 + const scopes = internalProperties.find(p => p.name === "[[Scopes]]"); + if (!scopes) { + throw new Error("Could not find [[Scopes]] property"); + } + + if (!scopes.value) { + throw new Error("[[Scopes]] property did not have [value]"); + } + + if (!scopes.value.objectId) { + throw new Error("[[Scopes]].value have objectId"); + } + + // This is sneaky, but we can actually map back from the [[Scopes]] object to a real in-memory + // v8 array-like value. Note: this isn't actually a real array. For example, it cannot be + // iterated. Nor can any actual methods be called on it. However, we can directly index into + // it, and we can. Similarly, the 'object' type it optionally points at is not a true JS + // object. So we can't call things like .hasOwnProperty on it. However, the values pointed to + // by 'object' are the real in-memory JS objects we are looking for. So we can find and return + // those successfully to our caller. + const scopesArray: { object?: Record }[] = await getValueForObjectId(scopes.value.objectId); + + // scopesArray is ordered from innermost to outermost. + for (let i = 0, n = scopesArray.length; i < n; i++) { + const scope = scopesArray[i]; + if (scope.object) { + if (freeVariable in scope.object) { + const val = scope.object[freeVariable]; + return val; + } + } + } + + if (throwOnFailure) { + throw new Error("Unexpected missing variable in closure environment: " + freeVariable); + } + + return undefined; +} + +// We want to call util.promisify on inspector.Session.post. However, due to all the overloads of +// that method, promisify gets confused. To prevent this, we cast our session object down to an +// interface containing only the single overload we care about. +type PostSession = { + post(method: TMethod, params?: TParams, callback?: (err: Error | null, params: TReturn) => void): void; +}; + +type EvaluationSession = PostSession<"Runtime.evaluate", inspector.Runtime.EvaluateParameterType, inspector.Runtime.EvaluateReturnType>; +type GetPropertiesSession = PostSession<"Runtime.getProperties", inspector.Runtime.GetPropertiesParameterType, inspector.Runtime.GetPropertiesReturnType>; +type CallFunctionSession = PostSession<"Runtime.callFunctionOn", inspector.Runtime.CallFunctionOnParameterType, inspector.Runtime.CallFunctionOnReturnType>; +type ContextSession = { + post(method: "Runtime.disable" | "Runtime.enable", callback?: (err: Error | null) => void): void; + once(event: "Runtime.executionContextCreated", listener: (message: inspector.InspectorNotification) => void): void; +}; + +type InflightContext = { + contextId: number; + functions: Record; + currentFunctionId: number; + calls: Record; + currentCallId: number; +}; +// Isolated singleton context accessible from the inspector. +// Used instead of `global` object to support executions with multiple V8 vm contexts as, e.g., done by Jest. +let inflightContextCache: Promise | undefined; +function inflightContext() { + if (inflightContextCache) { + return inflightContextCache; + } + inflightContextCache = createContext(); + return inflightContextCache; +} +async function createContext(): Promise { + const context: InflightContext = { + contextId: 0, + functions: {}, + currentFunctionId: 0, + calls: {}, + currentCallId: 0, + }; + const session = await v8Hooks.getSessionAsync(); + const post = util.promisify(session.post); + + // Create own context with known context id and functionsContext as `global` + await post.call(session, "Runtime.enable"); + const contextIdAsync = new Promise(resolve => { + session.once("Runtime.executionContextCreated", event => { + resolve(event.params.context.id); + }); + }); + vm.createContext(context); + context.contextId = await contextIdAsync; + await post.call(session, "Runtime.disable"); + + return context; +} + +async function getRuntimeIdForFunctionAsync(func: Function): Promise { + // In order to get information about an object, we need to put it in a well known location so + // that we can call Runtime.evaluate and find it. To do this, we use a special map on the + // 'global' object of a vm context only used for this purpose, and map from a unique-id to that + // object. We then call Runtime.evaluate with an expression that then points to that unique-id + // in that global object. The runtime will then find the object and give us back an internal id + // for it. We can then query for information about the object through that internal id. + // + // Note: the reason for the mapping object and the unique-id we create is so that we don't run + // into any issues when being called asynchronously. We don't want to place the object in a + // location that might be overwritten by another call while we're asynchronously waiting for our + // original call to complete. + + const session = await v8Hooks.getSessionAsync(); + const post = util.promisify(session.post); + + // Place the function in a unique location + const context = await inflightContext(); + const currentFunctionName = "id" + context.currentFunctionId++; + context.functions[currentFunctionName] = func; + const contextId = context.contextId; + const expression = `functions.${currentFunctionName}`; + + try { + const retType = await post.call(session, "Runtime.evaluate", { contextId, expression }); + + if (retType.exceptionDetails) { + throw new Error(`Error calling "Runtime.evaluate(${expression})" on context ${contextId}: ` + retType.exceptionDetails.text); + } + + const remoteObject = retType.result; + if (remoteObject.type !== "function") { + throw new Error("Remote object was not 'function': " + JSON.stringify(remoteObject)); + } + + if (!remoteObject.objectId) { + throw new Error("Remote function does not have 'objectId': " + JSON.stringify(remoteObject)); + } + + return remoteObject.objectId; + } + finally { + delete context.functions[currentFunctionName]; + } +} + +async function runtimeGetPropertiesAsync( + objectId: inspector.Runtime.RemoteObjectId, + ownProperties: boolean | undefined) { + const session = await v8Hooks.getSessionAsync(); + const post = util.promisify(session.post); + + // This cast will become unnecessary when we move to TS 3.1.6 or above. In that version they + // support typesafe '.call' calls. + const retType = await post.call( + session, "Runtime.getProperties", { objectId, ownProperties }); + + if (retType.exceptionDetails) { + throw new Error(`Error calling "Runtime.getProperties(${objectId}, ${ownProperties})": ` + + retType.exceptionDetails.text); + } + + return { internalProperties: retType.internalProperties || [], properties: retType.result }; +} + +async function getValueForObjectId(objectId: inspector.Runtime.RemoteObjectId): Promise { + // In order to get the raw JS value for the *remote wrapper* of the [[Scopes]] array, we use + // Runtime.callFunctionOn on it passing in a fresh function-declaration. The Node runtime will + // then compile that function, invoking it with the 'real' underlying scopes-array value in + // memory as the bound 'this' value. Inside that function declaration, we can then access + // 'this' and assign it to a unique-id in a well known mapping table we have set up. As above, + // the unique-id is to prevent any issues with multiple in-flight asynchronous calls. + + const session = await v8Hooks.getSessionAsync(); + const post = util.promisify(session.post); + const context = await inflightContext(); + + // Get an id for an unused location in the global table. + const tableId = "id" + context.currentCallId++; + + // Now, ask the runtime to call a fictitious method on the scopes-array object. When it + // does, it will get the actual underlying value for the scopes array and bind it to the + // 'this' value inside the function. Inside the function we then just grab 'this' and + // stash it in our global table. After this completes, we'll then have access to it. + + // This cast will become unnecessary when we move to TS 3.1.6 or above. In that version they + // support typesafe '.call' calls. + const retType = await post.call( + session, "Runtime.callFunctionOn", { + objectId, + functionDeclaration: `function () { + calls["${tableId}"] = this; + }`, + }); + + if (retType.exceptionDetails) { + throw new Error(`Error calling "Runtime.callFunction(${objectId})": ` + + retType.exceptionDetails.text); + } + + if (!context.calls.hasOwnProperty(tableId)) { + throw new Error(`Value was not stored into table after calling "Runtime.callFunctionOn(${objectId})"`); + } + + // Extract value and clear our table entry. + const val = context.calls[tableId]; + delete context.calls[tableId]; + + return val; +} diff --git a/sdk/nodejs/runtime/closure/v8Hooks.ts b/sdk/nodejs/runtime/closure/v8Hooks.ts index c7194997e69f..f4c9e5358f49 100644 --- a/sdk/nodejs/runtime/closure/v8Hooks.ts +++ b/sdk/nodejs/runtime/closure/v8Hooks.ts @@ -20,22 +20,12 @@ import * as v8 from "v8"; v8.setFlagsFromString("--allow-natives-syntax"); -import * as semver from "semver"; - -// On node11 and above, create an 'inspector session' that can be used to keep track of what is -// happening through a supported API. Pre-11 we can just call into % intrinsics for the same data. -/** @internal */ -export const isNodeAtLeastV11 = semver.gte(process.version, "11.0.0"); - let session: Promise | undefined = undefined; function getSession() { if (session !== undefined) { return session; } - if (!isNodeAtLeastV11) { - return Promise.resolve(undefined); - } session = createInspectorSessionAsync(); return session; } @@ -69,10 +59,6 @@ async function createInspectorSessionAsync(): Promise; -} - -/** @internal */ -export async function getFunctionLocationAsync(func: Function) { - const script = getScript(func); - const { line, column } = getLineColumn(); - - return { file: script ? script.name : "", line, column }; - - function getLineColumn() { - if (script) { - const pos = getSourcePosition(func); - - try { - if (isNodeAtLeastV10) { - return scriptPositionInfo(script, pos); - } else { - return script.locationFromPosition(pos); - } - } catch (err) { - // Be resilient to native functions not being available. In this case, we just return - // '0,0'. That's not great, but it at least lets us run, and it isn't a terrible - // experience. - // - // Specifically, we only need these locations when we're printing out an error about not - // being able to serialize something. In that case, we still print out the names of the - // functions (as well as the call-tree that got us there), *and* we print out the body - // of the function. With both of these, it is generally not too difficult to find out - // where the code actually lives. - } - } - - return { line: 0, column: 0 }; - } -} - -function getScript(func: Function): V8Script | undefined { - // The use of the Function constructor here and elsewhere in this file is because - // because V8 intrinsics are not valid JavaScript identifiers; they all begin with '%', - // which means that the TypeScript compiler issues errors for them. - const scriptFunc = new Function("func", "return %FunctionGetScript(func);") as any; - return scriptFunc(func); -} - - -// The second intrinsic is `FunctionGetScriptSourcePosition`, which does about what you'd -// expect. It returns a `V8SourcePosition`, which can be passed to `V8Script::locationFromPosition` -// to produce a `V8SourceLocation`. -const getSourcePosition: (func: Function) => V8SourcePosition = - new Function("func", "return %FunctionGetScriptSourcePosition(func);") as any; - -function scriptPositionInfo(script: V8Script, pos: V8SourcePosition): {line: number; column: number} { - if (isNodeAtLeastV10) { - const scriptPositionInfoFunc = - new Function("script", "pos", "return %ScriptPositionInfo(script, pos, false);") as any; - - return scriptPositionInfoFunc(script, pos); - } - - // Should not be called if running on Node<10.0.0. - return undefined; -} - -/** @internal */ -export async function lookupCapturedVariableValueAsync( - func: Function, freeVariable: string, throwOnFailure: boolean): Promise { - - // The implementation of this function is now very straightforward since the intrinsics do all of the - // difficult work. - const count = getFunctionScopeCount(func); - for (let i = 0; i < count; i++) { - const scope = getScopeForFunction(func, i); - if (freeVariable in scope.scopeObject) { - return scope.scopeObject[freeVariable]; - } - } - - if (throwOnFailure) { - throw new Error("Unexpected missing variable in closure environment: " + freeVariable); - } - - return undefined; -} - -// The last two intrinsics are `GetFunctionScopeCount` and `GetFunctionScopeDetails`. -// The former function returns the number of scopes in a given function's scope chain, while -// the latter function returns the i'th entry in a function's scope chain, given a function and -// index i. -function getFunctionScopeDetails(func: Function, index: number): any[] { - const getFunctionScopeDetailsFunc = - new Function("func", "index", "return %GetFunctionScopeDetails(func, index);") as any; - - return getFunctionScopeDetailsFunc(func, index); -} - -function getFunctionScopeCount(func: Function): number { - const getFunctionScopeCountFunc = new Function("func", "return %GetFunctionScopeCount(func);") as any; - return getFunctionScopeCountFunc(func); -} - -// getScopeForFunction extracts a V8ScopeDetails for the index'th element in the scope chain for the -// given function. -function getScopeForFunction(func: Function, index: number): V8ScopeDetails { - const scopeDetails = getFunctionScopeDetails(func, index); - return { - scopeObject: scopeDetails[V8ScopeDetailsFields.kScopeDetailsObjectIndex] as Record, - }; -} - -// All of these functions contain syntax that is not legal TS/JS (i.e. "%Whatever"). As such, -// we cannot serialize them. In case they somehow get captured, just block them from closure -// serialization entirely. -(getScript).doNotCapture = true; -(getSourcePosition).doNotCapture = true; -(getFunctionScopeDetails).doNotCapture = true; -(getFunctionScopeCount).doNotCapture = true; diff --git a/sdk/nodejs/runtime/closure/v8_v11andHigher.ts b/sdk/nodejs/runtime/closure/v8_v11andHigher.ts deleted file mode 100644 index 5da13e4ec818..000000000000 --- a/sdk/nodejs/runtime/closure/v8_v11andHigher.ts +++ /dev/null @@ -1,265 +0,0 @@ -// 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. - -/* eslint-disable max-len */ - -import * as inspector from "inspector"; -import * as util from "util"; -import * as vm from "vm"; -import * as v8Hooks from "./v8Hooks"; - -/** @internal */ -export async function getFunctionLocationAsync(func: Function) { - // First, find the runtime's internal id for this function. - const functionId = await getRuntimeIdForFunctionAsync(func); - - // Now, query for the internal properties the runtime sets up for it. - const { internalProperties } = await runtimeGetPropertiesAsync(functionId, /*ownProperties:*/ false); - - // There should normally be an internal property called [[FunctionLocation]]: - // https://chromium.googlesource.com/v8/v8.git/+/3f99afc93c9ba1ba5df19f123b93cc3079893c9b/src/inspector/v8-debugger.cc#793 - const functionLocation = internalProperties.find(p => p.name === "[[FunctionLocation]]"); - if (!functionLocation || !functionLocation.value || !functionLocation.value.value) { - return { file: "", line: 0, column: 0 }; - } - - const value = functionLocation.value.value; - - // Map from the scriptId the value has to a file-url. - const file = v8Hooks.getScriptUrl(value.scriptId) || ""; - const line = value.lineNumber || 0; - const column = value.columnNumber || 0; - - return { file, line, column }; -} - -/** @internal */ -export async function lookupCapturedVariableValueAsync( - func: Function, freeVariable: string, throwOnFailure: boolean): Promise { - - // First, find the runtime's internal id for this function. - const functionId = await getRuntimeIdForFunctionAsync(func); - - // Now, query for the internal properties the runtime sets up for it. - const { internalProperties } = await runtimeGetPropertiesAsync(functionId, /*ownProperties:*/ false); - - // There should normally be an internal property called [[Scopes]]: - // https://chromium.googlesource.com/v8/v8.git/+/3f99afc93c9ba1ba5df19f123b93cc3079893c9b/src/inspector/v8-debugger.cc#820 - const scopes = internalProperties.find(p => p.name === "[[Scopes]]"); - if (!scopes) { - throw new Error("Could not find [[Scopes]] property"); - } - - if (!scopes.value) { - throw new Error("[[Scopes]] property did not have [value]"); - } - - if (!scopes.value.objectId) { - throw new Error("[[Scopes]].value have objectId"); - } - - // This is sneaky, but we can actually map back from the [[Scopes]] object to a real in-memory - // v8 array-like value. Note: this isn't actually a real array. For example, it cannot be - // iterated. Nor can any actual methods be called on it. However, we can directly index into - // it, and we can. Similarly, the 'object' type it optionally points at is not a true JS - // object. So we can't call things like .hasOwnProperty on it. However, the values pointed to - // by 'object' are the real in-memory JS objects we are looking for. So we can find and return - // those successfully to our caller. - const scopesArray: { object?: Record }[] = await getValueForObjectId(scopes.value.objectId); - - // scopesArray is ordered from innermost to outermost. - for (let i = 0, n = scopesArray.length; i < n; i++) { - const scope = scopesArray[i]; - if (scope.object) { - if (freeVariable in scope.object) { - const val = scope.object[freeVariable]; - return val; - } - } - } - - if (throwOnFailure) { - throw new Error("Unexpected missing variable in closure environment: " + freeVariable); - } - - return undefined; -} - -// We want to call util.promisify on inspector.Session.post. However, due to all the overloads of -// that method, promisify gets confused. To prevent this, we cast our session object down to an -// interface containing only the single overload we care about. -type PostSession = { - post(method: TMethod, params?: TParams, callback?: (err: Error | null, params: TReturn) => void): void; -}; - -type EvaluationSession = PostSession<"Runtime.evaluate", inspector.Runtime.EvaluateParameterType, inspector.Runtime.EvaluateReturnType>; -type GetPropertiesSession = PostSession<"Runtime.getProperties", inspector.Runtime.GetPropertiesParameterType, inspector.Runtime.GetPropertiesReturnType>; -type CallFunctionSession = PostSession<"Runtime.callFunctionOn", inspector.Runtime.CallFunctionOnParameterType, inspector.Runtime.CallFunctionOnReturnType>; -type ContextSession = { - post(method: "Runtime.disable" | "Runtime.enable", callback?: (err: Error | null) => void): void; - once(event: "Runtime.executionContextCreated", listener: (message: inspector.InspectorNotification) => void): void; -}; - -type InflightContext = { - contextId: number; - functions: Record; - currentFunctionId: number; - calls: Record; - currentCallId: number; -}; -// Isolated singleton context accessible from the inspector. -// Used instead of `global` object to support executions with multiple V8 vm contexts as, e.g., done by Jest. -let inflightContextCache: Promise | undefined; -function inflightContext() { - if (inflightContextCache) { - return inflightContextCache; - } - inflightContextCache = createContext(); - return inflightContextCache; -} -async function createContext(): Promise { - const context: InflightContext = { - contextId: 0, - functions: {}, - currentFunctionId: 0, - calls: {}, - currentCallId: 0, - }; - const session = await v8Hooks.getSessionAsync(); - const post = util.promisify(session.post); - - // Create own context with known context id and functionsContext as `global` - await post.call(session, "Runtime.enable"); - const contextIdAsync = new Promise(resolve => { - session.once("Runtime.executionContextCreated", event => { - resolve(event.params.context.id); - }); - }); - vm.createContext(context); - context.contextId = await contextIdAsync; - await post.call(session, "Runtime.disable"); - - return context; -} - -async function getRuntimeIdForFunctionAsync(func: Function): Promise { - // In order to get information about an object, we need to put it in a well known location so - // that we can call Runtime.evaluate and find it. To do this, we use a special map on the - // 'global' object of a vm context only used for this purpose, and map from a unique-id to that - // object. We then call Runtime.evaluate with an expression that then points to that unique-id - // in that global object. The runtime will then find the object and give us back an internal id - // for it. We can then query for information about the object through that internal id. - // - // Note: the reason for the mapping object and the unique-id we create is so that we don't run - // into any issues when being called asynchronously. We don't want to place the object in a - // location that might be overwritten by another call while we're asynchronously waiting for our - // original call to complete. - - const session = await v8Hooks.getSessionAsync(); - const post = util.promisify(session.post); - - // Place the function in a unique location - const context = await inflightContext(); - const currentFunctionName = "id" + context.currentFunctionId++; - context.functions[currentFunctionName] = func; - const contextId = context.contextId; - const expression = `functions.${currentFunctionName}`; - - try { - const retType = await post.call(session, "Runtime.evaluate", { contextId, expression }); - - if (retType.exceptionDetails) { - throw new Error(`Error calling "Runtime.evaluate(${expression})" on context ${contextId}: ` + retType.exceptionDetails.text); - } - - const remoteObject = retType.result; - if (remoteObject.type !== "function") { - throw new Error("Remote object was not 'function': " + JSON.stringify(remoteObject)); - } - - if (!remoteObject.objectId) { - throw new Error("Remote function does not have 'objectId': " + JSON.stringify(remoteObject)); - } - - return remoteObject.objectId; - } - finally { - delete context.functions[currentFunctionName]; - } -} - -async function runtimeGetPropertiesAsync( - objectId: inspector.Runtime.RemoteObjectId, - ownProperties: boolean | undefined) { - const session = await v8Hooks.getSessionAsync(); - const post = util.promisify(session.post); - - // This cast will become unnecessary when we move to TS 3.1.6 or above. In that version they - // support typesafe '.call' calls. - const retType = await post.call( - session, "Runtime.getProperties", { objectId, ownProperties }); - - if (retType.exceptionDetails) { - throw new Error(`Error calling "Runtime.getProperties(${objectId}, ${ownProperties})": ` - + retType.exceptionDetails.text); - } - - return { internalProperties: retType.internalProperties || [], properties: retType.result }; -} - -async function getValueForObjectId(objectId: inspector.Runtime.RemoteObjectId): Promise { - // In order to get the raw JS value for the *remote wrapper* of the [[Scopes]] array, we use - // Runtime.callFunctionOn on it passing in a fresh function-declaration. The Node runtime will - // then compile that function, invoking it with the 'real' underlying scopes-array value in - // memory as the bound 'this' value. Inside that function declaration, we can then access - // 'this' and assign it to a unique-id in a well known mapping table we have set up. As above, - // the unique-id is to prevent any issues with multiple in-flight asynchronous calls. - - const session = await v8Hooks.getSessionAsync(); - const post = util.promisify(session.post); - const context = await inflightContext(); - - // Get an id for an unused location in the global table. - const tableId = "id" + context.currentCallId++; - - // Now, ask the runtime to call a fictitious method on the scopes-array object. When it - // does, it will get the actual underlying value for the scopes array and bind it to the - // 'this' value inside the function. Inside the function we then just grab 'this' and - // stash it in our global table. After this completes, we'll then have access to it. - - // This cast will become unnecessary when we move to TS 3.1.6 or above. In that version they - // support typesafe '.call' calls. - const retType = await post.call( - session, "Runtime.callFunctionOn", { - objectId, - functionDeclaration: `function () { - calls["${tableId}"] = this; - }`, - }); - - if (retType.exceptionDetails) { - throw new Error(`Error calling "Runtime.callFunction(${objectId})": ` - + retType.exceptionDetails.text); - } - - if (!context.calls.hasOwnProperty(tableId)) { - throw new Error(`Value was not stored into table after calling "Runtime.callFunctionOn(${objectId})"`); - } - - // Extract value and clear our table entry. - const val = context.calls[tableId]; - delete context.calls[tableId]; - - return val; -} diff --git a/sdk/nodejs/tests/output.spec.ts b/sdk/nodejs/tests/output.spec.ts index d957c17e7f64..a4f32f35e3f1 100644 --- a/sdk/nodejs/tests/output.spec.ts +++ b/sdk/nodejs/tests/output.spec.ts @@ -15,7 +15,7 @@ /* eslint-disable */ import * as assert from "assert"; -import { Output, all, concat, interpolate, output, unknown, secret, unsecret, isSecret } from "../output"; +import { Output, all, concat, interpolate, output, unknown, secret, unsecret, isSecret, jsonStringify } from "../output"; import { Resource } from "../resource"; import * as runtime from "../runtime"; @@ -138,8 +138,7 @@ describe("output", () => { describe("apply", () => { function createOutput(val: T, isKnown: boolean, isSecret: boolean = false): Output { - return new Output(new Set(), Promise.resolve(val), Promise.resolve(isKnown), Promise.resolve(isSecret), - Promise.resolve(new Set())); + return new Output(new Set(), Promise.resolve(val), Promise.resolve(isKnown), Promise.resolve(isSecret), Promise.resolve(new Set())); } it("can run on known value during preview", async () => { @@ -859,6 +858,72 @@ describe("output", () => { }); }); + describe("jsonStringify", () => { + it ("basic", async () => { + const x = output([0, 1]) + const result = jsonStringify(x) + assert.strictEqual(await result.promise(), "[0,1]"); + assert.strictEqual(await result.isKnown, true); + assert.strictEqual(await result.isSecret, false); + }); + + it ("nested", async () => { + const x = output([output(0), output(1)]) + const result = jsonStringify(x) + assert.strictEqual(await result.promise(), "[0,1]"); + assert.strictEqual(await result.isKnown, true); + assert.strictEqual(await result.isSecret, false); + }); + + it ("nested unknowns", async () => { + const x = output([ + new Output(new Set(), Promise.resolve(undefined), Promise.resolve(false), Promise.resolve(false), Promise.resolve(new Set())), + output(1)]) + const result = jsonStringify(x) + assert.strictEqual(await result.isKnown, false); + assert.strictEqual(await result.isSecret, false); + }); + + it ("nested secret", async () => { + const x = output([ + new Output(new Set(), Promise.resolve(0), Promise.resolve(true), Promise.resolve(true), Promise.resolve(new Set())), + output(1)]) + const result = jsonStringify(x) + assert.strictEqual(await result.promise(), "[0,1]"); + assert.strictEqual(await result.isKnown, true); + assert.strictEqual(await result.isSecret, true); + }); + + it ("with options", async () => { + const x = output([0, 1]) + const result = jsonStringify(x, undefined, " ") + assert.strictEqual(await result.promise(), "[\n 0,\n 1\n]"); + assert.strictEqual(await result.isKnown, true); + assert.strictEqual(await result.isSecret, false); + }); + + it ("nested dependencies", async () => { + // Output's don't actually _look_ at the resources, they just need to keep a collection of them + const mockResource : Resource = {} as any + const mockResources : Resource[] = [mockResource] + + const x = output([ + new Output(new Set(mockResources), Promise.resolve(0), Promise.resolve(true), Promise.resolve(true), Promise.resolve(new Set())), + output(1)]) + const result = jsonStringify(x) + assert.strictEqual(await result.promise(), "[0,1]"); + assert.strictEqual(await result.isKnown, true); + assert.strictEqual(await result.isSecret, true); + if (result.allResources === undefined) { + assert.fail("Output.allResources was undefined") + } + const allResources = await result.allResources(); + // We should have just the one mockResource in this set + assert.strictEqual(allResources.size, 1) + assert.ok(allResources.has(mockResource)) + }); + }); + describe("secret operations", () => { it("ensure secret", async () => { const sec = secret("foo"); diff --git a/sdk/nodejs/tests/runtime/tsClosureCases.ts b/sdk/nodejs/tests/runtime/tsClosureCases.ts index eed6ef740047..19b90290e5f2 100644 --- a/sdk/nodejs/tests/runtime/tsClosureCases.ts +++ b/sdk/nodejs/tests/runtime/tsClosureCases.ts @@ -859,10 +859,11 @@ return () => { let x = eval("undefined + null + NaN + Infinity + __filename"); r title: "Capture built in module by ref", func: () => os, expectText: `exports.handler = __f0; +const os = require("os"); function __f0() { return (function() { - with({ os: require("os") }) { + with({ }) { return () => os; @@ -886,10 +887,11 @@ return () => os; return { v }; }, expectText: `exports.handler = __f0; +const os = require("os"); function __f0(__0, __1, __2) { return (function() { - with({ os: require("os") }) { + with({ }) { return (a, b, c) => { const v = os; @@ -915,10 +917,11 @@ return (a, b, c) => { title: "Capture module through indirect function references", func: func, expectText: `exports.handler = __f0; +const os = require("os"); function __f1() { return (function() { - with({ os: require("os") }) { + with({ }) { return () => os; @@ -6645,10 +6648,11 @@ return function (thisArg, _arguments, P, generator) { } }).apply(undefined, undefined).apply(this, arguments); } +const mockpackage_1 = require("mockpackage"); function __f1() { return (function() { - with({ mockpackage_1: require("mockpackage") }) { + with({ }) { return () => mockpackage_1.z.object({ message: mockpackage_1.z.string(), diff --git a/sdk/nodejs/tsconfig.json b/sdk/nodejs/tsconfig.json index 70aa28fdb76d..6e596411597e 100644 --- a/sdk/nodejs/tsconfig.json +++ b/sdk/nodejs/tsconfig.json @@ -52,8 +52,6 @@ "runtime/closure/serializeClosure.ts", "runtime/closure/utils.ts", "runtime/closure/v8.ts", - "runtime/closure/v8_v10andLower.ts", - "runtime/closure/v8_v11andHigher.ts", "runtime/asyncIterableUtil.ts", "runtime/config.ts", diff --git a/sdk/python/.gitignore b/sdk/python/.gitignore index e10ec23834df..f073ece241e7 100644 --- a/sdk/python/.gitignore +++ b/sdk/python/.gitignore @@ -6,3 +6,4 @@ .venv/ venv/ .coverage +build/ diff --git a/sdk/python/lib/pulumi/automation/_server.py b/sdk/python/lib/pulumi/automation/_server.py index 13c2f6ce8651..ccdd8cd56f6a 100644 --- a/sdk/python/lib/pulumi/automation/_server.py +++ b/sdk/python/lib/pulumi/automation/_server.py @@ -43,6 +43,15 @@ def on_pulumi_exit(): def GetRequiredPlugins(self, request, context): return language_pb2.GetRequiredPluginsResponse() + def _exception_handler(self, loop, context): + # Exception are normally handler deeper in the stack. If this class of + # exception bubble up to here, something is wrong and we should stop + # the event loop + if "exception" in context and isinstance(context["exception"], grpc.RpcError): + loop.stop() + else: + loop.default_exception_handler(context) + def Run(self, request, context): _suppress_unobserved_task_logging() @@ -67,6 +76,7 @@ def Run(self, request, context): result = language_pb2.RunResponse() loop = asyncio.new_event_loop() + loop.set_exception_handler(self._exception_handler) try: loop.run_until_complete(run_in_stack(self.program)) except RunError as exn: diff --git a/sdk/python/lib/pulumi/runtime/stack.py b/sdk/python/lib/pulumi/runtime/stack.py index b78924185348..2ed495dbcc36 100644 --- a/sdk/python/lib/pulumi/runtime/stack.py +++ b/sdk/python/lib/pulumi/runtime/stack.py @@ -174,54 +174,67 @@ def massage(attr: Any, seen: List[Any]): if is_primitive(attr): return attr + if isinstance(attr, Output): + return attr.apply(lambda v: massage(v, seen)) + + if isawaitable(attr): + return Output.from_input(attr).apply(lambda v: massage(v, seen)) + # from this point on, we have complex objects. If we see them again, we don't want to emit them # again fully or else we'd loop infinitely. if reference_contains(attr, seen): # Note: for Resources we hit again, emit their urn so cycles can be easily understood in # the popo objects. if isinstance(attr, Resource): - return attr.urn - + return massage(attr.urn, seen) # otherwise just emit as nothing to stop the looping. return None - seen.append(attr) - - # first check if the value is an actual dictionary. If so, massage the values of it to deeply - # make sure this is a popo. - if isinstance(attr, dict): - result = {} - # Don't use attr.items() here, as it will error in the case of outputs with an `items` property. - for key in attr: - # ignore private keys - if not key.startswith("_"): - result[key] = massage(attr[key], seen) + try: + seen.append(attr) + return massage_complex(attr, seen) + finally: + popped = seen.pop() + if popped is not attr: + raise Exception("Invariant broken when processing stack outputs") - return result - if isinstance(attr, Output): - return attr.apply(lambda v: massage(v, seen)) +def massage_complex(attr: Any, seen: List[Any]) -> Any: + def is_public_key(key: str) -> bool: + return not key.startswith("_") - if isawaitable(attr): - return Output.from_input(attr).apply(lambda v: massage(v, seen)) + def serialize_all_keys(include: Callable[[str], bool]): + plain_object: Dict[str, Any] = {} + for key in attr.__dict__.keys(): + if include(key): + plain_object[key] = massage(attr.__dict__[key], seen) + return plain_object if isinstance(attr, Resource): - result = massage(attr.__dict__, seen) + serialized_attr = serialize_all_keys(is_public_key) # In preview only, we mark the result with "@isPulumiResource" to indicate that it is derived # from a resource. This allows the engine to perform resource-specific filtering of unknowns # from output diffs during a preview. This filtering is not necessary during an update because # all property values are known. - if is_dry_run(): - result["@isPulumiResource"] = True - return result + return ( + serialized_attr + if not is_dry_run() + else {**serialized_attr, "@isPulumiResource": True} + ) - if hasattr(attr, "__dict__"): - # recurse on the dictionary itself. It will be handled above. - return massage(attr.__dict__, seen) + # first check if the value is an actual dictionary. If so, massage the values of it to deeply + # make sure this is a popo. + if isinstance(attr, dict): + # Don't use attr.items() here, as it will error in the case of outputs with an `items` property. + return { + key: massage(attr[key], seen) for key in attr if not key.startswith("_") + } + + if hasattr(attr, "__iter__"): + return [massage(item, seen) for item in attr] - # finally, recurse through iterables, converting into a list of massaged values. - return [massage(a, seen) for a in attr] + return serialize_all_keys(is_public_key) def reference_contains(val1: Any, seen: List[Any]) -> bool: diff --git a/sdk/python/lib/test/langhost/stack_output/__main__.py b/sdk/python/lib/test/langhost/stack_output/__main__.py index 8c14150b7f09..1489f2a21e69 100644 --- a/sdk/python/lib/test/langhost/stack_output/__main__.py +++ b/sdk/python/lib/test/langhost/stack_output/__main__.py @@ -18,6 +18,9 @@ def __init__(self): self.num = 1 self._private = 2 + +my_test_class_instance = TestClass() + recursive = {"a": 1} recursive["b"] = 2 recursive["c"] = recursive @@ -34,3 +37,5 @@ def __init__(self): pulumi.export("output", pulumi.Output.from_input(1)) pulumi.export("class", TestClass()) pulumi.export("recursive", recursive) +pulumi.export("duplicate_output_0", my_test_class_instance) +pulumi.export("duplicate_output_1", my_test_class_instance) diff --git a/sdk/python/lib/test/langhost/stack_output/test_stack_output.py b/sdk/python/lib/test/langhost/stack_output/test_stack_output.py index ccd4b37e1d51..5810f69fcddf 100644 --- a/sdk/python/lib/test/langhost/stack_output/test_stack_output.py +++ b/sdk/python/lib/test/langhost/stack_output/test_stack_output.py @@ -39,4 +39,6 @@ def register_resource_outputs(self, _ctx, _dry_run, _urn, ty, _name, _resource, "output": 1.0, "class": {"num": 1.0}, "recursive": {"a": 1.0, "b": 2.0}, + "duplicate_output_0": {'num': 1.0}, + "duplicate_output_1": {'num': 1.0}, }, outputs) diff --git a/sdk/python/python_test.go b/sdk/python/python_test.go index ee5acb7c8cce..001812d577ca 100644 --- a/sdk/python/python_test.go +++ b/sdk/python/python_test.go @@ -30,8 +30,7 @@ func TestIsVirtualEnv(t *testing.T) { t.Parallel() // Create a new empty test directory. - tempdir, _ := ioutil.TempDir("", "test-env") - defer os.RemoveAll(tempdir) + tempdir := t.TempDir() // Assert the empty test directory is not a virtual environment. assert.False(t, IsVirtualEnv(tempdir)) @@ -94,8 +93,7 @@ func TestRunningPipInVirtualEnvironment(t *testing.T) { } // Create a new empty test directory. - tempdir, _ := ioutil.TempDir("", "test-env") - defer os.RemoveAll(tempdir) + tempdir := t.TempDir() // Create and run a python command to create a virtual environment. venvDir := filepath.Join(tempdir, "venv") diff --git a/tests/go.mod b/tests/go.mod index 6b92630d036e..03ab0e248af4 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -12,7 +12,7 @@ require ( github.com/blang/semver v3.5.1+incompatible github.com/golang/protobuf v1.5.2 github.com/pulumi/pulumi/pkg/v3 v3.34.1 - github.com/pulumi/pulumi/sdk/v3 v3.48.1-0.20221207010559-e812f69ba562 + github.com/pulumi/pulumi/sdk/v3 v3.49.0 github.com/stretchr/testify v1.8.0 google.golang.org/grpc v1.49.0 sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600 diff --git a/tests/integration/integration_go_test.go b/tests/integration/integration_go_test.go index 8f922c808c82..eecd4cac5059 100644 --- a/tests/integration/integration_go_test.go +++ b/tests/integration/integration_go_test.go @@ -707,9 +707,7 @@ func TestComponentProviderSchemaGo(t *testing.T) { // TestTracePropagationGo checks that --tracing flag lets golang sub-process to emit traces. func TestTracePropagationGo(t *testing.T) { - dir, err := os.MkdirTemp("", "pulumi-tracefiles") - assert.NoError(t, err) - defer os.RemoveAll(dir) + dir := t.TempDir() opts := &integration.ProgramTestOptions{ Dir: filepath.Join("empty", "go"),