Skip to content

Commit

Permalink
fix: do not start runnable it a task fails (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
queil authored Oct 2, 2024
1 parent f95277d commit 3497ff4
Show file tree
Hide file tree
Showing 25 changed files with 138 additions and 109 deletions.
2 changes: 1 addition & 1 deletion src/Queil.Ring.Configuration/ConfigSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public ConfigSet(string path, Dictionary<string, IRunnableConfig> bareConfigs)
}

Path = path;
Flavours = [..bareConfigs.Values.SelectMany(x => x.Tags)];
Flavours = [.. bareConfigs.Values.SelectMany(x => x.Tags)];
}

public HashSet<string> Flavours { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public static RunnableDetails Extract(IRunnableConfig cfg)
var details = cfg switch
{
CsProjRunnable c => New((DetailsKeys.CsProjPath, c.FullPath)),
_ => new Dictionary<string, object>()
_ => []
};

if (cfg.FriendlyName != null) details.Add(DetailsKeys.FriendlyName, cfg.FriendlyName);
Expand Down
1 change: 1 addition & 0 deletions src/Queil.Ring.DotNet.Cli/Abstractions/IRunnable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public interface IRunnable
string UniqueId { get; }
State State { get; }
IReadOnlyDictionary<string, object> Details { get; }
Task ConfigureAsync(CancellationToken token);
Task RunAsync(CancellationToken token);
Task TerminateAsync();
event EventHandler OnHealthCheckCompleted;
Expand Down
14 changes: 10 additions & 4 deletions src/Queil.Ring.DotNet.Cli/Abstractions/Runnable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace Queil.Ring.DotNet.Cli.Abstractions;
public abstract class Runnable<TContext, TConfig> : IRunnable
where TConfig : IRunnableConfig
{
private readonly Dictionary<string, object> _details = new();
private readonly Dictionary<string, object> _details = [];
private readonly Fsm _fsm = new();
private readonly ILogger<Runnable<TContext, TConfig>> _logger;
protected readonly ISender Sender;
Expand All @@ -45,14 +45,19 @@ protected Runnable(TConfig config, ILogger<Runnable<TContext, TConfig>> logger,
public event EventHandler? OnInitExecuted;
public IReadOnlyDictionary<string, object> Details => _details;

public async Task RunAsync(CancellationToken token)
public async Task ConfigureAsync(CancellationToken token)
{
using var _ = _logger.BeginScope(this.ToScope());
var fsm = await InitFsm(token);

await fsm.FireAsync(Trigger.Init);
}

public async Task RunAsync(CancellationToken token)
{
using var _ = _logger.BeginScope(this.ToScope());
await _fsm.FireAsync(Trigger.Start);
}

public async Task TerminateAsync()
{
using var _ = _logger.BeginScope(this.ToScope());
Expand Down Expand Up @@ -91,6 +96,8 @@ private async Task<Fsm> InitFsm(CancellationToken token)
.Ignore(Trigger.NoOp)
.Ignore(Trigger.HcOk)
.Ignore(Trigger.HcUnhealthy)
.Ignore(Trigger.Stop)
.Ignore(Trigger.Destroy)
.Permit(Trigger.Init, State.Idle);

_fsm.Configure(State.Idle)
Expand Down Expand Up @@ -198,7 +205,6 @@ private async Task InitCoreAsync(CancellationToken token)
_logger.LogContextDebug(_context);
_logger.LogDebug(LogEventStatus.OK);
await Sender.EnqueueAsync(Message.RunnableInitiated(UniqueId), token);
await _fsm.FireAsync(Trigger.Start);
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public string? ConfigPath
public class InitHookConfig
{
public string? Command { get; set; }
public string[] Args { get; set; } = Array.Empty<string>();
public string[] Args { get; set; } = [];
}

public class HooksConfiguration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ public static async Task RunRingAsync(this WebApplication app)
await app.Services.GetRequiredService<ICloneMaker>().CloneWorkspaceRepos(c.WorkspacePath, c.OutputDir);
break;
case ConfigDump:
{
var debugView = ((IConfigurationRoot)app.Services.GetRequiredService<IConfiguration>()).GetDebugView();
Console.WriteLine(debugView);
break;
}
{
var debugView = ((IConfigurationRoot)app.Services.GetRequiredService<IConfiguration>()).GetDebugView();
Console.WriteLine(debugView);
break;
}
case HeadlessOptions:
case ConsoleOptions:
await app.RunAsync();
Expand Down
50 changes: 25 additions & 25 deletions src/Queil.Ring.DotNet.Cli/Infrastructure/WsClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,38 +80,38 @@ private async Task AckLongRunning(CancellationToken token)
try
{
while (await _channel.Reader.WaitToReadAsync(token))
while (_channel.Reader.TryPeek(out var peek))
if (peek.IsCompleted)
{
if (!_channel.Reader.TryRead(out var task)) continue;

var ack = await task;
if (!logger.IsEnabled(LogLevel.Debug))
{
await Ws.SendAckAsync(ack, token);
}
else
while (_channel.Reader.TryPeek(out var peek))
if (peek.IsCompleted)
{
var sendTask = Ws.SendAckAsync(ack, token);
using (logger.WithSentScope(false, M.ACK))
if (!_channel.Reader.TryRead(out var task)) continue;

var ack = await task;
if (!logger.IsEnabled(LogLevel.Debug))
{
logger.LogDebug("{Payload} {Id} ({TaskId})", ack, Id, task.Id);
await Ws.SendAckAsync(ack, token);
}

await sendTask.ContinueWith(_ =>
else
{
using (logger.WithSentScope(true, M.ACK))
var sendTask = Ws.SendAckAsync(ack, token);
using (logger.WithSentScope(false, M.ACK))
{
logger.LogDebug("{Payload} {Id} ({TaskId})", ack, Id,
task.Id);
logger.LogDebug("{Payload} {Id} ({TaskId})", ack, Id, task.Id);
}
}, TaskContinuationOptions.OnlyOnRanToCompletion);

await sendTask.ContinueWith(_ =>
{
using (logger.WithSentScope(true, M.ACK))
{
logger.LogDebug("{Payload} {Id} ({TaskId})", ack, Id,
task.Id);
}
}, TaskContinuationOptions.OnlyOnRanToCompletion);
}
}
else
{
await Task.Delay(TimeSpan.FromMilliseconds(100), token);
}
}
else
{
await Task.Delay(TimeSpan.FromMilliseconds(100), token);
}
}
catch (OperationCanceledException)
{
Expand Down
10 changes: 5 additions & 5 deletions src/Queil.Ring.DotNet.Cli/Logging/ScopeLoggingExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ public static void LogContextDebug<T>(this ILogger<T> logger, object context)
}

public static IDisposable? WithClientScope<T>(this ILogger<T> logger) => logger.BeginScope(new Scope
{ [Scope.UniqueIdKey] = "PROTOCOL", [Scope.LogEventKey] = "CLIENT" });
{ [Scope.UniqueIdKey] = "PROTOCOL", [Scope.LogEventKey] = "CLIENT" });

public static IDisposable? WithReceivedScope<T>(this ILogger<T> logger, M protocolEvent) =>
logger.BeginScope(new Scope { [Scope.UniqueIdKey] = protocolEvent, [Scope.LogEventKey] = "<<" });

public static IDisposable? WithSentScope<T>(this ILogger<T> logger, bool isDelivered, M protocolEvent) =>
logger.BeginScope(new Scope
{ [Scope.UniqueIdKey] = protocolEvent, [Scope.LogEventKey] = isDelivered ? ">>" : "->" });
{ [Scope.UniqueIdKey] = protocolEvent, [Scope.LogEventKey] = isDelivered ? ">>" : "->" });

public static IDisposable? WithHostScope<T>(this ILogger<T> logger, LogEvent logEvent) =>
logger.WithScope("HOST", logEvent);
Expand All @@ -45,13 +45,13 @@ public static void LogContextDebug<T>(this ILogger<T> logger, object context)

public static IDisposable? WithTaskScope<T>(this ILogger<T> logger, string runtimeId, string taskId) =>
logger.BeginScope(new Scope
{ [Scope.UniqueIdKey] = $"{runtimeId}:{taskId}", [Scope.LogEventKey] = "TASK" });
{ [Scope.UniqueIdKey] = $"{runtimeId}:{taskId}", [Scope.LogEventKey] = "TASK" });

public static IDisposable? WithLogErrorScope<T>(this ILogger<T> logger) =>
logger.BeginScope(new Scope
{ [Scope.LogEventKey] = $"{Red}ERROR" });
{ [Scope.LogEventKey] = $"{Red}ERROR" });

public static IDisposable? WithLogInfoScope<T>(this ILogger<T> logger) =>
logger.BeginScope(new Scope
{ [Scope.LogEventKey] = $"{Gray}LOG" });
{ [Scope.LogEventKey] = $"{Gray}LOG" });
}
6 changes: 3 additions & 3 deletions src/Queil.Ring.DotNet.Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@ static string Ring(string ver) =>
.Where(t => typeof(IRunnable).IsAssignableFrom(t)).ToList();

var configMap = (from r in runnableTypes
let cfg = r.GetProperty(nameof(Runnable<object, IRunnableConfig>.Config))
where cfg != null
select (RunnableType: r, ConfigType: cfg.PropertyType))
let cfg = r.GetProperty(nameof(Runnable<object, IRunnableConfig>.Config))
where cfg != null
select (RunnableType: r, ConfigType: cfg.PropertyType))
.ToDictionary(x => x.ConfigType, x => x.RunnableType);

foreach (var (_, rt) in configMap) container.Register(rt, rt, new PerRequestLifeTime());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace Queil.Ring.DotNet.Cli.Runnables.Dotnet;
public class DotnetContext : ICsProjContext, ITrackRetries, ITrackProcessId, ITrackProcessOutput
{
public string ExePath => Path.ChangeExtension(EntryAssemblyPath, "exe");
public Dictionary<string, string> Env { get; set; } = new();
public Dictionary<string, string> Env { get; set; } = [];
public string CsProjPath { get; set; }
public string WorkingDir { get; set; }
public string TargetFramework { get; set; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
namespace Queil.Ring.DotNet.Cli.Runnables.Kustomize;

using System;
using Abstractions.Context;

public class KustomizeContext : ITrackRetries
{
public string KustomizationDir { get; set; }
public string CachePath { get; set; }
public Namespace[] Namespaces { get; set; } = Array.Empty<Namespace>();
public Namespace[] Namespaces { get; set; } = [];
public int ConsecutiveFailures { get; set; }
public int TotalFailures { get; set; }
}

public class Namespace
{
public string Name { get; set; }
public string[] Pods { get; set; } = Array.Empty<string>();
public string[] Pods { get; set; } = [];
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using static Queil.Ring.DotNet.Cli.Tools.ToolExtensions;
using static Queil.Ring.DotNet.Cli.Tools.RunProcess;
using KustomizeConfig = Queil.Ring.Configuration.Runnables.Kustomize;

namespace Queil.Ring.DotNet.Cli.Runnables.Kustomize;
Expand Down
13 changes: 6 additions & 7 deletions src/Queil.Ring.DotNet.Cli/Tools/DockerCompose.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
namespace Queil.Ring.DotNet.Cli.Tools;

using System;
using System.Threading;
using System.Threading.Tasks;
using Abstractions.Tools;
Expand All @@ -9,22 +8,22 @@ namespace Queil.Ring.DotNet.Cli.Tools;
public class DockerCompose(ILogger<ITool> logger) : ITool
{
public string Command { get; set; } = "docker-compose";
public string[] DefaultArgs { get; set; } = Array.Empty<string>();
public string[] DefaultArgs { get; set; } = [];
public ILogger<ITool> Logger { get; } = logger;

public async Task<ExecutionInfo> RmAsync(string composeFilePath, CancellationToken token) =>
await this.RunAsync(["-f", $"\"{composeFilePath}\"", "rm", "-f"], wait: true, token: token);
await this.RunAsync(["-f", $"\"{composeFilePath}\"", "rm", "-f"], foreground: true, token: token);

public async Task<ExecutionInfo> PullAsync(string composeFilePath, CancellationToken token) =>
await this.RunAsync(["-f", $"\"{composeFilePath}\"", "pull"], wait: true, token: token);
await this.RunAsync(["-f", $"\"{composeFilePath}\"", "pull"], foreground: true, token: token);

public async Task<ExecutionInfo> UpAsync(string composeFilePath, CancellationToken token) =>
await this.RunAsync(["-f", $"\"{composeFilePath}\"", "up", "--force-recreate"], wait: true,
await this.RunAsync(["-f", $"\"{composeFilePath}\"", "up", "--force-recreate"], foreground: true,
token: token);

public async Task<ExecutionInfo> DownAsync(string composeFilePath, CancellationToken token) =>
await this.RunAsync(["-f", $"\"{composeFilePath}\"", "down"], wait: true, token: token);
await this.RunAsync(["-f", $"\"{composeFilePath}\"", "down"], foreground: true, token: token);

public async Task<ExecutionInfo> StopAsync(string composeFilePath, CancellationToken token) =>
await this.RunAsync(["-f", $"\"{composeFilePath}\"", "stop"], wait: true, token: token);
await this.RunAsync(["-f", $"\"{composeFilePath}\"", "stop"], foreground: true, token: token);
}
4 changes: 2 additions & 2 deletions src/Queil.Ring.DotNet.Cli/Tools/DotnetCliBundle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class DotnetCliBundle(ProcessRunner processRunner, ILogger<DotnetCliBundl
private readonly Dictionary<string, string> DefaultEnvVars = new() { ["ASPNETCORE_ENVIRONMENT"] = "Development" };
public ILogger<ITool> Logger { get; } = logger;
public string Command { get; set; } = "dotnet";
public string[] DefaultArgs { get; set; } = Array.Empty<string>();
public string[] DefaultArgs { get; set; } = [];

public async Task<ExecutionInfo> RunAsync(DotnetContext ctx, CancellationToken token, string[]? urls = null)
{
Expand Down Expand Up @@ -47,6 +47,6 @@ void HandleUrls()
}

public async Task<ExecutionInfo> BuildAsync(string csProjFile, CancellationToken token) =>
await this.RunAsync(["build", csProjFile, "-v:q", "/nologo", "/nodereuse:false"], wait: true,
await this.RunAsync(["build", csProjFile, "-v:q", "/nologo", "/nodereuse:false"], foreground: true,
token: token);
}
6 changes: 3 additions & 3 deletions src/Queil.Ring.DotNet.Cli/Tools/GitClone.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public partial class GitClone(ILogger<GitClone> logger, IOptions<RingConfigurati
ringCfg?.Value ?? throw new NullReferenceException(nameof(ringCfg.Value));

public string Command { get; set; } = "git";
public string[] DefaultArgs { get; set; } = Array.Empty<string>();
public string[] DefaultArgs { get; set; } = [];
public ILogger<ITool> Logger { get; } = logger;

public string ResolveFullClonePath(IFromGit gitCfg, string? rootPathOverride = null)
Expand All @@ -45,7 +45,7 @@ public string ResolveFullClonePath(IFromGit gitCfg, string? rootPathOverride = n
private Func<CancellationToken, Task<ExecutionInfo>> Git(params string[] args)
{
return token => this.TryAsync(3, TimeSpan.FromSeconds(10),
t => t.RunAsync(args, wait: true, token: token), token);
t => t.RunAsync(args, foreground: true, token: token), token);
}

public async Task<ExecutionInfo> CloneOrPullAsync(IFromGit gitCfg, CancellationToken token, bool shallow = false,
Expand All @@ -66,7 +66,7 @@ public async Task<ExecutionInfo> CloneOrPullAsync(IFromGit gitCfg, CancellationT

if (shallow)
{
var remoteBranchName = BranchRegex().Match(output.Output);
var remoteBranchName = BranchRegex().Match(output.Output.Split(Environment.NewLine)[0]);
if (!remoteBranchName.Success)
throw new InvalidOperationException(
$"Could not get branch name from git status output: {output.Output}");
Expand Down
25 changes: 17 additions & 8 deletions src/Queil.Ring.DotNet.Cli/Tools/Kubectl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ public class KubectlBundle(
IOptions<RingConfiguration> config)
: ITool
{
private readonly string[] _allowedContexts = config.Value.Kubernetes.AllowedContexts ?? Array.Empty<string>();
private readonly string[] _allowedContexts = config.Value.Kubernetes.AllowedContexts ?? [];

public string Command { get; set; } = "kubectl";
public string[] DefaultArgs { get; set; } = Array.Empty<string>();
public string[] DefaultArgs { get; set; } = [];
public ILogger<ITool> Logger { get; } = logger;

public async Task EnsureContextIsAllowed(CancellationToken token)
{
var result = await this.RunAsync(["config", "current-context"], wait: true, token: token);
var result = await this.RunAsync(["config", "current-context"], foreground: true, token: token);
var currentContext = result.Output;
if (!_allowedContexts.Contains(currentContext))
throw new InvalidOperationException(
Expand All @@ -35,8 +35,12 @@ public async Task EnsureContextIsAllowed(CancellationToken token)
public async Task<bool> IsValidManifestAsync(string filePath, CancellationToken token)
{
var result = await this.RunAsync([
"apply", "--validate=true", "--dry-run=client", "-f", $"\"{filePath}\""
], wait: true, token: token);
"apply",
"--validate=true",
"--dry-run=client",
"-f",
$"\"{filePath}\""
], foreground: true, token: token);
return result.IsSuccess;
}

Expand All @@ -48,7 +52,7 @@ public async Task<ExecutionInfo> ApplyJsonPathAsync(string path, string jsonPath
{
await EnsureContextIsAllowed(token);
return await this.RunAsync(["apply", "-o", $"jsonpath=\"{jsonPath}\"", "-f", $"\"{path}\""],
wait: true, token: token);
foreground: true, token: token);
}

public async Task<string[]> GetPods(string nameSpace)
Expand All @@ -68,7 +72,12 @@ public async Task<ExecutionInfo> DeleteAsync(string path, CancellationToken _)
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60));
await EnsureContextIsAllowed(cts.Token);
return await this.RunAsync([
"delete", "--ignore-not-found", "--wait=false", "--now=true", "-f", $"\"{path}\""
], wait: true, token: cts.Token);
"delete",
"--ignore-not-found",
"--wait=false",
"--now=true",
"-f",
$"\"{path}\""
], foreground: true, token: cts.Token);
}
}
Loading

0 comments on commit 3497ff4

Please sign in to comment.