update
This commit is contained in:
38
.idea/.idea.LiquidCode.Tester/.idea/workspace.xml
generated
38
.idea/.idea.LiquidCode.Tester/.idea/workspace.xml
generated
@@ -12,12 +12,14 @@
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="1d3190f0-8175-44b9-bab6-12e025e4819d" name="Changes" comment="">
|
||||
<change afterPath="$PROJECT_DIR$/.dockerignore" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/LiquidCode.Tester.sln" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/global.json" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/LiquidCode.Tester.Common/Dockerfile" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/LiquidCode.Tester.Worker/Dockerfile" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/global.json" beforeDir="false" afterPath="$PROJECT_DIR$/global.json" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/LiquidCode.Tester.Common/LiquidCode.Tester.Common.csproj" beforeDir="false" afterPath="$PROJECT_DIR$/src/LiquidCode.Tester.Common/LiquidCode.Tester.Common.csproj" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/LiquidCode.Tester.Common/Program.cs" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/LiquidCode.Tester.Gateway/LiquidCode.Tester.Gateway.csproj" beforeDir="false" afterPath="$PROJECT_DIR$/src/LiquidCode.Tester.Gateway/LiquidCode.Tester.Gateway.csproj" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/LiquidCode.Tester.Gateway/Program.cs" beforeDir="false" afterPath="$PROJECT_DIR$/src/LiquidCode.Tester.Gateway/Program.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/LiquidCode.Tester.Gateway/appsettings.Development.json" beforeDir="false" afterPath="$PROJECT_DIR$/src/LiquidCode.Tester.Gateway/appsettings.Development.json" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/LiquidCode.Tester.Gateway/appsettings.json" beforeDir="false" afterPath="$PROJECT_DIR$/src/LiquidCode.Tester.Gateway/appsettings.json" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/LiquidCode.Tester.Worker/LiquidCode.Tester.Worker.csproj" beforeDir="false" afterPath="$PROJECT_DIR$/src/LiquidCode.Tester.Worker/LiquidCode.Tester.Worker.csproj" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@@ -263,8 +265,17 @@
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1761331001679</updated>
|
||||
<workItem from="1761331002792" duration="1650000" />
|
||||
<workItem from="1761331002792" duration="2278000" />
|
||||
</task>
|
||||
<task id="LOCAL-00001" summary="init">
|
||||
<option name="closed" value="true" />
|
||||
<created>1761333540672</created>
|
||||
<option name="number" value="00001" />
|
||||
<option name="presentableId" value="LOCAL-00001" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1761333540672</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="2" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
@@ -272,8 +283,21 @@
|
||||
</component>
|
||||
<component name="UnityCheckinConfiguration" checkUnsavedScenes="true" />
|
||||
<component name="UnityProjectConfiguration" hasMinimizedUI="false" />
|
||||
<component name="Vcs.Log.Tabs.Properties">
|
||||
<option name="TAB_STATES">
|
||||
<map>
|
||||
<entry key="MAIN">
|
||||
<value>
|
||||
<State />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
<component name="VcsManagerConfiguration">
|
||||
<option name="CLEAR_INITIAL_COMMIT_MESSAGE" value="true" />
|
||||
<MESSAGE value="init" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="init" />
|
||||
</component>
|
||||
<component name="XDebuggerManager">
|
||||
<breakpoint-manager>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "9.0.0",
|
||||
"version": "10.0.100-rc.2.25502.107",
|
||||
"rollForward": "latestMinor",
|
||||
"allowPrerelease": false
|
||||
"allowPrerelease": true
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
12
src/LiquidCode.Tester.Common/Models/ErrorCode.cs
Normal file
12
src/LiquidCode.Tester.Common/Models/ErrorCode.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace LiquidCode.Tester.Common.Models;
|
||||
|
||||
public enum ErrorCode
|
||||
{
|
||||
None,
|
||||
CompileError,
|
||||
RuntimeError,
|
||||
MemoryError,
|
||||
TimeLimitError,
|
||||
IncorrectAnswer,
|
||||
UnknownError
|
||||
}
|
||||
9
src/LiquidCode.Tester.Common/Models/State.cs
Normal file
9
src/LiquidCode.Tester.Common/Models/State.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace LiquidCode.Tester.Common.Models;
|
||||
|
||||
public enum State
|
||||
{
|
||||
Waiting,
|
||||
Compiling,
|
||||
Testing,
|
||||
Done
|
||||
}
|
||||
11
src/LiquidCode.Tester.Common/Models/SubmitForTesterModel.cs
Normal file
11
src/LiquidCode.Tester.Common/Models/SubmitForTesterModel.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace LiquidCode.Tester.Common.Models;
|
||||
|
||||
public record SubmitForTesterModel(
|
||||
long Id,
|
||||
long MissionId,
|
||||
string Language,
|
||||
string LanguageVersion,
|
||||
string SourceCode,
|
||||
string PackageUrl,
|
||||
string CallbackUrl
|
||||
);
|
||||
10
src/LiquidCode.Tester.Common/Models/TesterResponseModel.cs
Normal file
10
src/LiquidCode.Tester.Common/Models/TesterResponseModel.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace LiquidCode.Tester.Common.Models;
|
||||
|
||||
public record TesterResponseModel(
|
||||
long SubmitId,
|
||||
State State,
|
||||
ErrorCode ErrorCode,
|
||||
string Message,
|
||||
int CurrentTest,
|
||||
int AmountOfTests
|
||||
);
|
||||
@@ -1,3 +0,0 @@
|
||||
// See https://aka.ms/new-console-template for more information
|
||||
|
||||
Console.WriteLine("Hello, World!");
|
||||
@@ -0,0 +1,52 @@
|
||||
using LiquidCode.Tester.Common.Models;
|
||||
using LiquidCode.Tester.Gateway.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace LiquidCode.Tester.Gateway.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class TesterController : ControllerBase
|
||||
{
|
||||
private readonly IPackageDownloadService _packageDownloadService;
|
||||
private readonly IWorkerClientService _workerClientService;
|
||||
private readonly ILogger<TesterController> _logger;
|
||||
|
||||
public TesterController(
|
||||
IPackageDownloadService packageDownloadService,
|
||||
IWorkerClientService workerClientService,
|
||||
ILogger<TesterController> logger)
|
||||
{
|
||||
_packageDownloadService = packageDownloadService;
|
||||
_workerClientService = workerClientService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpPost("submit")]
|
||||
public async Task<IActionResult> Submit([FromBody] SubmitForTesterModel request)
|
||||
{
|
||||
_logger.LogInformation("Received submit request for ID {SubmitId}", request.Id);
|
||||
|
||||
try
|
||||
{
|
||||
// Download the package
|
||||
var packagePath = await _packageDownloadService.DownloadPackageAsync(request.PackageUrl);
|
||||
|
||||
// Send to appropriate worker based on language
|
||||
await _workerClientService.SendToWorkerAsync(request, packagePath);
|
||||
|
||||
return Accepted(new { message = "Submit accepted for testing", submitId = request.Id });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to process submit {SubmitId}", request.Id);
|
||||
return StatusCode(500, new { error = "Failed to process submit", details = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("health")]
|
||||
public IActionResult Health()
|
||||
{
|
||||
return Ok(new { status = "healthy", timestamp = DateTime.UtcNow });
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,10 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.10"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\LiquidCode.Tester.Common\LiquidCode.Tester.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\.dockerignore">
|
||||
<Link>.dockerignore</Link>
|
||||
|
||||
@@ -1,41 +1,26 @@
|
||||
using LiquidCode.Tester.Gateway.Services;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
||||
// Add services to the container
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddOpenApi();
|
||||
|
||||
// Add HttpClient
|
||||
builder.Services.AddHttpClient();
|
||||
|
||||
// Register application services
|
||||
builder.Services.AddSingleton<IPackageDownloadService, PackageDownloadService>();
|
||||
builder.Services.AddSingleton<IWorkerClientService, WorkerClientService>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
// Configure the HTTP request pipeline
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.MapOpenApi();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
var summaries = new[]
|
||||
{
|
||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||
};
|
||||
|
||||
app.MapGet("/weatherforecast", () =>
|
||||
{
|
||||
var forecast = Enumerable.Range(1, 5).Select(index =>
|
||||
new WeatherForecast
|
||||
(
|
||||
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
|
||||
Random.Shared.Next(-20, 55),
|
||||
summaries[Random.Shared.Next(summaries.Length)]
|
||||
))
|
||||
.ToArray();
|
||||
return forecast;
|
||||
})
|
||||
.WithName("GetWeatherForecast");
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
|
||||
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
|
||||
{
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace LiquidCode.Tester.Gateway.Services;
|
||||
|
||||
public interface IPackageDownloadService
|
||||
{
|
||||
/// <summary>
|
||||
/// Downloads a package from the specified URL
|
||||
/// </summary>
|
||||
/// <param name="packageUrl">URL to download the package from</param>
|
||||
/// <returns>Path to the downloaded package file</returns>
|
||||
Task<string> DownloadPackageAsync(string packageUrl);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using LiquidCode.Tester.Common.Models;
|
||||
|
||||
namespace LiquidCode.Tester.Gateway.Services;
|
||||
|
||||
public interface IWorkerClientService
|
||||
{
|
||||
/// <summary>
|
||||
/// Sends a submit to the appropriate worker based on the language
|
||||
/// </summary>
|
||||
/// <param name="submit">Submit data</param>
|
||||
/// <param name="packagePath">Local path to the downloaded package</param>
|
||||
Task SendToWorkerAsync(SubmitForTesterModel submit, string packagePath);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
namespace LiquidCode.Tester.Gateway.Services;
|
||||
|
||||
public class PackageDownloadService : IPackageDownloadService
|
||||
{
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly ILogger<PackageDownloadService> _logger;
|
||||
private readonly string _downloadDirectory;
|
||||
|
||||
public PackageDownloadService(
|
||||
IHttpClientFactory httpClientFactory,
|
||||
ILogger<PackageDownloadService> logger,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_logger = logger;
|
||||
_downloadDirectory = configuration["PackageDownloadDirectory"] ?? Path.Combine(Path.GetTempPath(), "packages");
|
||||
|
||||
if (!Directory.Exists(_downloadDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(_downloadDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string> DownloadPackageAsync(string packageUrl)
|
||||
{
|
||||
_logger.LogInformation("Downloading package from {Url}", packageUrl);
|
||||
|
||||
try
|
||||
{
|
||||
var httpClient = _httpClientFactory.CreateClient();
|
||||
var response = await httpClient.GetAsync(packageUrl);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var fileName = $"package_{Guid.NewGuid()}.zip";
|
||||
var filePath = Path.Combine(_downloadDirectory, fileName);
|
||||
|
||||
await using var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
await response.Content.CopyToAsync(fileStream);
|
||||
|
||||
_logger.LogInformation("Package downloaded successfully to {Path}", filePath);
|
||||
return filePath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to download package from {Url}", packageUrl);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
using System.Net.Http.Headers;
|
||||
using LiquidCode.Tester.Common.Models;
|
||||
|
||||
namespace LiquidCode.Tester.Gateway.Services;
|
||||
|
||||
public class WorkerClientService : IWorkerClientService
|
||||
{
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly ILogger<WorkerClientService> _logger;
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public WorkerClientService(
|
||||
IHttpClientFactory httpClientFactory,
|
||||
ILogger<WorkerClientService> logger,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_logger = logger;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
public async Task SendToWorkerAsync(SubmitForTesterModel submit, string packagePath)
|
||||
{
|
||||
var workerUrl = GetWorkerUrlForLanguage(submit.Language);
|
||||
_logger.LogInformation("Sending submit {SubmitId} to worker at {WorkerUrl}", submit.Id, workerUrl);
|
||||
|
||||
try
|
||||
{
|
||||
var httpClient = _httpClientFactory.CreateClient();
|
||||
|
||||
using var form = new MultipartFormDataContent();
|
||||
|
||||
// Add submit metadata
|
||||
form.Add(new StringContent(submit.Id.ToString()), "Id");
|
||||
form.Add(new StringContent(submit.MissionId.ToString()), "MissionId");
|
||||
form.Add(new StringContent(submit.Language), "Language");
|
||||
form.Add(new StringContent(submit.LanguageVersion), "LanguageVersion");
|
||||
form.Add(new StringContent(submit.SourceCode), "SourceCode");
|
||||
form.Add(new StringContent(submit.CallbackUrl), "CallbackUrl");
|
||||
|
||||
// Add package file
|
||||
var fileStream = File.OpenRead(packagePath);
|
||||
var fileContent = new StreamContent(fileStream);
|
||||
fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
|
||||
form.Add(fileContent, "Package", Path.GetFileName(packagePath));
|
||||
|
||||
var response = await httpClient.PostAsync($"{workerUrl}/api/test", form);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
_logger.LogInformation("Submit {SubmitId} sent successfully to worker", submit.Id);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to send submit {SubmitId} to worker", submit.Id);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Clean up downloaded package
|
||||
try
|
||||
{
|
||||
if (File.Exists(packagePath))
|
||||
{
|
||||
File.Delete(packagePath);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to delete package file {Path}", packagePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetWorkerUrlForLanguage(string language)
|
||||
{
|
||||
var workerUrl = language.ToLowerInvariant() switch
|
||||
{
|
||||
"c++" => _configuration["Workers:Cpp"],
|
||||
"java" => _configuration["Workers:Java"],
|
||||
"kotlin" => _configuration["Workers:Kotlin"],
|
||||
"c#" => _configuration["Workers:CSharp"],
|
||||
_ => throw new NotSupportedException($"Language {language} is not supported")
|
||||
};
|
||||
|
||||
if (string.IsNullOrEmpty(workerUrl))
|
||||
{
|
||||
throw new InvalidOperationException($"Worker URL for language {language} is not configured");
|
||||
}
|
||||
|
||||
return workerUrl;
|
||||
}
|
||||
}
|
||||
@@ -4,5 +4,9 @@
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"PackageDownloadDirectory": "C:\\temp\\packages",
|
||||
"Workers": {
|
||||
"Cpp": "http://localhost:8081"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,5 +5,12 @@
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
"AllowedHosts": "*",
|
||||
"PackageDownloadDirectory": "/tmp/packages",
|
||||
"Workers": {
|
||||
"Cpp": "http://liquidcode-tester-worker-cpp:8080",
|
||||
"Java": "http://liquidcode-tester-worker-java:8080",
|
||||
"Kotlin": "http://liquidcode-tester-worker-kotlin:8080",
|
||||
"CSharp": "http://liquidcode-tester-worker-csharp:8080"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.10"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\LiquidCode.Tester.Common\LiquidCode.Tester.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\..\.dockerignore">
|
||||
<Link>.dockerignore</Link>
|
||||
|
||||
Reference in New Issue
Block a user