add configs

This commit is contained in:
prixod
2025-10-24 23:55:10 +04:00
parent 6cead15a5f
commit dee93134ae
11 changed files with 648 additions and 45 deletions

51
.gitignore vendored
View File

@@ -1,5 +1,50 @@
bin/ ## Ignore Visual Studio temporary files, build results, and
obj/ ## files generated by popular Visual Studio add-ons.
/packages/
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio cache/options directory
.vs/
.vscode/
# Rider
.idea/
*.sln.iml
riderModule.iml riderModule.iml
/_ReSharper.Caches/ /_ReSharper.Caches/
# NuGet Packages
*.nupkg
**/packages/*
!**/packages/build/
# Docker
docker-compose.override.yml
# Temporary files
*.tmp
*.log
*.swp
*~
# OS files
.DS_Store
Thumbs.db

237
README.md Normal file
View File

@@ -0,0 +1,237 @@
# LiquidCode Tester
Система автоматической проверки решений задач по программированию (Online Judge) для платформы LiquidCode.
## Архитектура
Проект состоит из трёх основных компонентов:
### 1. Gateway (Шлюз)
- **Технология**: ASP.NET Core Web API
- **Функции**:
- Приём запросов на тестирование через REST API
- Скачивание пакетов задач из Polygon
- Маршрутизация запросов к соответствующим Worker'ам по языку программирования
### 2. Worker (Тестировщик)
- **Технология**: ASP.NET Core Web API + компиляторы языков
- **Функции**:
- Компиляция пользовательского кода
- Запуск в изолированной среде
- Тестирование на наборе тестов
- Контроль ограничений по времени и памяти
- Отправка статусов на callback URL
### 3. Common (Общая библиотека)
- **Технология**: .NET Class Library
- **Функции**: Общие модели данных для Gateway и Worker
## Поддерживаемые языки
- **C++** (текущая реализация)
- Java (планируется)
- Kotlin (планируется)
- C# (планируется)
## Модель данных
### SubmitForTesterModel (запрос на тестирование)
```csharp
public record SubmitForTesterModel(
long Id,
long MissionId,
string Language,
string LanguageVersion,
string SourceCode,
string PackageUrl,
string CallbackUrl
);
```
### TesterResponseModel (результат тестирования)
```csharp
public record TesterResponseModel(
long SubmitId,
State State, // Waiting, Compiling, Testing, Done
ErrorCode ErrorCode, // None, CompileError, RuntimeError, etc.
string Message,
int CurrentTest,
int AmountOfTests
);
```
## Быстрый старт
### Локальная разработка (Docker Compose)
```bash
# Сборка и запуск всех сервисов
docker-compose up --build
# Gateway доступен на http://localhost:8080
# Worker доступен на http://localhost:8081
```
### Разработка без Docker
```bash
# Сборка всего решения
dotnet build
# Запуск Gateway
cd src/LiquidCode.Tester.Gateway
dotnet run
# Запуск Worker (в другом терминале)
cd src/LiquidCode.Tester.Worker
dotnet run
```
## API Endpoints
### Gateway
**POST /api/tester/submit**
```json
{
"id": 123,
"missionId": 456,
"language": "C++",
"languageVersion": "17",
"sourceCode": "#include <iostream>\nint main() { ... }",
"packageUrl": "https://example.com/package.zip",
"callbackUrl": "https://example.com/api/submit/callback"
}
```
**GET /api/tester/health**
- Проверка состояния Gateway
### Worker
**POST /api/test**
- Принимает multipart/form-data с метаданными и ZIP файлом пакета
**GET /api/test/health**
- Проверка состояния Worker
## Процесс тестирования
1. **Приём запроса**: Gateway получает запрос с кодом и URL пакета
2. **Скачивание пакета**: Gateway скачивает Polygon пакет
3. **Маршрутизация**: Gateway отправляет запрос в Worker для нужного языка
4. **Парсинг**: Worker распаковывает и парсит пакет (тесты, лимиты)
5. **Компиляция**: Worker компилирует код (g++ для C++)
6. **Тестирование**: Worker последовательно запускает все тесты
7. **Callback**: Worker отправляет статусы на callback URL на каждом этапе
8. **Cleanup**: Worker удаляет временные файлы
## Изоляция и безопасность
### Текущая реализация
- Процессы запускаются с ограничением по времени
- Контроль памяти через PeakWorkingSet64
- Процессы убиваются при превышении лимитов
### Production рекомендации
- Linux namespaces (PID, network, mount)
- cgroups для контроля CPU и памяти
- seccomp для ограничения системных вызовов
- AppArmor/SELinux профили
- chroot окружение
- Отключение доступа к сети для тестируемого кода
## Деплой в Kubernetes
См. [k8s/README.md](k8s/README.md) для подробной инструкции.
```bash
# Быстрый деплой
kubectl apply -f k8s/namespace.yaml
kubectl apply -f k8s/configmap.yaml
kubectl apply -f k8s/worker-cpp-deployment.yaml
kubectl apply -f k8s/gateway-deployment.yaml
```
## Конфигурация
### Gateway (appsettings.json)
```json
{
"PackageDownloadDirectory": "/tmp/packages",
"Workers": {
"Cpp": "http://liquidcode-tester-worker-cpp:8080"
}
}
```
### Worker (appsettings.json)
```json
{
"Cpp": {
"Compiler": "g++",
"CompilerFlags": "-O2 -std=c++17 -Wall"
}
}
```
## Структура проекта
```
LiquidCode.Tester/
├── src/
│ ├── LiquidCode.Tester.Common/ # Общие модели
│ │ └── Models/
│ ├── LiquidCode.Tester.Gateway/ # API Gateway
│ │ ├── Controllers/
│ │ ├── Services/
│ │ └── Dockerfile
│ └── LiquidCode.Tester.Worker/ # C++ Worker
│ ├── Controllers/
│ ├── Services/
│ └── Dockerfile
├── k8s/ # Kubernetes манифесты
├── compose.yaml # Docker Compose
└── README.md
```
## Требования
- .NET 9.0 SDK
- Docker & Docker Compose (для контейнеризации)
- g++ (для C++ Worker)
- Kubernetes cluster (для production)
## Расширение на другие языки
Для добавления поддержки нового языка:
1. Создайте новый Worker проект (или расширьте существующий)
2. Реализуйте `ICompilationService` для языка
3. Реализуйте `IExecutionService` для языка
4. Обновите конфигурацию Gateway
5. Создайте Dockerfile с нужным компилятором/runtime
6. Добавьте Kubernetes манифесты
## Разработка
### Структура кода
- **Controllers**: REST API endpoints
- **Services**: Бизнес-логика (компиляция, тестирование, callback)
- **Models**: Модели данных
### Логирование
Используется встроенный ILogger ASP.NET Core.
### Тестирование
```bash
dotnet test
```
## Лицензия
[Укажите лицензию]
## Авторы
LiquidCode Team

View File

@@ -1,19 +1,42 @@
services: services:
liquidcode.tester.gateway: gateway:
image: liquidcode.tester.gateway image: liquidcode-tester-gateway:latest
container_name: liquidcode-tester-gateway
build: build:
context: . context: .
dockerfile: LiquidCode.Tester.Gateway/Dockerfile dockerfile: src/LiquidCode.Tester.Gateway/Dockerfile
ports:
- "8080:8080"
environment:
- ASPNETCORE_ENVIRONMENT=Development
- Workers__Cpp=http://worker-cpp:8080
networks:
- liquidcode-network
depends_on:
- worker-cpp
liquidcode.tester.worker: worker-cpp:
image: liquidcode.tester.worker image: liquidcode-tester-worker-cpp:latest
container_name: liquidcode-tester-worker-cpp
build: build:
context: . context: .
dockerfile: src/LiquidCode.Tester.Worker/Dockerfile dockerfile: src/LiquidCode.Tester.Worker/Dockerfile
ports:
- "8081:8080"
environment:
- ASPNETCORE_ENVIRONMENT=Development
networks:
- liquidcode-network
# For better isolation in production, consider:
# security_opt:
# - no-new-privileges:true
# cap_drop:
# - ALL
# cap_add:
# - SETUID
# - SETGID
liquidcode.tester.common: networks:
image: liquidcode.tester.common liquidcode-network:
build: driver: bridge
context: .
dockerfile: src/LiquidCode.Tester.Common/Dockerfile

92
k8s/README.md Normal file
View File

@@ -0,0 +1,92 @@
# Kubernetes Deployment
## Prerequisites
- Kubernetes cluster (minikube, kind, or cloud provider)
- kubectl configured
- Docker images built and available
## Building Docker Images
```bash
# Build Gateway image
docker build -t liquidcode-tester-gateway:latest -f src/LiquidCode.Tester.Gateway/Dockerfile .
# Build C++ Worker image
docker build -t liquidcode-tester-worker-cpp:latest -f src/LiquidCode.Tester.Worker/Dockerfile .
```
## Deploying to Kubernetes
```bash
# Create namespace
kubectl apply -f k8s/namespace.yaml
# Apply ConfigMap
kubectl apply -f k8s/configmap.yaml
# Deploy Worker (must be deployed first)
kubectl apply -f k8s/worker-cpp-deployment.yaml
# Deploy Gateway
kubectl apply -f k8s/gateway-deployment.yaml
```
## Checking Status
```bash
# Check all resources
kubectl get all -n liquidcode-tester
# Check pods
kubectl get pods -n liquidcode-tester
# Check services
kubectl get services -n liquidcode-tester
# View logs
kubectl logs -n liquidcode-tester -l app=gateway
kubectl logs -n liquidcode-tester -l app=worker-cpp
```
## Access the Gateway
```bash
# Get the external IP (for LoadBalancer)
kubectl get service liquidcode-tester-gateway -n liquidcode-tester
# For minikube
minikube service liquidcode-tester-gateway -n liquidcode-tester
# Port forward (alternative)
kubectl port-forward -n liquidcode-tester service/liquidcode-tester-gateway 8080:80
```
## Scaling Workers
```bash
# Scale C++ workers
kubectl scale deployment liquidcode-tester-worker-cpp -n liquidcode-tester --replicas=5
```
## Cleanup
```bash
# Delete all resources
kubectl delete namespace liquidcode-tester
```
## Production Considerations
1. **Image Registry**: Push images to a container registry (Docker Hub, GCR, ECR, etc.)
2. **Resource Limits**: Adjust CPU/Memory limits based on workload
3. **Persistent Storage**: Add PersistentVolumes for package storage if needed
4. **Monitoring**: Add Prometheus/Grafana for metrics
5. **Logging**: Configure centralized logging (ELK, Loki, etc.)
6. **Security**:
- Use NetworkPolicies to restrict traffic
- Enable Pod Security Standards
- Use secrets for sensitive data
- Consider using a service mesh (Istio, Linkerd)
7. **Autoscaling**: Configure HorizontalPodAutoscaler for workers
8. **Ingress**: Use Ingress controller instead of LoadBalancer for production

37
k8s/configmap.yaml Normal file
View File

@@ -0,0 +1,37 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: liquidcode-tester-config
namespace: liquidcode-tester
data:
gateway.appsettings.json: |
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"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"
}
}
worker.appsettings.json: |
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Cpp": {
"Compiler": "g++",
"CompilerFlags": "-O2 -std=c++17 -Wall"
}
}

View File

@@ -0,0 +1,63 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: liquidcode-tester-gateway
namespace: liquidcode-tester
labels:
app: gateway
spec:
replicas: 2
selector:
matchLabels:
app: gateway
template:
metadata:
labels:
app: gateway
spec:
containers:
- name: gateway
image: liquidcode-tester-gateway:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
name: http
env:
- name: ASPNETCORE_ENVIRONMENT
value: "Production"
- name: Workers__Cpp
value: "http://liquidcode-tester-worker-cpp:8080"
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /api/tester/health
port: 8080
initialDelaySeconds: 10
periodSeconds: 30
readinessProbe:
httpGet:
path: /api/tester/health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: liquidcode-tester-gateway
namespace: liquidcode-tester
spec:
type: LoadBalancer
selector:
app: gateway
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: http

4
k8s/namespace.yaml Normal file
View File

@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: liquidcode-tester

View File

@@ -0,0 +1,82 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: liquidcode-tester-worker-cpp
namespace: liquidcode-tester
labels:
app: worker-cpp
language: cpp
spec:
replicas: 3
selector:
matchLabels:
app: worker-cpp
template:
metadata:
labels:
app: worker-cpp
language: cpp
spec:
containers:
- name: worker-cpp
image: liquidcode-tester-worker-cpp:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
name: http
env:
- name: ASPNETCORE_ENVIRONMENT
value: "Production"
- name: Cpp__Compiler
value: "g++"
- name: Cpp__CompilerFlags
value: "-O2 -std=c++17 -Wall"
resources:
requests:
memory: "256Mi"
cpu: "200m"
limits:
memory: "2Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /api/test/health
port: 8080
initialDelaySeconds: 10
periodSeconds: 30
readinessProbe:
httpGet:
path: /api/test/health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
# Security context for isolation
securityContext:
allowPrivilegeEscalation: false
runAsNonRoot: true
runAsUser: 1000
capabilities:
drop:
- ALL
readOnlyRootFilesystem: false
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: liquidcode-tester-worker-cpp
namespace: liquidcode-tester
spec:
type: ClusterIP
selector:
app: worker-cpp
ports:
- port: 8080
targetPort: 8080
protocol: TCP
name: http

View File

@@ -1,21 +0,0 @@
FROM mcr.microsoft.com/dotnet/runtime:9.0 AS base
USER $APP_UID
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["src/LiquidCode.Tester.Common/LiquidCode.Tester.Common.csproj", "src/LiquidCode.Tester.Common/"]
RUN dotnet restore "src/LiquidCode.Tester.Common/LiquidCode.Tester.Common.csproj"
COPY . .
WORKDIR "/src/src/LiquidCode.Tester.Common"
RUN dotnet build "./LiquidCode.Tester.Common.csproj" -c $BUILD_CONFIGURATION -o /app/build
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./LiquidCode.Tester.Common.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "LiquidCode.Tester.Common.dll"]

View File

@@ -1,16 +1,25 @@
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
USER $APP_UID
WORKDIR /app WORKDIR /app
EXPOSE 8080 EXPOSE 8080
EXPOSE 8081
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
ARG BUILD_CONFIGURATION=Release ARG BUILD_CONFIGURATION=Release
WORKDIR /src WORKDIR /src
COPY ["LiquidCode.Tester.Gateway/LiquidCode.Tester.Gateway.csproj", "LiquidCode.Tester.Gateway/"]
RUN dotnet restore "LiquidCode.Tester.Gateway/LiquidCode.Tester.Gateway.csproj" # Copy Common project
COPY ["src/LiquidCode.Tester.Common/LiquidCode.Tester.Common.csproj", "src/LiquidCode.Tester.Common/"]
# Copy Gateway project
COPY ["src/LiquidCode.Tester.Gateway/LiquidCode.Tester.Gateway.csproj", "src/LiquidCode.Tester.Gateway/"]
# Restore dependencies
RUN dotnet restore "src/LiquidCode.Tester.Gateway/LiquidCode.Tester.Gateway.csproj"
# Copy all source files
COPY . . COPY . .
WORKDIR "/src/LiquidCode.Tester.Gateway"
# Build
WORKDIR "/src/src/LiquidCode.Tester.Gateway"
RUN dotnet build "./LiquidCode.Tester.Gateway.csproj" -c $BUILD_CONFIGURATION -o /app/build RUN dotnet build "./LiquidCode.Tester.Gateway.csproj" -c $BUILD_CONFIGURATION -o /app/build
FROM build AS publish FROM build AS publish
@@ -20,4 +29,10 @@ RUN dotnet publish "./LiquidCode.Tester.Gateway.csproj" -c $BUILD_CONFIGURATION
FROM base AS final FROM base AS final
WORKDIR /app WORKDIR /app
COPY --from=publish /app/publish . COPY --from=publish /app/publish .
# Create directory for package downloads
RUN mkdir -p /tmp/packages
ENV ASPNETCORE_URLS=http://+:8080
ENTRYPOINT ["dotnet", "LiquidCode.Tester.Gateway.dll"] ENTRYPOINT ["dotnet", "LiquidCode.Tester.Gateway.dll"]

View File

@@ -1,21 +1,47 @@
FROM mcr.microsoft.com/dotnet/runtime:9.0 AS base # Build stage
USER $APP_UID
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
ARG BUILD_CONFIGURATION=Release ARG BUILD_CONFIGURATION=Release
WORKDIR /src WORKDIR /src
# Copy Common project
COPY ["src/LiquidCode.Tester.Common/LiquidCode.Tester.Common.csproj", "src/LiquidCode.Tester.Common/"]
# Copy Worker project
COPY ["src/LiquidCode.Tester.Worker/LiquidCode.Tester.Worker.csproj", "src/LiquidCode.Tester.Worker/"] COPY ["src/LiquidCode.Tester.Worker/LiquidCode.Tester.Worker.csproj", "src/LiquidCode.Tester.Worker/"]
# Restore dependencies
RUN dotnet restore "src/LiquidCode.Tester.Worker/LiquidCode.Tester.Worker.csproj" RUN dotnet restore "src/LiquidCode.Tester.Worker/LiquidCode.Tester.Worker.csproj"
# Copy all source files
COPY . . COPY . .
# Build
WORKDIR "/src/src/LiquidCode.Tester.Worker" WORKDIR "/src/src/LiquidCode.Tester.Worker"
RUN dotnet build "./LiquidCode.Tester.Worker.csproj" -c $BUILD_CONFIGURATION -o /app/build RUN dotnet build "./LiquidCode.Tester.Worker.csproj" -c $BUILD_CONFIGURATION -o /app/build
# Publish stage
FROM build AS publish FROM build AS publish
ARG BUILD_CONFIGURATION=Release ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./LiquidCode.Tester.Worker.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false RUN dotnet publish "./LiquidCode.Tester.Worker.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
FROM base AS final # Final stage - use aspnet runtime with C++ compiler
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS final
WORKDIR /app WORKDIR /app
# Install C++ compiler and build tools
RUN apt-get update && \
apt-get install -y --no-install-recommends \
g++ \
gcc \
make \
&& rm -rf /var/lib/apt/lists/*
# Copy published app
COPY --from=publish /app/publish . COPY --from=publish /app/publish .
# Create temp directory for compilation and testing
RUN mkdir -p /tmp/testing
ENV ASPNETCORE_URLS=http://+:8080
ENTRYPOINT ["dotnet", "LiquidCode.Tester.Worker.dll"] ENTRYPOINT ["dotnet", "LiquidCode.Tester.Worker.dll"]