Merge branch 'automated-teststarter' into deployment-json-rework

This commit is contained in:
benbierens 2023-11-03 14:57:32 +01:00
commit db0a21bc60
No known key found for this signature in database
GPG Key ID: FE44815D96D0A1AA
22 changed files with 418 additions and 81 deletions

View File

@ -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}");

View File

@ -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; }
}
}

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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;

View File

@ -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)

View File

@ -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);
}
}
}

View File

@ -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; }
}
}

View File

@ -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();
}
}
}

View File

@ -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>

View File

@ -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.");

View File

@ -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

View File

@ -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; }
}
}

View File

@ -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";
}
}

View File

@ -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));
}
}

View File

@ -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>

View File

@ -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}

View File

@ -0,0 +1,2 @@
docker build -f deployandrun.Dockerfile -t thatbenbierens/dist-tests-deployandrun:initial ..
docker push thatbenbierens/dist-tests-deployandrun:initial

View File

@ -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"]

View File

@ -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

View File

@ -0,0 +1,5 @@
#!/bin/bash
echo "Running continuous tests..."
cd /app/Tests/CodexContinuousTests
exec "$@"