Skip to content

Commit

Permalink
Support microsoft logging extensions with inline programs (pulumi/pul…
Browse files Browse the repository at this point in the history
…umi#7117)

* Demystify serilog logger messages
https://github.com/benaadams/Ben.Demystifier

* Update changelog
  • Loading branch information
gitfool committed Jun 11, 2021
1 parent 5533913 commit 2f6b936
Show file tree
Hide file tree
Showing 23 changed files with 311 additions and 110 deletions.
146 changes: 127 additions & 19 deletions sdk/Pulumi.Automation.Tests/LocalWorkspaceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,16 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Pulumi.Automation.Commands.Exceptions;
using Pulumi.Automation.Events;
using Pulumi.Automation.Exceptions;
using Semver;
using Serilog;
using Serilog.Extensions.Logging;
using Xunit;
using Xunit.Abstractions;
using ILogger = Microsoft.Extensions.Logging.ILogger;

namespace Pulumi.Automation.Tests
{
Expand Down Expand Up @@ -50,6 +55,20 @@ private static string NormalizeConfigKey(string key, string projectName)
return string.Empty;
}

private ILogger TestLogger { get; }

public LocalWorkspaceTests(ITestOutputHelper output)
{
var logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.WriteTo.TestOutput(output, outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}")
.CreateLogger();

var loggerFactory = new SerilogLoggerFactory(logger);

TestLogger = loggerFactory.CreateLogger<LocalWorkspaceTests>();
}

[Theory]
[InlineData("yaml")]
[InlineData("yml")]
Expand Down Expand Up @@ -725,27 +744,27 @@ public async Task HandlesEvents()
try
{
// pulumi preview
var previewResult = await RunCommand<PreviewResult, PreviewOptions>(stack.PreviewAsync, "preview");
var previewResult = await RunCommand(stack.PreviewAsync, "preview", new PreviewOptions());
Assert.True(previewResult.ChangeSummary.TryGetValue(OperationType.Create, out var createCount));
Assert.Equal(1, createCount);

// pulumi up
var upResult = await RunCommand<UpResult, UpOptions>(stack.UpAsync, "up");
var upResult = await RunCommand(stack.UpAsync, "up", new UpOptions());
Assert.Equal(UpdateKind.Update, upResult.Summary.Kind);
Assert.Equal(UpdateState.Succeeded, upResult.Summary.Result);

// pulumi preview
var previewResultAgain = await RunCommand<PreviewResult, PreviewOptions>(stack.PreviewAsync, "preview");
var previewResultAgain = await RunCommand(stack.PreviewAsync, "preview", new PreviewOptions());
Assert.True(previewResultAgain.ChangeSummary.TryGetValue(OperationType.Same, out var sameCount));
Assert.Equal(1, sameCount);

// pulumi refresh
var refreshResult = await RunCommand<UpdateResult, RefreshOptions>(stack.RefreshAsync, "refresh");
var refreshResult = await RunCommand(stack.RefreshAsync, "refresh", new RefreshOptions());
Assert.Equal(UpdateKind.Refresh, refreshResult.Summary.Kind);
Assert.Equal(UpdateState.Succeeded, refreshResult.Summary.Result);

// pulumi destroy
var destroyResult = await RunCommand<UpdateResult, DestroyOptions>(stack.DestroyAsync, "destroy");
var destroyResult = await RunCommand(stack.DestroyAsync, "destroy", new DestroyOptions());
Assert.Equal(UpdateKind.Destroy, destroyResult.Summary.Kind);
Assert.Equal(UpdateState.Succeeded, destroyResult.Summary.Result);
}
Expand All @@ -754,12 +773,12 @@ public async Task HandlesEvents()
await stack.Workspace.RemoveStackAsync(stackName);
}

static async Task<T> RunCommand<T, TOptions>(Func<TOptions, CancellationToken, Task<T>> func, string command)
static async Task<T> RunCommand<T, TOptions>(Func<TOptions, CancellationToken, Task<T>> func, string command, TOptions options)
where TOptions : UpdateOptions, new()
{
var events = new List<EngineEvent>();

var result = await func(new TOptions { OnEvent = events.Add }, CancellationToken.None);
options.OnEvent = events.Add;
var result = await func(options, CancellationToken.None);

var seenSummaryEvent = events.Any(@event => @event.SummaryEvent != null);
var seenCancelEvent = events.Any(@event => @event.CancelEvent != null);
Expand Down Expand Up @@ -872,17 +891,17 @@ public async Task ConfigSecretWarnings()
await stack.SetAllConfigAsync(config);

// pulumi preview
await RunCommand<PreviewResult, PreviewOptions>(stack.PreviewAsync, "preview");
await RunCommand(stack.PreviewAsync, "preview", new PreviewOptions());

// pulumi up
await RunCommand<UpResult, UpOptions>(stack.UpAsync, "up");
await RunCommand(stack.UpAsync, "up", new UpOptions());
}
finally
{
await stack.Workspace.RemoveStackAsync(stackName);
}

static async Task<T> RunCommand<T, TOptions>(Func<TOptions, CancellationToken, Task<T>> func, string command)
static async Task<T> RunCommand<T, TOptions>(Func<TOptions, CancellationToken, Task<T>> func, string command, TOptions options)
where TOptions : UpdateOptions, new()
{
var expectedWarnings = new[]
Expand Down Expand Up @@ -927,17 +946,14 @@ public async Task ConfigSecretWarnings()
};

var events = new List<DiagnosticEvent>();

var result = await func(new TOptions
options.OnEvent = @event =>
{
OnEvent = @event =>
if (@event.DiagnosticEvent?.Severity == "warning")
{
if (@event.DiagnosticEvent?.Severity == "warning")
{
events.Add(@event.DiagnosticEvent);
}
events.Add(@event.DiagnosticEvent);
}
}, CancellationToken.None);
};
var result = await func(options, CancellationToken.None);

foreach (var expected in expectedWarnings)
{
Expand Down Expand Up @@ -1467,10 +1483,102 @@ public async Task DetectsProjectSettingConflictTest()
);
}

[Fact]
public async Task InlineProgramLoggerCanBeOverridden()
{
var program = PulumiFn.Create(() =>
{
Log.Debug("test");
});

var loggerWasInvoked = false;
var logger = new CustomLogger(() => loggerWasInvoked = true);

var stackName = $"{RandomStackName()}";
var projectName = "inline_logger_override";

using var stack = await LocalWorkspace.CreateOrSelectStackAsync(
new InlineProgramArgs(projectName, stackName, program)
{
Logger = logger,
});

// make sure workspace logger is used
await stack.PreviewAsync();
Assert.True(loggerWasInvoked);

// preview logger is used
loggerWasInvoked = false;
stack.Workspace.Logger = null;
await stack.PreviewAsync(new PreviewOptions
{
Logger = logger,
});
Assert.True(loggerWasInvoked);

// up logger is used
loggerWasInvoked = false;
await stack.UpAsync(new UpOptions
{
Logger = logger,
});
Assert.True(loggerWasInvoked);

await stack.DestroyAsync();
}

[Fact]
public async Task InlineProgramLoggerCanRedirectToTestOutput()
{
var program = PulumiFn.Create(() =>
{
Log.Info("Pulumi.Log calls appear in test output");
});

var stackName = $"{RandomStackName()}";
var projectName = "inline_logger_test_output";

using var stack = await LocalWorkspace.CreateOrSelectStackAsync(
new InlineProgramArgs(projectName, stackName, program)
{
Logger = TestLogger
});

TestLogger.LogInformation("Previewing stack...");
await stack.PreviewAsync();

TestLogger.LogInformation("Updating stack...");
await stack.UpAsync();

TestLogger.LogInformation("Destroying stack...");
await stack.DestroyAsync();
}

private string ResourcePath(string path, [CallerFilePath] string pathBase = "LocalWorkspaceTests.cs")
{
var dir = Path.GetDirectoryName(pathBase) ?? ".";
return Path.Combine(dir, path);
}

private class CustomLogger : ILogger
{
private readonly Action _action;

public CustomLogger(Action action)
{
_action = action;
}

public IDisposable BeginScope<TState>(TState state)
{
throw new NotImplementedException();
}

public bool IsEnabled(LogLevel logLevel)
=> true;

public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
=> _action();
}
}
}
1 change: 1 addition & 0 deletions sdk/Pulumi.Automation.Tests/Pulumi.Automation.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
<PackageReference Include="Moq" Version="4.13.1" />
<PackageReference Include="Serilog.Sinks.XUnit" Version="2.0.4" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
</ItemGroup>
Expand Down
5 changes: 3 additions & 2 deletions sdk/Pulumi.Automation.Tests/xunit.runner.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
"parallelizeTestCollections": true
"$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
"diagnosticMessages": true,
"parallelizeTestCollections": true
}
5 changes: 5 additions & 0 deletions sdk/Pulumi.Automation/LocalWorkspace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Pulumi.Automation.Commands;
using Pulumi.Automation.Exceptions;
using Pulumi.Automation.Serialization;
Expand Down Expand Up @@ -53,6 +54,9 @@ public sealed class LocalWorkspace : Workspace
/// <inheritdoc/>
public override PulumiFn? Program { get; set; }

/// <inheritdoc/>
public override ILogger? Logger { get; set; }

/// <inheritdoc/>
public override IDictionary<string, string?>? EnvironmentVariables { get; set; }

Expand Down Expand Up @@ -307,6 +311,7 @@ public static Task<WorkspaceStack> CreateOrSelectStackAsync(LocalProgramArgs arg

this.PulumiHome = options.PulumiHome;
this.Program = options.Program;
this.Logger = options.Logger;
this.SecretsProvider = options.SecretsProvider;

if (options.EnvironmentVariables != null)
Expand Down
7 changes: 7 additions & 0 deletions sdk/Pulumi.Automation/LocalWorkspaceOptions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2016-2021, Pulumi Corporation

using System.Collections.Generic;
using Microsoft.Extensions.Logging;

namespace Pulumi.Automation
{
Expand Down Expand Up @@ -34,6 +35,12 @@ public class LocalWorkspaceOptions
/// </summary>
public PulumiFn? Program { get; set; }

/// <summary>
/// A custom logger instance that will be used for inline programs. Note that it will only be used
/// if <see cref="Program"/> is also provided.
/// </summary>
public ILogger? Logger { get; set; }

/// <summary>
/// Environment values scoped to the current workspace. These will be supplied to every
/// Pulumi command.
Expand Down
7 changes: 7 additions & 0 deletions sdk/Pulumi.Automation/PreviewOptions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2016-2021, Pulumi Corporation

using System.Collections.Generic;
using Microsoft.Extensions.Logging;

namespace Pulumi.Automation
{
Expand All @@ -18,5 +19,11 @@ public sealed class PreviewOptions : UpdateOptions
public bool? TargetDependents { get; set; }

public PulumiFn? Program { get; set; }

/// <summary>
/// A custom logger instance that will be used for the action. Note that it will only be used
/// if <see cref="Program"/> is also provided.
/// </summary>
public ILogger? Logger { get; set; }
}
}
10 changes: 10 additions & 0 deletions sdk/Pulumi.Automation/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ Pulumi.Automation.LocalWorkspaceOptions
Pulumi.Automation.LocalWorkspaceOptions.EnvironmentVariables.get -> System.Collections.Generic.IDictionary<string, string>
Pulumi.Automation.LocalWorkspaceOptions.EnvironmentVariables.set -> void
Pulumi.Automation.LocalWorkspaceOptions.LocalWorkspaceOptions() -> void
Pulumi.Automation.LocalWorkspaceOptions.Logger.get -> Microsoft.Extensions.Logging.ILogger
Pulumi.Automation.LocalWorkspaceOptions.Logger.set -> void
Pulumi.Automation.LocalWorkspaceOptions.Program.get -> Pulumi.Automation.PulumiFn
Pulumi.Automation.LocalWorkspaceOptions.Program.set -> void
Pulumi.Automation.LocalWorkspaceOptions.ProjectSettings.get -> Pulumi.Automation.ProjectSettings
Expand Down Expand Up @@ -165,6 +167,8 @@ Pulumi.Automation.PreviewOptions.ExpectNoChanges.get -> bool?
Pulumi.Automation.PreviewOptions.ExpectNoChanges.set -> void
Pulumi.Automation.PreviewOptions.Diff.get -> bool?
Pulumi.Automation.PreviewOptions.Diff.set -> void
Pulumi.Automation.PreviewOptions.Logger.get -> Microsoft.Extensions.Logging.ILogger
Pulumi.Automation.PreviewOptions.Logger.set -> void
Pulumi.Automation.PreviewOptions.PreviewOptions() -> void
Pulumi.Automation.PreviewOptions.Program.get -> Pulumi.Automation.PulumiFn
Pulumi.Automation.PreviewOptions.Program.set -> void
Expand Down Expand Up @@ -271,6 +275,8 @@ Pulumi.Automation.UpOptions.ExpectNoChanges.get -> bool?
Pulumi.Automation.UpOptions.ExpectNoChanges.set -> void
Pulumi.Automation.UpOptions.Diff.get -> bool?
Pulumi.Automation.UpOptions.Diff.set -> void
Pulumi.Automation.UpOptions.Logger.get -> Microsoft.Extensions.Logging.ILogger
Pulumi.Automation.UpOptions.Logger.set -> void
Pulumi.Automation.UpOptions.Program.get -> Pulumi.Automation.PulumiFn
Pulumi.Automation.UpOptions.Program.set -> void
Pulumi.Automation.UpOptions.Replace.get -> System.Collections.Generic.List<string>
Expand Down Expand Up @@ -356,6 +362,8 @@ abstract Pulumi.Automation.Workspace.GetStackSettingsAsync(string stackName, Sys
abstract Pulumi.Automation.Workspace.InstallPluginAsync(string name, string version, Pulumi.Automation.PluginKind kind = Pulumi.Automation.PluginKind.Resource, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
abstract Pulumi.Automation.Workspace.ListPluginsAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Immutable.ImmutableList<Pulumi.Automation.PluginInfo>>
abstract Pulumi.Automation.Workspace.ListStacksAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Immutable.ImmutableList<Pulumi.Automation.StackSummary>>
abstract Pulumi.Automation.Workspace.Logger.get -> Microsoft.Extensions.Logging.ILogger
abstract Pulumi.Automation.Workspace.Logger.set -> void
abstract Pulumi.Automation.Workspace.PostCommandCallbackAsync(string stackName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
abstract Pulumi.Automation.Workspace.Program.get -> Pulumi.Automation.PulumiFn
abstract Pulumi.Automation.Workspace.Program.set -> void
Expand Down Expand Up @@ -387,6 +395,8 @@ override Pulumi.Automation.LocalWorkspace.GetStackSettingsAsync(string stackName
override Pulumi.Automation.LocalWorkspace.InstallPluginAsync(string name, string version, Pulumi.Automation.PluginKind kind = Pulumi.Automation.PluginKind.Resource, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
override Pulumi.Automation.LocalWorkspace.ListPluginsAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Immutable.ImmutableList<Pulumi.Automation.PluginInfo>>
override Pulumi.Automation.LocalWorkspace.ListStacksAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Immutable.ImmutableList<Pulumi.Automation.StackSummary>>
override Pulumi.Automation.LocalWorkspace.Logger.get -> Microsoft.Extensions.Logging.ILogger
override Pulumi.Automation.LocalWorkspace.Logger.set -> void
override Pulumi.Automation.LocalWorkspace.PostCommandCallbackAsync(string stackName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
override Pulumi.Automation.LocalWorkspace.Program.get -> Pulumi.Automation.PulumiFn
override Pulumi.Automation.LocalWorkspace.Program.set -> void
Expand Down
Loading

0 comments on commit 2f6b936

Please sign in to comment.