Skip to content

Commit

Permalink
Merge #11291 #11294
Browse files Browse the repository at this point in the history
11291: [auto/go] Test remote operations r=justinvp a=justinvp

Also cleans up some error messages to be consistent with the CLI and other languages.

Related:
- #11290
- #11292
- #11293
- #11294

11294: [auto/dotnet] Test remote operations r=justinvp a=justinvp

Also cleans up some error messages to be consistent with the CLI and other languages.

Related:
- #11290
- #11291
- #11292
- #11293

Co-authored-by: Justin Van Patten <jvp@justinvp.com>
  • Loading branch information
bors[bot] and justinvp committed Nov 24, 2022
3 parents fbabd14 + b707d18 + 7d64482 commit 449307d
Show file tree
Hide file tree
Showing 7 changed files with 473 additions and 31 deletions.
22 changes: 22 additions & 0 deletions sdk/dotnet/Pulumi.Automation.Tests/DeploymentsApiFactAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2016-2022, Pulumi Corporation

using System;
using Xunit;

namespace Pulumi.Automation.Tests
{
public sealed class DeploymentsApiFactAttribute : FactAttribute
{
public DeploymentsApiFactAttribute()
{
if (Environment.GetEnvironmentVariable("PULUMI_ACCESS_TOKEN") is null)
{
Skip = "PULUMI_ACCESS_TOKEN not set";
}
else if (Environment.GetEnvironmentVariable("PULUMI_TEST_DEPLOYMENTS_API") is null)
{
Skip = "PULUMI_TEST_DEPLOYMENTS_API not set";
}
}
}
}
21 changes: 2 additions & 19 deletions sdk/dotnet/Pulumi.Automation.Tests/LocalWorkspaceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,14 @@
using Xunit.Abstractions;
using ILogger = Microsoft.Extensions.Logging.ILogger;

using static Pulumi.Automation.Tests.Utility;

namespace Pulumi.Automation.Tests
{
public class LocalWorkspaceTests
{
private static readonly string _pulumiOrg = GetTestOrg();

private static string GetTestSuffix()
{
var random = new Random();
var result = random.Next(); // 31 bits, highest bit will be 0 (signed)
return result.ToString("x"); // 8 hex characters
}

private static string RandomStackName()
{
const string chars = "abcdefghijklmnopqrstuvwxyz";
return new string(Enumerable.Range(1, 8).Select(_ => chars[new Random().Next(chars.Length)]).ToArray());
}

private static string GetTestOrg() =>
Environment.GetEnvironmentVariable("PULUMI_TEST_ORG") ?? "pulumi-test";

private static string FullyQualifiedStackName(string org, string project, string stack) =>
$"{org}/{project}/{stack}";

private static string NormalizeConfigKey(string key, string projectName)
{
var parts = key.Split(":");
Expand Down
199 changes: 199 additions & 0 deletions sdk/dotnet/Pulumi.Automation.Tests/RemoteWorkspaceTests.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,210 @@
// Copyright 2016-2022, Pulumi Corporation

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xunit;

using static Pulumi.Automation.Tests.Utility;

namespace Pulumi.Automation.Tests
{
public class RemoteWorkspaceTests
{
private const string _testRepo = "https://github.com/pulumi/test-repo.git";

public static IEnumerable<object[]> ErrorsData()
{
const string stackName = "owner/project/stack";

var factories = new Func<RemoteGitProgramArgs, Task<RemoteWorkspaceStack>>[]
{
RemoteWorkspace.CreateStackAsync,
RemoteWorkspace.CreateOrSelectStackAsync,
RemoteWorkspace.SelectStackAsync,
};

var tests = new[]
{
new
{
Args = new RemoteGitProgramArgs(null!, _testRepo),
Error = "StackName \"\" not fully qualified.",
},
new
{
Args = new RemoteGitProgramArgs("", _testRepo),
Error = "StackName \"\" not fully qualified.",
},
new
{
Args = new RemoteGitProgramArgs("name", _testRepo),
Error = "StackName \"name\" not fully qualified.",
},
new
{
Args = new RemoteGitProgramArgs("owner/name", _testRepo),
Error = "StackName \"owner/name\" not fully qualified.",
},
new
{
Args = new RemoteGitProgramArgs("/", _testRepo),
Error = "StackName \"/\" not fully qualified.",
},
new
{
Args = new RemoteGitProgramArgs("//", _testRepo),
Error = "StackName \"//\" not fully qualified.",
},
new
{
Args = new RemoteGitProgramArgs("///", _testRepo),
Error = "StackName \"///\" not fully qualified.",
},
new
{
Args = new RemoteGitProgramArgs("owner/project/stack/wat", _testRepo),
Error = "StackName \"owner/project/stack/wat\" not fully qualified.",
},
new
{
Args = new RemoteGitProgramArgs(stackName, null!),
Error = "Url is required.",
},
new
{
Args = new RemoteGitProgramArgs(stackName, ""),
Error = "Url is required.",
},
new
{
Args = new RemoteGitProgramArgs(stackName, _testRepo),
Error = "either Branch or CommitHash is required.",
},
new
{
Args = new RemoteGitProgramArgs(stackName, _testRepo)
{
Branch = "",
CommitHash = "",
},
Error = "either Branch or CommitHash is required.",
},
new
{
Args = new RemoteGitProgramArgs(stackName, _testRepo)
{
Branch = "branch",
CommitHash = "commit",
},
Error = "Branch and CommitHash cannot both be specified.",
},
new
{
Args = new RemoteGitProgramArgs(stackName, _testRepo)
{
Branch = "branch",
Auth = new RemoteGitAuthArgs
{
SshPrivateKey = "key",
SshPrivateKeyPath = "path",
},
},
Error = "SshPrivateKey and SshPrivateKeyPath cannot both be specified.",
},
};

foreach (var factory in factories)
{
foreach (var test in tests)
{
yield return new object[] { factory, test.Args, test.Error };
}
}
}

[Theory]
[MemberData(nameof(ErrorsData))]
public async Task Errors(Func<RemoteGitProgramArgs, Task<RemoteWorkspaceStack>> factory,
RemoteGitProgramArgs args, string error)
{
var exception = await Assert.ThrowsAsync<ArgumentException>(() => factory(args));
Assert.Equal(error, exception.Message);
}

// This test requires the service with access to Pulumi Deployments.
// Set PULUMI_ACCESS_TOKEN to an access token with access to Pulumi Deployments
// and set PULUMI_TEST_DEPLOYMENTS_API to any value to enable the test.
[DeploymentsApiFact]
public Task CreateStackLifecycle()
=> TestStackLifeCycle(RemoteWorkspace.CreateStackAsync);

// This test requires the service with access to Pulumi Deployments.
// Set PULUMI_ACCESS_TOKEN to an access token with access to Pulumi Deployments
// and set PULUMI_TEST_DEPLOYMENTS_API to any value to enable the test.
[DeploymentsApiFact]
public Task CreateOrSelectStackLifecycle()
=> TestStackLifeCycle(RemoteWorkspace.CreateOrSelectStackAsync);

private static async Task TestStackLifeCycle(Func<RemoteGitProgramArgs, Task<RemoteWorkspaceStack>> factory)
{
var stackName = FullyQualifiedStackName(GetTestOrg(), "go_remote_proj", RandomStackName());
using var stack = await factory(new RemoteGitProgramArgs(stackName, _testRepo)
{
Branch = "refs/heads/master",
ProjectPath = "goproj",
PreRunCommands =
{
$"pulumi config set bar abc --stack {stackName}",
$"pulumi config set --secret buzz secret --stack {stackName}",
},
});

try
{
// pulumi up
var upResult = await stack.UpAsync();
Assert.Equal(UpdateKind.Update, upResult.Summary.Kind);
Assert.Equal(UpdateState.Succeeded, upResult.Summary.Result);
Assert.Equal(3, upResult.Outputs.Count);

// exp_static
Assert.True(upResult.Outputs.TryGetValue("exp_static", out var expStaticValue));
Assert.Equal("foo", expStaticValue!.Value);
Assert.False(expStaticValue.IsSecret);

// exp_cfg
Assert.True(upResult.Outputs.TryGetValue("exp_cfg", out var expConfigValue));
Assert.Equal("abc", expConfigValue!.Value);
Assert.False(expConfigValue.IsSecret);

// exp_secret
Assert.True(upResult.Outputs.TryGetValue("exp_secret", out var expSecretValue));
Assert.Equal("secret", expSecretValue!.Value);
Assert.True(expSecretValue.IsSecret);

// pulumi preview
var previewResult = await stack.PreviewAsync();
Assert.True(previewResult.ChangeSummary.TryGetValue(OperationType.Same, out var sameCount));
Assert.Equal(1, sameCount);

// pulumi refresh
var refreshResult = await stack.RefreshAsync();
Assert.Equal(UpdateKind.Refresh, refreshResult.Summary.Kind);
Assert.Equal(UpdateState.Succeeded, refreshResult.Summary.Result);

// pulumi destroy
var destroyResult = await stack.DestroyAsync();
Assert.Equal(UpdateKind.Destroy, destroyResult.Summary.Kind);
Assert.Equal(UpdateState.Succeeded, destroyResult.Summary.Result);
}
finally
{
using var local = await LocalWorkspace.CreateAsync();
await local.RemoveStackAsync(stackName);
}
}

[Theory]
[InlineData("owner/project/stack", true)]
[InlineData("", false)]
Expand Down
29 changes: 29 additions & 0 deletions sdk/dotnet/Pulumi.Automation.Tests/Utility.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2016-2022, Pulumi Corporation

using System;
using System.Linq;

namespace Pulumi.Automation.Tests
{
public static class Utility
{
public static string GetTestSuffix()
{
var random = new Random();
var result = random.Next(); // 31 bits, highest bit will be 0 (signed)
return result.ToString("x"); // 8 hex characters
}

public static string RandomStackName()
{
const string chars = "abcdefghijklmnopqrstuvwxyz";
return new string(Enumerable.Range(1, 8).Select(_ => chars[new Random().Next(chars.Length)]).ToArray());
}

public static string GetTestOrg()
=> Environment.GetEnvironmentVariable("PULUMI_TEST_ORG") ?? "pulumi-test";

public static string FullyQualifiedStackName(string org, string project, string stack)
=> $"{org}/{project}/{stack}";
}
}
10 changes: 5 additions & 5 deletions sdk/dotnet/Pulumi.Automation/RemoteWorkspace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,19 @@ public static Task<RemoteWorkspaceStack> CreateOrSelectStackAsync(RemoteGitProgr
{
if (!IsFullyQualifiedStackName(args.StackName))
{
throw new ArgumentException($"{nameof(args.StackName)} not fully qualified.");
throw new ArgumentException($"{nameof(args.StackName)} \"{args.StackName}\" not fully qualified.");
}
if (string.IsNullOrWhiteSpace(args.Url))
{
throw new ArgumentException($"{nameof(args.Url)} is required.");
}
if (!string.IsNullOrWhiteSpace(args.CommitHash) && !string.IsNullOrWhiteSpace(args.Branch))
if (!string.IsNullOrWhiteSpace(args.Branch) && !string.IsNullOrWhiteSpace(args.CommitHash))
{
throw new ArgumentException($"{nameof(args.CommitHash)} and {nameof(args.Branch)} cannot both be specified.");
throw new ArgumentException($"{nameof(args.Branch)} and {nameof(args.CommitHash)} cannot both be specified.");
}
if (string.IsNullOrWhiteSpace(args.CommitHash) && string.IsNullOrWhiteSpace(args.Branch))
if (string.IsNullOrWhiteSpace(args.Branch) && string.IsNullOrWhiteSpace(args.CommitHash))
{
throw new ArgumentException($"either {nameof(args.CommitHash)} or {nameof(args.Branch)} is required.");
throw new ArgumentException($"either {nameof(args.Branch)} or {nameof(args.CommitHash)} is required.");
}
if (!(args.Auth is null))
{
Expand Down
14 changes: 7 additions & 7 deletions sdk/go/auto/remote_workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func NewRemoteStackGitSource(
opts ...RemoteWorkspaceOption,
) (RemoteStack, error) {
if !isFullyQualifiedStackName(stackName) {
return RemoteStack{}, fmt.Errorf("%q stack name must be fully qualified", stackName)
return RemoteStack{}, fmt.Errorf("stack name %q must be fully qualified", stackName)
}

localOpts, err := remoteToLocalOptions(repo, opts...)
Expand Down Expand Up @@ -58,7 +58,7 @@ func UpsertRemoteStackGitSource(
opts ...RemoteWorkspaceOption,
) (RemoteStack, error) {
if !isFullyQualifiedStackName(stackName) {
return RemoteStack{}, fmt.Errorf("%q stack name must be fully qualified", stackName)
return RemoteStack{}, fmt.Errorf("stack name %q must be fully qualified", stackName)
}

localOpts, err := remoteToLocalOptions(repo, opts...)
Expand All @@ -85,7 +85,7 @@ func SelectRemoteStackGitSource(
opts ...RemoteWorkspaceOption,
) (RemoteStack, error) {
if !isFullyQualifiedStackName(stackName) {
return RemoteStack{}, fmt.Errorf("%q stack name must be fully qualified", stackName)
return RemoteStack{}, fmt.Errorf("stack name %q must be fully qualified", stackName)
}

localOpts, err := remoteToLocalOptions(repo, opts...)
Expand All @@ -111,11 +111,11 @@ func remoteToLocalOptions(repo GitRepo, opts ...RemoteWorkspaceOption) ([]LocalW
if repo.URL == "" {
return nil, errors.New("repo.URL is required")
}
if repo.CommitHash != "" && repo.Branch != "" {
return nil, errors.New("repo.CommitHash and repo.Branch cannot both be specified")
if repo.Branch != "" && repo.CommitHash != "" {
return nil, errors.New("repo.Branch and repo.CommitHash cannot both be specified")
}
if repo.CommitHash == "" && repo.Branch == "" {
return nil, errors.New("at least repo.CommitHash or repo.Branch are required")
if repo.Branch == "" && repo.CommitHash == "" {
return nil, errors.New("either repo.Branch or repo.CommitHash is required")
}
if repo.Auth != nil {
if repo.Auth.SSHPrivateKey != "" && repo.Auth.SSHPrivateKeyPath != "" {
Expand Down

0 comments on commit 449307d

Please sign in to comment.