feat: Initialize Zastava Webhook service with TLS and Authority authentication
- Added Program.cs to set up the web application with Serilog for logging, health check endpoints, and a placeholder admission endpoint. - Configured Kestrel server to use TLS 1.3 and handle client certificates appropriately. - Created StellaOps.Zastava.Webhook.csproj with necessary dependencies including Serilog and Polly. - Documented tasks in TASKS.md for the Zastava Webhook project, outlining current work and exit criteria for each task.
This commit is contained in:
		@@ -0,0 +1,128 @@
 | 
			
		||||
using Microsoft.Extensions.Options;
 | 
			
		||||
using MongoDB.Driver;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using StellaOps.Authority.Storage.Mongo.Options;
 | 
			
		||||
 | 
			
		||||
namespace StellaOps.Authority.Storage.Mongo.Sessions;
 | 
			
		||||
 | 
			
		||||
public interface IAuthorityMongoSessionAccessor : IAsyncDisposable
 | 
			
		||||
{
 | 
			
		||||
    ValueTask<IClientSessionHandle> GetSessionAsync(CancellationToken cancellationToken = default);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
internal sealed class AuthorityMongoSessionAccessor : IAuthorityMongoSessionAccessor
 | 
			
		||||
{
 | 
			
		||||
    private readonly IMongoClient client;
 | 
			
		||||
    private readonly AuthorityMongoOptions options;
 | 
			
		||||
    private readonly object gate = new();
 | 
			
		||||
    private Task<IClientSessionHandle>? sessionTask;
 | 
			
		||||
    private IClientSessionHandle? session;
 | 
			
		||||
    private bool disposed;
 | 
			
		||||
 | 
			
		||||
    public AuthorityMongoSessionAccessor(
 | 
			
		||||
        IMongoClient client,
 | 
			
		||||
        IOptions<AuthorityMongoOptions> options)
 | 
			
		||||
    {
 | 
			
		||||
        this.client = client ?? throw new ArgumentNullException(nameof(client));
 | 
			
		||||
        this.options = options?.Value ?? throw new ArgumentNullException(nameof(options));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async ValueTask<IClientSessionHandle> GetSessionAsync(CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        ObjectDisposedException.ThrowIf(disposed, this);
 | 
			
		||||
 | 
			
		||||
        var existing = Volatile.Read(ref session);
 | 
			
		||||
        if (existing is not null)
 | 
			
		||||
        {
 | 
			
		||||
            return existing;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Task<IClientSessionHandle> startTask;
 | 
			
		||||
 | 
			
		||||
        lock (gate)
 | 
			
		||||
        {
 | 
			
		||||
            if (session is { } cached)
 | 
			
		||||
            {
 | 
			
		||||
                return cached;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            sessionTask ??= StartSessionInternalAsync(cancellationToken);
 | 
			
		||||
            startTask = sessionTask;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var handle = await startTask.WaitAsync(cancellationToken).ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
            if (session is null)
 | 
			
		||||
            {
 | 
			
		||||
                lock (gate)
 | 
			
		||||
                {
 | 
			
		||||
                    if (session is null)
 | 
			
		||||
                    {
 | 
			
		||||
                        session = handle;
 | 
			
		||||
                        sessionTask = Task.FromResult(handle);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return handle;
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
            lock (gate)
 | 
			
		||||
            {
 | 
			
		||||
                if (ReferenceEquals(sessionTask, startTask))
 | 
			
		||||
                {
 | 
			
		||||
                    sessionTask = null;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            throw;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async Task<IClientSessionHandle> StartSessionInternalAsync(CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        var sessionOptions = new ClientSessionOptions
 | 
			
		||||
        {
 | 
			
		||||
            CausalConsistency = true,
 | 
			
		||||
            DefaultTransactionOptions = new TransactionOptions(
 | 
			
		||||
                readPreference: ReadPreference.Primary,
 | 
			
		||||
                readConcern: ReadConcern.Majority,
 | 
			
		||||
                writeConcern: WriteConcern.WMajority.With(wTimeout: options.CommandTimeout))
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        var handle = await client.StartSessionAsync(sessionOptions, cancellationToken).ConfigureAwait(false);
 | 
			
		||||
        return handle;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ValueTask DisposeAsync()
 | 
			
		||||
    {
 | 
			
		||||
        if (disposed)
 | 
			
		||||
        {
 | 
			
		||||
            return ValueTask.CompletedTask;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        disposed = true;
 | 
			
		||||
 | 
			
		||||
        IClientSessionHandle? handle;
 | 
			
		||||
 | 
			
		||||
        lock (gate)
 | 
			
		||||
        {
 | 
			
		||||
            handle = session;
 | 
			
		||||
            session = null;
 | 
			
		||||
            sessionTask = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (handle is not null)
 | 
			
		||||
        {
 | 
			
		||||
            handle.Dispose();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        GC.SuppressFinalize(this);
 | 
			
		||||
        return ValueTask.CompletedTask;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user