cs-codex-dist-tests/KubernetesWorkflow/StartupWorkflow.cs

204 lines
7.0 KiB
C#
Raw Normal View History

2023-04-25 09:31:15 +00:00
using Logging;
using Utils;
namespace KubernetesWorkflow
2023-04-12 11:53:55 +00:00
{
2023-09-11 14:57:57 +00:00
public interface IStartupWorkflow
{
RunningContainers Start(int numberOfContainers, Location location, ContainerRecipeFactory recipeFactory, StartupConfig startupConfig);
void Stop(RunningContainers runningContainers);
2023-09-12 11:32:06 +00:00
void DownloadContainerLog(RunningContainer container, ILogHandler logHandler, int? tailLines = null);
2023-09-11 14:57:57 +00:00
string ExecuteCommand(RunningContainer container, string command, params string[] args);
2023-09-12 08:31:55 +00:00
void DeleteNamespace();
void DeleteNamespacesStartingWith(string namespacePrefix);
2023-09-11 14:57:57 +00:00
}
public class StartupWorkflow : IStartupWorkflow
2023-04-12 11:53:55 +00:00
{
2023-09-12 08:31:55 +00:00
private readonly ILog log;
private readonly WorkflowNumberSource numberSource;
private readonly K8sCluster cluster;
private readonly KnownK8sPods knownK8SPods;
2023-09-12 08:31:55 +00:00
private readonly string k8sNamespace;
2023-04-12 11:53:55 +00:00
private readonly RecipeComponentFactory componentFactory = new RecipeComponentFactory();
2023-09-12 08:31:55 +00:00
internal StartupWorkflow(ILog log, WorkflowNumberSource numberSource, K8sCluster cluster, KnownK8sPods knownK8SPods, string k8sNamespace)
2023-04-12 11:53:55 +00:00
{
2023-04-25 09:31:15 +00:00
this.log = log;
this.numberSource = numberSource;
this.cluster = cluster;
this.knownK8SPods = knownK8SPods;
2023-09-12 08:31:55 +00:00
this.k8sNamespace = k8sNamespace;
2023-04-12 11:53:55 +00:00
}
public RunningContainers Start(int numberOfContainers, Location location, ContainerRecipeFactory recipeFactory, StartupConfig startupConfig)
2023-04-12 11:53:55 +00:00
{
return K8s(controller =>
{
var recipes = CreateRecipes(numberOfContainers, recipeFactory, startupConfig);
var runningPod = controller.BringOnline(recipes, location);
2023-09-11 14:57:57 +00:00
var containers = CreateContainers(runningPod, recipes, startupConfig);
2023-04-12 11:53:55 +00:00
2023-09-11 14:57:57 +00:00
if (startupConfig.CreateCrashWatcher) CreateCrashWatchers(controller, containers);
2023-04-12 11:53:55 +00:00
2023-09-11 14:57:57 +00:00
return new RunningContainers(startupConfig, runningPod, containers);
});
2023-08-14 13:10:36 +00:00
}
2023-04-13 09:07:36 +00:00
public void Stop(RunningContainers runningContainers)
{
K8s(controller =>
{
controller.Stop(runningContainers.RunningPod);
});
}
2023-09-12 11:32:06 +00:00
public void DownloadContainerLog(RunningContainer container, ILogHandler logHandler, int? tailLines = null)
2023-04-13 09:30:19 +00:00
{
K8s(controller =>
{
2023-08-16 14:13:29 +00:00
controller.DownloadPodLog(container.Pod, container.Recipe, logHandler, tailLines);
2023-04-13 09:30:19 +00:00
});
}
2023-04-14 07:54:07 +00:00
public string ExecuteCommand(RunningContainer container, string command, params string[] args)
{
return K8s(controller =>
{
return controller.ExecuteCommand(container.Pod, container.Recipe.Name, command, args);
});
}
2023-09-12 08:31:55 +00:00
public void DeleteNamespace()
{
K8s(controller =>
{
2023-09-12 08:31:55 +00:00
controller.DeleteNamespace();
});
2023-04-12 11:53:55 +00:00
}
2023-09-12 08:31:55 +00:00
public void DeleteNamespacesStartingWith(string namespacePrefix)
2023-05-03 12:18:37 +00:00
{
K8s(controller =>
{
2023-09-12 08:31:55 +00:00
controller.DeleteAllNamespacesStartingWith(namespacePrefix);
2023-05-03 12:18:37 +00:00
});
}
2023-09-11 14:57:57 +00:00
private void CreateCrashWatchers(K8sController controller, RunningContainer[] runningContainers)
{
foreach (var container in runningContainers)
{
container.CrashWatcher = controller.CreateCrashWatcher(container);
}
}
private RunningContainer[] CreateContainers(RunningPod runningPod, ContainerRecipe[] recipes, StartupConfig startupConfig)
2023-04-12 11:53:55 +00:00
{
2023-04-25 09:31:15 +00:00
log.Debug();
return recipes.Select(r =>
{
var servicePorts = runningPod.GetServicePortsForContainerRecipe(r);
log.Debug($"{r} -> service ports: {string.Join(",", servicePorts.Select(p => p.Number))}");
var name = GetContainerName(r, startupConfig);
return new RunningContainer(runningPod, r, servicePorts, name,
GetContainerExternalAddress(runningPod, servicePorts),
GetContainerInternalAddress(r));
}).ToArray();
2023-04-12 11:53:55 +00:00
}
private string GetContainerName(ContainerRecipe recipe, StartupConfig startupConfig)
{
if (startupConfig == null) return "";
if (!string.IsNullOrEmpty(startupConfig.NameOverride))
{
return $"<{startupConfig.NameOverride}{recipe.Number}>";
}
else
{
return $"<{recipe.Name}>";
}
}
private Address GetContainerExternalAddress(RunningPod pod, Port[] servicePorts)
{
return new Address(
pod.Cluster.HostAddress,
GetServicePort(servicePorts));
}
private Address GetContainerInternalAddress(ContainerRecipe recipe)
{
var serviceName = "service-" + numberSource.WorkflowNumber;
var port = GetInternalPort(recipe);
return new Address(
2023-09-12 08:31:55 +00:00
$"http://{serviceName}.{k8sNamespace}.svc.cluster.local",
port);
}
private static int GetServicePort(Port[] servicePorts)
{
if (servicePorts.Any()) return servicePorts.First().Number;
return 0;
}
private static int GetInternalPort(ContainerRecipe recipe)
{
if (recipe.ExposedPorts.Any()) return recipe.ExposedPorts.First().Number;
return 0;
}
2023-04-12 11:53:55 +00:00
private ContainerRecipe[] CreateRecipes(int numberOfContainers, ContainerRecipeFactory recipeFactory, StartupConfig startupConfig)
{
2023-04-25 09:31:15 +00:00
log.Debug();
2023-04-12 11:53:55 +00:00
var result = new List<ContainerRecipe>();
for (var i = 0; i < numberOfContainers; i++)
{
result.Add(recipeFactory.CreateRecipe(i, numberSource.GetContainerNumber(), componentFactory, startupConfig));
2023-04-12 11:53:55 +00:00
}
return result.ToArray();
}
private void K8s(Action<K8sController> action)
{
2023-09-12 08:31:55 +00:00
var controller = new K8sController(log, cluster, knownK8SPods, numberSource, k8sNamespace);
action(controller);
controller.Dispose();
}
private T K8s<T>(Func<K8sController, T> action)
2023-08-07 13:51:44 +00:00
{
2023-09-12 08:31:55 +00:00
var controller = new K8sController(log, cluster, knownK8SPods, numberSource, k8sNamespace);
2023-08-10 09:47:34 +00:00
var result = action(controller);
controller.Dispose();
return result;
}
2023-04-13 09:30:19 +00:00
}
2023-04-13 09:30:19 +00:00
public interface ILogHandler
{
void Log(Stream log);
2023-04-12 11:53:55 +00:00
}
public abstract class LogHandler : ILogHandler
{
public void Log(Stream log)
{
using var reader = new StreamReader(log);
var line = reader.ReadLine();
while (line != null)
{
ProcessLine(line);
line = reader.ReadLine();
}
}
protected abstract void ProcessLine(string line);
}
2023-04-12 11:53:55 +00:00
}