Skip to content

drQuintilis/executor-service

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Executor Service

Simple service for executing shell commands on dynamically provisioned Docker-based executors.

The service allows a user to:

  • send a command script to execute,
  • define required resources (cpuCount, memoryMb),
  • check execution status.

When a command is received, the service starts a dedicated executor, waits for it to be ready, runs the command, and updates the execution status.

Tech Stack

  • Java 17
  • Spring Boot 4 (Web MVC, Validation, Async)
  • Docker / Docker Compose
  • JUnit 5 + Mockito + MockMvc

Architecture

The same JAR runs in two modes depending on the active Spring profile:

  • Manager mode (default profile, port 8080)

    • exposes public API: POST /executions, GET /executions/{id}
    • starts one dedicated executor container per execution
    • tracks execution lifecycle in memory
  • Executor mode (executor profile, port 8081)

    • internal endpoints: GET /ready, POST /execute
    • executes shell command with bash -c ...

Execution flow:

  1. Client sends POST /executions.
  2. Manager creates execution with QUEUED status.
  3. Manager starts executor container with requested CPU/RAM; sets status to IN_PROGRESS and records startedAt.
  4. Manager polls executor /ready (up to 10 retries × 500 ms = 5 s max).
  5. Manager sends command to executor /execute (30-second command timeout; exit code 124 on timeout).
  6. Manager updates execution to FINISHED (exit code 0) or FAILED (non-zero / timeout / error) and records finishedAt.
  7. Manager stops executor container (always, even on failure).

API

1) Create execution

POST /executions

Request body:

{
  "command": "echo hello",
  "cpuCount": 2,
  "memoryMb": 512
}

Validation:

  • command: required, not blank
  • cpuCount: required, min 1
  • memoryMb: required, min 1

Response:

  • 201 Created

Example response:

{
  "id": "4f4eaf52-2f1d-4d89-bad3-7024a5b90f7a",
  "command": "echo hello",
  "cpuCount": 2,
  "memoryMb": 512,
  "status": "QUEUED",
  "output": null,
  "error": null,
  "createdAt": "2026-03-22T16:00:00",
  "startedAt": null,
  "finishedAt": null
}

2) Get execution

GET /executions/{id}

Response:

  • 200 OK with current execution snapshot,
  • 404 Not Found if ID does not exist.

Example (finished):

{
  "id": "4f4eaf52-2f1d-4d89-bad3-7024a5b90f7a",
  "command": "echo hello",
  "cpuCount": 2,
  "memoryMb": 512,
  "status": "FINISHED",
  "output": "hello\n",
  "error": null,
  "createdAt": "2026-03-22T16:00:00",
  "startedAt": "2026-03-22T16:00:01",
  "finishedAt": "2026-03-22T16:00:02"
}

Example (failed — non-zero exit code):

{
  "id": "7a1bcf03-9e2d-4f11-abc1-3318b4c70e52",
  "command": "exit 1",
  "cpuCount": 1,
  "memoryMb": 256,
  "status": "FAILED",
  "output": "",
  "error": "Command failed with exit code 1",
  "createdAt": "2026-03-22T16:05:00",
  "startedAt": "2026-03-22T16:05:01",
  "finishedAt": "2026-03-22T16:05:02"
}

The error field is populated from:

  • command stderr (if non-empty),
  • or "Command failed with exit code <N>" fallback,
  • or "Command timed out after 30 seconds" when the 30-second timeout is exceeded (exit code 124).

Execution Statuses

  • QUEUED
  • IN_PROGRESS
  • FINISHED
  • FAILED

Local Run

Prerequisites:

  • Docker Desktop / Docker Engine running
  • Java 17+

Start from scratch

docker compose down --remove-orphans
./mvnw clean package
docker compose up --build -d

Check logs

docker compose logs -f manager

Stop

docker compose down

Quick API Test

Create execution:

curl -X POST http://localhost:8080/executions \
  -H "Content-Type: application/json" \
  -d '{"command":"echo hello","cpuCount":2,"memoryMb":512}'

Read execution:

curl http://localhost:8080/executions/<execution-id>

Configuration

src/main/resources/application.properties:

  • executor.image=executor-service:latest
  • executor.network=executor-net

docker-compose.yml maps them via env vars:

  • EXECUTOR_IMAGE
  • EXECUTOR_NETWORK

Tests

Run all tests:

./mvnw test

Tests cover:

  • execution creation and async scheduling,
  • async success and failure paths,
  • readiness failure path,
  • controller validation (400),
  • controller HTTP contract (201, 404),
  • timestamps in API payload.

Timeouts

Timeout Value Notes
Executor readiness 5 s (10 × 500 ms) Manager retries /ready; fails execution if not ready in time
Command execution 30 s Executor force-kills the process; returns exit code 124
HTTP connect 2 s Manager -> executor connection
HTTP read 45 s Manager waiting for executor response (must exceed command timeout)

Notes

  • Execution data is kept in memory. No database, data is lost on restart.
  • Executor runs arbitrary shell commands, so this is intended for local/demo use only.
  • Don't expose executor endpoints (/ready, /execute) publicly.
  • Each execution gets its own container, removed automatically after the command finishes.

About

Recruitment task for JetBrains internship - Autoscaling of virtual machines in TeamCity Cloud.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors