2022-11-03 20:30:35 +00:00
|
|
|
// 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.
|
|
|
|
|
2023-01-30 19:30:30 +00:00
|
|
|
//go:build (python || all) && !xplatform_acceptance
|
2020-09-02 16:11:15 +00:00
|
|
|
|
|
|
|
package ints
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2021-04-02 17:09:17 +00:00
|
|
|
"encoding/json"
|
2020-09-02 16:11:15 +00:00
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"runtime"
|
2021-03-24 19:51:46 +00:00
|
|
|
"strings"
|
2020-09-02 16:11:15 +00:00
|
|
|
"testing"
|
|
|
|
|
2024-01-18 13:54:09 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/apitype"
|
|
|
|
|
2021-03-24 19:51:46 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
[sdk/python] Adds a default exception when dependency cycles are created (#14597)
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
Currently, when we detect that we've created a cycle in the dependency
graph, we early-exit. This works well enough for simple cycles, but when
early exiting is not sufficient (as when providing a resource output as
an argument to another resource's inputs), we will still fail to resolve
the full dependency graph. This PR introduces an exception-by-default,
as any cycles represent an unsafe/invalid dependency graph and should be
resolved manually. We also provide an escape hatch to fall back to
current behavior, in case users would prefer to retain the ability to
create unsafe dependency graphs (potentially introducing infinite hangs
when resolving those graphs).
Fixes https://github.com/pulumi/pulumi/issues/13551
## Checklist
- [x] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [x] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-11-21 16:26:02 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2021-03-24 19:51:46 +00:00
|
|
|
|
2021-04-01 11:18:12 +00:00
|
|
|
pygen "github.com/pulumi/pulumi/pkg/v3/codegen/python"
|
|
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen/schema"
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/testing/integration"
|
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
|
|
|
|
ptesting "github.com/pulumi/pulumi/sdk/v3/go/common/testing"
|
2020-09-02 16:11:15 +00:00
|
|
|
)
|
|
|
|
|
2022-09-07 15:42:38 +00:00
|
|
|
// This checks that error logs are not being emitted twice
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2022-09-07 15:42:38 +00:00
|
|
|
func TestMissingMainDoesNotEmitStackTrace(t *testing.T) {
|
|
|
|
stdout := &bytes.Buffer{}
|
|
|
|
stderr := &bytes.Buffer{}
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("python", "missing-main"),
|
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
Stdout: stdout,
|
|
|
|
Stderr: stderr,
|
|
|
|
Quick: true,
|
|
|
|
ExpectFailure: true,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
// ensure ` error: ` is only being shown once by the program
|
|
|
|
assert.NotContains(t, stdout.String()+stderr.String(), "Traceback")
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
Fix #3982, missing output which lacks newlines (#8671)
* Fix #3982, missing output which lacks newlines
If the last line printed to stdout or stderr was missing a
terminating newline, it would go entirely missing (in all languages).
The reason for this is a bug in the engine's handling of plugin
outputs: Go's Reader.ReadString('\n') returns a string containing what
was read and/or an error; if the string terminated in a '\n', the
error is nil, and the entire line is returned; if the stream ends,
however, a non-nil error is returned *and* what was read is returned,
even though it wasn't terminated in a newline. The fix is simple:
instead of ignoring that text, we use it, and *then* exit the read-loop.
Also added some test cases since this is subtle and easy to regress.
* Add a changelog entry
2022-01-03 22:39:10 +00:00
|
|
|
// TestPrintfPython tests that we capture stdout and stderr streams properly, even when the last line lacks an \n.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
Fix #3982, missing output which lacks newlines (#8671)
* Fix #3982, missing output which lacks newlines
If the last line printed to stdout or stderr was missing a
terminating newline, it would go entirely missing (in all languages).
The reason for this is a bug in the engine's handling of plugin
outputs: Go's Reader.ReadString('\n') returns a string containing what
was read and/or an error; if the string terminated in a '\n', the
error is nil, and the entire line is returned; if the stream ends,
however, a non-nil error is returned *and* what was read is returned,
even though it wasn't terminated in a newline. The fix is simple:
instead of ignoring that text, we use it, and *then* exit the read-loop.
Also added some test cases since this is subtle and easy to regress.
* Add a changelog entry
2022-01-03 22:39:10 +00:00
|
|
|
func TestPrintfPython(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("printf", "python"),
|
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
Quick: true,
|
|
|
|
ExtraRuntimeValidation: printfTestValidation,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestStackOutputsPython(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
2021-02-11 19:34:07 +00:00
|
|
|
Dir: filepath.Join("stack_outputs", "python"),
|
2020-09-02 16:11:15 +00:00
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
2021-02-12 02:16:07 +00:00
|
|
|
Quick: true,
|
2020-09-02 16:11:15 +00:00
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
// Ensure the checkpoint contains a single resource, the Stack, with two outputs.
|
|
|
|
fmt.Printf("Deployment: %v", stackInfo.Deployment)
|
|
|
|
assert.NotNil(t, stackInfo.Deployment)
|
|
|
|
if assert.Equal(t, 1, len(stackInfo.Deployment.Resources)) {
|
|
|
|
stackRes := stackInfo.Deployment.Resources[0]
|
|
|
|
assert.NotNil(t, stackRes)
|
|
|
|
assert.Equal(t, resource.RootStackType, stackRes.URN.Type())
|
|
|
|
assert.Equal(t, 0, len(stackRes.Inputs))
|
|
|
|
assert.Equal(t, 2, len(stackRes.Outputs))
|
|
|
|
assert.Equal(t, "ABC", stackRes.Outputs["xyz"])
|
|
|
|
assert.Equal(t, float64(42), stackRes.Outputs["foo"])
|
|
|
|
}
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tests basic configuration from the perspective of a Pulumi program.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestConfigBasicPython(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
2021-02-11 19:34:07 +00:00
|
|
|
Dir: filepath.Join("config_basic", "python"),
|
2020-09-02 16:11:15 +00:00
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
2021-02-12 02:16:07 +00:00
|
|
|
Quick: true,
|
2020-09-02 16:11:15 +00:00
|
|
|
Config: map[string]string{
|
|
|
|
"aConfigValue": "this value is a Pythonic value",
|
|
|
|
},
|
|
|
|
Secrets: map[string]string{
|
|
|
|
"bEncryptedSecret": "this super Pythonic secret is encrypted",
|
|
|
|
},
|
|
|
|
OrderedConfig: []integration.ConfigValue{
|
|
|
|
{Key: "outer.inner", Value: "value", Path: true},
|
|
|
|
{Key: "names[0]", Value: "a", Path: true},
|
|
|
|
{Key: "names[1]", Value: "b", Path: true},
|
|
|
|
{Key: "names[2]", Value: "c", Path: true},
|
|
|
|
{Key: "names[3]", Value: "super secret name", Path: true, Secret: true},
|
|
|
|
{Key: "servers[0].port", Value: "80", Path: true},
|
|
|
|
{Key: "servers[0].host", Value: "example", Path: true},
|
|
|
|
{Key: "a.b[0].c", Value: "true", Path: true},
|
|
|
|
{Key: "a.b[1].c", Value: "false", Path: true},
|
|
|
|
{Key: "tokens[0]", Value: "shh", Path: true, Secret: true},
|
|
|
|
{Key: "foo.bar", Value: "don't tell", Path: true, Secret: true},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-06-22 08:45:24 +00:00
|
|
|
// Tests configuration error from the perspective of a Pulumi program.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2023-06-22 08:45:24 +00:00
|
|
|
func TestConfigMissingPython(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("config_missing", "python"),
|
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
Quick: true,
|
|
|
|
ExpectFailure: true,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.NotEmpty(t, stackInfo.Events)
|
|
|
|
text1 := "Missing required configuration variable 'config_missing_py:notFound'"
|
|
|
|
text2 := "\tplease set a value using the command `pulumi config set --secret config_missing_py:notFound <value>`"
|
|
|
|
var found1, found2 bool
|
|
|
|
for _, event := range stackInfo.Events {
|
|
|
|
if event.DiagnosticEvent != nil && strings.Contains(event.DiagnosticEvent.Message, text1) {
|
|
|
|
found1 = true
|
|
|
|
}
|
|
|
|
if event.DiagnosticEvent != nil && strings.Contains(event.DiagnosticEvent.Message, text2) {
|
|
|
|
found2 = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert.True(t, found1, "expected error %q", text1)
|
|
|
|
assert.True(t, found2, "expected error %q", text2)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-05-18 22:00:30 +00:00
|
|
|
// Tests that accessing config secrets using non-secret APIs results in warnings being logged.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2021-05-18 22:00:30 +00:00
|
|
|
func TestConfigSecretsWarnPython(t *testing.T) {
|
2021-05-24 23:06:27 +00:00
|
|
|
// TODO[pulumi/pulumi#7127]: Re-enabled the warning.
|
|
|
|
t.Skip("Temporarily skipping test until we've re-enabled the warning - pulumi/pulumi#7127")
|
2021-05-18 22:00:30 +00:00
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("config_secrets_warn", "python"),
|
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
Quick: true,
|
|
|
|
Config: map[string]string{
|
|
|
|
"plainstr1": "1",
|
|
|
|
"plainstr2": "2",
|
|
|
|
"plainstr3": "3",
|
|
|
|
"plainstr4": "4",
|
|
|
|
"plainbool1": "true",
|
|
|
|
"plainbool2": "true",
|
|
|
|
"plainbool3": "true",
|
|
|
|
"plainbool4": "true",
|
|
|
|
"plainint1": "1",
|
|
|
|
"plainint2": "2",
|
|
|
|
"plainint3": "3",
|
|
|
|
"plainint4": "4",
|
|
|
|
"plainfloat1": "1.1",
|
|
|
|
"plainfloat2": "2.2",
|
|
|
|
"plainfloat3": "3.3",
|
|
|
|
"plainfloat4": "4.4",
|
|
|
|
"plainobj1": "{}",
|
|
|
|
"plainobj2": "{}",
|
|
|
|
"plainobj3": "{}",
|
|
|
|
"plainobj4": "{}",
|
|
|
|
},
|
|
|
|
Secrets: map[string]string{
|
|
|
|
"str1": "1",
|
|
|
|
"str2": "2",
|
|
|
|
"str3": "3",
|
|
|
|
"str4": "4",
|
|
|
|
"bool1": "true",
|
|
|
|
"bool2": "true",
|
|
|
|
"bool3": "true",
|
|
|
|
"bool4": "true",
|
|
|
|
"int1": "1",
|
|
|
|
"int2": "2",
|
|
|
|
"int3": "3",
|
|
|
|
"int4": "4",
|
|
|
|
"float1": "1.1",
|
|
|
|
"float2": "2.2",
|
|
|
|
"float3": "3.3",
|
|
|
|
"float4": "4.4",
|
|
|
|
"obj1": "{}",
|
|
|
|
"obj2": "{}",
|
|
|
|
"obj3": "{}",
|
|
|
|
"obj4": "{}",
|
|
|
|
},
|
|
|
|
OrderedConfig: []integration.ConfigValue{
|
|
|
|
{Key: "parent1.foo", Value: "plain1", Path: true},
|
|
|
|
{Key: "parent1.bar", Value: "secret1", Path: true, Secret: true},
|
|
|
|
{Key: "parent2.foo", Value: "plain2", Path: true},
|
|
|
|
{Key: "parent2.bar", Value: "secret2", Path: true, Secret: true},
|
|
|
|
{Key: "names1[0]", Value: "plain1", Path: true},
|
|
|
|
{Key: "names1[1]", Value: "secret1", Path: true, Secret: true},
|
|
|
|
{Key: "names2[0]", Value: "plain2", Path: true},
|
|
|
|
{Key: "names2[1]", Value: "secret2", Path: true, Secret: true},
|
|
|
|
},
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.NotEmpty(t, stackInfo.Events)
|
|
|
|
//nolint:lll
|
|
|
|
expectedWarnings := []string{
|
|
|
|
"Configuration 'config_secrets_python:str1' value is a secret; use `get_secret` instead of `get`",
|
|
|
|
"Configuration 'config_secrets_python:str2' value is a secret; use `require_secret` instead of `require`",
|
|
|
|
"Configuration 'config_secrets_python:bool1' value is a secret; use `get_secret_bool` instead of `get_bool`",
|
|
|
|
"Configuration 'config_secrets_python:bool2' value is a secret; use `require_secret_bool` instead of `require_bool`",
|
|
|
|
"Configuration 'config_secrets_python:int1' value is a secret; use `get_secret_int` instead of `get_int`",
|
|
|
|
"Configuration 'config_secrets_python:int2' value is a secret; use `require_secret_int` instead of `require_int`",
|
|
|
|
"Configuration 'config_secrets_python:float1' value is a secret; use `get_secret_float` instead of `get_float`",
|
|
|
|
"Configuration 'config_secrets_python:float2' value is a secret; use `require_secret_float` instead of `require_float`",
|
|
|
|
"Configuration 'config_secrets_python:obj1' value is a secret; use `get_secret_object` instead of `get_object`",
|
|
|
|
"Configuration 'config_secrets_python:obj2' value is a secret; use `require_secret_object` instead of `require_object`",
|
|
|
|
"Configuration 'config_secrets_python:parent1' value is a secret; use `get_secret_object` instead of `get_object`",
|
|
|
|
"Configuration 'config_secrets_python:parent2' value is a secret; use `require_secret_object` instead of `require_object`",
|
|
|
|
"Configuration 'config_secrets_python:names1' value is a secret; use `get_secret_object` instead of `get_object`",
|
|
|
|
"Configuration 'config_secrets_python:names2' value is a secret; use `require_secret_object` instead of `require_object`",
|
|
|
|
}
|
|
|
|
for _, warning := range expectedWarnings {
|
|
|
|
var found bool
|
|
|
|
for _, event := range stackInfo.Events {
|
|
|
|
if event.DiagnosticEvent != nil && event.DiagnosticEvent.Severity == "warning" &&
|
|
|
|
strings.Contains(event.DiagnosticEvent.Message, warning) {
|
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert.True(t, found, "expected warning %q", warning)
|
|
|
|
}
|
|
|
|
|
|
|
|
// These keys should not be in any warning messages.
|
|
|
|
unexpectedWarnings := []string{
|
|
|
|
"plainstr1",
|
|
|
|
"plainstr2",
|
|
|
|
"plainstr3",
|
|
|
|
"plainstr4",
|
|
|
|
"plainbool1",
|
|
|
|
"plainbool2",
|
|
|
|
"plainbool3",
|
|
|
|
"plainbool4",
|
|
|
|
"plainint1",
|
|
|
|
"plainint2",
|
|
|
|
"plainint3",
|
|
|
|
"plainint4",
|
|
|
|
"plainfloat1",
|
|
|
|
"plainfloat2",
|
|
|
|
"plainfloat3",
|
|
|
|
"plainfloat4",
|
|
|
|
"plainobj1",
|
|
|
|
"plainobj2",
|
|
|
|
"plainobj3",
|
|
|
|
"plainobj4",
|
|
|
|
"str3",
|
|
|
|
"str4",
|
|
|
|
"bool3",
|
|
|
|
"bool4",
|
|
|
|
"int3",
|
|
|
|
"int4",
|
|
|
|
"float3",
|
|
|
|
"float4",
|
|
|
|
"obj3",
|
|
|
|
"obj4",
|
|
|
|
}
|
|
|
|
for _, warning := range unexpectedWarnings {
|
|
|
|
for _, event := range stackInfo.Events {
|
|
|
|
if event.DiagnosticEvent != nil {
|
|
|
|
assert.NotContains(t, event.DiagnosticEvent.Message, warning)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestMultiStackReferencePython(t *testing.T) {
|
|
|
|
if owner := os.Getenv("PULUMI_TEST_OWNER"); owner == "" {
|
|
|
|
t.Skipf("Skipping: PULUMI_TEST_OWNER is not set")
|
|
|
|
}
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
|
|
|
|
// build a stack with an export
|
|
|
|
exporterOpts := &integration.ProgramTestOptions{
|
2022-09-14 02:34:01 +00:00
|
|
|
RequireService: true,
|
|
|
|
|
2021-02-11 19:34:07 +00:00
|
|
|
Dir: filepath.Join("stack_reference_multi", "python", "exporter"),
|
2020-09-02 16:11:15 +00:00
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
2021-02-12 02:16:07 +00:00
|
|
|
Quick: true,
|
2020-09-02 16:11:15 +00:00
|
|
|
Config: map[string]string{
|
|
|
|
"org": os.Getenv("PULUMI_TEST_OWNER"),
|
|
|
|
},
|
2022-03-04 08:17:41 +00:00
|
|
|
DestroyOnCleanup: true,
|
2020-09-02 16:11:15 +00:00
|
|
|
}
|
|
|
|
exporterStackName := exporterOpts.GetStackName().String()
|
2022-03-04 08:17:41 +00:00
|
|
|
integration.ProgramTest(t, exporterOpts)
|
2020-09-02 16:11:15 +00:00
|
|
|
|
|
|
|
importerOpts := &integration.ProgramTestOptions{
|
2022-09-14 02:34:01 +00:00
|
|
|
RequireService: true,
|
|
|
|
|
2021-02-11 19:34:07 +00:00
|
|
|
Dir: filepath.Join("stack_reference_multi", "python", "importer"),
|
2020-09-02 16:11:15 +00:00
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
2021-02-12 02:16:07 +00:00
|
|
|
Quick: true,
|
2020-09-02 16:11:15 +00:00
|
|
|
Config: map[string]string{
|
|
|
|
"org": os.Getenv("PULUMI_TEST_OWNER"),
|
|
|
|
"exporter_stack_name": exporterStackName,
|
|
|
|
},
|
2022-03-04 08:17:41 +00:00
|
|
|
DestroyOnCleanup: true,
|
2020-09-02 16:11:15 +00:00
|
|
|
}
|
|
|
|
integration.ProgramTest(t, importerOpts)
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-10-01 21:57:51 +00:00
|
|
|
func TestResourceWithSecretSerializationPython(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
2021-02-11 19:34:07 +00:00
|
|
|
Dir: filepath.Join("secret_outputs", "python"),
|
2020-10-01 21:57:51 +00:00
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
2021-02-12 02:16:07 +00:00
|
|
|
Quick: true,
|
2020-10-01 21:57:51 +00:00
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
// The program exports three resources:
|
|
|
|
// 1. One named `withSecret` who's prefix property should be secret, specified via `pulumi.secret()`.
|
|
|
|
// 2. One named `withSecretAdditional` who's prefix property should be a secret, specified via
|
|
|
|
// additionalSecretOutputs.
|
|
|
|
// 3. One named `withoutSecret` which should not be a secret.
|
|
|
|
// We serialize both of the these as plain old objects, so they appear as maps in the output.
|
|
|
|
withSecretProps, ok := stackInfo.Outputs["withSecret"].(map[string]interface{})
|
|
|
|
assert.Truef(t, ok, "POJO output was not serialized as a map")
|
|
|
|
|
|
|
|
withSecretAdditionalProps, ok := stackInfo.Outputs["withSecretAdditional"].(map[string]interface{})
|
|
|
|
assert.Truef(t, ok, "POJO output was not serialized as a map")
|
|
|
|
|
|
|
|
withoutSecretProps, ok := stackInfo.Outputs["withoutSecret"].(map[string]interface{})
|
|
|
|
assert.Truef(t, ok, "POJO output was not serialized as a map")
|
|
|
|
|
|
|
|
// The secret prop should have been serialized as a secret
|
|
|
|
secretPropValue, ok := withSecretProps["prefix"].(map[string]interface{})
|
|
|
|
assert.Truef(t, ok, "secret output was not serialized as a secret")
|
|
|
|
assert.Equal(t, resource.SecretSig, secretPropValue[resource.SigKey].(string))
|
|
|
|
|
|
|
|
// The other secret prop should have been serialized as a secret
|
|
|
|
secretAdditionalPropValue, ok := withSecretAdditionalProps["prefix"].(map[string]interface{})
|
|
|
|
assert.Truef(t, ok, "secret output was not serialized as a secret")
|
|
|
|
assert.Equal(t, resource.SecretSig, secretAdditionalPropValue[resource.SigKey].(string))
|
|
|
|
|
|
|
|
// And here, the prop was not set, it should just be a string value
|
|
|
|
_, isString := withoutSecretProps["prefix"].(string)
|
|
|
|
assert.Truef(t, isString, "non-secret output was not a string")
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-09-02 16:11:15 +00:00
|
|
|
// Tests that we issue an error if we fail to locate the Python command when running
|
|
|
|
// a Python example.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestPython3NotInstalled(t *testing.T) {
|
2021-11-15 20:17:20 +00:00
|
|
|
// TODO[pulumi/pulumi#6304]
|
2021-02-11 00:35:21 +00:00
|
|
|
t.Skip("Temporarily skipping failing test - pulumi/pulumi#6304")
|
2020-09-02 16:11:15 +00:00
|
|
|
stderr := &bytes.Buffer{}
|
|
|
|
badPython := "python3000"
|
|
|
|
expectedError := fmt.Sprintf(
|
2020-12-04 03:22:16 +00:00
|
|
|
"Failed to locate any of %q on your PATH. Have you installed Python 3.6 or greater?",
|
2020-09-02 16:11:15 +00:00
|
|
|
[]string{badPython})
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
2021-02-11 19:34:07 +00:00
|
|
|
Dir: filepath.Join("empty", "python"),
|
2020-09-02 16:11:15 +00:00
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
2021-02-12 02:16:07 +00:00
|
|
|
Quick: true,
|
2020-09-02 16:11:15 +00:00
|
|
|
Env: []string{
|
|
|
|
// Note: we use PULUMI_PYTHON_CMD to override the default behavior of searching
|
|
|
|
// for Python 3, since anyone running tests surely already has Python 3 installed on their
|
|
|
|
// machine. The code paths are functionally the same.
|
|
|
|
fmt.Sprintf("PULUMI_PYTHON_CMD=%s", badPython),
|
|
|
|
},
|
|
|
|
ExpectFailure: true,
|
|
|
|
Stderr: stderr,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
output := stderr.String()
|
|
|
|
assert.Contains(t, output, expectedError)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-08-17 21:15:53 +00:00
|
|
|
// Tests custom resource type name of dynamic provider in Python.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2021-08-17 21:15:53 +00:00
|
|
|
func TestCustomResourceTypeNameDynamicPython(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("dynamic", "python-resource-type-name"),
|
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
|
|
|
|
urnOut := stack.Outputs["urn"].(string)
|
|
|
|
urn := resource.URN(urnOut)
|
|
|
|
typ := urn.Type().String()
|
|
|
|
assert.Equal(t, "pulumi-python:dynamic/custom-provider:CustomResource", typ)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestPartialValuesPython(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
2021-02-11 19:34:07 +00:00
|
|
|
Dir: filepath.Join("partial_values", "python"),
|
2020-09-02 16:11:15 +00:00
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
AllowEmptyPreviewChanges: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tests a resource with a large (>4mb) string prop in Python
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestLargeResourcePython(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
2021-02-11 19:34:07 +00:00
|
|
|
Dir: filepath.Join("large_resource", "python"),
|
2020-09-02 16:11:15 +00:00
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-11-25 01:15:11 +00:00
|
|
|
// Test enum outputs
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-11-25 01:15:11 +00:00
|
|
|
func TestEnumOutputsPython(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
2021-02-11 19:34:07 +00:00
|
|
|
Dir: filepath.Join("enums", "python"),
|
2020-11-25 01:15:11 +00:00
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.NotNil(t, stack.Outputs)
|
|
|
|
|
|
|
|
assert.Equal(t, "Burgundy", stack.Outputs["myTreeType"])
|
|
|
|
assert.Equal(t, "Pulumi Planters Inc.foo", stack.Outputs["myTreeFarmChanged"])
|
|
|
|
assert.Equal(t, "My Burgundy Rubber tree is from Pulumi Planters Inc.", stack.Outputs["mySentence"])
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-09-02 16:11:15 +00:00
|
|
|
// Test to ensure Pylint is clean.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestPythonPylint(t *testing.T) {
|
|
|
|
t.Skip("Temporarily skipping test - pulumi/pulumi#4849")
|
|
|
|
var opts *integration.ProgramTestOptions
|
|
|
|
opts = &integration.ProgramTestOptions{
|
2021-02-11 19:34:07 +00:00
|
|
|
Dir: filepath.Join("python", "pylint"),
|
2020-09-02 16:11:15 +00:00
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
|
|
|
|
randomURN := stack.Outputs["random_urn"].(string)
|
|
|
|
assert.NotEmpty(t, randomURN)
|
|
|
|
|
|
|
|
randomID := stack.Outputs["random_id"].(string)
|
|
|
|
randomVal := stack.Outputs["random_val"].(string)
|
|
|
|
assert.Equal(t, randomID, randomVal)
|
|
|
|
|
|
|
|
cwd := stack.Outputs["cwd"].(string)
|
|
|
|
assert.NotEmpty(t, cwd)
|
|
|
|
|
|
|
|
pylint := filepath.Join("venv", "bin", "pylint")
|
|
|
|
if runtime.GOOS == WindowsOS {
|
|
|
|
pylint = filepath.Join("venv", "Scripts", "pylint")
|
|
|
|
}
|
|
|
|
|
|
|
|
err := integration.RunCommand(t, "pylint", []string{pylint, "__main__.py"}, cwd, opts)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
},
|
2021-02-12 02:16:07 +00:00
|
|
|
Quick: true,
|
2020-09-02 16:11:15 +00:00
|
|
|
}
|
|
|
|
integration.ProgramTest(t, opts)
|
|
|
|
}
|
2020-09-30 21:09:20 +00:00
|
|
|
|
2021-04-02 17:09:17 +00:00
|
|
|
// Test Python SDK codegen to ensure <Resource>Args and traditional keyword args work.
|
|
|
|
func TestPythonResourceArgs(t *testing.T) {
|
2023-12-02 17:16:09 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2021-04-02 17:09:17 +00:00
|
|
|
testdir := filepath.Join("python", "resource_args")
|
|
|
|
|
|
|
|
// Generate example library from schema.
|
|
|
|
schemaBytes, err := os.ReadFile(filepath.Join(testdir, "schema.json"))
|
|
|
|
assert.NoError(t, err)
|
|
|
|
var spec schema.PackageSpec
|
|
|
|
assert.NoError(t, json.Unmarshal(schemaBytes, &spec))
|
|
|
|
pkg, err := schema.ImportSpec(spec, nil)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
files, err := pygen.GeneratePackage("test", pkg, map[string][]byte{})
|
|
|
|
assert.NoError(t, err)
|
|
|
|
outdir := filepath.Join(testdir, "lib")
|
|
|
|
assert.NoError(t, os.RemoveAll(outdir))
|
|
|
|
for f, contents := range files {
|
|
|
|
outfile := filepath.Join(outdir, f)
|
2023-03-03 16:36:39 +00:00
|
|
|
assert.NoError(t, os.MkdirAll(filepath.Dir(outfile), 0o755))
|
|
|
|
assert.NoError(t, os.WriteFile(outfile, contents, 0o600))
|
2021-04-02 17:09:17 +00:00
|
|
|
}
|
2023-03-03 16:36:39 +00:00
|
|
|
assert.NoError(t, os.WriteFile(filepath.Join(outdir, "README.md"), []byte(""), 0o600))
|
2021-04-02 17:09:17 +00:00
|
|
|
|
|
|
|
// Test the program.
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: testdir,
|
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
filepath.Join(testdir, "lib"),
|
|
|
|
},
|
2023-12-02 17:16:09 +00:00
|
|
|
Quick: true,
|
|
|
|
NoParallel: true,
|
2021-04-02 17:09:17 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-08-18 14:46:04 +00:00
|
|
|
// Test to ensure that internal stacks are hidden
|
|
|
|
func TestPythonStackTruncate(t *testing.T) {
|
2023-12-02 17:16:09 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2022-08-18 14:46:04 +00:00
|
|
|
cases := []string{
|
|
|
|
"normal",
|
|
|
|
"main_specified",
|
|
|
|
"main_dir_specified",
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2022-08-18 14:46:04 +00:00
|
|
|
for _, name := range cases {
|
|
|
|
// Test the program.
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("python", "stack_truncate", name),
|
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
Quick: true,
|
|
|
|
// This test should fail because it raises an exception
|
|
|
|
ExpectFailure: true,
|
|
|
|
// We need to validate that the failure has a truncated stack trace
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
// Ensure that we have a non-empty list of events.
|
|
|
|
assert.NotEmpty(t, stackInfo.Events)
|
|
|
|
|
|
|
|
const stacktraceLinePrefix = " File "
|
|
|
|
|
|
|
|
// get last DiagnosticEvent containing python stack trace
|
|
|
|
stackTraceMessage := ""
|
|
|
|
for _, e := range stackInfo.Events {
|
|
|
|
if e.DiagnosticEvent == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
msg := e.DiagnosticEvent.Message
|
|
|
|
if !strings.Contains(msg, "Traceback") {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !strings.Contains(msg, stacktraceLinePrefix) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
stackTraceMessage = msg
|
|
|
|
}
|
|
|
|
assert.NotEqual(t, "", stackTraceMessage)
|
|
|
|
|
|
|
|
// make sure that the stack trace contains 2 frames as per the
|
|
|
|
// program we're running
|
|
|
|
numStackTraces := strings.Count(stackTraceMessage, stacktraceLinePrefix)
|
|
|
|
assert.Equal(t, 2, numStackTraces)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-05 18:11:27 +00:00
|
|
|
// Test remote component construction with a child resource that takes a long time to be created, ensuring it's created.
|
|
|
|
func TestConstructSlowPython(t *testing.T) {
|
2023-12-02 17:16:09 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2022-09-20 15:16:25 +00:00
|
|
|
localProvider := testComponentSlowLocalProvider(t)
|
2022-07-25 15:51:40 +00:00
|
|
|
|
2021-04-05 18:11:27 +00:00
|
|
|
// TODO[pulumi/pulumi#5455]: Dynamic providers fail to load when used from multi-lang components.
|
|
|
|
// Until we've addressed this, set PULUMI_TEST_YARN_LINK_PULUMI, which tells the integration test
|
|
|
|
// module to run `yarn install && yarn link @pulumi/pulumi` in the Python program's directory, allowing
|
|
|
|
// the Node.js dynamic provider plugin to load.
|
|
|
|
// When the underlying issue has been fixed, the use of this environment variable inside the integration
|
|
|
|
// test module should be removed.
|
|
|
|
const testYarnLinkPulumiEnv = "PULUMI_TEST_YARN_LINK_PULUMI=true"
|
|
|
|
|
2022-09-14 02:24:22 +00:00
|
|
|
testDir := "construct_component_slow"
|
|
|
|
runComponentSetup(t, testDir)
|
|
|
|
|
2021-04-15 18:49:51 +00:00
|
|
|
opts := &integration.ProgramTestOptions{
|
2022-09-20 15:16:25 +00:00
|
|
|
Env: []string{testYarnLinkPulumiEnv},
|
2022-09-14 02:24:22 +00:00
|
|
|
Dir: filepath.Join(testDir, "python"),
|
2021-04-05 18:11:27 +00:00
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
2022-09-20 15:16:25 +00:00
|
|
|
LocalProviders: []integration.LocalDependency{localProvider},
|
|
|
|
Quick: true,
|
2023-12-02 17:16:09 +00:00
|
|
|
NoParallel: true,
|
2021-04-05 18:11:27 +00:00
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.NotNil(t, stackInfo.Deployment)
|
|
|
|
if assert.Equal(t, 5, len(stackInfo.Deployment.Resources)) {
|
|
|
|
stackRes := stackInfo.Deployment.Resources[0]
|
|
|
|
assert.NotNil(t, stackRes)
|
|
|
|
assert.Equal(t, resource.RootStackType, stackRes.Type)
|
|
|
|
assert.Equal(t, "", string(stackRes.Parent))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
integration.ProgramTest(t, opts)
|
|
|
|
}
|
|
|
|
|
2021-04-09 21:36:22 +00:00
|
|
|
// Test remote component construction with prompt inputs.
|
|
|
|
func TestConstructPlainPython(t *testing.T) {
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Parallel()
|
2022-09-14 02:24:22 +00:00
|
|
|
|
|
|
|
testDir := "construct_component_plain"
|
|
|
|
runComponentSetup(t, testDir)
|
|
|
|
|
2021-04-18 16:18:25 +00:00
|
|
|
tests := []struct {
|
|
|
|
componentDir string
|
|
|
|
expectedResourceCount int
|
|
|
|
env []string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
componentDir: "testcomponent",
|
|
|
|
expectedResourceCount: 9,
|
|
|
|
// TODO[pulumi/pulumi#5455]: Dynamic providers fail to load when used from multi-lang components.
|
|
|
|
// Until we've addressed this, set PULUMI_TEST_YARN_LINK_PULUMI, which tells the integration test
|
|
|
|
// module to run `yarn install && yarn link @pulumi/pulumi` in the Go program's directory, allowing
|
|
|
|
// the Node.js dynamic provider plugin to load.
|
|
|
|
// When the underlying issue has been fixed, the use of this environment variable inside the integration
|
|
|
|
// test module should be removed.
|
|
|
|
env: []string{"PULUMI_TEST_YARN_LINK_PULUMI=true"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
componentDir: "testcomponent-python",
|
|
|
|
expectedResourceCount: 9,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
componentDir: "testcomponent-go",
|
|
|
|
expectedResourceCount: 8, // One less because no dynamic provider.
|
|
|
|
},
|
|
|
|
}
|
2021-04-09 21:36:22 +00:00
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2021-04-18 16:18:25 +00:00
|
|
|
for _, test := range tests {
|
2022-03-04 08:17:41 +00:00
|
|
|
test := test
|
2021-04-18 16:18:25 +00:00
|
|
|
t.Run(test.componentDir, func(t *testing.T) {
|
2023-03-03 16:36:39 +00:00
|
|
|
localProviders := []integration.LocalDependency{
|
|
|
|
{Package: "testcomponent", Path: filepath.Join(testDir, test.componentDir)},
|
|
|
|
}
|
2021-04-18 16:18:25 +00:00
|
|
|
integration.ProgramTest(t,
|
2022-09-20 15:16:25 +00:00
|
|
|
optsForConstructPlainPython(t, test.expectedResourceCount, localProviders, test.env...))
|
2021-04-18 16:18:25 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2021-04-15 18:49:51 +00:00
|
|
|
|
2022-09-20 15:16:25 +00:00
|
|
|
func optsForConstructPlainPython(t *testing.T, expectedResourceCount int, localProviders []integration.LocalDependency,
|
2023-03-03 16:36:39 +00:00
|
|
|
env ...string,
|
|
|
|
) *integration.ProgramTestOptions {
|
2021-04-18 16:18:25 +00:00
|
|
|
return &integration.ProgramTestOptions{
|
|
|
|
Env: env,
|
2021-04-09 21:36:22 +00:00
|
|
|
Dir: filepath.Join("construct_component_plain", "python"),
|
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
2022-09-20 15:16:25 +00:00
|
|
|
LocalProviders: localProviders,
|
|
|
|
Quick: true,
|
2021-04-09 21:36:22 +00:00
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.NotNil(t, stackInfo.Deployment)
|
2021-04-18 16:18:25 +00:00
|
|
|
assert.Equal(t, expectedResourceCount, len(stackInfo.Deployment.Resources))
|
2021-04-09 21:36:22 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-21 16:51:41 +00:00
|
|
|
// Test remote component inputs properly handle unknowns.
|
|
|
|
func TestConstructUnknownPython(t *testing.T) {
|
2023-12-02 17:16:09 +00:00
|
|
|
t.Parallel()
|
2021-06-21 16:51:41 +00:00
|
|
|
testConstructUnknown(t, "python", filepath.Join("..", "..", "sdk", "python", "env", "src"))
|
|
|
|
}
|
|
|
|
|
2021-06-30 14:48:56 +00:00
|
|
|
// Test methods on remote components.
|
|
|
|
func TestConstructMethodsPython(t *testing.T) {
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Parallel()
|
2022-09-14 02:24:22 +00:00
|
|
|
|
|
|
|
testDir := "construct_component_methods"
|
|
|
|
runComponentSetup(t, testDir)
|
|
|
|
|
2021-06-30 14:48:56 +00:00
|
|
|
tests := []struct {
|
|
|
|
componentDir string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
componentDir: "testcomponent",
|
|
|
|
},
|
2021-07-19 21:58:55 +00:00
|
|
|
{
|
|
|
|
componentDir: "testcomponent-python",
|
|
|
|
},
|
2021-07-01 22:46:16 +00:00
|
|
|
{
|
|
|
|
componentDir: "testcomponent-go",
|
|
|
|
},
|
2021-06-30 14:48:56 +00:00
|
|
|
}
|
2023-12-02 17:16:09 +00:00
|
|
|
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2021-06-30 14:48:56 +00:00
|
|
|
for _, test := range tests {
|
2022-03-04 08:17:41 +00:00
|
|
|
test := test
|
2021-06-30 14:48:56 +00:00
|
|
|
t.Run(test.componentDir, func(t *testing.T) {
|
2022-09-20 15:16:25 +00:00
|
|
|
localProvider := integration.LocalDependency{
|
2022-09-22 14:10:40 +00:00
|
|
|
Package: "testcomponent", Path: filepath.Join(testDir, test.componentDir),
|
2022-09-20 15:16:25 +00:00
|
|
|
}
|
2021-06-30 14:48:56 +00:00
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
2022-09-14 02:24:22 +00:00
|
|
|
Dir: filepath.Join(testDir, "python"),
|
2021-06-30 14:48:56 +00:00
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
2022-09-20 15:16:25 +00:00
|
|
|
LocalProviders: []integration.LocalDependency{localProvider},
|
|
|
|
Quick: true,
|
2021-06-30 14:48:56 +00:00
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.Equal(t, "Hello World, Alice!", stackInfo.Outputs["message"])
|
|
|
|
},
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-18 13:54:09 +00:00
|
|
|
func findResource(token string, resources []apitype.ResourceV3) *apitype.ResourceV3 {
|
|
|
|
for _, r := range resources {
|
|
|
|
if string(r.Type) == token {
|
|
|
|
return &r
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
|
|
|
func TestConstructComponentWithIdOutputPython(t *testing.T) {
|
|
|
|
testDir := "construct_component_id_output"
|
|
|
|
|
|
|
|
// the component implementation is written as a simple provider in go
|
|
|
|
localProvider := integration.LocalDependency{
|
|
|
|
Package: "testcomponent", Path: filepath.Join(testDir, "testcomponent-go"),
|
|
|
|
}
|
|
|
|
|
|
|
|
// run python program against the component
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join(testDir, "python"),
|
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
LocalProviders: []integration.LocalDependency{localProvider},
|
|
|
|
Quick: true,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
component := findResource("testcomponent:index:Component", stackInfo.Deployment.Resources)
|
|
|
|
require.NotNil(t, component, "component should be present in the deployment")
|
|
|
|
require.NotNil(t, component.Outputs, "component should have outputs")
|
|
|
|
componentID, ok := component.Outputs["id"].(string)
|
|
|
|
require.True(t, ok, "component should have an output called ID")
|
|
|
|
require.Equal(t, "42-hello", componentID, "component id output should be '42-hello'")
|
|
|
|
|
|
|
|
// the stack should also have an output called ID
|
|
|
|
stack := findResource("pulumi:pulumi:Stack", stackInfo.Deployment.Resources)
|
|
|
|
require.NotNil(t, stack, "stack should be present in the deployment")
|
|
|
|
require.NotNil(t, stack.Outputs, "stack should have outputs")
|
|
|
|
stackID, ok := stack.Outputs["id"].(string)
|
|
|
|
require.True(t, ok, "stack should have an output named 'id'")
|
|
|
|
require.Equal(t, "42-hello", stackID, "stack id output should be '42-hello'")
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-07-29 18:11:52 +00:00
|
|
|
func TestConstructMethodsUnknownPython(t *testing.T) {
|
2023-12-02 17:16:09 +00:00
|
|
|
t.Parallel()
|
2021-07-29 18:11:52 +00:00
|
|
|
testConstructMethodsUnknown(t, "python", filepath.Join("..", "..", "sdk", "python", "env", "src"))
|
2021-11-15 19:17:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestConstructMethodsResourcesPython(t *testing.T) {
|
2023-12-02 17:16:09 +00:00
|
|
|
t.Parallel()
|
2021-11-15 19:17:53 +00:00
|
|
|
testConstructMethodsResources(t, "python", filepath.Join("..", "..", "sdk", "python", "env", "src"))
|
2021-07-29 18:11:52 +00:00
|
|
|
}
|
|
|
|
|
2021-11-16 16:58:46 +00:00
|
|
|
func TestConstructMethodsErrorsPython(t *testing.T) {
|
2023-12-02 17:16:09 +00:00
|
|
|
t.Parallel()
|
2021-11-16 16:58:46 +00:00
|
|
|
testConstructMethodsErrors(t, "python", filepath.Join("..", "..", "sdk", "python", "env", "src"))
|
|
|
|
}
|
|
|
|
|
[sdk/nodejs] Fix provider for resource methods (#13796)
The `Resource` class in the Node.js SDK has the following internal
property:
```typescript
/** @internal */
readonly __prov?: ProviderResource;
```
When a resource is created, the provider specified for the resource is
stored in this property. If it is set, it is passed along in the `Call`
request when a method is called on the resource.
Prior to #13282, the property was only set for custom resources in
`Resource`'s constructor:
```typescript
this.__prov = custom ? opts.provider : undefined;
```
With #13282, it was changed to also store the value for remote
components:
```diff
- this.__prov = custom ? opts.provider : undefined;
+ this.__prov = custom || remote ? opts.provider : undefined;
```
This regressed the behavior when calling a method on a remote component
that had an explicit provider that wasn't the component provider, but
some other provider (e.g. AWS provider) specified as:
```typescript
const component = new MyRemoteComponent("comp", {
}, { provider: awsProvider });
```
The `awsProvider` was being stored in `Resource.__prov`, and when making
the method call on the resource, it would try to invoke `Call` on the
AWS provider, rather than calling the remote component provider's
`Call`, which resulted in an error.
Note that specifying the AWS provider using the more verbose `providers:
[awsProvider]` works around the issue.
The fix is to only set `__prov` if the provider's package is the same as
the resource's package. Otherwise, don't set it, because the user is
specifying a provider with the `provider: awsProvider` syntax as
shorthand for `providers: [awsProvider]`.
Fixes #13777
2023-08-30 14:49:53 +00:00
|
|
|
func TestConstructMethodsProviderPython(t *testing.T) {
|
2023-12-02 17:16:09 +00:00
|
|
|
t.Parallel()
|
[sdk/nodejs] Fix provider for resource methods (#13796)
The `Resource` class in the Node.js SDK has the following internal
property:
```typescript
/** @internal */
readonly __prov?: ProviderResource;
```
When a resource is created, the provider specified for the resource is
stored in this property. If it is set, it is passed along in the `Call`
request when a method is called on the resource.
Prior to #13282, the property was only set for custom resources in
`Resource`'s constructor:
```typescript
this.__prov = custom ? opts.provider : undefined;
```
With #13282, it was changed to also store the value for remote
components:
```diff
- this.__prov = custom ? opts.provider : undefined;
+ this.__prov = custom || remote ? opts.provider : undefined;
```
This regressed the behavior when calling a method on a remote component
that had an explicit provider that wasn't the component provider, but
some other provider (e.g. AWS provider) specified as:
```typescript
const component = new MyRemoteComponent("comp", {
}, { provider: awsProvider });
```
The `awsProvider` was being stored in `Resource.__prov`, and when making
the method call on the resource, it would try to invoke `Call` on the
AWS provider, rather than calling the remote component provider's
`Call`, which resulted in an error.
Note that specifying the AWS provider using the more verbose `providers:
[awsProvider]` works around the issue.
The fix is to only set `__prov` if the provider's package is the same as
the resource's package. Otherwise, don't set it, because the user is
specifying a provider with the `provider: awsProvider` syntax as
shorthand for `providers: [awsProvider]`.
Fixes #13777
2023-08-30 14:49:53 +00:00
|
|
|
testConstructMethodsProvider(t, "python", filepath.Join("..", "..", "sdk", "python", "env", "src"))
|
|
|
|
}
|
|
|
|
|
2021-07-23 21:10:06 +00:00
|
|
|
func TestConstructProviderPython(t *testing.T) {
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Parallel()
|
2022-09-14 02:24:22 +00:00
|
|
|
|
2021-07-23 21:10:06 +00:00
|
|
|
const testDir = "construct_component_provider"
|
2022-09-14 02:24:22 +00:00
|
|
|
runComponentSetup(t, testDir)
|
|
|
|
|
2021-07-23 21:10:06 +00:00
|
|
|
tests := []struct {
|
|
|
|
componentDir string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
componentDir: "testcomponent",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
componentDir: "testcomponent-python",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
componentDir: "testcomponent-go",
|
|
|
|
},
|
|
|
|
}
|
2023-12-02 17:16:09 +00:00
|
|
|
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2021-07-23 21:10:06 +00:00
|
|
|
for _, test := range tests {
|
2022-03-04 08:17:41 +00:00
|
|
|
test := test
|
2021-07-23 21:10:06 +00:00
|
|
|
t.Run(test.componentDir, func(t *testing.T) {
|
2022-09-20 15:16:25 +00:00
|
|
|
localProvider := integration.LocalDependency{
|
2022-09-22 14:10:40 +00:00
|
|
|
Package: "testcomponent", Path: filepath.Join(testDir, test.componentDir),
|
2022-09-20 15:16:25 +00:00
|
|
|
}
|
2021-07-23 21:10:06 +00:00
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join(testDir, "python"),
|
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
2022-09-20 15:16:25 +00:00
|
|
|
LocalProviders: []integration.LocalDependency{localProvider},
|
|
|
|
Quick: true,
|
2021-07-23 21:10:06 +00:00
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.Equal(t, "hello world", stackInfo.Outputs["message"])
|
|
|
|
},
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-11-20 21:13:23 +00:00
|
|
|
func TestGetResourcePython(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
2021-02-11 19:34:07 +00:00
|
|
|
Dir: filepath.Join("get_resource", "python"),
|
2020-11-20 21:13:23 +00:00
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
AllowEmptyPreviewChanges: true,
|
|
|
|
})
|
|
|
|
}
|
2021-03-24 19:51:46 +00:00
|
|
|
|
2021-03-30 17:56:17 +00:00
|
|
|
func TestPythonAwaitOutputs(t *testing.T) {
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2021-03-30 17:56:17 +00:00
|
|
|
t.Run("SuccessSimple", func(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("python_await", "success"),
|
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
AllowEmptyPreviewChanges: true,
|
|
|
|
Quick: true,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
|
|
|
|
sawMagicStringMessage := false
|
|
|
|
for _, evt := range stack.Events {
|
|
|
|
if evt.DiagnosticEvent != nil {
|
|
|
|
if strings.Contains(evt.DiagnosticEvent.Message, "magic string") {
|
|
|
|
sawMagicStringMessage = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert.True(t, sawMagicStringMessage, "Did not see printed message from unexported output")
|
|
|
|
},
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2021-03-30 17:56:17 +00:00
|
|
|
t.Run("SuccessMultipleOutputs", func(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("python_await", "multiple_outputs"),
|
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
AllowEmptyPreviewChanges: true,
|
|
|
|
Quick: true,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
|
|
|
|
sawMagicString := false
|
|
|
|
sawFoo := false
|
|
|
|
sawBar := false
|
|
|
|
for _, evt := range stack.Events {
|
|
|
|
if evt.DiagnosticEvent != nil {
|
|
|
|
if strings.Contains(evt.DiagnosticEvent.Message, "magic string") {
|
|
|
|
sawMagicString = true
|
|
|
|
}
|
|
|
|
if strings.Contains(evt.DiagnosticEvent.Message, "bar") {
|
|
|
|
sawBar = true
|
|
|
|
}
|
|
|
|
if strings.Contains(evt.DiagnosticEvent.Message, "foo") {
|
|
|
|
sawFoo = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
msg := "Did not see printed message from unexported output"
|
|
|
|
assert.True(t, sawMagicString, msg)
|
|
|
|
assert.True(t, sawFoo, msg)
|
|
|
|
assert.True(t, sawBar, msg)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2021-03-30 17:56:17 +00:00
|
|
|
t.Run("CreateWithinApply", func(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("python_await", "create_inside_apply"),
|
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
AllowEmptyPreviewChanges: true,
|
|
|
|
Quick: true,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
|
|
|
|
sawUrn := false
|
|
|
|
for _, evt := range stack.Events {
|
|
|
|
if evt.DiagnosticEvent != nil {
|
|
|
|
if strings.Contains(evt.DiagnosticEvent.Message, "pulumi-python:dynamic:Resource::magic_string") {
|
|
|
|
sawUrn = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert.True(t, sawUrn)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2021-03-30 17:56:17 +00:00
|
|
|
t.Run("ErrorHandlingSuccess", func(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("python_await", "error_handling"),
|
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
AllowEmptyPreviewChanges: true,
|
|
|
|
Quick: true,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
|
|
|
|
sawMagicStringMessage := false
|
|
|
|
for _, evt := range stack.Events {
|
|
|
|
if evt.DiagnosticEvent != nil {
|
|
|
|
if strings.Contains(evt.DiagnosticEvent.Message, "oh yeah") {
|
|
|
|
sawMagicStringMessage = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert.True(t, sawMagicStringMessage, "Did not see printed message from unexported output")
|
|
|
|
},
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2021-03-30 17:56:17 +00:00
|
|
|
t.Run("FailureSimple", func(t *testing.T) {
|
|
|
|
stderr := &bytes.Buffer{}
|
|
|
|
expectedError := "IndexError: list index out of range"
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("python_await", "failure"),
|
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
AllowEmptyPreviewChanges: true,
|
|
|
|
ExpectFailure: true,
|
|
|
|
Quick: true,
|
|
|
|
Stderr: stderr,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
output := stderr.String()
|
|
|
|
assert.Contains(t, output, expectedError)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2021-03-30 17:56:17 +00:00
|
|
|
t.Run("FailureWithExportedOutput", func(t *testing.T) {
|
|
|
|
stderr := &bytes.Buffer{}
|
|
|
|
expectedError := "IndexError: list index out of range"
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("python_await", "failure_exported_output"),
|
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
AllowEmptyPreviewChanges: true,
|
|
|
|
ExpectFailure: true,
|
|
|
|
Quick: true,
|
|
|
|
Stderr: stderr,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
|
|
|
|
output := stderr.String()
|
|
|
|
assert.Contains(t, output, expectedError)
|
|
|
|
sawFoo := false
|
|
|
|
sawPrinted := false
|
|
|
|
sawNotPrinted := false
|
|
|
|
for _, evt := range stack.Events {
|
|
|
|
if evt.DiagnosticEvent != nil {
|
|
|
|
if strings.Contains(evt.DiagnosticEvent.Message, "not printed") {
|
|
|
|
sawNotPrinted = true
|
|
|
|
}
|
|
|
|
if strings.Contains(evt.DiagnosticEvent.Message, "printed") {
|
|
|
|
sawPrinted = true
|
|
|
|
}
|
|
|
|
if strings.Contains(evt.DiagnosticEvent.Message, "foo") {
|
|
|
|
sawFoo = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert.True(t, sawPrinted)
|
|
|
|
assert.True(t, sawFoo)
|
|
|
|
assert.False(t, sawNotPrinted)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2021-03-30 17:56:17 +00:00
|
|
|
t.Run("FailureMultipleOutputs", func(t *testing.T) {
|
|
|
|
stderr := &bytes.Buffer{}
|
|
|
|
expectedError := "IndexError: list index out of range"
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("python_await", "failure_multiple_unexported_outputs"),
|
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
AllowEmptyPreviewChanges: true,
|
|
|
|
ExpectFailure: true,
|
|
|
|
Quick: true,
|
|
|
|
Stderr: stderr,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
|
|
|
|
output := stderr.String()
|
|
|
|
assert.Contains(t, output, expectedError)
|
|
|
|
sawFoo := false
|
|
|
|
sawPrinted := false
|
|
|
|
sawNotPrinted := false
|
|
|
|
for _, evt := range stack.Events {
|
|
|
|
if evt.DiagnosticEvent != nil {
|
|
|
|
if strings.Contains(evt.DiagnosticEvent.Message, "not printed") {
|
|
|
|
sawNotPrinted = true
|
|
|
|
}
|
|
|
|
if strings.Contains(evt.DiagnosticEvent.Message, "printed") {
|
|
|
|
sawPrinted = true
|
|
|
|
}
|
|
|
|
if strings.Contains(evt.DiagnosticEvent.Message, "foo") {
|
|
|
|
sawFoo = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert.True(t, sawPrinted)
|
|
|
|
assert.True(t, sawFoo)
|
|
|
|
assert.False(t, sawNotPrinted)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
2021-04-06 19:25:03 +00:00
|
|
|
|
|
|
|
// Test dict key translations.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2021-04-06 19:25:03 +00:00
|
|
|
func TestPythonTranslation(t *testing.T) {
|
2023-12-06 13:29:40 +00:00
|
|
|
t.Skip("Temporarily skipping test - pulumi/pulumi#14765")
|
2021-04-06 19:25:03 +00:00
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("python", "translation"),
|
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
Quick: true,
|
|
|
|
})
|
|
|
|
}
|
2021-05-19 14:11:18 +00:00
|
|
|
|
|
|
|
func TestComponentProviderSchemaPython(t *testing.T) {
|
2023-12-02 17:16:09 +00:00
|
|
|
t.Parallel()
|
2023-03-06 14:14:49 +00:00
|
|
|
// TODO[https://github.com/pulumi/pulumi/issues/12365] we no longer have shim files so there's no native
|
|
|
|
// binary for the testComponentProviderSchema to just exec. It _ought_ to be rewritten to use the plugin
|
|
|
|
// host framework so that it starts the component up the same as all the other tests are doing (via
|
|
|
|
// shimless).
|
|
|
|
t.Skip("testComponentProviderSchema needs to be updated to use a plugin host to deal with non-native-binary providers")
|
|
|
|
|
2021-05-19 14:11:18 +00:00
|
|
|
path := filepath.Join("component_provider_schema", "testcomponent-python", "pulumi-resource-testcomponent")
|
|
|
|
if runtime.GOOS == WindowsOS {
|
|
|
|
path += ".cmd"
|
|
|
|
}
|
2021-12-07 15:59:07 +00:00
|
|
|
testComponentProviderSchema(t, path)
|
2021-05-19 14:11:18 +00:00
|
|
|
}
|
2021-06-23 15:27:17 +00:00
|
|
|
|
2021-08-27 17:39:49 +00:00
|
|
|
// Test that the about command works as expected. Because about parses the
|
|
|
|
// results of each runtime independently, we have an integration test in each
|
|
|
|
// language.
|
|
|
|
func TestAboutPython(t *testing.T) {
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Parallel()
|
2021-08-27 17:39:49 +00:00
|
|
|
dir := filepath.Join("about", "python")
|
|
|
|
|
|
|
|
e := ptesting.NewEnvironment(t)
|
|
|
|
defer func() {
|
|
|
|
if !t.Failed() {
|
2023-11-28 16:43:48 +00:00
|
|
|
e.DeleteEnvironment()
|
2021-08-27 17:39:49 +00:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
e.ImportDirectory(dir)
|
|
|
|
|
|
|
|
stdout, _ := e.RunCommand("pulumi", "about", "--json")
|
|
|
|
// Assert we parsed the dependencies
|
|
|
|
assert.Contains(t, stdout, "pulumi-kubernetes")
|
|
|
|
}
|
2021-11-15 23:42:04 +00:00
|
|
|
|
|
|
|
func TestConstructOutputValuesPython(t *testing.T) {
|
2023-12-02 17:16:09 +00:00
|
|
|
t.Parallel()
|
2021-11-15 23:42:04 +00:00
|
|
|
testConstructOutputValues(t, "python", filepath.Join("..", "..", "sdk", "python", "env", "src"))
|
|
|
|
}
|
2022-11-30 19:25:07 +00:00
|
|
|
|
|
|
|
// TestResourceRefsGetResourcePython tests that invoking the built-in 'pulumi:pulumi:getResource' function
|
|
|
|
// returns resource references for any resource reference in a resource's state.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2022-11-30 19:25:07 +00:00
|
|
|
func TestResourceRefsGetResourcePython(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("resource_refs_get_resource", "python"),
|
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
Quick: true,
|
|
|
|
})
|
|
|
|
}
|
[sdk/{go,nodejs,python}] Fix DeletedWith resource option
This change fixes the `DeletedWith` resource option in the Go, Node.js,
and Python SDKs and adds tests.
This feature was a community contribution and while there were engine
tests included with the original PR, there weren't any tests confirming
the functionality worked correctly from each SDK.
Here's a summary of the fixes:
* Go: The `DeletedWith` resource option was never usable as it accepted
a URN instead of a Resource. We discussed this internally a while back
and decided to go ahead and fix this. (Note: While changing the
signature is technically a breaking change, the feature is currently
unusable, so the change would not break anyone, so there's no need to
wait for a major version bump.)
* Node.js: The `deletedWith` resource option did not work at all from
the Node.js SDK because it was incorrectly passing the resource object
itself in the RegisterResource request, rather than the resource's
URN.
* Python: The `deleted_with` resource option did not work at all from
the Python SDK because it was incorrectly passing the resource object
itself in the RegisterResource request, rather than the resource's
URN.
A `FailsOnDelete` resource has been added to the testprovider, which
will fail when its `Delete` gRPC is called. The tests use this to ensure
`Delete` is not called for resources of this type with the `DeletedWith`
option specified.
2023-01-16 00:19:26 +00:00
|
|
|
|
|
|
|
// TestDeletedWithPython tests the DeletedWith resource option.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
[sdk/{go,nodejs,python}] Fix DeletedWith resource option
This change fixes the `DeletedWith` resource option in the Go, Node.js,
and Python SDKs and adds tests.
This feature was a community contribution and while there were engine
tests included with the original PR, there weren't any tests confirming
the functionality worked correctly from each SDK.
Here's a summary of the fixes:
* Go: The `DeletedWith` resource option was never usable as it accepted
a URN instead of a Resource. We discussed this internally a while back
and decided to go ahead and fix this. (Note: While changing the
signature is technically a breaking change, the feature is currently
unusable, so the change would not break anyone, so there's no need to
wait for a major version bump.)
* Node.js: The `deletedWith` resource option did not work at all from
the Node.js SDK because it was incorrectly passing the resource object
itself in the RegisterResource request, rather than the resource's
URN.
* Python: The `deleted_with` resource option did not work at all from
the Python SDK because it was incorrectly passing the resource object
itself in the RegisterResource request, rather than the resource's
URN.
A `FailsOnDelete` resource has been added to the testprovider, which
will fail when its `Delete` gRPC is called. The tests use this to ensure
`Delete` is not called for resources of this type with the `DeletedWith`
option specified.
2023-01-16 00:19:26 +00:00
|
|
|
func TestDeletedWithPython(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("deleted_with", "python"),
|
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
LocalProviders: []integration.LocalDependency{
|
2022-11-23 09:53:41 +00:00
|
|
|
{Package: "testprovider", Path: filepath.Join("..", "testprovider")},
|
[sdk/{go,nodejs,python}] Fix DeletedWith resource option
This change fixes the `DeletedWith` resource option in the Go, Node.js,
and Python SDKs and adds tests.
This feature was a community contribution and while there were engine
tests included with the original PR, there weren't any tests confirming
the functionality worked correctly from each SDK.
Here's a summary of the fixes:
* Go: The `DeletedWith` resource option was never usable as it accepted
a URN instead of a Resource. We discussed this internally a while back
and decided to go ahead and fix this. (Note: While changing the
signature is technically a breaking change, the feature is currently
unusable, so the change would not break anyone, so there's no need to
wait for a major version bump.)
* Node.js: The `deletedWith` resource option did not work at all from
the Node.js SDK because it was incorrectly passing the resource object
itself in the RegisterResource request, rather than the resource's
URN.
* Python: The `deleted_with` resource option did not work at all from
the Python SDK because it was incorrectly passing the resource object
itself in the RegisterResource request, rather than the resource's
URN.
A `FailsOnDelete` resource has been added to the testprovider, which
will fail when its `Delete` gRPC is called. The tests use this to ensure
`Delete` is not called for resources of this type with the `DeletedWith`
option specified.
2023-01-16 00:19:26 +00:00
|
|
|
},
|
|
|
|
Quick: true,
|
|
|
|
})
|
|
|
|
}
|
2023-04-05 18:43:12 +00:00
|
|
|
|
|
|
|
func TestConstructProviderPropagationPython(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
testConstructProviderPropagation(t, "python", []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
})
|
|
|
|
}
|
2023-05-25 13:43:39 +00:00
|
|
|
|
|
|
|
// Regression test for https://github.com/pulumi/pulumi/issues/9411
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2023-05-25 13:43:39 +00:00
|
|
|
func TestDuplicateOutputPython(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("python", "duplicate-output"),
|
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
|
|
|
|
expected := []interface{}{float64(1), float64(2)}
|
|
|
|
assert.Equal(t, expected, stack.Outputs["export1"])
|
|
|
|
assert.Equal(t, expected, stack.Outputs["export2"])
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
2023-06-26 06:20:14 +00:00
|
|
|
|
|
|
|
func TestConstructProviderExplicitPython(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
testConstructProviderExplicit(t, "python", []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
})
|
|
|
|
}
|
[sdk/python] Adds a default exception when dependency cycles are created (#14597)
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
Currently, when we detect that we've created a cycle in the dependency
graph, we early-exit. This works well enough for simple cycles, but when
early exiting is not sufficient (as when providing a resource output as
an argument to another resource's inputs), we will still fail to resolve
the full dependency graph. This PR introduces an exception-by-default,
as any cycles represent an unsafe/invalid dependency graph and should be
resolved manually. We also provide an escape hatch to fall back to
current behavior, in case users would prefer to retain the ability to
create unsafe dependency graphs (potentially introducing infinite hangs
when resolving those graphs).
Fixes https://github.com/pulumi/pulumi/issues/13551
## Checklist
- [x] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [x] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-11-21 16:26:02 +00:00
|
|
|
|
|
|
|
// Regression test for https://github.com/pulumi/pulumi/issues/13551
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTestManualLifeCycle calls t.Parallel()
|
[sdk/python] Adds a default exception when dependency cycles are created (#14597)
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
Currently, when we detect that we've created a cycle in the dependency
graph, we early-exit. This works well enough for simple cycles, but when
early exiting is not sufficient (as when providing a resource output as
an argument to another resource's inputs), we will still fail to resolve
the full dependency graph. This PR introduces an exception-by-default,
as any cycles represent an unsafe/invalid dependency graph and should be
resolved manually. We also provide an escape hatch to fall back to
current behavior, in case users would prefer to retain the ability to
create unsafe dependency graphs (potentially introducing infinite hangs
when resolving those graphs).
Fixes https://github.com/pulumi/pulumi/issues/13551
## Checklist
- [x] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [x] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-11-21 16:26:02 +00:00
|
|
|
func TestFailsOnImplicitDependencyCyclesPython(t *testing.T) {
|
2023-12-01 17:22:51 +00:00
|
|
|
t.Skip("Temporarily skipping flakey test - pulumi/pulumi#14708")
|
|
|
|
|
[sdk/python] Adds a default exception when dependency cycles are created (#14597)
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
Currently, when we detect that we've created a cycle in the dependency
graph, we early-exit. This works well enough for simple cycles, but when
early exiting is not sufficient (as when providing a resource output as
an argument to another resource's inputs), we will still fail to resolve
the full dependency graph. This PR introduces an exception-by-default,
as any cycles represent an unsafe/invalid dependency graph and should be
resolved manually. We also provide an escape hatch to fall back to
current behavior, in case users would prefer to retain the ability to
create unsafe dependency graphs (potentially introducing infinite hangs
when resolving those graphs).
Fixes https://github.com/pulumi/pulumi/issues/13551
## Checklist
- [x] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [x] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-11-21 16:26:02 +00:00
|
|
|
stdout := &bytes.Buffer{}
|
|
|
|
pt := integration.ProgramTestManualLifeCycle(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("python", "implicit-dependency-cycles"),
|
|
|
|
Dependencies: []string{
|
|
|
|
filepath.Join("..", "..", "sdk", "python", "env", "src"),
|
|
|
|
},
|
|
|
|
Stdout: stdout,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
|
2023-11-28 16:43:48 +00:00
|
|
|
assert.Contains(
|
|
|
|
t, stdout.String(),
|
|
|
|
"RuntimeError: We have detected a circular dependency involving a resource of type "+
|
|
|
|
"my:module:Child-1 named a-child-1.")
|
|
|
|
assert.Contains(
|
|
|
|
t, stdout.String(),
|
|
|
|
"Please review any `depends_on`, `parent` or other dependency relationships between "+
|
|
|
|
"your resources to ensure no cycles have been introduced in your program.")
|
[sdk/python] Adds a default exception when dependency cycles are created (#14597)
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
Currently, when we detect that we've created a cycle in the dependency
graph, we early-exit. This works well enough for simple cycles, but when
early exiting is not sufficient (as when providing a resource output as
an argument to another resource's inputs), we will still fail to resolve
the full dependency graph. This PR introduces an exception-by-default,
as any cycles represent an unsafe/invalid dependency graph and should be
resolved manually. We also provide an escape hatch to fall back to
current behavior, in case users would prefer to retain the ability to
create unsafe dependency graphs (potentially introducing infinite hangs
when resolving those graphs).
Fixes https://github.com/pulumi/pulumi/issues/13551
## Checklist
- [x] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [x] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-11-21 16:26:02 +00:00
|
|
|
},
|
|
|
|
})
|
|
|
|
require.NoError(t, pt.TestLifeCyclePrepare(), "prepare")
|
|
|
|
t.Cleanup(pt.TestCleanUp)
|
|
|
|
|
|
|
|
require.NoError(t, pt.TestLifeCycleInitialize(), "initialize")
|
|
|
|
require.Error(t, pt.TestPreviewUpdateAndEdits(), "preview")
|
|
|
|
}
|