Service as a Service API#
graph TD
%% Main Service entity
S[Service]
%% Target components
NT[NodeTarget]
NST[NodeSetTarget]
%% Service types
ST[ServiceType]
SS[Simple]
SO[Oneshot]
SM[Monopoly]
SMO[MonopolyOneshot]
%% Dependencies
D1[Before/After Dependencies]
CS[CmdShell]
ST2["ServiceTarget (TBD)"]
%% Relationships
S -->|deploys to| NT
S -->|deploys to| NST
S -->|has type| ST
ST -->|can be| SS
ST -->|can be| SO
ST -->|can be| SM
ST -->|can be| SMO
S -->|has| D1
D1 -->|can be| CS
D1 -->|can be| ST2
%% Legend
class S main
class NT,NST target
class SS,SO,SM,SMO type
class CS,ST2 dependency
Overview#
The Service resource provides a REST-based API for creating and managing systemd services across compute nodes. It enables declarative service management with support for different service types, dependencies, and lifecycle management.
Core Components#
Service: The main service entity that defines a systemd service configurationServiceType: Defines the service behavior (simple, oneshot, monopoly, monopoly_oneshot)Target: Specifies where to deploy the service (node or node_set)Before/After: Dependencies that run before or after the service (shell commands or other services)ServiceNode: The actual deployed service instance on a specific node
Service#
The main service entity that manages:
- Status: NEW, IN_PROGRESS, ACTIVE, ERROR
- Target Status: enabled, disabled
- Name: Service identifier (alphanumeric, underscores, hyphens)
- Path: The command or script to execute
- User/Group: Execution context (defaults to root)
Service Types#
| Type | Description | Use Case |
|---|---|---|
simple |
Regular service with configurable instance count | Long-running services like web servers |
oneshot |
One-time execution service | Initialization scripts, migrations |
monopoly |
Only one instance across all nodes | Singleton services like schedulers |
monopoly_oneshot |
One-time execution, only one instance | One-time initialization tasks |
Target Types#
node: Deploy service to a specific compute nodenode_set: Deploy service to all nodes in a node set
Dependencies#
Services can define dependencies that run before or after the main service:
- CmdShell: Execute a shell command
command: The shell command to execute
- ServiceTarget (TBD): Reference another service for ordering (currently disabled, service relationships are being reworked)
API Structure#
Creating a Service#
Simple Service#
{
"name": "my-web-server",
"description": "Production web server",
"path": "/usr/local/bin/start-web-server.sh",
"user": "www-data",
"group": "www-data",
"target": {
"kind": "node",
"node": "12345678-c625-4fee-81d5-f691897b8142"
},
"service_type": {
"kind": "simple",
"count": 2
},
"target_status": "enabled",
"before": [],
"after": []
}
Oneshot Service#
{
"name": "db-migration",
"description": "Database migration script",
"path": "/usr/local/bin/migrate-db.sh",
"user": "root",
"target": {
"kind": "node_set",
"node_set": "abcdef12-3456-7890-abcd-ef1234567890"
},
"service_type": {
"kind": "oneshot"
},
"target_status": "enabled",
"before": [
{
"kind": "shell",
"command": "echo 'Starting migration...'"
}
],
"after": [
{
"kind": "shell",
"command": "echo 'Migration complete'"
}
]
}
Monopoly Service#
{
"name": "scheduler",
"description": "Global scheduler service",
"path": "/usr/local/bin/scheduler",
"user": "scheduler",
"group": "scheduler",
"target": {
"kind": "node_set",
"node_set": "abcdef12-3456-7890-abcd-ef1234567890"
},
"service_type": {
"kind": "monopoly",
"count": 1
},
"target_status": "enabled",
"before": [],
"after": []
}
Service with Shell Dependencies#
{
"name": "app-with-deps",
"description": "Application with pre/post commands",
"path": "/opt/app/start.sh",
"user": "appuser",
"target": {
"kind": "node",
"node": "12345678-c625-4fee-81d5-f691897b8142"
},
"service_type": {
"kind": "simple",
"count": 1
},
"target_status": "enabled",
"before": [
{
"kind": "shell",
"command": "mkdir -p /var/log/app && chown appuser:appuser /var/log/app"
}
],
"after": [
{
"kind": "shell",
"command": "curl -X POST http://monitoring/notify/service-up"
}
]
}
Service with Service Dependencies (TBD)#
{
"name": "dependent-service",
"description": "Service that depends on another service",
"path": "/opt/app/dependent-start.sh",
"user": "appuser",
"target": {
"kind": "node",
"node": "12345678-c625-4fee-81d5-f691897b8142"
},
"service_type": {
"kind": "simple",
"count": 1
},
"target_status": "enabled",
"before": [
{
"kind": "service",
"service": "abcdef12-3456-7890-abcd-ef1234567890",
"service_name": "base-service"
}
],
"after": []
}
Validation Rules#
Service Name Validation#
- Must match regex
^[A-Za-z0-9_-]{0,100}$ - Alphanumeric characters, underscores, and hyphens only
- Maximum 100 characters
Path Validation#
- Required field
- Minimum 1 character, maximum 255 characters
- Should be an absolute path to executable
Target Validation#
- Must specify either
nodeornode_settarget - Target node/node_set must exist
Service Type Validation#
- Must be one of:
simple,oneshot,monopoly,monopoly_oneshot simpleandmonopolytypes supportcountparametermonopolytypes enforce count=1 across all nodes
Dependency Validation#
beforeandafterare required arrays (can be empty)- Each dependency must have a valid
kind:shellorservice - Shell dependencies require a
commandfield - Service dependencies require
service(UUID) andservice_namefields
Dataplane Behavior#
When a Service is created, the system:
- Creates a systemd service file at
/etc/systemd/system/genesis_srv_<name>_<uuid>.service - Runs
systemctl daemon-reload - Enables and starts the service if
target_statusisenabled - Disables the service if
target_statusisdisabled
Systemd Service Template#
The generated systemd service file follows this template:
[Unit]
Description=Genesis Core: dynamic service {name}
After=network.target
[Service]
Type={simple|oneshot}
Restart={always|on-failure}
ExecStart=/usr/bin/bash -c '{command}'
User={user}
Group={group}
ExecStartPre=+/usr/bin/bash -c '{before_command}'
ExecStartPost=+/usr/bin/bash -c '{after_command}'
RestartSec=5s
TimeoutStopSec=30
[Install]
WantedBy=multi-user.target
Status Lifecycle#
NEW → IN_PROGRESS → ACTIVE
↓
ERROR
- NEW: Service record created, not yet processed
- IN_PROGRESS: Service is being deployed to target nodes
- ACTIVE: All ServiceNode instances are active
- ERROR: Deployment failed (check logs for details)
Element Manifest Example#
Basic manifest for creating a service:
requirements:
core:
from_version: "0.0.0"
imports:
core_compute_node:
element: "$core"
kind: "resource"
link: "$core.compute.nodes.$my_node"
resources:
$core.em.services:
my_service:
project_id: "12345678-c625-4fee-81d5-f691897b8142"
name: "my-app-service"
path: "/opt/myapp/start.sh"
user: "appuser"
group: "appuser"
target:
kind: "node"
node: "$core.compute.nodes.$my_node:uuid"
service_type:
kind: "simple"
count: 1
target_status: "enabled"
before:
- kind: "shell"
command: "mkdir -p /var/log/myapp"
after: []
Complex Example with Node Set#
requirements:
core:
from_version: "0.0.0"
imports:
core_compute_nodeset:
element: "$core"
kind: "resource"
link: "$core.compute.sets.$my_nodeset"
resources:
$core.em.services:
monopoly_scheduler:
project_id: "12345678-c625-4fee-81d5-f691897b8142"
name: "scheduler"
path: "/usr/local/bin/scheduler --config /etc/scheduler.conf"
user: "scheduler"
group: "scheduler"
target:
kind: "node_set"
node_set: "$core.compute.sets.$my_nodeset:uuid"
service_type:
kind: "monopoly"
count: 1
target_status: "enabled"
before:
- kind: "shell"
command: "/usr/local/bin/wait-for-db.sh"
after:
- kind: "shell"
command: "echo 'Scheduler started on $(hostname)'"
API Endpoints#
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/em/services/ |
List all services |
| POST | /v1/em/services/ |
Create a new service |
| GET | /v1/em/services/<uuid> |
Get service details |
| PUT | /v1/em/services/<uuid> |
Update service |
| DELETE | /v1/em/services/<uuid> |
Delete service |
Permissions#
The Service resource uses the following IAM policies:
- Service:
em.service
Available actions:
create: Create new servicesread: View service detailsupdate: Modify existing servicesdelete: Remove serviceslist: List all services