diff --git a/AutoClientCenter/AutoClientCenter.csproj b/AutoClientCenter/AutoClientCenter.csproj new file mode 100644 index 0000000..d5e2255 --- /dev/null +++ b/AutoClientCenter/AutoClientCenter.csproj @@ -0,0 +1,16 @@ + + + + net7.0 + enable + enable + 80f55fc5-9f8f-4bb4-89bc-314ed7379c5f + Linux + + + + + + + + diff --git a/AutoClientCenter/AutoClientCenter.csproj.user b/AutoClientCenter/AutoClientCenter.csproj.user new file mode 100644 index 0000000..dd2d54c --- /dev/null +++ b/AutoClientCenter/AutoClientCenter.csproj.user @@ -0,0 +1,6 @@ + + + + Container (Dockerfile) + + \ No newline at end of file diff --git a/AutoClientCenter/AutoClientCenter.http b/AutoClientCenter/AutoClientCenter.http new file mode 100644 index 0000000..251d650 --- /dev/null +++ b/AutoClientCenter/AutoClientCenter.http @@ -0,0 +1,6 @@ +@AutoClientCenter_HostAddress = http://localhost:5185 + +GET {{AutoClientCenter_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/AutoClientCenter/Controllers/ConfigController.cs b/AutoClientCenter/Controllers/ConfigController.cs new file mode 100644 index 0000000..e259ae5 --- /dev/null +++ b/AutoClientCenter/Controllers/ConfigController.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Mvc; + +namespace AutoClientCenter.Controllers +{ + [ApiController] + [Route("[controller]")] + public class ConfigController : ControllerBase + { + private readonly ITaskService taskService; + + public ConfigController(ITaskService taskService) + { + this.taskService = taskService; + } + + [HttpGet("Stats")] + public AcStats Get() + { + return taskService.GetStats(); + } + + [HttpPost("Set")] + public void Post([FromBody] AcTasks tasks) + { + taskService.SetConfig(tasks); + } + } +} diff --git a/AutoClientCenter/Controllers/TasksController.cs b/AutoClientCenter/Controllers/TasksController.cs new file mode 100644 index 0000000..9b5e184 --- /dev/null +++ b/AutoClientCenter/Controllers/TasksController.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Mvc; + +namespace AutoClientCenter.Controllers +{ + [ApiController] + [Route("[controller]")] + public class TasksController : ControllerBase + { + private readonly ITaskService taskService; + private static readonly object processLock = new object(); + + public TasksController(ITaskService taskService) + { + this.taskService = taskService; + } + + [HttpGet] + public AcTasks Get() + { + return taskService.GetTasks(); + } + + [HttpPost("Results")] + public void Post([FromBody] AcTaskStep[] taskSteps) + { + Task.Run(() => + { + lock (processLock) + { + taskService.ProcessResults(taskSteps); + } + }); + } + } +} diff --git a/AutoClientCenter/Dockerfile b/AutoClientCenter/Dockerfile new file mode 100644 index 0000000..4f8996e --- /dev/null +++ b/AutoClientCenter/Dockerfile @@ -0,0 +1,25 @@ +#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +USER app +WORKDIR /app +EXPOSE 8080 +EXPOSE 8081 + +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +COPY ["AutoClientCenter/AutoClientCenter.csproj", "AutoClientCenter/"] +RUN dotnet restore "./AutoClientCenter/AutoClientCenter.csproj" +COPY . . +WORKDIR "/src/AutoClientCenter" +RUN dotnet build "./AutoClientCenter.csproj" -c $BUILD_CONFIGURATION -o /app/build + +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "./AutoClientCenter.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "AutoClientCenter.dll"] \ No newline at end of file diff --git a/AutoClientCenter/Model.cs b/AutoClientCenter/Model.cs new file mode 100644 index 0000000..e37ef61 --- /dev/null +++ b/AutoClientCenter/Model.cs @@ -0,0 +1,63 @@ +namespace AutoClientCenter +{ + public class AcTasks + { + public TimeSpan StartTaskEvery { get; set; } = TimeSpan.FromHours(6); + public AcTask[] Tasks { get; set; } = Array.Empty(); + } + + public class AcTask + { + public int ChanceWeight { get; set; } + public AcTaskStep[] Steps { get; set; } = Array.Empty(); + } + + public class AcTaskStep + { + public string Id { get; set; } = string.Empty; + public AcUploadStep? UploadStep { get; set; } + public AcStoreStep? StoreStep { get; set; } + public AcDownloadStep? DownloadStep { get; set; } + public string? ResultErrorMsg { get; set; } + } + + public class AcUploadStep + { + public long SizeInBytes { get; set; } + public string? ResultCid { get; set; } + } + + public class AcStoreStep + { + public int ContractDurationMinutes { get; set; } + public int ContractExpiryMinutes { get; set; } + public int NumHosts { get; set; } + public int HostTolerance { get; set; } + public int Price { get; set; } + public int RequiredCollateral { get; set; } + public string? ResultPurchaseId { get; set; } + public string? ResultCid { get; set; } + } + + public class AcDownloadStep + { + public string[] Cids { get; set; } = Array.Empty(); + public long[] ResultDownloadTimeSeconds { get; set; } = Array.Empty(); + } + + public class AcStats + { + public int NumberOfAutoClients { get; set; } todo send client peerId + + public DateTime ServiceStartUtc { get; set; } = DateTime.MinValue; + public int TotalUploads { get; set; } + public int TotalUploadsFailed { get; set; } + public int TotalDownloads { get; set; } + public long[] DownloadTimesSeconds { get; set; } = Array.Empty(); + public int TotalDownloadsFailed { get; set; } + public int TotalContractsStarted { get; set; } + public int TotalContractsCompleted { get; set; } + public int TotalContractsExpired { get; set; } + public int TotalContractsFailed { get; set; } + } +} diff --git a/AutoClientCenter/Program.cs b/AutoClientCenter/Program.cs new file mode 100644 index 0000000..c2aeec7 --- /dev/null +++ b/AutoClientCenter/Program.cs @@ -0,0 +1,33 @@ + +namespace AutoClientCenter +{ + public class Program + { + public static void Main(string[] args) + { + var builder = WebApplication.CreateBuilder(args); + + builder.Services.AddSingleton(new TaskService()); + builder.Services.AddControllers(); + builder.Services.AddEndpointsApiExplorer(); + builder.Services.AddSwaggerGen(); + + var app = builder.Build(); + + if (app.Environment.IsDevelopment()) + { + app.UseSwagger(); + app.UseSwaggerUI(); + } + + app.UseHttpsRedirection(); + + app.UseAuthorization(); + + + app.MapControllers(); + + app.Run(); + } + } +} diff --git a/AutoClientCenter/Properties/launchSettings.json b/AutoClientCenter/Properties/launchSettings.json new file mode 100644 index 0000000..4f58636 --- /dev/null +++ b/AutoClientCenter/Properties/launchSettings.json @@ -0,0 +1,52 @@ +{ + "profiles": { + "http": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5185" + }, + "https": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "https://localhost:7077;http://localhost:5185" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Container (Dockerfile)": { + "commandName": "Docker", + "launchBrowser": true, + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", + "environmentVariables": { + "ASPNETCORE_HTTPS_PORTS": "8081", + "ASPNETCORE_HTTP_PORTS": "8080" + }, + "publishAllPorts": true, + "useSSL": true + } + }, + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:50475", + "sslPort": 44395 + } + } +} \ No newline at end of file diff --git a/AutoClientCenter/TaskService.cs b/AutoClientCenter/TaskService.cs new file mode 100644 index 0000000..59aa490 --- /dev/null +++ b/AutoClientCenter/TaskService.cs @@ -0,0 +1,56 @@ +namespace AutoClientCenter +{ + public interface ITaskService + { + AcStats GetStats(); + AcTasks GetTasks(); + void ProcessResults(AcTaskStep[] taskSteps); + void SetConfig(AcTasks tasks); + } + + public class TaskService : ITaskService + { + private readonly List downloadTimes = new List(); + private readonly AcStats stats = new AcStats + { + ServiceStartUtc = DateTime.UtcNow, + }; + + private AcTasks tasks = new AcTasks + { + StartTaskEvery = TimeSpan.FromHours(8), + Tasks = Array.Empty() + }; + + public AcStats GetStats() + { + return stats; + } + + public AcTasks GetTasks() + { + return tasks; + } + + public void SetConfig(AcTasks newTasks) + { + if (newTasks.StartTaskEvery < TimeSpan.FromMinutes(10)) return; + foreach (var task in newTasks.Tasks) + { + if (task.ChanceWeight < 1) return; + foreach (var step in task.Steps) + { + if (string.IsNullOrWhiteSpace(step.Id)) return; + if (step.UploadStep == null && step.StoreStep == null && step.DownloadStep == null) return; + } + } + + tasks = newTasks; + } + + public void ProcessResults(AcTaskStep[] taskSteps) + { + throw new NotImplementedException(); + } + } +} diff --git a/AutoClientCenter/appsettings.Development.json b/AutoClientCenter/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/AutoClientCenter/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/AutoClientCenter/appsettings.json b/AutoClientCenter/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/AutoClientCenter/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/ProjectPlugins/CodexPlugin/CodexContainerRecipe.cs b/ProjectPlugins/CodexPlugin/CodexContainerRecipe.cs index 5e84883..3ba8ee5 100644 --- a/ProjectPlugins/CodexPlugin/CodexContainerRecipe.cs +++ b/ProjectPlugins/CodexPlugin/CodexContainerRecipe.cs @@ -7,7 +7,7 @@ namespace CodexPlugin { public class CodexContainerRecipe : ContainerRecipeFactory { - private const string DefaultDockerImage = "codexstorage/nim-codex:sha-64b82de-dist-tests"; + private const string DefaultDockerImage = "codexstorage/nim-codex:sha-1e2ad95-dist-tests"; public const string ApiPortTag = "codex_api_port"; public const string ListenPortTag = "codex_listen_port"; diff --git a/cs-codex-dist-testing.sln b/cs-codex-dist-testing.sln index 89a0f79..adbea6d 100644 --- a/cs-codex-dist-testing.sln +++ b/cs-codex-dist-testing.sln @@ -74,7 +74,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OverwatchTranscript", "Fram EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TranscriptAnalysis", "Tools\TranscriptAnalysis\TranscriptAnalysis.csproj", "{C0EEBD32-23CB-45EC-A863-79FB948508C8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MarketInsights", "Tools\MarketInsights\MarketInsights.csproj", "{004614DF-1C65-45E3-882D-59AE44282573}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MarketInsights", "Tools\MarketInsights\MarketInsights.csproj", "{004614DF-1C65-45E3-882D-59AE44282573}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoClientCenter", "AutoClientCenter\AutoClientCenter.csproj", "{023B6C60-406B-47A1-9ED8-12688759D2CC}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -202,6 +204,10 @@ Global {004614DF-1C65-45E3-882D-59AE44282573}.Debug|Any CPU.Build.0 = Debug|Any CPU {004614DF-1C65-45E3-882D-59AE44282573}.Release|Any CPU.ActiveCfg = Release|Any CPU {004614DF-1C65-45E3-882D-59AE44282573}.Release|Any CPU.Build.0 = Release|Any CPU + {023B6C60-406B-47A1-9ED8-12688759D2CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023B6C60-406B-47A1-9ED8-12688759D2CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023B6C60-406B-47A1-9ED8-12688759D2CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023B6C60-406B-47A1-9ED8-12688759D2CC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -237,6 +243,7 @@ Global {870DDFBE-D7ED-4196-9681-13CA947BDEA6} = {81AE04BC-CBFA-4E6F-B039-8208E9AFAAE7} {C0EEBD32-23CB-45EC-A863-79FB948508C8} = {7591C5B3-D86E-4AE4-8ED2-B272D17FE7E3} {004614DF-1C65-45E3-882D-59AE44282573} = {7591C5B3-D86E-4AE4-8ED2-B272D17FE7E3} + {023B6C60-406B-47A1-9ED8-12688759D2CC} = {7591C5B3-D86E-4AE4-8ED2-B272D17FE7E3} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {237BF0AA-9EC4-4659-AD9A-65DEB974250C}