2024-01-26 16:01:17 +00:00
|
|
|
// Copyright 2016-2024, 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.
|
|
|
|
|
|
|
|
package auto
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
2024-01-27 01:25:23 +00:00
|
|
|
"path/filepath"
|
|
|
|
"runtime"
|
2024-01-26 16:01:17 +00:00
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/blang/semver"
|
|
|
|
"github.com/pulumi/pulumi/sdk/v3"
|
2024-03-27 21:12:00 +00:00
|
|
|
ptesting "github.com/pulumi/pulumi/sdk/v3/go/common/testing"
|
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
|
2024-01-26 16:01:17 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestInstallDefaultRoot(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
requestedVersion := semver.Version{Major: 3, Minor: 98, Patch: 0}
|
|
|
|
|
|
|
|
_, err := InstallPulumiCommand(context.Background(), &PulumiCommandOptions{Version: requestedVersion})
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
homeDir, err := os.UserHomeDir()
|
|
|
|
require.NoError(t, err)
|
2024-01-27 01:25:23 +00:00
|
|
|
pulumiBin := filepath.Join(homeDir, ".pulumi", "versions", requestedVersion.String(), "bin", "pulumi")
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
pulumiBin += ".exe"
|
|
|
|
}
|
2024-01-26 16:01:17 +00:00
|
|
|
_, err = os.Stat(pulumiBin)
|
|
|
|
require.NoError(t, err, "did not find pulumi binary in the expected path")
|
|
|
|
cmd := exec.Command(pulumiBin, "version")
|
|
|
|
out, err := cmd.Output()
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, "v3.98.0", strings.TrimSpace(string(out)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestOptionDefaults(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
opts := &PulumiCommandOptions{}
|
|
|
|
|
|
|
|
opts, err := opts.withDefaults()
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
homeDir, err := os.UserHomeDir()
|
|
|
|
require.NoError(t, err)
|
2024-01-27 01:25:23 +00:00
|
|
|
root := filepath.Join(homeDir, ".pulumi", "versions", sdk.Version.String())
|
2024-01-26 16:01:17 +00:00
|
|
|
require.Equal(t, root, opts.Root)
|
|
|
|
require.Equal(t, sdk.Version, opts.Version)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestInstallTwice(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
dir, err := os.MkdirTemp("", "automation-test-")
|
|
|
|
require.NoError(t, err)
|
|
|
|
defer os.RemoveAll(dir)
|
|
|
|
version := semver.Version{Major: 3, Minor: 98, Patch: 0}
|
|
|
|
|
|
|
|
_, err = InstallPulumiCommand(context.Background(), &PulumiCommandOptions{Root: dir, Version: version})
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
2024-01-27 01:25:23 +00:00
|
|
|
pulumiPath := filepath.Join(dir, "bin", "pulumi")
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
pulumiPath += ".exe"
|
|
|
|
}
|
2024-01-26 16:01:17 +00:00
|
|
|
stat, err := os.Stat(pulumiPath)
|
|
|
|
require.NoError(t, err, "did not find pulumi binary in the expected path")
|
|
|
|
modTime1 := stat.ModTime()
|
|
|
|
|
|
|
|
_, err = InstallPulumiCommand(context.Background(), &PulumiCommandOptions{Root: dir, Version: version})
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
stat, err = os.Stat(pulumiPath)
|
|
|
|
require.NoError(t, err, "did not find pulumi binary in the expected path")
|
|
|
|
modTime2 := stat.ModTime()
|
|
|
|
require.Equal(t, modTime1, modTime2)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestErrorIncompatibleVersion(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
dir, err := os.MkdirTemp("", "automation-test-")
|
|
|
|
require.NoError(t, err)
|
|
|
|
defer os.RemoveAll(dir)
|
|
|
|
installedVersion := semver.Version{Major: 3, Minor: 98, Patch: 0}
|
|
|
|
_, err = InstallPulumiCommand(context.Background(), &PulumiCommandOptions{Root: dir, Version: installedVersion})
|
|
|
|
require.NoError(t, err)
|
|
|
|
requestedVersion := semver.Version{Major: 3, Minor: 101, Patch: 0}
|
|
|
|
|
|
|
|
// Try getting an incompatible version
|
|
|
|
_, err = NewPulumiCommand(&PulumiCommandOptions{Root: dir, Version: requestedVersion})
|
|
|
|
|
|
|
|
require.ErrorContains(t, err, "version requirement failed")
|
|
|
|
|
|
|
|
// Succeeds when disabling version check
|
|
|
|
_, err = NewPulumiCommand(&PulumiCommandOptions{Root: dir, Version: requestedVersion, SkipVersionCheck: true})
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
|
|
|
|
2024-03-27 21:12:00 +00:00
|
|
|
//nolint:paralleltest // mutates environment variables
|
|
|
|
func TestNoGlobalPulumi(t *testing.T) {
|
|
|
|
dir, err := os.MkdirTemp("", "automation-test-")
|
|
|
|
require.NoError(t, err)
|
|
|
|
defer os.RemoveAll(dir)
|
|
|
|
version := semver.Version{Major: 3, Minor: 98, Patch: 0}
|
|
|
|
|
|
|
|
// Install before we mutate path, we need some system binaries available to run the install script.
|
|
|
|
_, err = InstallPulumiCommand(context.Background(), &PulumiCommandOptions{Root: dir, Version: version})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
t.Setenv("PATH", "") // Clear path so we don't have access to a globally installed pulumi command.
|
|
|
|
|
|
|
|
// Grab a new pulumi command for our installation, but now env.PATH is
|
|
|
|
// empty, so we can't accidentally use a globally installed pulumi.
|
|
|
|
pulumiCommand, err := InstallPulumiCommand(context.Background(), &PulumiCommandOptions{Root: dir, Version: version})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
deployFunc := func(ctx *pulumi.Context) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
projectName := "autoInstall"
|
|
|
|
stackName := ptesting.RandomStackName()
|
|
|
|
|
|
|
|
_, err = UpsertStackInlineSource(ctx, stackName, projectName, deployFunc, Pulumi(pulumiCommand))
|
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
|
|
|
|
2024-01-26 16:01:17 +00:00
|
|
|
func TestFixupPath(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
Fix slice bounds out of range panic in fixupPath (#16575)
I noticed that sst currently panics if an environment variable's name is
shorter than 5 characters. This fixes the root cause.
```
V=1 sst deploy --stage=dev
SST ❍ ion 0.0.416 ready!
➜ App: myapp
Stage: dev
~ Deploying
panic: runtime error: slice bounds out of range [:5] with length 3
goroutine 1 [running]:
github.com/pulumi/pulumi/sdk/v3/go/auto.fixupPath({0x140018ec408, 0xb1, 0x1400013dfc0?}, {0x1400013dfc0, 0x32})
/home/runner/go/pkg/mod/github.com/pulumi/pulumi/sdk/v3@v3.115.2/go/auto/cmd.go:324 +0x270
github.com/pulumi/pulumi/sdk/v3/go/auto.pulumiCommand.Run({{0x3, 0x73, 0x2, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}}, {0x1400013dfc0, ...}}, ...)
/home/runner/go/pkg/mod/github.com/pulumi/pulumi/sdk/v3@v3.115.2/go/auto/cmd.go:273 +0x394
github.com/pulumi/pulumi/sdk/v3/go/auto.(*LocalWorkspace).runPulumiInputCmdSync(0x14001494000, {0x106fb2b28, 0x1400061c0f0}, {0x0, 0x0}, {0x14000aeba00, 0x4, 0x4})
/home/runner/go/pkg/mod/github.com/pulumi/pulumi/sdk/v3@v3.115.2/go/auto/local_workspace.go:776 +0x1f4
github.com/pulumi/pulumi/sdk/v3/go/auto.(*LocalWorkspace).runPulumiCmdSync(...)
/home/runner/go/pkg/mod/github.com/pulumi/pulumi/sdk/v3@v3.115.2/go/auto/local_workspace.go:790
github.com/pulumi/pulumi/sdk/v3/go/auto.(*LocalWorkspace).SelectStack(0x14001494000, {0x106fb2b28, 0x1400061c0f0}, {0x16b392623, 0x3})
/home/runner/go/pkg/mod/github.com/pulumi/pulumi/sdk/v3@v3.115.2/go/auto/local_workspace.go:568 +0x160
github.com/pulumi/pulumi/sdk/v3/go/auto.SelectStack({0x106fb2b28?, 0x1400061c0f0?}, {0x16b392623, 0x3}, {0x106fcd6e0, 0x14001494000})
/home/runner/go/pkg/mod/github.com/pulumi/pulumi/sdk/v3@v3.115.2/go/auto/stack.go:179 +0x5c
github.com/pulumi/pulumi/sdk/v3/go/auto.UpsertStack({0x106fb2b28, 0x1400061c0f0}, {0x16b392623, 0x3}, {0x106fcd6e0, 0x14001494000})
/home/runner/go/pkg/mod/github.com/pulumi/pulumi/sdk/v3@v3.115.2/go/auto/stack.go:191 +0x34
github.com/sst/ion/pkg/project.(*stack).Run(0x14000a42058, {0x106fb2b28, 0x1400061c0f0}, 0x140000bbcc0)
/home/runner/work/ion/ion/pkg/project/stack.go:283 +0x1cf4
main.init.func3(0x14000064550)
/home/runner/work/ion/ion/cmd/sst/main.go:458 +0x2c8
main.run()
/home/runner/work/ion/ion/cmd/sst/main.go:160 +0x6cc
main.main()
/home/runner/work/ion/ion/cmd/sst/main.go:52 +0x12c
```
2024-07-08 09:10:58 +00:00
|
|
|
env := fixupPath([]string{"FOO=bar", "V=1"}, "/pulumi-root/bin")
|
2024-01-26 16:01:17 +00:00
|
|
|
|
|
|
|
require.Contains(t, env, "PATH=/pulumi-root/bin")
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestFixupPathExistingPath(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
env := fixupPath([]string{"FOO=bar", "PATH=/usr/local/bin"}, "/pulumi-root/bin")
|
|
|
|
|
|
|
|
require.Contains(t, env, "PATH=/pulumi-root/bin"+string(os.PathListSeparator)+"/usr/local/bin")
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
PARSE = `Unable to parse`
|
|
|
|
MAJOR = `Major version mismatch.`
|
|
|
|
MINIMUM = `Minimum version requirement failed.`
|
|
|
|
)
|
|
|
|
|
|
|
|
var minVersionTests = []struct {
|
|
|
|
name string
|
|
|
|
currentVersion string
|
|
|
|
expectedError string
|
|
|
|
optOut bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
"higher_major",
|
|
|
|
"100.0.0",
|
|
|
|
MAJOR,
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"lower_major",
|
|
|
|
"1.0.0",
|
|
|
|
MINIMUM,
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"higher_minor",
|
|
|
|
"2.2.0",
|
|
|
|
MINIMUM,
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"lower_minor",
|
|
|
|
"2.1.0",
|
|
|
|
MINIMUM,
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"equal_minor_higher_patch",
|
|
|
|
"2.2.2",
|
|
|
|
MINIMUM,
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"equal_minor_equal_patch",
|
|
|
|
"2.2.1",
|
|
|
|
MINIMUM,
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"equal_minor_lower_patch",
|
|
|
|
"2.2.0",
|
|
|
|
MINIMUM,
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"equal_minor_equal_patch_prerelease",
|
|
|
|
// Note that prerelease < release so this case will error
|
|
|
|
"2.21.1-alpha.1234",
|
|
|
|
MINIMUM,
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"opt_out_of_check_would_fail_otherwise",
|
|
|
|
"2.2.0",
|
|
|
|
"",
|
|
|
|
true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"opt_out_of_check_would_succeed_otherwise",
|
|
|
|
"2.2.0",
|
|
|
|
"",
|
|
|
|
true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"unparsable_version",
|
|
|
|
"invalid",
|
|
|
|
PARSE,
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"opt_out_unparsable_version",
|
|
|
|
"invalid",
|
|
|
|
"",
|
|
|
|
true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMinimumVersion(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
for _, tt := range minVersionTests {
|
|
|
|
tt := tt
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
minVersion := semver.Version{Major: 2, Minor: 21, Patch: 1}
|
|
|
|
|
|
|
|
_, err := parseAndValidatePulumiVersion(minVersion, tt.currentVersion, tt.optOut)
|
|
|
|
|
|
|
|
if tt.expectedError != "" {
|
|
|
|
require.ErrorContains(t, err, tt.expectedError)
|
|
|
|
} else {
|
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|