Skip to content

Runner Microservice

The Runner microservice executes scripts for digital twins in the DTaaS platform. It accepts HTTP requests to run pre-configured commands and returns the execution status and logs to the caller.

Package Structure

servers/execution/runner/
├── src/                           # Application source code
│   ├── main.ts                    # Application entry point
│   ├── app.module.ts              # Root NestJS module
│   ├── app.controller.ts          # REST API controller
│   ├── execa-manager.service.ts   # Command execution manager
│   ├── execa-runner.ts            # Command runner implementation
│   ├── queue.service.ts           # Command queue service
│   ├── runner-factory.service.ts  # Runner instance factory
│   ├── validation.pipe.ts         # Request validation pipe
│   ├── config/                    # Configuration module
│   │   ├── commander.ts           # CLI argument parsing
│   │   ├── config.interface.ts    # Configuration interface
│   │   ├── configuration.service.ts  # Configuration service
│   │   └── util.ts                # Configuration utilities
│   ├── dto/                       # Data transfer objects
│   │   └── command.dto.ts         # Command request DTO
│   └── interfaces/                # Interface definitions
│       ├── command.interface.ts   # Command types
│       └── runner.interface.ts    # Runner interface
├── api/                           # API documentation
├── config/                        # Sample configuration files
├── test/                          # Test suites
└── dist/                          # Compiled output

Architecture and Design

The microservice is built with the NestJS framework and runs commands with the execa library. A queue holds the execution requests, and a factory creates the runner instances.

Core Components

classDiagram
    class AppController {
        -manager: ExecaManager
        +getHistory(): ExecuteCommandDto[]
        +newCommand(dto): void
        +cmdStatus(): CommandStatus
    }

    class ExecaManager {
        -commandQueue: Queue
        -config: Config
        +newCommand(name): [boolean, Map]
        +checkStatus(): CommandStatus
        +checkHistory(): ExecuteCommandDto[]
    }

    class Queue {
        -queue: Command[]
        +enqueue(command): void
        +checkHistory(): ExecuteCommandDto[]
        +activeCommand(): Command
    }

    class RunnerFactory {
        +create(command): Runner
    }

    class ExecaRunner {
        -command: string
        -stdout: string
        -stderr: string
        +run(): Promise~boolean~
        +checkLogs(): Map
    }

    class Config {
        -configValues: ConfigValues
        +loadConfig(options): void
        +permitCommands(): string[]
        +getPort(): number
        +getLocation(): string
    }

    AppController --> ExecaManager : uses
    ExecaManager --> Queue : uses
    ExecaManager --> Config : uses
    ExecaManager --> RunnerFactory : uses
    RunnerFactory --> ExecaRunner : creates

Main Components

REST API Controller

The AppController exposes three endpoints:

Endpoint Method Purpose
/ POST Submit a new command for execution
/ GET Get status of the current command
/history GET Retrieve execution history

Execution Manager

The ExecaManager service runs each command in these steps:

  1. Adds the command to the queue, marked invalid.
  2. Checks the command against the list of permitted commands.
  3. For a permitted command, creates a runner through the factory and runs the script from the configured location.
  4. Marks the command valid if the script succeeds, and returns the status and logs.

Command Queue

The Queue service keeps the execution history and tracks the active command. Each command is stored with its execution status (valid or invalid).

Runner Interface

The Runner interface defines the contract for command executors:

1
2
3
4
interface Runner {
  run(): Promise<boolean>;
  checkLogs(): Map<string, string>;
}

Configuration

The service reads its configuration from a YAML file (see config/runner.yaml.sample) with these settings:

Setting Purpose
port HTTP server port
location Base directory for executable scripts
commands Allowlist of permitted command names

Security Considerations

The runner only executes commands that are listed in the configuration file. A request for any other command is recorded as invalid and not run, which prevents arbitrary command execution.