From 83e24765f3d3ddc99d26e8785b6c38d13eb4cfe3 Mon Sep 17 00:00:00 2001 From: Ian Wahbe Date: Wed, 27 Oct 2021 20:54:23 -0700 Subject: [PATCH] .NET & python SDKs parity for bad pulumi versions (#8297) * .NET & python SDKs parity for bad pulumi versions They handle invalid Pulumi CLI version gracefully. * Make python version property lazy * Clarify .NET logic * Add python test for validate_pulumi_version * Add tests for invalid versions * Fix python test * Fix typo * Fix tests * Have _validate_pulumi_version handle parsing * Modify python and .NET to parseAndValidate * Modify typescript and go to parseAndValidate * fix name --- .../LocalWorkspaceTests.cs | 8 ++- .../Pulumi.Automation/LocalWorkspace.cs | 36 ++++++---- sdk/go/auto/local_workspace.go | 45 +++++------- sdk/go/auto/local_workspace_test.go | 71 +++++++++++-------- sdk/nodejs/automation/localWorkspace.ts | 15 ++-- .../tests/automation/localWorkspace.spec.ts | 63 +++++++++------- .../lib/pulumi/automation/_local_workspace.py | 46 +++++++++--- .../test/automation/test_local_workspace.py | 43 +++++------ 8 files changed, 191 insertions(+), 136 deletions(-) diff --git a/sdk/dotnet/Pulumi.Automation.Tests/LocalWorkspaceTests.cs b/sdk/dotnet/Pulumi.Automation.Tests/LocalWorkspaceTests.cs index 2730e1783..bc0b08a70 100644 --- a/sdk/dotnet/Pulumi.Automation.Tests/LocalWorkspaceTests.cs +++ b/sdk/dotnet/Pulumi.Automation.Tests/LocalWorkspaceTests.cs @@ -1508,17 +1508,21 @@ namespace Pulumi.Automation.Tests [InlineData("2.21.1-alpha.1234", true, false)] [InlineData("2.20.0", false, true)] [InlineData("2.22.0", false, true)] + // Invalid version check + [InlineData("invalid", false, true)] + [InlineData("invalid", true, false)] public void ValidVersionTheory(string currentVersion, bool errorExpected, bool optOut) { var testMinVersion = SemVersion.Parse("2.21.1"); + if (errorExpected) { - void ValidatePulumiVersion() => LocalWorkspace.ValidatePulumiVersion(testMinVersion, currentVersion, optOut); + void ValidatePulumiVersion() => LocalWorkspace.ParseAndValidatePulumiVersion(testMinVersion, currentVersion, optOut); Assert.Throws(ValidatePulumiVersion); } else { - LocalWorkspace.ValidatePulumiVersion(testMinVersion, currentVersion, optOut); + LocalWorkspace.ParseAndValidatePulumiVersion(testMinVersion, currentVersion, optOut); } } diff --git a/sdk/dotnet/Pulumi.Automation/LocalWorkspace.cs b/sdk/dotnet/Pulumi.Automation/LocalWorkspace.cs index c63fb0bda..4bd8f2d22 100644 --- a/sdk/dotnet/Pulumi.Automation/LocalWorkspace.cs +++ b/sdk/dotnet/Pulumi.Automation/LocalWorkspace.cs @@ -264,6 +264,8 @@ namespace Pulumi.Automation public static Task CreateOrSelectStackAsync(LocalProgramArgs args, CancellationToken cancellationToken) => CreateStackHelperAsync(args, WorkspaceStack.CreateOrSelectAsync, cancellationToken); + private static string SkipVersionCheckVar = "PULUMI_AUTOMATION_API_SKIP_VERSION_CHECK"; + private static async Task CreateStackHelperAsync( InlineProgramArgs args, Func> initFunc, @@ -374,31 +376,35 @@ namespace Pulumi.Automation var result = await this.RunCommandAsync(new[] { "version" }, cancellationToken).ConfigureAwait(false); var versionString = result.StandardOutput.Trim(); versionString = versionString.TrimStart('v'); - if (!SemVersion.TryParse(versionString, out var version)) - { - throw new InvalidOperationException("Failed to get Pulumi version."); - } - var skipVersionCheckVar = "PULUMI_AUTOMATION_API_SKIP_VERSION_CHECK"; - var hasSkipEnvVar = this.EnvironmentVariables?.ContainsKey(skipVersionCheckVar) ?? false; - var optOut = hasSkipEnvVar || Environment.GetEnvironmentVariable(skipVersionCheckVar) != null; - ValidatePulumiVersion(_minimumVersion, version, optOut); - this._pulumiVersion = version; + + var hasSkipEnvVar = this.EnvironmentVariables?.ContainsKey(SkipVersionCheckVar) ?? false; + var optOut = hasSkipEnvVar || Environment.GetEnvironmentVariable(SkipVersionCheckVar) != null; + this._pulumiVersion = ParseAndValidatePulumiVersion(_minimumVersion, versionString, optOut); } - internal static void ValidatePulumiVersion(SemVersion minVersion, SemVersion currentVersion, bool optOut) + internal static SemVersion? ParseAndValidatePulumiVersion(SemVersion minVersion, string currentVersion, bool optOut) { + if (!SemVersion.TryParse(currentVersion, out var version)) + { + version = null; + } if (optOut) { - return; + return version; } - if (minVersion.Major < currentVersion.Major) + if (version == null) { - throw new InvalidOperationException($"Major version mismatch. You are using Pulumi CLI version {currentVersion} with Automation SDK v{minVersion.Major}. Please update the SDK."); + throw new InvalidOperationException("Failed to get Pulumi version. This is probably a pulumi error. You can override by version checking by setting {SkipVersionCheckVar}=true."); } - if (minVersion > currentVersion) + if (minVersion.Major < version.Major) { - throw new InvalidOperationException($"Minimum version requirement failed. The minimum CLI version requirement is {minVersion}, your current CLI version is {currentVersion}. Please update the Pulumi CLI."); + throw new InvalidOperationException($"Major version mismatch. You are using Pulumi CLI version {version} with Automation SDK v{minVersion.Major}. Please update the SDK."); } + if (minVersion > version) + { + throw new InvalidOperationException($"Minimum version requirement failed. The minimum CLI version requirement is {minVersion}, your current CLI version is {version}. Please update the Pulumi CLI."); + } + return version; } /// diff --git a/sdk/go/auto/local_workspace.go b/sdk/go/auto/local_workspace.go index d3d6e4c81..1fae33e68 100644 --- a/sdk/go/auto/local_workspace.go +++ b/sdk/go/auto/local_workspace.go @@ -506,30 +506,30 @@ func (l *LocalWorkspace) StackOutputs(ctx context.Context, stackName string) (Ou return res, nil } -func (l *LocalWorkspace) getPulumiVersion(ctx context.Context) (semver.Version, error) { +func (l *LocalWorkspace) getPulumiVersion(ctx context.Context) (string, error) { stdout, stderr, errCode, err := l.runPulumiCmdSync(ctx, "version") if err != nil { - return semver.Version{}, newAutoError(errors.Wrap(err, "could not determine pulumi version"), stdout, stderr, errCode) + return "", newAutoError(errors.Wrap(err, "could not determine pulumi version"), stdout, stderr, errCode) } - version, err := semver.ParseTolerant(stdout) - if err != nil { - return semver.Version{}, newAutoError(errors.Wrap(err, "could not determine pulumi version"), stdout, stderr, errCode) - } - return version, nil + return stdout, nil } //nolint:lll -func validatePulumiVersion(minVersion semver.Version, currentVersion semver.Version, optOut bool) error { +func parseAndValidatePulumiVersion(minVersion semver.Version, currentVersion string, optOut bool) (semver.Version, error) { + version, err := semver.ParseTolerant(currentVersion) + if err != nil && !optOut { + return semver.Version{}, errors.Wrapf(err, "Unable to parse Pulumi CLI version (skip with %s=true)", skipVersionCheckVar) + } if optOut { - return nil + return version, nil } - if minVersion.Major < currentVersion.Major { - return errors.Errorf("Major version mismatch. You are using Pulumi CLI version %s with Automation SDK v%v. Please update the SDK.", currentVersion, minVersion.Major) + if minVersion.Major < version.Major { + return semver.Version{}, errors.Errorf("Major version mismatch. You are using Pulumi CLI version %s with Automation SDK v%v. Please update the SDK.", currentVersion, minVersion.Major) } - if minVersion.GT(currentVersion) { - return errors.Errorf("Minimum version requirement failed. The minimum CLI version requirement is %s, your current CLI version is %s. Please update the Pulumi CLI.", minimumVersion, currentVersion) + if minVersion.GT(version) { + return semver.Version{}, errors.Errorf("Minimum version requirement failed. The minimum CLI version requirement is %s, your current CLI version is %s. Please update the Pulumi CLI.", minimumVersion, currentVersion) } - return nil + return version, nil } func (l *LocalWorkspace) runPulumiCmdSync( @@ -597,19 +597,12 @@ func NewLocalWorkspace(ctx context.Context, opts ...LocalWorkspaceOption) (Works if val, ok := lwOpts.EnvVars[skipVersionCheckVar]; ok { optOut = optOut || cmdutil.IsTruthy(val) } - v, err := l.getPulumiVersion(ctx) - if err == nil { - l.pulumiVersion = v + currentVersion, err := l.getPulumiVersion(ctx) + if err != nil { + return nil, err } - if !optOut { - if err != nil { - return nil, errors.Wrapf(err, - "failed to create workspace, unable to get pulumi version (skip with %s=true)", skipVersionCheckVar) - } - if err = validatePulumiVersion(minimumVersion, l.pulumiVersion, optOut); err != nil { - return nil, err - } - + if l.pulumiVersion, err = parseAndValidatePulumiVersion(minimumVersion, currentVersion, optOut); err != nil { + return nil, err } if lwOpts.Project != nil { diff --git a/sdk/go/auto/local_workspace_test.go b/sdk/go/auto/local_workspace_test.go index 46f97b264..366d968a1 100644 --- a/sdk/go/auto/local_workspace_test.go +++ b/sdk/go/auto/local_workspace_test.go @@ -1,4 +1,4 @@ -// Copyright 2016-2020, Pulumi Corporation. +// Copyright 2016-2021, Pulumi Corporation. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -1507,72 +1507,87 @@ func TestPulumiVersion(t *testing.T) { assert.Regexp(t, `(\d+\.)(\d+\.)(\d+)(-.*)?`, version) } +const PARSE = `Unable to parse` +const MAJOR = `Major version mismatch.` +const MINIMUM = `Minimum version requirement failed.` + var minVersionTests = []struct { name string - currentVersion semver.Version - expectError bool + currentVersion string + expectedError string optOut bool }{ { "higher_major", - semver.Version{Major: 100, Minor: 0, Patch: 0}, - true, + "100.0.0", + MAJOR, false, }, { "lower_major", - semver.Version{Major: 1, Minor: 0, Patch: 0}, - true, + "1.0.0", + MINIMUM, false, }, { "higher_minor", - semver.Version{Major: 2, Minor: 22, Patch: 0}, - false, + "2.2.0", + MINIMUM, false, }, { "lower_minor", - semver.Version{Major: 2, Minor: 1, Patch: 0}, - true, + "2.1.0", + MINIMUM, false, }, { "equal_minor_higher_patch", - semver.Version{Major: 2, Minor: 21, Patch: 2}, - false, + "2.2.2", + MINIMUM, false, }, { "equal_minor_equal_patch", - semver.Version{Major: 2, Minor: 21, Patch: 1}, - false, + "2.2.1", + MINIMUM, false, }, { "equal_minor_lower_patch", - semver.Version{Major: 2, Minor: 21, Patch: 0}, - true, + "2.2.0", + MINIMUM, false, }, { "equal_minor_equal_patch_prerelease", // Note that prerelease < release so this case will error - semver.Version{Major: 2, Minor: 21, Patch: 1, - Pre: []semver.PRVersion{{VersionStr: "alpha"}, {VersionNum: 1234, IsNum: true}}}, - true, + "2.21.1-alpha.1234", + MINIMUM, false, }, { "opt_out_of_check_would_fail_otherwise", - semver.Version{Major: 2, Minor: 20, Patch: 0}, - false, + "2.2.0", + "", true, }, { "opt_out_of_check_would_succeed_otherwise", - semver.Version{Major: 2, Minor: 22, Patch: 0}, + "2.2.0", + "", + true, + }, + { + "unparsable_version", + "invalid", + PARSE, false, + }, + { + "opt_out_unparsable_version", + "invalid", + "", true, }, } @@ -1582,15 +1597,11 @@ func TestMinimumVersion(t *testing.T) { t.Run(tt.name, func(t *testing.T) { minVersion := semver.Version{Major: 2, Minor: 21, Patch: 1} - err := validatePulumiVersion(minVersion, tt.currentVersion, tt.optOut) + _, err := parseAndValidatePulumiVersion(minVersion, tt.currentVersion, tt.optOut) - if tt.expectError { + if tt.expectedError != "" { assert.Error(t, err) - if minVersion.Major < tt.currentVersion.Major { - assert.Regexp(t, `Major version mismatch.`, err.Error()) - } else { - assert.Regexp(t, `Minimum version requirement failed.`, err.Error()) - } + assert.Regexp(t, tt.expectedError, err.Error()) } else { assert.Nil(t, err) } diff --git a/sdk/nodejs/automation/localWorkspace.ts b/sdk/nodejs/automation/localWorkspace.ts index 61dbb0053..917493319 100644 --- a/sdk/nodejs/automation/localWorkspace.ts +++ b/sdk/nodejs/automation/localWorkspace.ts @@ -592,9 +592,8 @@ export class LocalWorkspace implements Workspace { } private async getPulumiVersion(minVersion: semver.SemVer) { const result = await this.runPulumiCmd(["version"]); - const version = semver.parse(result.stdout.trim()); const optOut = !!this.envVars[SKIP_VERSION_CHECK_VAR] || !!process.env[SKIP_VERSION_CHECK_VAR]; - validatePulumiVersion(minVersion, version, optOut); + const version = parseAndValidatePulumiVersion(minVersion, result.stdout.trim(), optOut); if (version != null) { this._pulumiVersion = version; } @@ -732,17 +731,19 @@ function loadProjectSettings(workDir: string) { * @param currentVersion The currently known version. `null` indicates that the current version is unknown. * @paramoptOut If the user has opted out of the version check. */ -export function validatePulumiVersion(minVersion: semver.SemVer, currentVersion: semver.SemVer | null, optOut: boolean) { +export function parseAndValidatePulumiVersion(minVersion: semver.SemVer, currentVersion: string, optOut: boolean): semver.SemVer | null { + const version = semver.parse(currentVersion); if (optOut) { - return; + return version; } - if (currentVersion == null) { + if (version == null) { throw new Error(`Failed to parse Pulumi CLI version. This is probably an internal error. You can override this by setting "${SKIP_VERSION_CHECK_VAR}" to "true".`); } - if (minVersion.major < currentVersion.major) { + if (minVersion.major < version.major) { throw new Error(`Major version mismatch. You are using Pulumi CLI version ${currentVersion.toString()} with Automation SDK v${minVersion.major}. Please update the SDK.`); } - if (minVersion.compare(currentVersion) === 1) { + if (minVersion.compare(version) === 1) { throw new Error(`Minimum version requirement failed. The minimum CLI version requirement is ${minVersion.toString()}, your current CLI version is ${currentVersion.toString()}. Please update the Pulumi CLI.`); } + return version; } diff --git a/sdk/nodejs/tests/automation/localWorkspace.spec.ts b/sdk/nodejs/tests/automation/localWorkspace.spec.ts index 41c006dd0..5c2ccc71f 100644 --- a/sdk/nodejs/tests/automation/localWorkspace.spec.ts +++ b/sdk/nodejs/tests/automation/localWorkspace.spec.ts @@ -1,4 +1,4 @@ -// Copyright 2016-2020, Pulumi Corporation. +// Copyright 2016-2021, Pulumi Corporation. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ import { OutputMap, ProjectSettings, Stack, - validatePulumiVersion, + parseAndValidatePulumiVersion, } from "../../automation"; import { Config, output } from "../../index"; import { asyncTest } from "../util"; @@ -502,7 +502,7 @@ describe("LocalWorkspace", () => { await stack.workspace.removeStack(stackName); })); - it(`imports and exports stacks`, asyncTest(async() => { + it(`imports and exports stacks`, asyncTest(async () => { const program = async () => { const config = new Config(); return { @@ -647,8 +647,8 @@ describe("LocalWorkspace", () => { const projectName = "correct_project"; const stackName = fullyQualifiedStackName(getTestOrg(), projectName, `int_test${getTestSuffix()}`); const stack = await LocalWorkspace.createStack( - {stackName, projectName, program: async() => { return; }}, - {workDir: upath.joinSafe(__dirname, "data", "correct_project")}, + { stackName, projectName, program: async () => { return; } }, + { workDir: upath.joinSafe(__dirname, "data", "correct_project") }, ); const projectSettings = await stack.workspace.projectSettings(); assert.strictEqual(projectSettings.name, "correct_project"); @@ -658,7 +658,7 @@ describe("LocalWorkspace", () => { })); it(`correctly sets config on multiple stacks concurrently`, asyncTest(async () => { const dones = []; - const stacks = [ "dev", "dev2", "dev3", "dev4", "dev5" ]; + const stacks = ["dev", "dev2", "dev3", "dev4", "dev5"]; const workDir = upath.joinSafe(__dirname, "data", "tcfg"); const ws = await LocalWorkspace.create({ workDir, @@ -679,7 +679,7 @@ describe("LocalWorkspace", () => { const s = stacks[i]; dones.push((async () => { for (let j = 0; j < 20; j++) { - await ws.setConfig(s, "var-" + j, { value: ((x*20)+j).toString()}); + await ws.setConfig(s, "var-" + j, { value: ((x * 20) + j).toString() }); } })()); } @@ -697,84 +697,95 @@ describe("LocalWorkspace", () => { })); }); +const MAJOR = /Major version mismatch./; +const MINIMUM = /Minimum version requirement failed./; +const PARSE = /Failed to parse/; + describe(`checkVersionIsValid`, () => { const versionTests = [ { name: "higher_major", currentVersion: "100.0.0", - expectError: true, + expectError: MAJOR, optOut: false, }, { name: "lower_major", currentVersion: "1.0.0", - expectError: true, + expectError: MINIMUM, optOut: false, }, { name: "higher_minor", currentVersion: "v2.22.0", - expectError: false, + expectError: null, optOut: false, }, { name: "lower_minor", currentVersion: "v2.1.0", - expectError: true, + expectError: MINIMUM, optOut: false, }, { name: "equal_minor_higher_patch", currentVersion: "v2.21.2", - expectError: false, + expectError: null, optOut: false, }, { name: "equal_minor_equal_patch", currentVersion: "v2.21.1", - expectError: false, + expectError: null, optOut: false, }, { name: "equal_minor_lower_patch", currentVersion: "v2.21.0", - expectError: true, + expectError: MINIMUM, optOut: false, }, { name: "equal_minor_equal_patch_prerelease", // Note that prerelease < release so this case will error currentVersion: "v2.21.1-alpha.1234", - expectError: true, + expectError: MINIMUM, optOut: false, }, { name: "opt_out_of_check_would_fail_otherwise", currentVersion: "v2.20.0", - expectError: false, + expectError: null, optOut: true, }, { name: "opt_out_of_check_would_succeed_otherwise", currentVersion: "v2.22.0", - expectError: false, + expectError: null, + optOut: true, + }, + { + name: "invalid_version", + currentVersion: "invalid", + expectError: PARSE, + optOut: false, + }, + { + name: "invalid_version_opt_out", + currentVersion: "invalid", + expectError: null, optOut: true, }, ]; const minVersion = new semver.SemVer("v2.21.1"); versionTests.forEach(test => { - it(`validates ${test.currentVersion}`, () => { - const currentVersion = new semver.SemVer(test.currentVersion); - + it(`validates ${test.name} (${test.currentVersion})`, () => { + const validate = () => parseAndValidatePulumiVersion(minVersion, test.currentVersion, test.optOut); if (test.expectError) { - if (minVersion.major < currentVersion.major) { - assert.throws(() => validatePulumiVersion(minVersion, currentVersion, test.optOut), /Major version mismatch./); - } else { - assert.throws(() => validatePulumiVersion(minVersion, currentVersion, test.optOut), /Minimum version requirement failed./); - } + assert.throws(validate, test.expectError); } else { - assert.doesNotThrow(() => validatePulumiVersion(minVersion, currentVersion, test.optOut)); + assert.doesNotThrow(validate); } }); }); diff --git a/sdk/python/lib/pulumi/automation/_local_workspace.py b/sdk/python/lib/pulumi/automation/_local_workspace.py index 705c79d9d..511f1c860 100644 --- a/sdk/python/lib/pulumi/automation/_local_workspace.py +++ b/sdk/python/lib/pulumi/automation/_local_workspace.py @@ -91,8 +91,8 @@ class LocalWorkspace(Workspace): opt_out = os.getenv(_SKIP_VERSION_CHECK_VAR) is not None if env_vars: opt_out = opt_out or env_vars.get(_SKIP_VERSION_CHECK_VAR) is not None - _validate_pulumi_version(_MINIMUM_VERSION, pulumi_version, opt_out) - self.pulumi_version = str(pulumi_version) + version = _parse_and_validate_pulumi_version(_MINIMUM_VERSION, pulumi_version, opt_out) + self.__pulumi_version = str(version) if version else None if project_settings: self.save_project_settings(project_settings) @@ -100,6 +100,17 @@ class LocalWorkspace(Workspace): for key in stack_settings: self.save_stack_settings(key, stack_settings[key]) + # mypy does not support properties: https://github.com/python/mypy/issues/1362 + @property # type: ignore + def pulumi_version(self) -> str: # type: ignore + if self.__pulumi_version: + return self.__pulumi_version + raise InvalidVersionError("Could not get Pulumi CLI version") + + @pulumi_version.setter # type: ignore + def pulumi_version(self, v: str): + self.__pulumi_version = v + def __repr__(self): return f"{self.__class__.__name__}(work_dir={self.work_dir!r}, " \ f"program={self.program.__name__ if self.program else None}, " \ @@ -288,12 +299,12 @@ class LocalWorkspace(Workspace): outputs[key] = OutputValue(value=plaintext_outputs[key], secret=secret) return outputs - def _get_pulumi_version(self) -> VersionInfo: + def _get_pulumi_version(self) -> str: result = self._run_pulumi_cmd_sync(["version"]) version_string = result.stdout.strip() if version_string[0] == "v": version_string = version_string[1:] - return VersionInfo.parse(version_string) + return version_string def _run_pulumi_cmd_sync(self, args: List[str], on_output: Optional[OnOutput] = None) -> CommandResult: envs = {"PULUMI_HOME": self.pulumi_home} if self.pulumi_home else {} @@ -474,16 +485,31 @@ def get_stack_settings_name(name: str) -> str: return parts[-1] -def _validate_pulumi_version(min_version: VersionInfo, current_version: VersionInfo, opt_out: bool): +def _parse_and_validate_pulumi_version(min_version: VersionInfo, + current_version: str, + opt_out: bool) -> Optional[VersionInfo]: + """ + Parse and return a version. An error is raised if the version is not + valid. If *current_version* is not a valid version but *opt_out* is true, + *None* is returned. + """ + try: + version: Optional[VersionInfo] = VersionInfo.parse(current_version) + except ValueError: + version = None if opt_out: - return - if min_version.major < current_version.major: - raise InvalidVersionError(f"Major version mismatch. You are using Pulumi CLI version {current_version} with " + return version + if version is None: + raise InvalidVersionError(f"Could not parse the Pulumi CLI version. This is probably an internal error. " + f"If you are sure you have the correct version, set {_SKIP_VERSION_CHECK_VAR}=true.") + if min_version.major < version.major: + raise InvalidVersionError(f"Major version mismatch. You are using Pulumi CLI version {version} with " f"Automation SDK v{min_version.major}. Please update the SDK.") - if min_version.compare(current_version) == 1: + if min_version.compare(version) == 1: raise InvalidVersionError(f"Minimum version requirement failed. The minimum CLI version requirement is " - f"{min_version}, your current CLI version is {current_version}. " + f"{min_version}, your current CLI version is {version}. " f"Please update the Pulumi CLI.") + return version def _load_project_settings(work_dir: str) -> ProjectSettings: diff --git a/sdk/python/lib/test/automation/test_local_workspace.py b/sdk/python/lib/test/automation/test_local_workspace.py index 0e3e54ee0..e7b207b6a 100644 --- a/sdk/python/lib/test/automation/test_local_workspace.py +++ b/sdk/python/lib/test/automation/test_local_workspace.py @@ -38,23 +38,30 @@ from pulumi.automation import ( StackAlreadyExistsError, fully_qualified_stack_name, ) -from pulumi.automation._local_workspace import _validate_pulumi_version +from pulumi.automation._local_workspace import _parse_and_validate_pulumi_version extensions = ["json", "yaml", "yml"] +MAJOR = "Major version mismatch." +MINIMAL = "Minimum version requirement failed." +PARSE = "Could not parse the Pulumi CLI" version_tests = [ - ("100.0.0", True, False), - ("1.0.0", True, False), - ("2.22.0", False, False), - ("2.1.0", True, False), - ("2.21.2", False, False), - ("2.21.1", False, False), - ("2.21.0", True, False), + # current_version, expected_error regex, opt_out + ("100.0.0", MAJOR, False), + ("1.0.0", MINIMAL, False), + ("2.22.0", None, False), + ("2.1.0", MINIMAL, False), + ("2.21.2", None, False), + ("2.21.1", None, False), + ("2.21.0", MINIMAL, False), # Note that prerelease < release so this case will error - ("2.21.1-alpha.1234", True, False), + ("2.21.1-alpha.1234", MINIMAL, False), # Test opting out of version check - ("2.20.0", False, True), - ("2.22.0", False, True) + ("2.20.0", None, True), + ("2.22.0", None, True), + # Test invalid version + ("invalid", PARSE, False), + ("invalid", None, True), ] test_min_version = VersionInfo.parse("2.21.1") @@ -454,21 +461,17 @@ class TestLocalWorkspace(unittest.TestCase): self.assertRegex(ws.pulumi_version, r"(\d+\.)(\d+\.)(\d+)(-.*)?") def test_validate_pulumi_version(self): - for current_version, expect_error, opt_out in version_tests: + for current_version, expected_error, opt_out in version_tests: with self.subTest(): - current_version = VersionInfo.parse(current_version) - if expect_error: - error_regex = "Major version mismatch." \ - if test_min_version.major < current_version.major \ - else "Minimum version requirement failed." + if expected_error: with self.assertRaisesRegex( InvalidVersionError, - error_regex, + expected_error, msg=f"min_version:{test_min_version}, current_version:{current_version}" ): - _validate_pulumi_version(test_min_version, current_version, opt_out) + _parse_and_validate_pulumi_version(test_min_version, current_version, opt_out) else: - self.assertIsNone(_validate_pulumi_version(test_min_version, current_version, opt_out)) + _parse_and_validate_pulumi_version(test_min_version, current_version, opt_out) def test_project_settings_respected(self): project_name = "correct_project"