Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attempt to avoid leaving local deploy process hanging with named pipes #15186

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@ protected override async Task RunServer(ConnectionOptions connectionOptions, Res

builder.Services.AddGrpc();
builder.Services.AddSingleton(dispatcher);
var app = builder.Build();
using var app = builder.Build();
app.MapGrpcService<BicepExtensionImpl>();

await app.RunAsync();
await Task.WhenAll(
Task.Delay(Timeout.Infinite, cancellationToken),
app.RunAsync());
}
}
19 changes: 8 additions & 11 deletions src/Bicep.Local.Deploy/Extensibility/GrpcBuiltInLocalExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ public class GrpcBuiltInLocalExtension : LocalExtensibilityHost
private readonly BicepExtension.BicepExtensionClient client;
private readonly Process process;

private GrpcBuiltInLocalExtension(BicepExtension.BicepExtensionClient client, Process process)
private GrpcBuiltInLocalExtension(BicepExtension.BicepExtensionClient client, Process process, Uri binaryPath)
{
this.client = client;
this.process = process;
this.BinaryPath = binaryPath;
}

public static async Task<LocalExtensibilityHost> Start(Uri pathToBinary)
Expand Down Expand Up @@ -84,7 +85,7 @@ public static async Task<LocalExtensibilityHost> Start(Uri pathToBinary)

await GrpcChannelHelper.WaitForConnectionAsync(client, cts.Token);

return new GrpcBuiltInLocalExtension(client, process);
return new GrpcBuiltInLocalExtension(client, process, pathToBinary);
}
catch (Exception ex)
{
Expand All @@ -93,6 +94,8 @@ public static async Task<LocalExtensibilityHost> Start(Uri pathToBinary)
}
}

public override Uri BinaryPath { get; }

public override async Task<LocalExtensibilityOperationResponse> CreateOrUpdate(ExtensibilityV2.ResourceSpecification request, CancellationToken cancellationToken)
=> Convert(await client.CreateOrUpdateAsync(Convert(request), cancellationToken: cancellationToken));

Expand Down Expand Up @@ -172,14 +175,8 @@ public override async ValueTask DisposeAsync()

private static async Task TerminateProcess(Process process)
{
try
{
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15));
await process.WaitForExitAsync(cts.Token);
}
finally
{
process.Kill();
}
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15));
process.Kill();
await process.WaitForExitAsync(cts.Token);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public static class LocalExtensibilityOperationResponseJsonDefaults

public abstract class LocalExtensibilityHost : IAsyncDisposable
{
public abstract Uri BinaryPath { get; }

public abstract Task<LocalExtensibilityOperationResponse> Delete(ResourceReference request, CancellationToken cancellationToken);

public abstract Task<LocalExtensibilityOperationResponse> Get(ResourceReference request, CancellationToken cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,9 @@ await Task.WhenAll(RegisteredExtensions.Values.Select(async extension =>
{
await extension.DisposeAsync();
}
catch
catch (Exception ex)
{
// TODO: handle errors shutting down processes gracefully
Trace.WriteLine($"Failed to gracefully shut down extension '{extension.BinaryPath}'. Exception: {ex}");
}
}));
}
Expand Down
6 changes: 4 additions & 2 deletions src/Bicep.Local.Extension.Mock/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@ protected override async Task RunServer(ConnectionOptions connectionOptions, Res

builder.Services.AddGrpc();
builder.Services.AddSingleton(dispatcher);
var app = builder.Build();
using var app = builder.Build();
app.MapGrpcService<BicepExtensionImpl>();

await app.RunAsync();
await Task.WhenAll(
Task.Delay(Timeout.Infinite, cancellationToken),
app.RunAsync());
}
}
23 changes: 6 additions & 17 deletions src/Bicep.Local.Extension/Rpc/GrpcChannelHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,16 @@ namespace Bicep.Local.Extension.Rpc;

public static class GrpcChannelHelper
{
public class UnixDomainSocketsConnectionFactory
public static GrpcChannel CreateDomainSocketChannel(string socketPath)
{
private readonly EndPoint endPoint;

public UnixDomainSocketsConnectionFactory(EndPoint endPoint)
{
this.endPoint = endPoint;
}

public async ValueTask<Stream> ConnectAsync(SocketsHttpConnectionContext _,
CancellationToken cancellationToken = default)
static async ValueTask<Stream> connectSocket(string socketPath, CancellationToken cancellationToken)
{
var udsEndPoint = new UnixDomainSocketEndPoint(socketPath);
var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified);

try
{
await socket.ConnectAsync(this.endPoint, cancellationToken).ConfigureAwait(false);
await socket.ConnectAsync(udsEndPoint, cancellationToken).ConfigureAwait(false);
return new NetworkStream(socket, true);
}
catch
Expand All @@ -40,15 +33,10 @@ public async ValueTask<Stream> ConnectAsync(SocketsHttpConnectionContext _,
throw;
}
}
}

public static GrpcChannel CreateDomainSocketChannel(string socketPath)
{
var udsEndPoint = new UnixDomainSocketEndPoint(socketPath);
var connectionFactory = new UnixDomainSocketsConnectionFactory(udsEndPoint);
var socketsHttpHandler = new SocketsHttpHandler
{
ConnectCallback = connectionFactory.ConnectAsync
ConnectCallback = (context, cancellationToken) => connectSocket(socketPath, cancellationToken),
};

// The URL is not used, but it must be a valid URI.
Expand Down Expand Up @@ -86,6 +74,7 @@ static async ValueTask<Stream> connectPipe(string pipeName, CancellationToken ca
ConnectCallback = (context, cancellationToken) => connectPipe(pipeName, cancellationToken),
};

// The URL is not used, but it must be a valid URI.
return GrpcChannel.ForAddress("http://localhost", new GrpcChannelOptions
{
HttpHandler = socketsHttpHandler
Expand Down
Loading