Merge branch 'automated-teststarter' into deployment-json-rework
This commit is contained in:
commit
db0a21bc60
|
@ -93,7 +93,7 @@ namespace Core
|
|||
{
|
||||
var response = PostJsonString(route, body);
|
||||
if (response == null) throw new Exception("Received no response.");
|
||||
var result = JsonConvert.DeserializeObject<TResponse>(response);
|
||||
var result = Deserialize<TResponse>(response);
|
||||
if (result == null) throw new Exception("Failed to deserialize response");
|
||||
return result;
|
||||
}, $"HTTO-POST-JSON: {route}");
|
||||
|
|
|
@ -112,15 +112,21 @@
|
|||
|
||||
public class VolumeMount
|
||||
{
|
||||
public VolumeMount(string volumeName, string mountPath, string resourceQuantity)
|
||||
public VolumeMount(string volumeName, string mountPath, string? subPath = null, string? resourceQuantity = null, string? secret = null, string? hostPath = null)
|
||||
{
|
||||
VolumeName = volumeName;
|
||||
MountPath = mountPath;
|
||||
SubPath = subPath;
|
||||
ResourceQuantity = resourceQuantity;
|
||||
Secret = secret;
|
||||
HostPath = hostPath;
|
||||
}
|
||||
|
||||
public string VolumeName { get; }
|
||||
public string MountPath { get; }
|
||||
public string ResourceQuantity { get; }
|
||||
public string? SubPath { get; }
|
||||
public string? ResourceQuantity { get; }
|
||||
public string? Secret { get; }
|
||||
public string? HostPath { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,12 +97,18 @@ namespace KubernetesWorkflow
|
|||
podAnnotations.Add(name, value);
|
||||
}
|
||||
|
||||
protected void AddVolume(string name, string mountPath, string? subPath = null, string? secret = null, string? hostPath = null)
|
||||
{
|
||||
var size = 10.MB().ToSuffixNotation();
|
||||
volumeMounts.Add(new VolumeMount(name, mountPath, subPath, size, secret, hostPath));
|
||||
}
|
||||
|
||||
protected void AddVolume(string mountPath, ByteSize volumeSize)
|
||||
{
|
||||
volumeMounts.Add(new VolumeMount(
|
||||
$"autovolume-{Guid.NewGuid().ToString().ToLowerInvariant()}",
|
||||
mountPath,
|
||||
volumeSize.ToSuffixNotation()));
|
||||
resourceQuantity: volumeSize.ToSuffixNotation()));
|
||||
}
|
||||
|
||||
protected void Additional(object userData)
|
||||
|
|
|
@ -9,15 +9,14 @@ namespace KubernetesWorkflow
|
|||
{
|
||||
private readonly ILog log;
|
||||
private readonly K8sCluster cluster;
|
||||
private readonly KnownK8sPods knownPods;
|
||||
private readonly WorkflowNumberSource workflowNumberSource;
|
||||
private readonly K8sClient client;
|
||||
private const string podLabelKey = "pod-uuid";
|
||||
|
||||
public K8sController(ILog log, K8sCluster cluster, KnownK8sPods knownPods, WorkflowNumberSource workflowNumberSource, string k8sNamespace)
|
||||
public K8sController(ILog log, K8sCluster cluster, WorkflowNumberSource workflowNumberSource, string k8sNamespace)
|
||||
{
|
||||
this.log = log;
|
||||
this.cluster = cluster;
|
||||
this.knownPods = knownPods;
|
||||
this.workflowNumberSource = workflowNumberSource;
|
||||
client = new K8sClient(cluster.GetK8sClientConfig());
|
||||
|
||||
|
@ -34,13 +33,30 @@ namespace KubernetesWorkflow
|
|||
log.Debug();
|
||||
EnsureNamespace();
|
||||
|
||||
var deploymentName = CreateDeployment(containerRecipes, location);
|
||||
var podLabel = K8sNameUtils.Format(Guid.NewGuid().ToString());
|
||||
var deploymentName = CreateDeployment(containerRecipes, location, podLabel);
|
||||
var (serviceName, servicePortsMap) = CreateService(containerRecipes);
|
||||
var podInfo = FetchNewPod();
|
||||
|
||||
var pod = FindPodByLabel(podLabel);
|
||||
var podInfo = CreatePodInfo(pod);
|
||||
|
||||
return new RunningPod(cluster, podInfo, deploymentName, serviceName, servicePortsMap.ToArray());
|
||||
}
|
||||
|
||||
private V1Pod FindPodByLabel(string podLabel)
|
||||
{
|
||||
var pods = client.Run(c => c.ListNamespacedPod(K8sNamespace));
|
||||
foreach (var pod in pods.Items)
|
||||
{
|
||||
var label = pod.GetLabel(podLabelKey);
|
||||
if (label == podLabel)
|
||||
{
|
||||
return pod;
|
||||
}
|
||||
}
|
||||
throw new Exception("Unable to find pod by label.");
|
||||
}
|
||||
|
||||
public void Stop(RunningPod pod)
|
||||
{
|
||||
log.Debug();
|
||||
|
@ -299,7 +315,7 @@ namespace KubernetesWorkflow
|
|||
|
||||
#region Deployment management
|
||||
|
||||
private string CreateDeployment(ContainerRecipe[] containerRecipes, ILocation location)
|
||||
private string CreateDeployment(ContainerRecipe[] containerRecipes, ILocation location, string podLabel)
|
||||
{
|
||||
var deploymentSpec = new V1Deployment
|
||||
{
|
||||
|
@ -316,7 +332,7 @@ namespace KubernetesWorkflow
|
|||
{
|
||||
Metadata = new V1ObjectMeta
|
||||
{
|
||||
Labels = GetSelector(containerRecipes),
|
||||
Labels = GetSelector(containerRecipes, podLabel),
|
||||
Annotations = GetAnnotations(containerRecipes)
|
||||
},
|
||||
Spec = new V1PodSpec
|
||||
|
@ -363,6 +379,13 @@ namespace KubernetesWorkflow
|
|||
return containerRecipes.First().PodLabels.GetLabels();
|
||||
}
|
||||
|
||||
private IDictionary<string, string> GetSelector(ContainerRecipe[] containerRecipes, string podLabel)
|
||||
{
|
||||
var labels = containerRecipes.First().PodLabels.Clone();
|
||||
labels.Add(podLabelKey, podLabel);
|
||||
return labels.GetLabels();
|
||||
}
|
||||
|
||||
private IDictionary<string, string> GetRunnerNamespaceSelector()
|
||||
{
|
||||
return new Dictionary<string, string> { { "kubernetes.io/metadata.name", "default" } };
|
||||
|
@ -441,7 +464,8 @@ namespace KubernetesWorkflow
|
|||
return new V1VolumeMount
|
||||
{
|
||||
Name = v.VolumeName,
|
||||
MountPath = v.MountPath
|
||||
MountPath = v.MountPath,
|
||||
SubPath = v.SubPath,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -457,28 +481,28 @@ namespace KubernetesWorkflow
|
|||
|
||||
private V1Volume CreateVolume(VolumeMount v)
|
||||
{
|
||||
client.Run(c => c.CreateNamespacedPersistentVolumeClaim(new V1PersistentVolumeClaim
|
||||
CreatePersistentVolumeClaimIfNeeded(v);
|
||||
|
||||
if (!string.IsNullOrEmpty(v.HostPath))
|
||||
{
|
||||
ApiVersion = "v1",
|
||||
Metadata = new V1ObjectMeta
|
||||
return new V1Volume
|
||||
{
|
||||
Name = v.VolumeName
|
||||
},
|
||||
Spec = new V1PersistentVolumeClaimSpec
|
||||
{
|
||||
AccessModes = new List<string>
|
||||
Name = v.VolumeName,
|
||||
HostPath = new V1HostPathVolumeSource
|
||||
{
|
||||
"ReadWriteOnce"
|
||||
},
|
||||
Resources = new V1ResourceRequirements
|
||||
{
|
||||
Requests = new Dictionary<string, ResourceQuantity>
|
||||
{
|
||||
{"storage", new ResourceQuantity(v.ResourceQuantity) }
|
||||
}
|
||||
Path = v.HostPath
|
||||
}
|
||||
}
|
||||
}, K8sNamespace));
|
||||
};
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(v.Secret))
|
||||
{
|
||||
return new V1Volume
|
||||
{
|
||||
Name = v.VolumeName,
|
||||
Secret = CreateVolumeSecret(v)
|
||||
};
|
||||
}
|
||||
|
||||
return new V1Volume
|
||||
{
|
||||
|
@ -490,6 +514,50 @@ namespace KubernetesWorkflow
|
|||
};
|
||||
}
|
||||
|
||||
private void CreatePersistentVolumeClaimIfNeeded(VolumeMount v)
|
||||
{
|
||||
var pvcs = client.Run(c => c.ListNamespacedPersistentVolumeClaim(K8sNamespace));
|
||||
if (pvcs != null && pvcs.Items.Any(i => i.Name() == v.VolumeName)) return;
|
||||
|
||||
client.Run(c => c.CreateNamespacedPersistentVolumeClaim(new V1PersistentVolumeClaim
|
||||
{
|
||||
ApiVersion = "v1",
|
||||
Metadata = new V1ObjectMeta
|
||||
{
|
||||
Name = v.VolumeName,
|
||||
},
|
||||
Spec = new V1PersistentVolumeClaimSpec
|
||||
{
|
||||
AccessModes = new List<string>
|
||||
{
|
||||
"ReadWriteOnce"
|
||||
},
|
||||
Resources = CreateVolumeResourceRequirements(v),
|
||||
},
|
||||
}, K8sNamespace));
|
||||
}
|
||||
|
||||
private V1SecretVolumeSource CreateVolumeSecret(VolumeMount v)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(v.Secret)) return null!;
|
||||
return new V1SecretVolumeSource
|
||||
{
|
||||
SecretName = v.Secret
|
||||
};
|
||||
}
|
||||
|
||||
private V1ResourceRequirements CreateVolumeResourceRequirements(VolumeMount v)
|
||||
{
|
||||
if (v.ResourceQuantity == null) return null!;
|
||||
return new V1ResourceRequirements
|
||||
{
|
||||
Requests = new Dictionary<string, ResourceQuantity>()
|
||||
{
|
||||
{"storage", new ResourceQuantity(v.ResourceQuantity) }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private List<V1EnvVar> CreateEnv(ContainerRecipe recipe)
|
||||
{
|
||||
return recipe.EnvVars.Select(CreateEnvVar).ToList();
|
||||
|
@ -720,22 +788,15 @@ namespace KubernetesWorkflow
|
|||
return new CrashWatcher(log, cluster.GetK8sClientConfig(), K8sNamespace, container);
|
||||
}
|
||||
|
||||
private PodInfo FetchNewPod()
|
||||
private PodInfo CreatePodInfo(V1Pod pod)
|
||||
{
|
||||
var pods = client.Run(c => c.ListNamespacedPod(K8sNamespace)).Items;
|
||||
|
||||
var newPods = pods.Where(p => !knownPods.Contains(p.Name())).ToArray();
|
||||
if (newPods.Length != 1) throw new InvalidOperationException("Expected only 1 pod to be created. Test infra failure.");
|
||||
|
||||
var newPod = newPods.Single();
|
||||
var name = newPod.Name();
|
||||
var ip = newPod.Status.PodIP;
|
||||
var k8sNodeName = newPod.Spec.NodeName;
|
||||
var name = pod.Name();
|
||||
var ip = pod.Status.PodIP;
|
||||
var k8sNodeName = pod.Spec.NodeName;
|
||||
|
||||
if (string.IsNullOrEmpty(name)) throw new InvalidOperationException("Invalid pod name received. Test infra failure.");
|
||||
if (string.IsNullOrEmpty(ip)) throw new InvalidOperationException("Invalid pod IP received. Test infra failure.");
|
||||
|
||||
knownPods.Add(name);
|
||||
return new PodInfo(name, ip, k8sNodeName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
namespace KubernetesWorkflow
|
||||
{
|
||||
public class KnownK8sPods
|
||||
{
|
||||
private readonly List<string> knownActivePodNames = new List<string>();
|
||||
|
||||
public bool Contains(string name)
|
||||
{
|
||||
return knownActivePodNames.Contains(name);
|
||||
}
|
||||
|
||||
public void Add(string name)
|
||||
{
|
||||
knownActivePodNames.Add(name);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,17 +22,15 @@ namespace KubernetesWorkflow
|
|||
private readonly ILog log;
|
||||
private readonly WorkflowNumberSource numberSource;
|
||||
private readonly K8sCluster cluster;
|
||||
private readonly KnownK8sPods knownK8SPods;
|
||||
private readonly string k8sNamespace;
|
||||
private readonly RecipeComponentFactory componentFactory = new RecipeComponentFactory();
|
||||
private readonly LocationProvider locationProvider;
|
||||
|
||||
internal StartupWorkflow(ILog log, WorkflowNumberSource numberSource, K8sCluster cluster, KnownK8sPods knownK8SPods, string k8sNamespace)
|
||||
internal StartupWorkflow(ILog log, WorkflowNumberSource numberSource, K8sCluster cluster, string k8sNamespace)
|
||||
{
|
||||
this.log = log;
|
||||
this.numberSource = numberSource;
|
||||
this.cluster = cluster;
|
||||
this.knownK8SPods = knownK8SPods;
|
||||
this.k8sNamespace = k8sNamespace;
|
||||
|
||||
locationProvider = new LocationProvider(log, K8s);
|
||||
|
@ -196,7 +194,7 @@ namespace KubernetesWorkflow
|
|||
{
|
||||
try
|
||||
{
|
||||
var controller = new K8sController(log, cluster, knownK8SPods, numberSource, k8sNamespace);
|
||||
var controller = new K8sController(log, cluster, numberSource, k8sNamespace);
|
||||
action(controller);
|
||||
controller.Dispose();
|
||||
}
|
||||
|
@ -211,7 +209,7 @@ namespace KubernetesWorkflow
|
|||
{
|
||||
try
|
||||
{
|
||||
var controller = new K8sController(log, cluster, knownK8SPods, numberSource, k8sNamespace);
|
||||
var controller = new K8sController(log, cluster, numberSource, k8sNamespace);
|
||||
var result = action(controller);
|
||||
controller.Dispose();
|
||||
return result;
|
||||
|
|
|
@ -7,7 +7,6 @@ namespace KubernetesWorkflow
|
|||
{
|
||||
private readonly NumberSource numberSource = new NumberSource(0);
|
||||
private readonly NumberSource containerNumberSource = new NumberSource(0);
|
||||
private readonly KnownK8sPods knownPods = new KnownK8sPods();
|
||||
private readonly K8sCluster cluster;
|
||||
private readonly ILog log;
|
||||
private readonly Configuration configuration;
|
||||
|
@ -26,7 +25,7 @@ namespace KubernetesWorkflow
|
|||
var workflowNumberSource = new WorkflowNumberSource(numberSource.GetNextNumber(),
|
||||
containerNumberSource);
|
||||
|
||||
return new StartupWorkflow(log, workflowNumberSource, cluster, knownPods, GetNamespace(namespaceOverride));
|
||||
return new StartupWorkflow(log, workflowNumberSource, cluster, GetNamespace(namespaceOverride));
|
||||
}
|
||||
|
||||
private string GetNamespace(string? namespaceOverride)
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
using Core;
|
||||
using KubernetesWorkflow;
|
||||
|
||||
namespace DeployAndRunPlugin
|
||||
{
|
||||
public static class CoreInterfaceExtensions
|
||||
{
|
||||
public static RunningContainer DeployAndRunContinuousTests(this CoreInterface ci, RunConfig runConfig)
|
||||
{
|
||||
return ci.GetPlugin<DeployAndRunPlugin>().Run(runConfig);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
using KubernetesWorkflow;
|
||||
|
||||
namespace DeployAndRunPlugin
|
||||
{
|
||||
public class DeployAndRunContainerRecipe : ContainerRecipeFactory
|
||||
{
|
||||
public override string AppName => "deploy-and-run";
|
||||
public override string Image => "thatbenbierens/dist-tests-deployandrun:initial";
|
||||
|
||||
protected override void Initialize(StartupConfig config)
|
||||
{
|
||||
var setup = config.Get<RunConfig>();
|
||||
|
||||
if (setup.CodexImageOverride != null)
|
||||
{
|
||||
AddEnvVar("CODEXDOCKERIMAGE", setup.CodexImageOverride);
|
||||
}
|
||||
|
||||
AddEnvVar("DNR_REP", setup.Replications.ToString());
|
||||
AddEnvVar("DNR_NAME", setup.Name);
|
||||
AddEnvVar("DNR_FILTER", setup.Filter);
|
||||
AddEnvVar("DNR_DURATION", setup.Duration.TotalSeconds.ToString());
|
||||
|
||||
AddEnvVar("KUBECONFIG", "/opt/kubeconfig.yaml");
|
||||
AddEnvVar("LOGPATH", "/var/log/codex-continuous-tests");
|
||||
|
||||
AddVolume(name: "kubeconfig", mountPath: "/opt/kubeconfig.yaml", subPath: "kubeconfig.yaml", secret: "codex-dist-tests-app-kubeconfig");
|
||||
AddVolume(name: "logs", mountPath: "/var/log/codex-continuous-tests", hostPath: "/var/log/codex-continuous-tests");
|
||||
}
|
||||
}
|
||||
|
||||
public class RunConfig
|
||||
{
|
||||
public RunConfig(string name, string filter, TimeSpan duration, int replications, string? codexImageOverride = null)
|
||||
{
|
||||
Name = name;
|
||||
Filter = filter;
|
||||
Duration = duration;
|
||||
Replications = replications;
|
||||
CodexImageOverride = codexImageOverride;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
public string Filter { get; }
|
||||
public TimeSpan Duration { get; }
|
||||
public int Replications { get; }
|
||||
public string? CodexImageOverride { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using Core;
|
||||
using KubernetesWorkflow;
|
||||
|
||||
namespace DeployAndRunPlugin
|
||||
{
|
||||
public class DeployAndRunPlugin : IProjectPlugin
|
||||
{
|
||||
private readonly IPluginTools tools;
|
||||
|
||||
public DeployAndRunPlugin(IPluginTools tools)
|
||||
{
|
||||
this.tools = tools;
|
||||
}
|
||||
|
||||
public void Announce()
|
||||
{
|
||||
tools.GetLog().Log("Deploy-and-Run plugin loaded.");
|
||||
}
|
||||
|
||||
public void Decommission()
|
||||
{
|
||||
}
|
||||
|
||||
public RunningContainer Run(RunConfig config)
|
||||
{
|
||||
var workflow = tools.CreateWorkflow();
|
||||
var startupConfig = new StartupConfig();
|
||||
startupConfig.NameOverride = "dnr-" + config.Name;
|
||||
startupConfig.Add(config);
|
||||
|
||||
var location = workflow.GetAvailableLocations().Get("fixed-s-4vcpu-16gb-amd-yz8rd");
|
||||
var containers = workflow.Start(1, location, new DeployAndRunContainerRecipe(), startupConfig);
|
||||
return containers.Containers.Single();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Framework\Core\Core.csproj" />
|
||||
<ProjectReference Include="..\..\Framework\KubernetesWorkflow\KubernetesWorkflow.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -53,22 +53,25 @@ namespace ContinuousTests
|
|||
if (!filteredTests.Any())
|
||||
{
|
||||
overviewLog.Log("No tests selected.");
|
||||
return;
|
||||
Cancellation.Cts.Cancel();
|
||||
}
|
||||
var testLoops = filteredTests.Select(t => new TestLoop(entryPointFactory, taskFactory, config, overviewLog, t.GetType(), t.RunTestEvery, startupChecker, cancelToken)).ToArray();
|
||||
|
||||
foreach (var testLoop in testLoops)
|
||||
else
|
||||
{
|
||||
if (cancelToken.IsCancellationRequested) break;
|
||||
var testLoops = filteredTests.Select(t => new TestLoop(entryPointFactory, taskFactory, config, overviewLog, t.GetType(), t.RunTestEvery, startupChecker, cancelToken)).ToArray();
|
||||
|
||||
overviewLog.Log("Launching test-loop for " + testLoop.Name);
|
||||
testLoop.Begin();
|
||||
Thread.Sleep(TimeSpan.FromSeconds(5));
|
||||
foreach (var testLoop in testLoops)
|
||||
{
|
||||
if (cancelToken.IsCancellationRequested) break;
|
||||
|
||||
overviewLog.Log("Launching test-loop for " + testLoop.Name);
|
||||
testLoop.Begin();
|
||||
Thread.Sleep(TimeSpan.FromSeconds(5));
|
||||
}
|
||||
|
||||
overviewLog.Log("Finished launching test-loops.");
|
||||
WaitUntilFinished(overviewLog, statusLog, startTime, testLoops);
|
||||
overviewLog.Log("Stopping all test-loops...");
|
||||
}
|
||||
|
||||
overviewLog.Log("Finished launching test-loops.");
|
||||
WaitUntilFinished(overviewLog, statusLog, startTime, testLoops);
|
||||
overviewLog.Log("Stopping all test-loops...");
|
||||
taskFactory.WaitAll();
|
||||
overviewLog.Log("All tasks cancelled.");
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
set -e
|
||||
|
||||
replication=5
|
||||
name=testnamehere
|
||||
filter=TwoClient
|
||||
replication=$DNR_REP
|
||||
name=$DNR_NAME
|
||||
filter=$DNR_FILTER
|
||||
duration=$DNR_DURATION
|
||||
|
||||
echo "Deploying..."
|
||||
cd ../../Tools/CodexNetDeployer
|
||||
|
@ -45,7 +46,10 @@ do
|
|||
--filter=$filter \
|
||||
--cleanup=1 \
|
||||
--full-container-logs=1 \
|
||||
--target-duration=172800 # 48 hours
|
||||
--target-duration=$duration
|
||||
|
||||
sleep 30
|
||||
done
|
||||
|
||||
echo "Done! Sleeping indefinitely..."
|
||||
while true; do sleep 1d; done
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
using KubernetesWorkflow;
|
||||
|
||||
namespace TestClusterStarter
|
||||
{
|
||||
public class ClusterTestSetup
|
||||
{
|
||||
public ClusterTestSetup(ClusterTestSpec[] specs)
|
||||
{
|
||||
Specs = specs;
|
||||
}
|
||||
|
||||
public ClusterTestSpec[] Specs { get; }
|
||||
}
|
||||
|
||||
public class ClusterTestSpec
|
||||
{
|
||||
public ClusterTestSpec(string name, string filter, int replication, int durationSeconds, string? codexImageOverride)
|
||||
{
|
||||
Name = name;
|
||||
Filter = filter;
|
||||
Replication = replication;
|
||||
DurationSeconds = durationSeconds;
|
||||
CodexImageOverride = codexImageOverride;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
public string Filter { get; }
|
||||
public int Replication { get; }
|
||||
public int DurationSeconds { get; }
|
||||
public string? CodexImageOverride { get; }
|
||||
}
|
||||
|
||||
public class ClusterTestDeployment
|
||||
{
|
||||
public ClusterTestDeployment(RunningContainer[] containers)
|
||||
{
|
||||
Containers = containers;
|
||||
}
|
||||
|
||||
public RunningContainer[] Containers { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
using ArgsUniform;
|
||||
|
||||
namespace TestClusterStarter
|
||||
{
|
||||
public class Configuration
|
||||
{
|
||||
[Uniform("kube-config", "kc", "KUBECONFIG", true, "Path to Kubeconfig file. Use 'null' (default) to use local cluster.")]
|
||||
public string KubeConfigFile { get; set; } = "null";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
using ArgsUniform;
|
||||
using Core;
|
||||
using DeployAndRunPlugin;
|
||||
using KubernetesWorkflow;
|
||||
using Logging;
|
||||
using Newtonsoft.Json;
|
||||
using TestClusterStarter;
|
||||
|
||||
public class Program
|
||||
{
|
||||
private const string SpecsFile = "TestSpecs.json";
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var argsUniform = new ArgsUniform<TestClusterStarter.Configuration>(() => { }, args);
|
||||
var config = argsUniform.Parse();
|
||||
|
||||
ProjectPlugin.Load<DeployAndRunPlugin.DeployAndRunPlugin>();
|
||||
|
||||
if (!File.Exists(SpecsFile))
|
||||
{
|
||||
File.WriteAllText(SpecsFile, JsonConvert.SerializeObject(new ClusterTestSetup(new[]
|
||||
{
|
||||
new ClusterTestSpec("example", "peer", 2, Convert.ToInt32(TimeSpan.FromDays(2).TotalSeconds), "imageoverride")
|
||||
})));
|
||||
return;
|
||||
}
|
||||
var specs = JsonConvert.DeserializeObject<ClusterTestSetup>(File.ReadAllText(SpecsFile))!;
|
||||
|
||||
var kConfig = new KubernetesWorkflow.Configuration(config.KubeConfigFile, TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(10), kubernetesNamespace: "default");
|
||||
var entryPoint = new EntryPoint(new ConsoleLog(), kConfig, "datafolder");
|
||||
var ci = entryPoint.CreateInterface();
|
||||
|
||||
var rcs = new List<RunningContainer>();
|
||||
foreach (var spec in specs.Specs)
|
||||
{
|
||||
var rc = ci.DeployAndRunContinuousTests(new RunConfig(
|
||||
name: spec.Name,
|
||||
filter: spec.Filter,
|
||||
duration: TimeSpan.FromSeconds(spec.DurationSeconds),
|
||||
replications: spec.Replication,
|
||||
codexImageOverride: spec.CodexImageOverride));
|
||||
|
||||
rcs.Add(rc);
|
||||
}
|
||||
|
||||
var deployment = new ClusterTestDeployment(rcs.ToArray());
|
||||
File.WriteAllText("clustertest-deployment.json", JsonConvert.SerializeObject(deployment, Formatting.Indented));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Framework\ArgsUniform\ArgsUniform.csproj" />
|
||||
<ProjectReference Include="..\..\Framework\Core\Core.csproj" />
|
||||
<ProjectReference Include="..\..\Framework\KubernetesWorkflow\KubernetesWorkflow.csproj" />
|
||||
<ProjectReference Include="..\..\ProjectPlugins\DeployAndRunPlugin\DeployAndRunPlugin.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -47,6 +47,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BiblioTech", "Tools\BiblioT
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodexDiscordBotPlugin", "ProjectPlugins\CodexDiscordBotPlugin\CodexDiscordBotPlugin.csproj", "{FB96A58B-F7F0-490A-9A85-72A96A018042}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestClusterStarter", "Tools\TestClusterStarter\TestClusterStarter.csproj", "{3E38A906-C2FC-43DC-8CA2-FC07C79CF3CA}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeployAndRunPlugin", "ProjectPlugins\DeployAndRunPlugin\DeployAndRunPlugin.csproj", "{1CC5AF82-8924-4C7E-BFF1-3125D86E53FB}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -125,6 +129,14 @@ Global
|
|||
{FB96A58B-F7F0-490A-9A85-72A96A018042}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FB96A58B-F7F0-490A-9A85-72A96A018042}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FB96A58B-F7F0-490A-9A85-72A96A018042}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3E38A906-C2FC-43DC-8CA2-FC07C79CF3CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3E38A906-C2FC-43DC-8CA2-FC07C79CF3CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3E38A906-C2FC-43DC-8CA2-FC07C79CF3CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3E38A906-C2FC-43DC-8CA2-FC07C79CF3CA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1CC5AF82-8924-4C7E-BFF1-3125D86E53FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1CC5AF82-8924-4C7E-BFF1-3125D86E53FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1CC5AF82-8924-4C7E-BFF1-3125D86E53FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1CC5AF82-8924-4C7E-BFF1-3125D86E53FB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -148,6 +160,8 @@ Global
|
|||
{3417D508-E2F4-4974-8988-BB124046D9E2} = {7591C5B3-D86E-4AE4-8ED2-B272D17FE7E3}
|
||||
{078ABA6D-A04E-4F62-A44C-EA66F1B66548} = {7591C5B3-D86E-4AE4-8ED2-B272D17FE7E3}
|
||||
{FB96A58B-F7F0-490A-9A85-72A96A018042} = {8F1F1C2A-E313-4E0C-BE40-58FB0BA91124}
|
||||
{3E38A906-C2FC-43DC-8CA2-FC07C79CF3CA} = {7591C5B3-D86E-4AE4-8ED2-B272D17FE7E3}
|
||||
{1CC5AF82-8924-4C7E-BFF1-3125D86E53FB} = {8F1F1C2A-E313-4E0C-BE40-58FB0BA91124}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {237BF0AA-9EC4-4659-AD9A-65DEB974250C}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
docker build -f deployandrun.Dockerfile -t thatbenbierens/dist-tests-deployandrun:initial ..
|
||||
docker push thatbenbierens/dist-tests-deployandrun:initial
|
|
@ -0,0 +1,12 @@
|
|||
FROM mcr.microsoft.com/dotnet/sdk:7.0
|
||||
|
||||
RUN apt-get update && apt-get install -y screen
|
||||
WORKDIR /app
|
||||
COPY --chmod=0755 docker/docker-dnr-entrypoint.sh /
|
||||
COPY ./Tools ./Tools
|
||||
COPY ./Tests ./Tests
|
||||
COPY ./Framework ./Framework
|
||||
COPY ./ProjectPlugins ./ProjectPlugins
|
||||
|
||||
ENTRYPOINT ["/docker-dnr-entrypoint.sh"]
|
||||
CMD ["/bin/bash", "deploy-and-run.sh"]
|
|
@ -9,3 +9,12 @@ services:
|
|||
- KUBECONFIG=/opt/kubeconfig
|
||||
- LOGPATH=/opt/logs
|
||||
- RUNNERLOCATION=ExternalToCluster
|
||||
|
||||
continuous-test-run:
|
||||
image: thatbenbierens/dist-tests-deployandrun:initial
|
||||
environment:
|
||||
# - CODEXDOCKERIMAGE=imageoverride
|
||||
- DNR_REP=3
|
||||
- DNR_NAME=Tryout
|
||||
- DNR_FILTER=PeernBeer
|
||||
- DNR_DURATION=172800
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
echo "Running continuous tests..."
|
||||
cd /app/Tests/CodexContinuousTests
|
||||
exec "$@"
|
||||
|
Loading…
Reference in New Issue