pulumi / pulumi-dotnet

.NET support for Pulumi
Apache License 2.0
27 stars 24 forks source link

Erroneous behaviour when cancelling a program #134

Open jaxxstorm opened 1 year ago

jaxxstorm commented 1 year ago

What happened?

When running a pulumi program, especially with automation API, sending a single Ctrl+C to the execution hangs the program indefinitely

Expected Behavior

When sending a Ctrl+C to a Pulumi invocation, I'd expect the program to try and gracefully shut down and terminate

Steps to reproduce

Customer sent the following program that exhibits this behaviour

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Threading;
using System.Threading.Tasks;
using McMaster.Extensions.CommandLineUtils;
using Pulumi;
using Pulumi.Automation;
using Pulumi.Aws;
using Pulumi.Aws.S3;

namespace Snap;

[Command("pulumi", Description = "Deploy Pulumi stack")]
public class DeployPulumiCommand
{
    [Required]
    [Option("--project-name",
        "Pulumi project name",
        CommandOptionType.SingleValue,
        ShowInHelpText = true)]
    private string ProjectName { get; set; }

    public async Task<int> OnExecuteAsync(CancellationToken cancellationToken)
    {
        PulumiFn program = PulumiFn.Create(async () =>
        {
            var region = "us-west-2";
            var provider = new Provider(region, new()
            {
                Region = region,
            });

            var bucket = new Bucket("some-bucket", BucketArgs.Empty, new CustomResourceOptions { Provider = provider });
        });

        return await UpdatePulumiAsync(program, ProjectName, "test-stack", cancellationToken);
    }

    private static async Task<int> UpdatePulumiAsync(PulumiFn program, string projectName, string stackName, CancellationToken cancellationToken)
    {
        var stack = await SetupAsync(projectName, stackName, program, cancellationToken);

        Console.WriteLine("updating stack...");
        var result = await stack.UpAsync(new UpOptions
        {
            OnStandardOutput = Console.WriteLine,
            Parallel = Int32.MaxValue,
            Color = "always",
        }, cancellationToken);

        if (result.Summary.ResourceChanges != null)
        {
            Console.WriteLine("update summary:");
            foreach (var change in result.Summary.ResourceChanges)
                Console.WriteLine($"    {change.Key}: {change.Value}");
        }

        return 0;
    }

    private static async Task<WorkspaceStack> SetupAsync(string projectName, string stackName, PulumiFn program, CancellationToken cancellationToken)
    {
        var stackArgs = new InlineProgramArgs(projectName, stackName, program);
        var plugins = new Dictionary<string, string>
        {
            { "aws", "v4.24.1" },
        };
        var config = new Dictionary<string, ConfigValue>
        {
            { "aws:region", new ConfigValue("us-west-2") },
            { "aws-native:region", new ConfigValue("us-west-2") },
        };

        var stack = await LocalWorkspace.CreateOrSelectStackAsync(stackArgs, cancellationToken);
        Console.WriteLine("successfully initialized stack");

        Console.WriteLine("installing plugins...");
        foreach (var kvp in plugins)
        {
            await stack.Workspace.InstallPluginAsync(kvp.Key, kvp.Value, cancellationToken: cancellationToken);
        }
        Console.WriteLine("plugins installed");

        Console.WriteLine("setting up config...");
        foreach (var kvp in config)
        {
            await stack.SetConfigAsync(kvp.Key, kvp.Value, cancellationToken: cancellationToken);
        }
        Console.WriteLine("config set");

        Console.WriteLine("refreshing stack...");
        await stack.RefreshAsync(new RefreshOptions { OnStandardOutput = Console.WriteLine }, cancellationToken);
        Console.WriteLine("refresh complete");

        return stack;
    }
}

Output of pulumi about

N/A

Additional context

No response

Contributing

Vote on this issue by adding a šŸ‘ reaction. To contribute a fix for this issue, leave a comment (and link to your pull request, if you've opened one already).

justinvp commented 1 year ago

Potentially related to #124

justinvp commented 1 year ago

Need to actually try it out, but just from looking, it looks like McMaster.Extensions.CommandLineUtils is handling CTRL+C:

https://github.com/natemcmaster/CommandLineUtils/blob/81199c7ec68367ea9612be55719dc1fe08f658da/src/Hosting.CommandLine/Internal/CommandLineLifetime.cs#L83-L88

Is the parent process handling and cancelling the SIGINT, but child processes still being killed?