pulumi/tests/integration/integration_util_test.go

614 lines
18 KiB
Go
Raw Normal View History

// 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.
// The linter doesn't see the uses since the consumers are conditionally compiled tests.
//
sdk/go: Remove 'nolint' directives from package docs Go treats comments that match the following regex as directives. //[a-z0-9]+:[a-z0-9] Comments that are directives don't show in an entity's documentation. https://github.com/golang/go/commit/5a550b695117f07a4f2454039a4871250cd3ed09#diff-f56160fd9fcea272966a8a1d692ad9f49206fdd8dbcbfe384865a98cd9bc2749R165 Our code has `//nolint` directives that now show in the API Reference. This is because these directives are in one of the following forms, which don't get this special treatment. // nolint:foo //nolint: foo This change fixes all such directives found by the regex: `// nolint|//nolint: `. See bottom of commit for command used for the fix. Verification: Here's the output of `go doc` on some entities before and after this change. Before ``` % go doc github.com/pulumi/pulumi/sdk/v3/go/pulumi | head -n8 package pulumi // import "github.com/pulumi/pulumi/sdk/v3/go/pulumi" nolint: lll, interfacer nolint: lll, interfacer const EnvOrganization = "PULUMI_ORGANIZATION" ... var ErrPlugins = errors.New("pulumi: plugins requested") ``` After ``` % go doc github.com/pulumi/pulumi/sdk/v3/go/pulumi | head -n8 package pulumi // import "github.com/pulumi/pulumi/sdk/v3/go/pulumi" const EnvOrganization = "PULUMI_ORGANIZATION" ... var ErrPlugins = errors.New("pulumi: plugins requested") func BoolRef(v bool) *bool func Float64Ref(v float64) *float64 func IntRef(v int) *int func IsSecret(o Output) bool ``` Before ``` % go doc github.com/pulumi/pulumi/sdk/v3/go/pulumi URN_ package pulumi // import "github.com/pulumi/pulumi/sdk/v3/go/pulumi" func URN_(o string) ResourceOption URN_ is an optional URN of a previously-registered resource of this type to read from the engine. nolint: revive ``` After: ``` % go doc github.com/pulumi/pulumi/sdk/v3/go/pulumi URN_ package pulumi // import "github.com/pulumi/pulumi/sdk/v3/go/pulumi" func URN_(o string) ResourceOption URN_ is an optional URN of a previously-registered resource of this type to read from the engine. ``` Note that golangci-lint offers a 'nolintlint' linter that finds such miuses of nolint, but it also finds other issues so I've deferred that to a follow up PR. Resolves #11785 Related: https://github.com/golangci/golangci-lint/issues/892 [git-generate] FILES=$(mktemp) rg -l '// nolint|//nolint: ' | tee "$FILES" | xargs perl -p -i -e ' s|// nolint|//nolint|g; s|//nolint: |//nolint:|g; ' rg '.go$' < "$FILES" | xargs gofmt -w -s
2023-01-06 00:07:45 +00:00
//nolint:unused,deadcode,varcheck
package ints
import (
"bufio"
"bytes"
"context"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
test/integration: Avoid data race from FileMutex integration_util_test contains a helper function synchronouslyDo which attempts to run an operation with a file lock with a timeout, in a blocking manner. This implementation has a couple issues. First, there's a data race between Lock() and Unlock() of the mutex: the function defers an unlock regardless of whether a lock was acquired, and if the timing is right, it causes a data race in FileMutex on reading and writing the fsunlock field. ``` WARNING: DATA RACE Read at 0x00c000388040 by goroutine 16: github.com/pulumi/pulumi/sdk/v3/go/common/util/fsutil.(*FileMutex).Unlock() /Users/runner/work/pulumi/pulumi/sdk/go/common/util/fsutil/lock.go:64 +0x3e github.com/pulumi/pulumi/tests/integration.synchronouslyDo.func1() /Users/runner/work/pulumi/pulumi/tests/integration/integration_util_test.go:263 +0x39 [..] Previous write at 0x00c000388040 by goroutine 17: github.com/pulumi/pulumi/sdk/v3/go/common/util/fsutil.(*FileMutex).Lock() /Users/runner/work/pulumi/pulumi/sdk/go/common/util/fsutil/lock.go:55 +0x72 github.com/pulumi/pulumi/tests/integration.synchronouslyDo.func2() /Users/runner/work/pulumi/pulumi/tests/integration/integration_util_test.go:269 +0x4e ``` This data race is not expected because the contract for FileMutex is that the Unlock method should only be called after Lock, typicaly from the same goroutine --- synchronouslyDo does not do this. Secondly, synchronouslyDo has a minor bug: it will run the function *eventually* when the lock has been acquired even if the timeout has expirted and the test has failed by then. Resolve these issues by making the following changes: - use a context to track the timeout - defer an unlock only if a lock was successfully acquired - run the operation only if we still have time to run it Includes a previously failing test case.
2023-02-02 20:46:22 +00:00
"sync"
"testing"
"time"
"github.com/pulumi/pulumi/pkg/v3/testing/integration"
"github.com/pulumi/pulumi/sdk/v3/go/common/apitype"
ptesting "github.com/pulumi/pulumi/sdk/v3/go/common/testing"
"github.com/pulumi/pulumi/sdk/v3/go/common/testing/iotest"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/fsutil"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/rpcutil"
pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go"
"github.com/stretchr/testify/assert"
test/integration: Avoid data race from FileMutex integration_util_test contains a helper function synchronouslyDo which attempts to run an operation with a file lock with a timeout, in a blocking manner. This implementation has a couple issues. First, there's a data race between Lock() and Unlock() of the mutex: the function defers an unlock regardless of whether a lock was acquired, and if the timing is right, it causes a data race in FileMutex on reading and writing the fsunlock field. ``` WARNING: DATA RACE Read at 0x00c000388040 by goroutine 16: github.com/pulumi/pulumi/sdk/v3/go/common/util/fsutil.(*FileMutex).Unlock() /Users/runner/work/pulumi/pulumi/sdk/go/common/util/fsutil/lock.go:64 +0x3e github.com/pulumi/pulumi/tests/integration.synchronouslyDo.func1() /Users/runner/work/pulumi/pulumi/tests/integration/integration_util_test.go:263 +0x39 [..] Previous write at 0x00c000388040 by goroutine 17: github.com/pulumi/pulumi/sdk/v3/go/common/util/fsutil.(*FileMutex).Lock() /Users/runner/work/pulumi/pulumi/sdk/go/common/util/fsutil/lock.go:55 +0x72 github.com/pulumi/pulumi/tests/integration.synchronouslyDo.func2() /Users/runner/work/pulumi/pulumi/tests/integration/integration_util_test.go:269 +0x4e ``` This data race is not expected because the contract for FileMutex is that the Unlock method should only be called after Lock, typicaly from the same goroutine --- synchronouslyDo does not do this. Secondly, synchronouslyDo has a minor bug: it will run the function *eventually* when the lock has been acquired even if the timeout has expirted and the test has failed by then. Resolve these issues by making the following changes: - use a context to track the timeout - defer an unlock only if a lock was successfully acquired - run the operation only if we still have time to run it Includes a previously failing test case.
2023-02-02 20:46:22 +00:00
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
const WindowsOS = "windows"
// assertPerfBenchmark implements the integration.TestStatsReporter interface, and reports test
// failures when a scenario exceeds the provided threshold.
type assertPerfBenchmark struct {
T *testing.T
MaxPreviewDuration time.Duration
MaxUpdateDuration time.Duration
}
func (t assertPerfBenchmark) ReportCommand(stats integration.TestCommandStats) {
var maxDuration *time.Duration
if strings.HasPrefix(stats.StepName, "pulumi-preview") {
maxDuration = &t.MaxPreviewDuration
}
if strings.HasPrefix(stats.StepName, "pulumi-update") {
maxDuration = &t.MaxUpdateDuration
}
if maxDuration != nil && *maxDuration != 0 {
if stats.ElapsedSeconds < maxDuration.Seconds() {
t.T.Logf(
"Test step %q was under threshold. %.2fs (max %.2fs)",
stats.StepName, stats.ElapsedSeconds, maxDuration.Seconds())
} else {
t.T.Errorf(
"Test step %q took longer than expected. %.2fs vs. max %.2fs",
stats.StepName, stats.ElapsedSeconds, maxDuration.Seconds())
}
}
}
func testComponentSlowLocalProvider(t *testing.T) integration.LocalDependency {
return integration.LocalDependency{
Package: "testcomponent",
Path: filepath.Join("construct_component_slow", "testcomponent"),
}
}
func testComponentProviderSchema(t *testing.T, path string) {
runComponentSetup(t, "component_provider_schema")
tests := []struct {
name string
env []string
version int32
expected string
expectedError string
}{
{
name: "Default",
expected: "{}",
},
{
name: "Schema",
env: []string{"INCLUDE_SCHEMA=true"},
expected: `{"hello": "world"}`,
},
{
name: "Invalid Version",
version: 15,
expectedError: "unsupported schema version 15",
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
// Start the plugin binary.
cmd := exec.Command(path, "ignored")
cmd.Env = append(os.Environ(), test.env...)
stdout, err := cmd.StdoutPipe()
assert.NoError(t, err)
err = cmd.Start()
assert.NoError(t, err)
defer func() {
// Ignore the error as it may fail with access denied on Windows.
sdk/go: Remove 'nolint' directives from package docs Go treats comments that match the following regex as directives. //[a-z0-9]+:[a-z0-9] Comments that are directives don't show in an entity's documentation. https://github.com/golang/go/commit/5a550b695117f07a4f2454039a4871250cd3ed09#diff-f56160fd9fcea272966a8a1d692ad9f49206fdd8dbcbfe384865a98cd9bc2749R165 Our code has `//nolint` directives that now show in the API Reference. This is because these directives are in one of the following forms, which don't get this special treatment. // nolint:foo //nolint: foo This change fixes all such directives found by the regex: `// nolint|//nolint: `. See bottom of commit for command used for the fix. Verification: Here's the output of `go doc` on some entities before and after this change. Before ``` % go doc github.com/pulumi/pulumi/sdk/v3/go/pulumi | head -n8 package pulumi // import "github.com/pulumi/pulumi/sdk/v3/go/pulumi" nolint: lll, interfacer nolint: lll, interfacer const EnvOrganization = "PULUMI_ORGANIZATION" ... var ErrPlugins = errors.New("pulumi: plugins requested") ``` After ``` % go doc github.com/pulumi/pulumi/sdk/v3/go/pulumi | head -n8 package pulumi // import "github.com/pulumi/pulumi/sdk/v3/go/pulumi" const EnvOrganization = "PULUMI_ORGANIZATION" ... var ErrPlugins = errors.New("pulumi: plugins requested") func BoolRef(v bool) *bool func Float64Ref(v float64) *float64 func IntRef(v int) *int func IsSecret(o Output) bool ``` Before ``` % go doc github.com/pulumi/pulumi/sdk/v3/go/pulumi URN_ package pulumi // import "github.com/pulumi/pulumi/sdk/v3/go/pulumi" func URN_(o string) ResourceOption URN_ is an optional URN of a previously-registered resource of this type to read from the engine. nolint: revive ``` After: ``` % go doc github.com/pulumi/pulumi/sdk/v3/go/pulumi URN_ package pulumi // import "github.com/pulumi/pulumi/sdk/v3/go/pulumi" func URN_(o string) ResourceOption URN_ is an optional URN of a previously-registered resource of this type to read from the engine. ``` Note that golangci-lint offers a 'nolintlint' linter that finds such miuses of nolint, but it also finds other issues so I've deferred that to a follow up PR. Resolves #11785 Related: https://github.com/golangci/golangci-lint/issues/892 [git-generate] FILES=$(mktemp) rg -l '// nolint|//nolint: ' | tee "$FILES" | xargs perl -p -i -e ' s|// nolint|//nolint|g; s|//nolint: |//nolint:|g; ' rg '.go$' < "$FILES" | xargs gofmt -w -s
2023-01-06 00:07:45 +00:00
cmd.Process.Kill() //nolint:errcheck
}()
// Read the port from standard output.
reader := bufio.NewReader(stdout)
bytes, err := reader.ReadBytes('\n')
assert.NoError(t, err)
port := strings.TrimSpace(string(bytes))
// Create a connection to the server.
conn, err := grpc.Dial(
"127.0.0.1:"+port,
grpc.WithTransportCredentials(insecure.NewCredentials()),
rpcutil.GrpcChannelOptions(),
)
assert.NoError(t, err)
client := pulumirpc.NewResourceProviderClient(conn)
// Call GetSchema and verify the results.
resp, err := client.GetSchema(context.Background(), &pulumirpc.GetSchemaRequest{Version: test.version})
if test.expectedError != "" {
assert.ErrorContains(t, err, test.expectedError)
} else {
assert.Equal(t, test.expected, resp.GetSchema())
}
})
}
}
// Test remote component inputs properly handle unknowns.
func testConstructUnknown(t *testing.T, lang string, dependencies ...string) {
const testDir = "construct_component_unknown"
runComponentSetup(t, testDir)
tests := []struct {
componentDir string
}{
{
componentDir: "testcomponent",
},
{
componentDir: "testcomponent-python",
},
{
componentDir: "testcomponent-go",
},
}
for _, test := range tests {
test := test
t.Run(test.componentDir, func(t *testing.T) {
all: Reformat with gofumpt Per team discussion, switching to gofumpt. [gofumpt][1] is an alternative, stricter alternative to gofmt. It addresses other stylistic concerns that gofmt doesn't yet cover. [1]: https://github.com/mvdan/gofumpt See the full list of [Added rules][2], but it includes: - Dropping empty lines around function bodies - Dropping unnecessary variable grouping when there's only one variable - Ensuring an empty line between multi-line functions - simplification (`-s` in gofmt) is always enabled - Ensuring multi-line function signatures end with `) {` on a separate line. [2]: https://github.com/mvdan/gofumpt#Added-rules gofumpt is stricter, but there's no lock-in. All gofumpt output is valid gofmt output, so if we decide we don't like it, it's easy to switch back without any code changes. gofumpt support is built into the tooling we use for development so this won't change development workflows. - golangci-lint includes a gofumpt check (enabled in this PR) - gopls, the LSP for Go, includes a gofumpt option (see [installation instrutions][3]) [3]: https://github.com/mvdan/gofumpt#installation This change was generated by running: ```bash gofumpt -w $(rg --files -g '*.go' | rg -v testdata | rg -v compilation_error) ``` The following files were manually tweaked afterwards: - pkg/cmd/pulumi/stack_change_secrets_provider.go: one of the lines overflowed and had comments in an inconvenient place - pkg/cmd/pulumi/destroy.go: `var x T = y` where `T` wasn't necessary - pkg/cmd/pulumi/policy_new.go: long line because of error message - pkg/backend/snapshot_test.go: long line trying to assign three variables in the same assignment I have included mention of gofumpt in the CONTRIBUTING.md.
2023-03-03 16:36:39 +00:00
localProviders := []integration.LocalDependency{
{Package: "testcomponent", Path: filepath.Join(testDir, test.componentDir)},
}
integration.ProgramTest(t, &integration.ProgramTestOptions{
Dir: filepath.Join(testDir, lang),
Dependencies: dependencies,
LocalProviders: localProviders,
SkipRefresh: true,
SkipPreview: false,
SkipUpdate: true,
SkipExportImport: true,
SkipEmptyPreviewUpdate: true,
Quick: false,
})
})
}
}
// Test methods properly handle unknowns.
func testConstructMethodsUnknown(t *testing.T, lang string, dependencies ...string) {
const testDir = "construct_component_methods_unknown"
runComponentSetup(t, testDir)
tests := []struct {
componentDir string
}{
{
componentDir: "testcomponent",
},
{
componentDir: "testcomponent-python",
},
{
componentDir: "testcomponent-go",
},
}
for _, test := range tests {
test := test
t.Run(test.componentDir, func(t *testing.T) {
all: Reformat with gofumpt Per team discussion, switching to gofumpt. [gofumpt][1] is an alternative, stricter alternative to gofmt. It addresses other stylistic concerns that gofmt doesn't yet cover. [1]: https://github.com/mvdan/gofumpt See the full list of [Added rules][2], but it includes: - Dropping empty lines around function bodies - Dropping unnecessary variable grouping when there's only one variable - Ensuring an empty line between multi-line functions - simplification (`-s` in gofmt) is always enabled - Ensuring multi-line function signatures end with `) {` on a separate line. [2]: https://github.com/mvdan/gofumpt#Added-rules gofumpt is stricter, but there's no lock-in. All gofumpt output is valid gofmt output, so if we decide we don't like it, it's easy to switch back without any code changes. gofumpt support is built into the tooling we use for development so this won't change development workflows. - golangci-lint includes a gofumpt check (enabled in this PR) - gopls, the LSP for Go, includes a gofumpt option (see [installation instrutions][3]) [3]: https://github.com/mvdan/gofumpt#installation This change was generated by running: ```bash gofumpt -w $(rg --files -g '*.go' | rg -v testdata | rg -v compilation_error) ``` The following files were manually tweaked afterwards: - pkg/cmd/pulumi/stack_change_secrets_provider.go: one of the lines overflowed and had comments in an inconvenient place - pkg/cmd/pulumi/destroy.go: `var x T = y` where `T` wasn't necessary - pkg/cmd/pulumi/policy_new.go: long line because of error message - pkg/backend/snapshot_test.go: long line trying to assign three variables in the same assignment I have included mention of gofumpt in the CONTRIBUTING.md.
2023-03-03 16:36:39 +00:00
localProviders := []integration.LocalDependency{
{Package: "testcomponent", Path: filepath.Join(testDir, test.componentDir)},
}
integration.ProgramTest(t, &integration.ProgramTestOptions{
Dir: filepath.Join(testDir, lang),
Dependencies: dependencies,
LocalProviders: localProviders,
SkipRefresh: true,
SkipPreview: false,
SkipUpdate: true,
SkipExportImport: true,
SkipEmptyPreviewUpdate: true,
Quick: false,
})
})
}
}
func runComponentSetup(t *testing.T, testDir string) {
ptesting.YarnInstallMutex.Lock()
defer ptesting.YarnInstallMutex.Unlock()
setupFilename, err := filepath.Abs("component_setup.sh")
require.NoError(t, err, "could not determine absolute path")
// Even for Windows, we want forward slashes as bash treats backslashes as escape sequences.
setupFilename = filepath.ToSlash(setupFilename)
synchronouslyDo(t, filepath.Join(testDir, ".lock"), 10*time.Minute, func() {
out := iotest.LogWriter(t)
cmd := exec.Command("bash", "-x", setupFilename)
cmd.Dir = testDir
cmd.Stdout = out
cmd.Stderr = out
err := cmd.Run()
// This runs in a separate goroutine, so don't use 'require'.
assert.NoError(t, err, "failed to run setup script")
})
// The function above runs in a separate goroutine
// so it can't halt test execution.
// Verify that it didn't fail separately
// and halt execution if it did.
require.False(t, t.Failed(), "component setup failed")
}
test/integration: Avoid data race from FileMutex integration_util_test contains a helper function synchronouslyDo which attempts to run an operation with a file lock with a timeout, in a blocking manner. This implementation has a couple issues. First, there's a data race between Lock() and Unlock() of the mutex: the function defers an unlock regardless of whether a lock was acquired, and if the timing is right, it causes a data race in FileMutex on reading and writing the fsunlock field. ``` WARNING: DATA RACE Read at 0x00c000388040 by goroutine 16: github.com/pulumi/pulumi/sdk/v3/go/common/util/fsutil.(*FileMutex).Unlock() /Users/runner/work/pulumi/pulumi/sdk/go/common/util/fsutil/lock.go:64 +0x3e github.com/pulumi/pulumi/tests/integration.synchronouslyDo.func1() /Users/runner/work/pulumi/pulumi/tests/integration/integration_util_test.go:263 +0x39 [..] Previous write at 0x00c000388040 by goroutine 17: github.com/pulumi/pulumi/sdk/v3/go/common/util/fsutil.(*FileMutex).Lock() /Users/runner/work/pulumi/pulumi/sdk/go/common/util/fsutil/lock.go:55 +0x72 github.com/pulumi/pulumi/tests/integration.synchronouslyDo.func2() /Users/runner/work/pulumi/pulumi/tests/integration/integration_util_test.go:269 +0x4e ``` This data race is not expected because the contract for FileMutex is that the Unlock method should only be called after Lock, typicaly from the same goroutine --- synchronouslyDo does not do this. Secondly, synchronouslyDo has a minor bug: it will run the function *eventually* when the lock has been acquired even if the timeout has expirted and the test has failed by then. Resolve these issues by making the following changes: - use a context to track the timeout - defer an unlock only if a lock was successfully acquired - run the operation only if we still have time to run it Includes a previously failing test case.
2023-02-02 20:46:22 +00:00
func synchronouslyDo(t testing.TB, lockfile string, timeout time.Duration, fn func()) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
test/integration: Avoid data race from FileMutex integration_util_test contains a helper function synchronouslyDo which attempts to run an operation with a file lock with a timeout, in a blocking manner. This implementation has a couple issues. First, there's a data race between Lock() and Unlock() of the mutex: the function defers an unlock regardless of whether a lock was acquired, and if the timing is right, it causes a data race in FileMutex on reading and writing the fsunlock field. ``` WARNING: DATA RACE Read at 0x00c000388040 by goroutine 16: github.com/pulumi/pulumi/sdk/v3/go/common/util/fsutil.(*FileMutex).Unlock() /Users/runner/work/pulumi/pulumi/sdk/go/common/util/fsutil/lock.go:64 +0x3e github.com/pulumi/pulumi/tests/integration.synchronouslyDo.func1() /Users/runner/work/pulumi/pulumi/tests/integration/integration_util_test.go:263 +0x39 [..] Previous write at 0x00c000388040 by goroutine 17: github.com/pulumi/pulumi/sdk/v3/go/common/util/fsutil.(*FileMutex).Lock() /Users/runner/work/pulumi/pulumi/sdk/go/common/util/fsutil/lock.go:55 +0x72 github.com/pulumi/pulumi/tests/integration.synchronouslyDo.func2() /Users/runner/work/pulumi/pulumi/tests/integration/integration_util_test.go:269 +0x4e ``` This data race is not expected because the contract for FileMutex is that the Unlock method should only be called after Lock, typicaly from the same goroutine --- synchronouslyDo does not do this. Secondly, synchronouslyDo has a minor bug: it will run the function *eventually* when the lock has been acquired even if the timeout has expirted and the test has failed by then. Resolve these issues by making the following changes: - use a context to track the timeout - defer an unlock only if a lock was successfully acquired - run the operation only if we still have time to run it Includes a previously failing test case.
2023-02-02 20:46:22 +00:00
lockWait := make(chan struct{})
go func() {
test/integration: Avoid data race from FileMutex integration_util_test contains a helper function synchronouslyDo which attempts to run an operation with a file lock with a timeout, in a blocking manner. This implementation has a couple issues. First, there's a data race between Lock() and Unlock() of the mutex: the function defers an unlock regardless of whether a lock was acquired, and if the timing is right, it causes a data race in FileMutex on reading and writing the fsunlock field. ``` WARNING: DATA RACE Read at 0x00c000388040 by goroutine 16: github.com/pulumi/pulumi/sdk/v3/go/common/util/fsutil.(*FileMutex).Unlock() /Users/runner/work/pulumi/pulumi/sdk/go/common/util/fsutil/lock.go:64 +0x3e github.com/pulumi/pulumi/tests/integration.synchronouslyDo.func1() /Users/runner/work/pulumi/pulumi/tests/integration/integration_util_test.go:263 +0x39 [..] Previous write at 0x00c000388040 by goroutine 17: github.com/pulumi/pulumi/sdk/v3/go/common/util/fsutil.(*FileMutex).Lock() /Users/runner/work/pulumi/pulumi/sdk/go/common/util/fsutil/lock.go:55 +0x72 github.com/pulumi/pulumi/tests/integration.synchronouslyDo.func2() /Users/runner/work/pulumi/pulumi/tests/integration/integration_util_test.go:269 +0x4e ``` This data race is not expected because the contract for FileMutex is that the Unlock method should only be called after Lock, typicaly from the same goroutine --- synchronouslyDo does not do this. Secondly, synchronouslyDo has a minor bug: it will run the function *eventually* when the lock has been acquired even if the timeout has expirted and the test has failed by then. Resolve these issues by making the following changes: - use a context to track the timeout - defer an unlock only if a lock was successfully acquired - run the operation only if we still have time to run it Includes a previously failing test case.
2023-02-02 20:46:22 +00:00
mutex := fsutil.NewFileMutex(lockfile)
// ctx.Err will be non-nil when the context finishes
// either because it timed out or because it got canceled.
for ctx.Err() == nil {
if err := mutex.Lock(); err != nil {
time.Sleep(1 * time.Second)
continue
}
all: Fix revive issues Fixes the following issues found by revive included in the latest release of golangci-lint. Full list of issues: **pkg** ``` backend/display/object_diff.go:47:10: superfluous-else: if block ends with a break statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive) backend/display/object_diff.go:716:12: redefines-builtin-id: redefinition of the built-in function delete (revive) backend/display/object_diff.go:742:14: redefines-builtin-id: redefinition of the built-in function delete (revive) backend/display/object_diff.go:983:10: superfluous-else: if block ends with a continue statement, so drop this else and outdent its block (revive) backend/httpstate/backend.go:1814:4: redefines-builtin-id: redefinition of the built-in function cap (revive) backend/httpstate/backend.go:1824:5: redefines-builtin-id: redefinition of the built-in function cap (revive) backend/httpstate/client/client.go:444:2: if-return: redundant if ...; err != nil check, just return error instead. (revive) backend/httpstate/client/client.go:455:2: if-return: redundant if ...; err != nil check, just return error instead. (revive) cmd/pulumi/org.go:113:4: if-return: redundant if ...; err != nil check, just return error instead. (revive) cmd/pulumi/util.go:216:2: if-return: redundant if ...; err != nil check, just return error instead. (revive) codegen/docs/gen.go:428:2: redefines-builtin-id: redefinition of the built-in function copy (revive) codegen/hcl2/model/expression.go:2151:5: redefines-builtin-id: redefinition of the built-in function close (revive) codegen/hcl2/syntax/comments.go:151:2: redefines-builtin-id: redefinition of the built-in function close (revive) codegen/hcl2/syntax/comments.go:329:3: redefines-builtin-id: redefinition of the built-in function close (revive) codegen/hcl2/syntax/comments.go:381:5: redefines-builtin-id: redefinition of the built-in function close (revive) codegen/nodejs/gen.go:1367:5: redefines-builtin-id: redefinition of the built-in function copy (revive) codegen/python/gen_program_expressions.go:136:2: redefines-builtin-id: redefinition of the built-in function close (revive) codegen/python/gen_program_expressions.go:142:3: redefines-builtin-id: redefinition of the built-in function close (revive) codegen/report/report.go:126:6: redefines-builtin-id: redefinition of the built-in function panic (revive) codegen/schema/docs_test.go:210:10: superfluous-else: if block ends with a continue statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive) codegen/schema/schema.go:790:2: redefines-builtin-id: redefinition of the built-in type any (revive) codegen/schema/schema.go:793:4: redefines-builtin-id: redefinition of the built-in type any (revive) resource/deploy/plan.go:506:2: if-return: redundant if ...; err != nil check, just return error instead. (revive) resource/deploy/snapshot_test.go:59:3: redefines-builtin-id: redefinition of the built-in function copy (revive) resource/deploy/state_builder.go:108:2: redefines-builtin-id: redefinition of the built-in function copy (revive) ``` **sdk** ``` go/common/resource/plugin/context.go:142:2: redefines-builtin-id: redefinition of the built-in function copy (revive) go/common/resource/plugin/plugin.go:142:12: superfluous-else: if block ends with a break statement, so drop this else and outdent its block (revive) go/common/resource/properties_diff.go:114:2: redefines-builtin-id: redefinition of the built-in function len (revive) go/common/resource/properties_diff.go:117:4: redefines-builtin-id: redefinition of the built-in function len (revive) go/common/resource/properties_diff.go:122:4: redefines-builtin-id: redefinition of the built-in function len (revive) go/common/resource/properties_diff.go:127:4: redefines-builtin-id: redefinition of the built-in function len (revive) go/common/resource/properties_diff.go:132:4: redefines-builtin-id: redefinition of the built-in function len (revive) go/common/util/deepcopy/copy.go:30:1: redefines-builtin-id: redefinition of the built-in function copy (revive) go/common/workspace/creds.go:242:2: if-return: redundant if ...; err != nil check, just return error instead. (revive) go/pulumi-language-go/main.go:569:2: if-return: redundant if ...; err != nil check, just return error instead. (revive) go/pulumi-language-go/main.go:706:2: if-return: redundant if ...; err != nil check, just return error instead. (revive) go/pulumi/run_test.go:925:2: redefines-builtin-id: redefinition of the built-in type any (revive) go/pulumi/run_test.go:933:3: redefines-builtin-id: redefinition of the built-in type any (revive) nodejs/cmd/pulumi-language-nodejs/main.go:778:2: if-return: redundant if ...; err != nil check, just return error instead. (revive) python/cmd/pulumi-language-python/main.go:1011:2: if-return: redundant if ...; err != nil check, just return error instead. (revive) python/cmd/pulumi-language-python/main.go:863:2: if-return: redundant if ...; err != nil check, just return error instead. (revive) python/python.go:230:2: redefines-builtin-id: redefinition of the built-in function print (revive) ``` **tests** ``` integration/integration_util_test.go:282:11: superfluous-else: if block ends with a continue statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive) ```
2023-03-20 23:48:02 +00:00
defer func() {
assert.NoError(t, mutex.Unlock())
}()
break
}
test/integration: Avoid data race from FileMutex integration_util_test contains a helper function synchronouslyDo which attempts to run an operation with a file lock with a timeout, in a blocking manner. This implementation has a couple issues. First, there's a data race between Lock() and Unlock() of the mutex: the function defers an unlock regardless of whether a lock was acquired, and if the timing is right, it causes a data race in FileMutex on reading and writing the fsunlock field. ``` WARNING: DATA RACE Read at 0x00c000388040 by goroutine 16: github.com/pulumi/pulumi/sdk/v3/go/common/util/fsutil.(*FileMutex).Unlock() /Users/runner/work/pulumi/pulumi/sdk/go/common/util/fsutil/lock.go:64 +0x3e github.com/pulumi/pulumi/tests/integration.synchronouslyDo.func1() /Users/runner/work/pulumi/pulumi/tests/integration/integration_util_test.go:263 +0x39 [..] Previous write at 0x00c000388040 by goroutine 17: github.com/pulumi/pulumi/sdk/v3/go/common/util/fsutil.(*FileMutex).Lock() /Users/runner/work/pulumi/pulumi/sdk/go/common/util/fsutil/lock.go:55 +0x72 github.com/pulumi/pulumi/tests/integration.synchronouslyDo.func2() /Users/runner/work/pulumi/pulumi/tests/integration/integration_util_test.go:269 +0x4e ``` This data race is not expected because the contract for FileMutex is that the Unlock method should only be called after Lock, typicaly from the same goroutine --- synchronouslyDo does not do this. Secondly, synchronouslyDo has a minor bug: it will run the function *eventually* when the lock has been acquired even if the timeout has expirted and the test has failed by then. Resolve these issues by making the following changes: - use a context to track the timeout - defer an unlock only if a lock was successfully acquired - run the operation only if we still have time to run it Includes a previously failing test case.
2023-02-02 20:46:22 +00:00
// Context may hav expired
// by the time we acquired the lock.
if ctx.Err() == nil {
fn()
close(lockWait)
}
}()
select {
test/integration: Avoid data race from FileMutex integration_util_test contains a helper function synchronouslyDo which attempts to run an operation with a file lock with a timeout, in a blocking manner. This implementation has a couple issues. First, there's a data race between Lock() and Unlock() of the mutex: the function defers an unlock regardless of whether a lock was acquired, and if the timing is right, it causes a data race in FileMutex on reading and writing the fsunlock field. ``` WARNING: DATA RACE Read at 0x00c000388040 by goroutine 16: github.com/pulumi/pulumi/sdk/v3/go/common/util/fsutil.(*FileMutex).Unlock() /Users/runner/work/pulumi/pulumi/sdk/go/common/util/fsutil/lock.go:64 +0x3e github.com/pulumi/pulumi/tests/integration.synchronouslyDo.func1() /Users/runner/work/pulumi/pulumi/tests/integration/integration_util_test.go:263 +0x39 [..] Previous write at 0x00c000388040 by goroutine 17: github.com/pulumi/pulumi/sdk/v3/go/common/util/fsutil.(*FileMutex).Lock() /Users/runner/work/pulumi/pulumi/sdk/go/common/util/fsutil/lock.go:55 +0x72 github.com/pulumi/pulumi/tests/integration.synchronouslyDo.func2() /Users/runner/work/pulumi/pulumi/tests/integration/integration_util_test.go:269 +0x4e ``` This data race is not expected because the contract for FileMutex is that the Unlock method should only be called after Lock, typicaly from the same goroutine --- synchronouslyDo does not do this. Secondly, synchronouslyDo has a minor bug: it will run the function *eventually* when the lock has been acquired even if the timeout has expirted and the test has failed by then. Resolve these issues by making the following changes: - use a context to track the timeout - defer an unlock only if a lock was successfully acquired - run the operation only if we still have time to run it Includes a previously failing test case.
2023-02-02 20:46:22 +00:00
case <-ctx.Done():
t.Fatalf("timed out waiting for lock on %s", lockfile)
case <-lockWait:
// waited for fn, success.
}
}
test/integration: Avoid data race from FileMutex integration_util_test contains a helper function synchronouslyDo which attempts to run an operation with a file lock with a timeout, in a blocking manner. This implementation has a couple issues. First, there's a data race between Lock() and Unlock() of the mutex: the function defers an unlock regardless of whether a lock was acquired, and if the timing is right, it causes a data race in FileMutex on reading and writing the fsunlock field. ``` WARNING: DATA RACE Read at 0x00c000388040 by goroutine 16: github.com/pulumi/pulumi/sdk/v3/go/common/util/fsutil.(*FileMutex).Unlock() /Users/runner/work/pulumi/pulumi/sdk/go/common/util/fsutil/lock.go:64 +0x3e github.com/pulumi/pulumi/tests/integration.synchronouslyDo.func1() /Users/runner/work/pulumi/pulumi/tests/integration/integration_util_test.go:263 +0x39 [..] Previous write at 0x00c000388040 by goroutine 17: github.com/pulumi/pulumi/sdk/v3/go/common/util/fsutil.(*FileMutex).Lock() /Users/runner/work/pulumi/pulumi/sdk/go/common/util/fsutil/lock.go:55 +0x72 github.com/pulumi/pulumi/tests/integration.synchronouslyDo.func2() /Users/runner/work/pulumi/pulumi/tests/integration/integration_util_test.go:269 +0x4e ``` This data race is not expected because the contract for FileMutex is that the Unlock method should only be called after Lock, typicaly from the same goroutine --- synchronouslyDo does not do this. Secondly, synchronouslyDo has a minor bug: it will run the function *eventually* when the lock has been acquired even if the timeout has expirted and the test has failed by then. Resolve these issues by making the following changes: - use a context to track the timeout - defer an unlock only if a lock was successfully acquired - run the operation only if we still have time to run it Includes a previously failing test case.
2023-02-02 20:46:22 +00:00
// Verifies that if a file lock is already acquired,
// synchronouslyDo is able to time out properly.
func TestSynchronouslyDo_timeout(t *testing.T) {
t.Parallel()
path := filepath.Join(t.TempDir(), "foo")
mu := fsutil.NewFileMutex(path)
require.NoError(t, mu.Lock())
defer func() {
assert.NoError(t, mu.Unlock())
}()
fakeT := nonfatalT{T: t}
synchronouslyDo(&fakeT, path, 10*time.Millisecond, func() {
t.Errorf("timed-out operation should not be called")
})
assert.True(t, fakeT.fatal, "must have a fatal failure")
if assert.Len(t, fakeT.messages, 1) {
assert.Contains(t, fakeT.messages[0], "timed out waiting")
}
}
// nonfatalT wraps a testing.T to capture fatal errors.
type nonfatalT struct {
*testing.T
mu sync.Mutex
fatal bool
messages []string
}
func (t *nonfatalT) Fatalf(msg string, args ...interface{}) {
t.mu.Lock()
defer t.mu.Unlock()
t.fatal = true
t.messages = append(t.messages, fmt.Sprintf(msg, args...))
}
// Test methods that create resources.
func testConstructMethodsResources(t *testing.T, lang string, dependencies ...string) {
const testDir = "construct_component_methods_resources"
runComponentSetup(t, testDir)
tests := []struct {
componentDir string
}{
{
componentDir: "testcomponent",
},
{
componentDir: "testcomponent-python",
},
{
componentDir: "testcomponent-go",
},
}
for _, test := range tests {
test := test
t.Run(test.componentDir, func(t *testing.T) {
all: Reformat with gofumpt Per team discussion, switching to gofumpt. [gofumpt][1] is an alternative, stricter alternative to gofmt. It addresses other stylistic concerns that gofmt doesn't yet cover. [1]: https://github.com/mvdan/gofumpt See the full list of [Added rules][2], but it includes: - Dropping empty lines around function bodies - Dropping unnecessary variable grouping when there's only one variable - Ensuring an empty line between multi-line functions - simplification (`-s` in gofmt) is always enabled - Ensuring multi-line function signatures end with `) {` on a separate line. [2]: https://github.com/mvdan/gofumpt#Added-rules gofumpt is stricter, but there's no lock-in. All gofumpt output is valid gofmt output, so if we decide we don't like it, it's easy to switch back without any code changes. gofumpt support is built into the tooling we use for development so this won't change development workflows. - golangci-lint includes a gofumpt check (enabled in this PR) - gopls, the LSP for Go, includes a gofumpt option (see [installation instrutions][3]) [3]: https://github.com/mvdan/gofumpt#installation This change was generated by running: ```bash gofumpt -w $(rg --files -g '*.go' | rg -v testdata | rg -v compilation_error) ``` The following files were manually tweaked afterwards: - pkg/cmd/pulumi/stack_change_secrets_provider.go: one of the lines overflowed and had comments in an inconvenient place - pkg/cmd/pulumi/destroy.go: `var x T = y` where `T` wasn't necessary - pkg/cmd/pulumi/policy_new.go: long line because of error message - pkg/backend/snapshot_test.go: long line trying to assign three variables in the same assignment I have included mention of gofumpt in the CONTRIBUTING.md.
2023-03-03 16:36:39 +00:00
localProviders := []integration.LocalDependency{
{Package: "testcomponent", Path: filepath.Join(testDir, test.componentDir)},
}
integration.ProgramTest(t, &integration.ProgramTestOptions{
Dir: filepath.Join(testDir, lang),
Dependencies: dependencies,
LocalProviders: localProviders,
Quick: true,
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
assert.NotNil(t, stackInfo.Deployment)
assert.Equal(t, 6, len(stackInfo.Deployment.Resources))
var hasExpectedResource bool
var result string
for _, res := range stackInfo.Deployment.Resources {
Allow anything in resource names (#14107) <!--- 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. --> Fixes https://github.com/pulumi/pulumi/issues/13968. Fixes https://github.com/pulumi/pulumi/issues/8949. This requires changing the parsing of URN's slightly, it is _very_ likely that providers will need to update to handle URNs like this correctly. This changes resource names to be `string` not `QName`. We never validated this before and it turns out that users have put all manner of text for resource names so we just updating the system to correctly reflect that. ## 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 - [x] 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-20 08:59:00 +00:00
if res.URN.Name() == "myrandom" {
hasExpectedResource = true
result = res.Outputs["result"].(string)
assert.Equal(t, float64(10), res.Inputs["length"])
assert.Equal(t, 10, len(result))
}
}
assert.True(t, hasExpectedResource)
assert.Equal(t, result, stackInfo.Outputs["result"])
},
})
})
}
}
// Test failures returned from methods are observed.
func testConstructMethodsErrors(t *testing.T, lang string, dependencies ...string) {
const testDir = "construct_component_methods_errors"
runComponentSetup(t, testDir)
tests := []struct {
componentDir string
}{
{
componentDir: "testcomponent",
},
{
componentDir: "testcomponent-python",
},
{
componentDir: "testcomponent-go",
},
}
for _, test := range tests {
test := test
t.Run(test.componentDir, func(t *testing.T) {
stderr := &bytes.Buffer{}
expectedError := "the failure reason (the failure property)"
localProvider := integration.LocalDependency{
Package: "testcomponent", Path: filepath.Join(testDir, test.componentDir),
}
integration.ProgramTest(t, &integration.ProgramTestOptions{
Dir: filepath.Join(testDir, lang),
Dependencies: dependencies,
LocalProviders: []integration.LocalDependency{localProvider},
Quick: true,
Stderr: stderr,
ExpectFailure: true,
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
output := stderr.String()
assert.Contains(t, output, expectedError)
},
})
})
}
}
[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
// Tests methods work when there is an explicit provider for another provider set on the component.
func testConstructMethodsProvider(t *testing.T, lang string, dependencies ...string) {
const testDir = "construct_component_methods_provider"
runComponentSetup(t, testDir)
tests := []struct {
componentDir string
}{
{
componentDir: "testcomponent",
},
{
componentDir: "testcomponent-python",
},
{
componentDir: "testcomponent-go",
},
}
for _, test := range tests {
test := test
t.Run(test.componentDir, func(t *testing.T) {
localProvider := integration.LocalDependency{
Package: "testcomponent", Path: filepath.Join(testDir, test.componentDir),
}
testProvider := integration.LocalDependency{
Package: "testprovider", Path: filepath.Join("..", "testprovider"),
}
integration.ProgramTest(t, &integration.ProgramTestOptions{
Dir: filepath.Join(testDir, lang),
Dependencies: dependencies,
LocalProviders: []integration.LocalDependency{localProvider, testProvider},
Quick: true,
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
assert.Equal(t, "Hello World, Alice!", stackInfo.Outputs["message1"])
assert.Equal(t, "Hi There, Bob!", stackInfo.Outputs["message2"])
},
})
})
}
}
func testConstructOutputValues(t *testing.T, lang string, dependencies ...string) {
const testDir = "construct_component_output_values"
runComponentSetup(t, testDir)
tests := []struct {
componentDir string
}{
{
componentDir: "testcomponent",
},
{
componentDir: "testcomponent-python",
},
{
componentDir: "testcomponent-go",
},
}
for _, test := range tests {
test := test
t.Run(test.componentDir, func(t *testing.T) {
all: Reformat with gofumpt Per team discussion, switching to gofumpt. [gofumpt][1] is an alternative, stricter alternative to gofmt. It addresses other stylistic concerns that gofmt doesn't yet cover. [1]: https://github.com/mvdan/gofumpt See the full list of [Added rules][2], but it includes: - Dropping empty lines around function bodies - Dropping unnecessary variable grouping when there's only one variable - Ensuring an empty line between multi-line functions - simplification (`-s` in gofmt) is always enabled - Ensuring multi-line function signatures end with `) {` on a separate line. [2]: https://github.com/mvdan/gofumpt#Added-rules gofumpt is stricter, but there's no lock-in. All gofumpt output is valid gofmt output, so if we decide we don't like it, it's easy to switch back without any code changes. gofumpt support is built into the tooling we use for development so this won't change development workflows. - golangci-lint includes a gofumpt check (enabled in this PR) - gopls, the LSP for Go, includes a gofumpt option (see [installation instrutions][3]) [3]: https://github.com/mvdan/gofumpt#installation This change was generated by running: ```bash gofumpt -w $(rg --files -g '*.go' | rg -v testdata | rg -v compilation_error) ``` The following files were manually tweaked afterwards: - pkg/cmd/pulumi/stack_change_secrets_provider.go: one of the lines overflowed and had comments in an inconvenient place - pkg/cmd/pulumi/destroy.go: `var x T = y` where `T` wasn't necessary - pkg/cmd/pulumi/policy_new.go: long line because of error message - pkg/backend/snapshot_test.go: long line trying to assign three variables in the same assignment I have included mention of gofumpt in the CONTRIBUTING.md.
2023-03-03 16:36:39 +00:00
localProviders := []integration.LocalDependency{
{Package: "testcomponent", Path: filepath.Join(testDir, test.componentDir)},
}
integration.ProgramTest(t, &integration.ProgramTestOptions{
Dir: filepath.Join(testDir, lang),
Dependencies: dependencies,
LocalProviders: localProviders,
Quick: true,
})
})
}
}
var previewSummaryRegex = regexp.MustCompile(
`{\s+"steps": \[[\s\S]+],\s+"duration": \d+,\s+"changeSummary": {[\s\S]+}\s+}`)
func assertOutputContainsEvent(t *testing.T, evt apitype.EngineEvent, output string) {
evtJSON := bytes.Buffer{}
encoder := json.NewEncoder(&evtJSON)
encoder.SetEscapeHTML(false)
err := encoder.Encode(evt)
assert.NoError(t, err)
assert.Contains(t, output, evtJSON.String())
}
// printfTestValidation is used by the TestPrintfXYZ test cases in the language-specific test
// files. It validates that there are a precise count of expected stdout/stderr lines in the test output.
func printfTestValidation(t *testing.T, stack integration.RuntimeValidationStackInfo) {
var foundStdout int
var foundStderr int
for _, ev := range stack.Events {
if de := ev.DiagnosticEvent; de != nil {
if strings.HasPrefix(de.Message, fmt.Sprintf("Line %d", foundStdout)) {
foundStdout++
} else if strings.HasPrefix(de.Message, fmt.Sprintf("Errln %d", foundStderr+10)) {
foundStderr++
}
}
}
assert.Equal(t, 11, foundStdout)
assert.Equal(t, 11, foundStderr)
}
func testConstructProviderExplicit(t *testing.T, lang string, dependencies []string) {
const testDir = "construct_component_provider_explicit"
runComponentSetup(t, testDir)
localProvider := integration.LocalDependency{
Package: "testcomponent", Path: filepath.Join(testDir, "testcomponent-go"),
}
integration.ProgramTest(t, &integration.ProgramTestOptions{
Dir: filepath.Join(testDir, lang),
Dependencies: dependencies,
LocalProviders: []integration.LocalDependency{localProvider},
Quick: true,
NoParallel: true, // already called by tests
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
assert.Equal(t, "hello world", stackInfo.Outputs["message"])
assert.Equal(t, "hello world", stackInfo.Outputs["nestedMessage"])
},
})
}
Support returning plain values from methods (#13592) Support returning plain values from methods. Implements Node, Python and Go support. Remaining: - [x] test receiving unknowns - [x] acceptance tests written and passing locally for Node, Python, Go clients against a Go server - [x] acceptance tests passing in CI - [x] tickets filed for remaining languages - [x] https://github.com/pulumi/pulumi-yaml/issues/499 - [x] https://github.com/pulumi/pulumi-java/issues/1193 - [x] https://github.com/pulumi/pulumi-dotnet/issues/170 Known limitations: - this is technically a breaking change in case there is code out there that already uses methods that return Plain: true - struct-wrapping limitation: the provider for the component resource needs to still wrap the plain-returning Method response with a 1-arg struct; by convention the field is named "res", and this is how it travels through the plumbing - resources cannot return plain values yet - the provider for the component resource cannot have unknown configuration, if it does, the methods will not be called - Per Luke https://github.com/pulumi/pulumi/issues/11520 this might not be supported/realizable yet <!--- 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. --> Fixes https://github.com/pulumi/pulumi/issues/12709 ## Checklist - [ ] I have run `make tidy` to update any new dependencies - [ ] 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. --> - [ ] I have added tests that prove my fix is effective or that my feature works <!--- User-facing changes require a CHANGELOG entry. --> - [ ] 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-18 06:02:06 +00:00
func testConstructComponentConfigureProviderCommonOptions() integration.ProgramTestOptions {
const testDir = "construct_component_configure_provider"
localProvider := integration.LocalDependency{
Package: "metaprovider", Path: filepath.Join(testDir, "testcomponent-go"),
}
return integration.ProgramTestOptions{
NoParallel: true,
Support returning plain values from methods (#13592) Support returning plain values from methods. Implements Node, Python and Go support. Remaining: - [x] test receiving unknowns - [x] acceptance tests written and passing locally for Node, Python, Go clients against a Go server - [x] acceptance tests passing in CI - [x] tickets filed for remaining languages - [x] https://github.com/pulumi/pulumi-yaml/issues/499 - [x] https://github.com/pulumi/pulumi-java/issues/1193 - [x] https://github.com/pulumi/pulumi-dotnet/issues/170 Known limitations: - this is technically a breaking change in case there is code out there that already uses methods that return Plain: true - struct-wrapping limitation: the provider for the component resource needs to still wrap the plain-returning Method response with a 1-arg struct; by convention the field is named "res", and this is how it travels through the plumbing - resources cannot return plain values yet - the provider for the component resource cannot have unknown configuration, if it does, the methods will not be called - Per Luke https://github.com/pulumi/pulumi/issues/11520 this might not be supported/realizable yet <!--- 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. --> Fixes https://github.com/pulumi/pulumi/issues/12709 ## Checklist - [ ] I have run `make tidy` to update any new dependencies - [ ] 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. --> - [ ] I have added tests that prove my fix is effective or that my feature works <!--- User-facing changes require a CHANGELOG entry. --> - [ ] 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-18 06:02:06 +00:00
Config: map[string]string{
"proxy": "FromEnv",
},
LocalProviders: []integration.LocalDependency{localProvider},
Quick: false, // intentional, need to test preview here
AllowEmptyPreviewChanges: true, // Pulumi will warn that provider has unknowns in its config
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
assert.Contains(t, stackInfo.Outputs, "keyAlgo")
assert.Equal(t, "ECDSA", stackInfo.Outputs["keyAlgo"])
assert.Contains(t, stackInfo.Outputs, "keyAlgo2")
assert.Equal(t, "ECDSA", stackInfo.Outputs["keyAlgo2"])
var providerURNID string
for _, r := range stackInfo.Deployment.Resources {
if strings.Contains(string(r.URN), "PrivateKey") {
providerURNID = r.Provider
}
}
require.NotEmptyf(t, providerURNID, "Did not find the provider of PrivateKey resource")
var providerFromEnvSetting *bool
for _, r := range stackInfo.Deployment.Resources {
if fmt.Sprintf("%s::%s", r.URN, r.ID) == providerURNID {
providerFromEnvSetting = new(bool)
proxy, ok := r.Inputs["proxy"]
require.Truef(t, ok, "expected %q Inputs to contain 'proxy'", providerURNID)
proxyMap, ok := proxy.(map[string]any)
require.Truef(t, ok, "expected %q Inputs 'proxy' to be of type map[string]any", providerURNID)
fromEnv, ok := proxyMap["fromEnv"]
require.Truef(t, ok, "expected %q Inputs 'proxy' to contain 'fromEnv'", providerURNID)
fromEnvB, ok := fromEnv.(bool)
require.Truef(t, ok, "expected %q Inputs 'proxy.fromEnv' to have type bool", providerURNID)
*providerFromEnvSetting = fromEnvB
}
}
require.NotNilf(t, providerFromEnvSetting,
"Did not find the inputs of the provider PrivateKey was provisioned with")
require.Truef(t, *providerFromEnvSetting,
"Expected PrivateKey to be provisioned with a provider with fromEnv=true")
require.Equalf(t, float64(42), stackInfo.Outputs["meaningOfLife"],
"Expected meaningOfLife output to be set to the integer 42")
require.Equalf(t, float64(42), stackInfo.Outputs["meaningOfLife2"],
"Expected meaningOfLife2 output to be set to the integer 42")
},
}
}