.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
This commit is contained in:
parent
9a78ca1ca4
commit
83e24765f3
|
@ -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<InvalidOperationException>(ValidatePulumiVersion);
|
||||
}
|
||||
else
|
||||
{
|
||||
LocalWorkspace.ValidatePulumiVersion(testMinVersion, currentVersion, optOut);
|
||||
LocalWorkspace.ParseAndValidatePulumiVersion(testMinVersion, currentVersion, optOut);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -264,6 +264,8 @@ namespace Pulumi.Automation
|
|||
public static Task<WorkspaceStack> CreateOrSelectStackAsync(LocalProgramArgs args, CancellationToken cancellationToken)
|
||||
=> CreateStackHelperAsync(args, WorkspaceStack.CreateOrSelectAsync, cancellationToken);
|
||||
|
||||
private static string SkipVersionCheckVar = "PULUMI_AUTOMATION_API_SKIP_VERSION_CHECK";
|
||||
|
||||
private static async Task<WorkspaceStack> CreateStackHelperAsync(
|
||||
InlineProgramArgs args,
|
||||
Func<string, Workspace, CancellationToken, Task<WorkspaceStack>> 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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue