api: regenerate OpenAPI types and server code
- Update openapi.yaml spec - Regenerate server_gen.go with oapi-codegen - Update adapter, routes, and server configuration
This commit is contained in:
parent
743bc4be3b
commit
1f495dfbb7
6 changed files with 1731 additions and 155 deletions
734
api/openapi.yaml
734
api/openapi.yaml
|
|
@ -62,6 +62,30 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
type: integer
|
type: integer
|
||||||
default: 0
|
default: 0
|
||||||
|
- name: user_id
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Filter by user who submitted the task
|
||||||
|
- name: plugin_configs
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
$ref: '#/components/schemas/PluginConfig'
|
||||||
|
description: Plugin configurations for this task
|
||||||
|
- name: node_count
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
default: 1
|
||||||
|
description: Number of nodes for multi-node jobs
|
||||||
|
- name: reservation_id
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Pre-reserved capacity for this task
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: List of tasks
|
description: List of tasks
|
||||||
|
|
@ -141,6 +165,458 @@ paths:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/QueueStats'
|
$ref: '#/components/schemas/QueueStats'
|
||||||
|
'401':
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
/v1/plugins:
|
||||||
|
get:
|
||||||
|
summary: List available plugins
|
||||||
|
description: Returns all registered plugins and their status
|
||||||
|
tags:
|
||||||
|
- Plugins
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: List of plugins
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Plugin'
|
||||||
|
'401':
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/responses/Forbidden'
|
||||||
|
/v1/plugins/{pluginName}:
|
||||||
|
get:
|
||||||
|
summary: Get plugin details
|
||||||
|
description: Returns plugin configuration and status
|
||||||
|
tags:
|
||||||
|
- Plugins
|
||||||
|
parameters:
|
||||||
|
- name: pluginName
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Plugin details
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Plugin'
|
||||||
|
'401':
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/responses/Forbidden'
|
||||||
|
'404':
|
||||||
|
$ref: '#/components/responses/NotFound'
|
||||||
|
/v1/plugins/{pluginName}/config:
|
||||||
|
get:
|
||||||
|
summary: Get plugin configuration
|
||||||
|
description: Returns plugin configuration
|
||||||
|
tags:
|
||||||
|
- Plugins
|
||||||
|
parameters:
|
||||||
|
- name: pluginName
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Plugin configuration
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/PluginConfig'
|
||||||
|
'401':
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/responses/Forbidden'
|
||||||
|
'404':
|
||||||
|
$ref: '#/components/responses/NotFound'
|
||||||
|
put:
|
||||||
|
summary: Update plugin configuration
|
||||||
|
description: Update plugin configuration (hot-reload if supported)
|
||||||
|
tags:
|
||||||
|
- Plugins
|
||||||
|
parameters:
|
||||||
|
- name: pluginName
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/PluginConfig'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Configuration updated
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Plugin'
|
||||||
|
'401':
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/responses/Forbidden'
|
||||||
|
'404':
|
||||||
|
$ref: '#/components/responses/NotFound'
|
||||||
|
'422':
|
||||||
|
$ref: '#/components/responses/ValidationError'
|
||||||
|
delete:
|
||||||
|
summary: Disable/unload plugin
|
||||||
|
description: Disable plugin (may require restart if plugin requires it)
|
||||||
|
tags:
|
||||||
|
- Plugins
|
||||||
|
parameters:
|
||||||
|
- name: pluginName
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'204':
|
||||||
|
description: Plugin disabled
|
||||||
|
'401':
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/responses/Forbidden'
|
||||||
|
'404':
|
||||||
|
$ref: '#/components/responses/NotFound'
|
||||||
|
/v1/plugins/{pluginName}/health:
|
||||||
|
get:
|
||||||
|
summary: Check plugin health
|
||||||
|
description: Returns health status of plugin sidecars
|
||||||
|
tags:
|
||||||
|
- Plugins
|
||||||
|
parameters:
|
||||||
|
- name: pluginName
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Plugin health
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/HealthResponse'
|
||||||
|
'401':
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/responses/Forbidden'
|
||||||
|
'404':
|
||||||
|
$ref: '#/components/responses/NotFound'
|
||||||
|
/v1/scheduler/status:
|
||||||
|
get:
|
||||||
|
summary: Get scheduler status
|
||||||
|
description: Returns queue depths, worker counts, and metrics
|
||||||
|
tags:
|
||||||
|
- Scheduler
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Scheduler status
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/SchedulerStatus'
|
||||||
|
'401':
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/responses/Forbidden'
|
||||||
|
/v1/scheduler/status/stream:
|
||||||
|
get:
|
||||||
|
summary: SSE stream of scheduler state changes
|
||||||
|
description: Emits events on queue depth changes, worker connect/disconnect, job transitions
|
||||||
|
tags:
|
||||||
|
- Scheduler
|
||||||
|
produces:
|
||||||
|
- text/event-stream
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: SSE stream
|
||||||
|
content:
|
||||||
|
text/event-stream:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
'401':
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/responses/Forbidden'
|
||||||
|
/v1/scheduler/workers:
|
||||||
|
get:
|
||||||
|
summary: List connected workers
|
||||||
|
description: Returns all workers and their capabilities
|
||||||
|
tags:
|
||||||
|
- Scheduler
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: List of workers
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Worker'
|
||||||
|
'401':
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/responses/Forbidden'
|
||||||
|
/v1/scheduler/workers/{workerId}:
|
||||||
|
get:
|
||||||
|
summary: Get worker details
|
||||||
|
description: Returns detailed worker information
|
||||||
|
tags:
|
||||||
|
- Scheduler
|
||||||
|
parameters:
|
||||||
|
- name: workerId
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Worker details
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Worker'
|
||||||
|
'401':
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/responses/Forbidden'
|
||||||
|
'404':
|
||||||
|
$ref: '#/components/responses/NotFound'
|
||||||
|
delete:
|
||||||
|
summary: Disconnect/drain worker
|
||||||
|
description: Gracefully drain and disconnect a worker
|
||||||
|
tags:
|
||||||
|
- Scheduler
|
||||||
|
parameters:
|
||||||
|
- name: workerId
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'204':
|
||||||
|
description: Worker draining initiated
|
||||||
|
'401':
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/responses/Forbidden'
|
||||||
|
'404':
|
||||||
|
$ref: '#/components/responses/NotFound'
|
||||||
|
/v1/scheduler/reservations:
|
||||||
|
get:
|
||||||
|
summary: List active reservations
|
||||||
|
description: Returns all active capacity reservations
|
||||||
|
tags:
|
||||||
|
- Scheduler
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: List of reservations
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Reservation'
|
||||||
|
'401':
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/responses/Forbidden'
|
||||||
|
post:
|
||||||
|
summary: Create reservation
|
||||||
|
description: Reserve capacity for large jobs
|
||||||
|
tags:
|
||||||
|
- Scheduler
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CreateReservationRequest'
|
||||||
|
responses:
|
||||||
|
'201':
|
||||||
|
description: Reservation created
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Reservation'
|
||||||
|
'401':
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/responses/Forbidden'
|
||||||
|
'422':
|
||||||
|
$ref: '#/components/responses/ValidationError'
|
||||||
|
/v1/scheduler/jobs/{jobId}/priority:
|
||||||
|
patch:
|
||||||
|
summary: Update job priority
|
||||||
|
description: Change the priority of a queued or running job
|
||||||
|
tags:
|
||||||
|
- Scheduler
|
||||||
|
parameters:
|
||||||
|
- name: jobId
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- priority
|
||||||
|
properties:
|
||||||
|
priority:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
maximum: 10
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Priority updated
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Task'
|
||||||
|
'401':
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/responses/Forbidden'
|
||||||
|
'404':
|
||||||
|
$ref: '#/components/responses/NotFound'
|
||||||
|
/v1/scheduler/jobs/{jobId}/stream:
|
||||||
|
get:
|
||||||
|
summary: SSE stream of job progress
|
||||||
|
description: Emits events on job state transitions and priority changes
|
||||||
|
tags:
|
||||||
|
- Scheduler
|
||||||
|
produces:
|
||||||
|
- text/event-stream
|
||||||
|
parameters:
|
||||||
|
- name: jobId
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: SSE stream
|
||||||
|
content:
|
||||||
|
text/event-stream:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
'401':
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/responses/Forbidden'
|
||||||
|
'404':
|
||||||
|
$ref: '#/components/responses/NotFound'
|
||||||
|
/v1/audit/events:
|
||||||
|
get:
|
||||||
|
summary: Query audit events
|
||||||
|
description: Filter by time range, event type, user
|
||||||
|
tags:
|
||||||
|
- Audit
|
||||||
|
parameters:
|
||||||
|
- name: from
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: Start timestamp
|
||||||
|
- name: to
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: End timestamp
|
||||||
|
- name: event_type
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
enum: [job_queued, job_started, job_completed, file_access, auth_attempt, plugin_configured, scheduler_drain, audit_verified]
|
||||||
|
description: Filter by event type
|
||||||
|
- name: user_id
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Filter by user
|
||||||
|
- name: limit
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 100
|
||||||
|
maximum: 1000
|
||||||
|
- name: offset
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 0
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: List of audit events
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/AuditEventList'
|
||||||
|
'401':
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/responses/Forbidden'
|
||||||
|
'429':
|
||||||
|
$ref: '#/components/responses/RateLimited'
|
||||||
|
/v1/audit/verify:
|
||||||
|
post:
|
||||||
|
summary: Verify audit chain integrity
|
||||||
|
description: Validates the hash chain for tampering
|
||||||
|
tags:
|
||||||
|
- Audit
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Verification result
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/VerificationResult'
|
||||||
|
'401':
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/responses/Forbidden'
|
||||||
|
/v1/audit/chain-root:
|
||||||
|
get:
|
||||||
|
summary: Get current chain root hash
|
||||||
|
description: Returns the latest event hash for external verification
|
||||||
|
tags:
|
||||||
|
- Audit
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Chain root hash
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
root_hash:
|
||||||
|
type: string
|
||||||
|
timestamp:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
total_events:
|
||||||
|
type: integer
|
||||||
|
'401':
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/responses/Forbidden'
|
||||||
/v1/experiments:
|
/v1/experiments:
|
||||||
get:
|
get:
|
||||||
summary: List experiments
|
summary: List experiments
|
||||||
|
|
@ -276,6 +752,9 @@ components:
|
||||||
type: string
|
type: string
|
||||||
output:
|
output:
|
||||||
type: string
|
type: string
|
||||||
|
entrypoint:
|
||||||
|
type: string
|
||||||
|
description: Entrypoint script or command executed for this task
|
||||||
snapshot_id:
|
snapshot_id:
|
||||||
type: string
|
type: string
|
||||||
datasets:
|
datasets:
|
||||||
|
|
@ -294,6 +773,19 @@ components:
|
||||||
type: integer
|
type: integer
|
||||||
max_retries:
|
max_retries:
|
||||||
type: integer
|
type: integer
|
||||||
|
plugin_status:
|
||||||
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
$ref: '#/components/schemas/PluginStatus'
|
||||||
|
description: Status of tracking plugins for this task
|
||||||
|
node_assignments:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/NodeAssignment'
|
||||||
|
description: Worker node assignments for multi-node jobs
|
||||||
|
priority_aged:
|
||||||
|
type: number
|
||||||
|
description: Effective priority with aging applied
|
||||||
CreateTaskRequest:
|
CreateTaskRequest:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
|
|
@ -311,7 +803,14 @@ components:
|
||||||
default: 5
|
default: 5
|
||||||
args:
|
args:
|
||||||
type: string
|
type: string
|
||||||
description: Command-line arguments for the training script
|
description: Command-line arguments for the entrypoint
|
||||||
|
entrypoint:
|
||||||
|
type: string
|
||||||
|
description: Entrypoint script or command (e.g., train.py, run.sh, /bin/bash -c "echo hello")
|
||||||
|
examples:
|
||||||
|
- train.py
|
||||||
|
- run.sh
|
||||||
|
- /bin/bash -c "python train.py --epochs 10"
|
||||||
snapshot_id:
|
snapshot_id:
|
||||||
type: string
|
type: string
|
||||||
description: Reference to experiment snapshot
|
description: Reference to experiment snapshot
|
||||||
|
|
@ -433,6 +932,232 @@ components:
|
||||||
image:
|
image:
|
||||||
type: string
|
type: string
|
||||||
default: jupyter/pytorch:latest
|
default: jupyter/pytorch:latest
|
||||||
|
PluginConfig:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
enabled:
|
||||||
|
type: boolean
|
||||||
|
mode:
|
||||||
|
type: string
|
||||||
|
enum: [sidecar, remote, disabled]
|
||||||
|
image:
|
||||||
|
type: string
|
||||||
|
settings:
|
||||||
|
type: object
|
||||||
|
additionalProperties: true
|
||||||
|
PluginStatus:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
enum: [healthy, unhealthy, starting, stopped]
|
||||||
|
url:
|
||||||
|
type: string
|
||||||
|
last_check:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
NodeAssignment:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
node_id:
|
||||||
|
type: integer
|
||||||
|
worker_id:
|
||||||
|
type: string
|
||||||
|
slot_assigned:
|
||||||
|
type: boolean
|
||||||
|
Plugin:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
description: Plugin name
|
||||||
|
enabled:
|
||||||
|
type: boolean
|
||||||
|
description: Whether plugin is enabled
|
||||||
|
mode:
|
||||||
|
type: string
|
||||||
|
enum: [sidecar, remote, disabled]
|
||||||
|
description: Provisioning mode
|
||||||
|
status:
|
||||||
|
$ref: '#/components/schemas/PluginStatus'
|
||||||
|
description: Current plugin status
|
||||||
|
config:
|
||||||
|
$ref: '#/components/schemas/PluginConfig'
|
||||||
|
requires_restart:
|
||||||
|
type: boolean
|
||||||
|
description: Whether plugin requires restart on config change
|
||||||
|
version:
|
||||||
|
type: string
|
||||||
|
description: Plugin version
|
||||||
|
Worker:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
description: Worker unique identifier
|
||||||
|
connected_at:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: When worker connected
|
||||||
|
last_heartbeat:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: Last heartbeat timestamp
|
||||||
|
capabilities:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
gpu_count:
|
||||||
|
type: integer
|
||||||
|
description: Number of GPUs
|
||||||
|
gpu_type:
|
||||||
|
type: string
|
||||||
|
description: GPU type (e.g., A100, H100)
|
||||||
|
cpu_cores:
|
||||||
|
type: integer
|
||||||
|
memory_gb:
|
||||||
|
type: integer
|
||||||
|
slots:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
batch_available:
|
||||||
|
type: integer
|
||||||
|
batch_total:
|
||||||
|
type: integer
|
||||||
|
service_available:
|
||||||
|
type: integer
|
||||||
|
service_total:
|
||||||
|
type: integer
|
||||||
|
active_tasks:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
description: IDs of tasks currently running on this worker
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
enum: [active, draining, offline]
|
||||||
|
SchedulerStatus:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
workers_total:
|
||||||
|
type: integer
|
||||||
|
workers_active:
|
||||||
|
type: integer
|
||||||
|
workers_draining:
|
||||||
|
type: integer
|
||||||
|
batch_queue_depth:
|
||||||
|
type: integer
|
||||||
|
service_queue_depth:
|
||||||
|
type: integer
|
||||||
|
tasks_running:
|
||||||
|
type: integer
|
||||||
|
tasks_completed_24h:
|
||||||
|
type: integer
|
||||||
|
reservations_active:
|
||||||
|
type: integer
|
||||||
|
timestamp:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
Reservation:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
user_id:
|
||||||
|
type: string
|
||||||
|
gpu_count:
|
||||||
|
type: integer
|
||||||
|
gpu_type:
|
||||||
|
type: string
|
||||||
|
node_count:
|
||||||
|
type: integer
|
||||||
|
expires_at:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
enum: [active, claimed, expired]
|
||||||
|
CreateReservationRequest:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- gpu_count
|
||||||
|
properties:
|
||||||
|
gpu_count:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
gpu_type:
|
||||||
|
type: string
|
||||||
|
node_count:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
default: 1
|
||||||
|
expires_minutes:
|
||||||
|
type: integer
|
||||||
|
default: 30
|
||||||
|
AuditEvent:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
timestamp:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
event_type:
|
||||||
|
type: string
|
||||||
|
enum: [job_queued, job_started, job_completed, file_access, auth_attempt, plugin_configured, scheduler_drain, audit_verified]
|
||||||
|
user_id:
|
||||||
|
type: string
|
||||||
|
resource:
|
||||||
|
type: string
|
||||||
|
description: Resource being acted upon
|
||||||
|
action:
|
||||||
|
type: string
|
||||||
|
description: Action performed
|
||||||
|
success:
|
||||||
|
type: boolean
|
||||||
|
ip_address:
|
||||||
|
type: string
|
||||||
|
error:
|
||||||
|
type: string
|
||||||
|
prev_hash:
|
||||||
|
type: string
|
||||||
|
description: Previous event hash in chain
|
||||||
|
event_hash:
|
||||||
|
type: string
|
||||||
|
description: This event's hash
|
||||||
|
sequence_num:
|
||||||
|
type: integer
|
||||||
|
description: Position in audit chain
|
||||||
|
metadata:
|
||||||
|
type: object
|
||||||
|
additionalProperties: true
|
||||||
|
AuditEventList:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
events:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/AuditEvent'
|
||||||
|
total:
|
||||||
|
type: integer
|
||||||
|
limit:
|
||||||
|
type: integer
|
||||||
|
offset:
|
||||||
|
type: integer
|
||||||
|
VerificationResult:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
valid:
|
||||||
|
type: boolean
|
||||||
|
total_events:
|
||||||
|
type: integer
|
||||||
|
first_tampered:
|
||||||
|
type: integer
|
||||||
|
description: Sequence number of first tampered event (if any)
|
||||||
|
chain_root_hash:
|
||||||
|
type: string
|
||||||
|
verified_at:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
ErrorResponse:
|
ErrorResponse:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
|
|
@ -449,6 +1174,13 @@ components:
|
||||||
trace_id:
|
trace_id:
|
||||||
type: string
|
type: string
|
||||||
description: Support correlation ID
|
description: Support correlation ID
|
||||||
|
tags:
|
||||||
|
- name: Plugins
|
||||||
|
description: Plugin management endpoints
|
||||||
|
- name: Scheduler
|
||||||
|
description: Scheduler and worker management
|
||||||
|
- name: Audit
|
||||||
|
description: Audit log and chain verification
|
||||||
responses:
|
responses:
|
||||||
BadRequest:
|
BadRequest:
|
||||||
description: Invalid request
|
description: Invalid request
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,23 @@ package api
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/jfraeys/fetch_ml/internal/api/audit"
|
||||||
"github.com/jfraeys/fetch_ml/internal/api/datasets"
|
"github.com/jfraeys/fetch_ml/internal/api/datasets"
|
||||||
"github.com/jfraeys/fetch_ml/internal/api/jobs"
|
"github.com/jfraeys/fetch_ml/internal/api/jobs"
|
||||||
"github.com/jfraeys/fetch_ml/internal/api/jupyter"
|
"github.com/jfraeys/fetch_ml/internal/api/jupyter"
|
||||||
|
"github.com/jfraeys/fetch_ml/internal/api/plugins"
|
||||||
|
sch "github.com/jfraeys/fetch_ml/internal/api/scheduler"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HandlerAdapter implements the generated ServerInterface using existing handlers
|
// HandlerAdapter implements the generated ServerInterface using existing handlers
|
||||||
type HandlerAdapter struct {
|
type HandlerAdapter struct {
|
||||||
jobsHandler *jobs.Handler
|
jobsHandler *jobs.Handler
|
||||||
jupyterHandler *jupyter.Handler
|
jupyterHandler *jupyter.Handler
|
||||||
datasetsHandler *datasets.Handler
|
datasetsHandler *datasets.Handler
|
||||||
|
pluginsHandler *plugins.Handler
|
||||||
|
schedulerHandler *sch.APIHandler
|
||||||
|
auditHandler *audit.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHandlerAdapter creates a new handler adapter
|
// NewHandlerAdapter creates a new handler adapter
|
||||||
|
|
@ -22,11 +28,17 @@ func NewHandlerAdapter(
|
||||||
jobsHandler *jobs.Handler,
|
jobsHandler *jobs.Handler,
|
||||||
jupyterHandler *jupyter.Handler,
|
jupyterHandler *jupyter.Handler,
|
||||||
datasetsHandler *datasets.Handler,
|
datasetsHandler *datasets.Handler,
|
||||||
|
pluginsHandler *plugins.Handler,
|
||||||
|
schedulerHandler *sch.APIHandler,
|
||||||
|
auditHandler *audit.Handler,
|
||||||
) *HandlerAdapter {
|
) *HandlerAdapter {
|
||||||
return &HandlerAdapter{
|
return &HandlerAdapter{
|
||||||
jobsHandler: jobsHandler,
|
jobsHandler: jobsHandler,
|
||||||
jupyterHandler: jupyterHandler,
|
jupyterHandler: jupyterHandler,
|
||||||
datasetsHandler: datasetsHandler,
|
datasetsHandler: datasetsHandler,
|
||||||
|
pluginsHandler: pluginsHandler,
|
||||||
|
schedulerHandler: schedulerHandler,
|
||||||
|
auditHandler: auditHandler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -154,3 +166,207 @@ func (a *HandlerAdapter) GetWs(ctx echo.Context) error {
|
||||||
"message": "Use WebSocket protocol to connect to this endpoint",
|
"message": "Use WebSocket protocol to connect to this endpoint",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Plugin handlers
|
||||||
|
|
||||||
|
// GetV1Plugins lists all plugins
|
||||||
|
func (a *HandlerAdapter) GetV1Plugins(ctx echo.Context) error {
|
||||||
|
if a.pluginsHandler == nil {
|
||||||
|
return ctx.JSON(503, map[string]any{
|
||||||
|
"error": "Plugin service not available",
|
||||||
|
"code": "SERVICE_UNAVAILABLE",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return toHTTPHandler(a.pluginsHandler.GetV1Plugins)(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetV1PluginsPluginName gets plugin details
|
||||||
|
func (a *HandlerAdapter) GetV1PluginsPluginName(ctx echo.Context, pluginName string) error {
|
||||||
|
if a.pluginsHandler == nil {
|
||||||
|
return ctx.JSON(503, map[string]any{
|
||||||
|
"error": "Plugin service not available",
|
||||||
|
"code": "SERVICE_UNAVAILABLE",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return toHTTPHandler(a.pluginsHandler.GetV1PluginsPluginName)(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetV1PluginsPluginNameConfig gets plugin configuration
|
||||||
|
func (a *HandlerAdapter) GetV1PluginsPluginNameConfig(ctx echo.Context, pluginName string) error {
|
||||||
|
if a.pluginsHandler == nil {
|
||||||
|
return ctx.JSON(503, map[string]any{
|
||||||
|
"error": "Plugin service not available",
|
||||||
|
"code": "SERVICE_UNAVAILABLE",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return toHTTPHandler(a.pluginsHandler.GetV1PluginsPluginNameConfig)(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutV1PluginsPluginNameConfig updates plugin configuration
|
||||||
|
func (a *HandlerAdapter) PutV1PluginsPluginNameConfig(ctx echo.Context, pluginName string) error {
|
||||||
|
if a.pluginsHandler == nil {
|
||||||
|
return ctx.JSON(503, map[string]any{
|
||||||
|
"error": "Plugin service not available",
|
||||||
|
"code": "SERVICE_UNAVAILABLE",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return toHTTPHandler(a.pluginsHandler.PutV1PluginsPluginNameConfig)(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteV1PluginsPluginNameConfig disables/unloads plugin
|
||||||
|
func (a *HandlerAdapter) DeleteV1PluginsPluginNameConfig(ctx echo.Context, pluginName string) error {
|
||||||
|
if a.pluginsHandler == nil {
|
||||||
|
return ctx.JSON(503, map[string]any{
|
||||||
|
"error": "Plugin service not available",
|
||||||
|
"code": "SERVICE_UNAVAILABLE",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return toHTTPHandler(a.pluginsHandler.DeleteV1PluginsPluginNameConfig)(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetV1PluginsPluginNameHealth checks plugin health
|
||||||
|
func (a *HandlerAdapter) GetV1PluginsPluginNameHealth(ctx echo.Context, pluginName string) error {
|
||||||
|
if a.pluginsHandler == nil {
|
||||||
|
return ctx.JSON(503, map[string]any{
|
||||||
|
"error": "Plugin service not available",
|
||||||
|
"code": "SERVICE_UNAVAILABLE",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return toHTTPHandler(a.pluginsHandler.GetV1PluginsPluginNameHealth)(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scheduler handlers
|
||||||
|
|
||||||
|
// GetV1SchedulerStatus gets scheduler status
|
||||||
|
func (a *HandlerAdapter) GetV1SchedulerStatus(ctx echo.Context) error {
|
||||||
|
if a.schedulerHandler == nil {
|
||||||
|
return ctx.JSON(503, map[string]any{
|
||||||
|
"error": "Scheduler service not available",
|
||||||
|
"code": "SERVICE_UNAVAILABLE",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return toHTTPHandler(a.schedulerHandler.GetV1SchedulerStatus)(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetV1SchedulerStatusStream gets scheduler status stream (SSE)
|
||||||
|
func (a *HandlerAdapter) GetV1SchedulerStatusStream(ctx echo.Context) error {
|
||||||
|
if a.schedulerHandler == nil {
|
||||||
|
return ctx.JSON(503, map[string]any{
|
||||||
|
"error": "Scheduler service not available",
|
||||||
|
"code": "SERVICE_UNAVAILABLE",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return toHTTPHandler(a.schedulerHandler.GetV1SchedulerStatusStream)(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetV1SchedulerWorkers lists connected workers
|
||||||
|
func (a *HandlerAdapter) GetV1SchedulerWorkers(ctx echo.Context) error {
|
||||||
|
if a.schedulerHandler == nil {
|
||||||
|
return ctx.JSON(503, map[string]any{
|
||||||
|
"error": "Scheduler service not available",
|
||||||
|
"code": "SERVICE_UNAVAILABLE",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return toHTTPHandler(a.schedulerHandler.GetV1SchedulerWorkers)(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetV1SchedulerWorkersWorkerId gets worker details
|
||||||
|
func (a *HandlerAdapter) GetV1SchedulerWorkersWorkerId(ctx echo.Context, workerId string) error {
|
||||||
|
if a.schedulerHandler == nil {
|
||||||
|
return ctx.JSON(503, map[string]any{
|
||||||
|
"error": "Scheduler service not available",
|
||||||
|
"code": "SERVICE_UNAVAILABLE",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return toHTTPHandler(a.schedulerHandler.GetV1SchedulerWorkersWorkerID)(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteV1SchedulerWorkersWorkerId drains a worker
|
||||||
|
func (a *HandlerAdapter) DeleteV1SchedulerWorkersWorkerId(ctx echo.Context, workerId string) error {
|
||||||
|
if a.schedulerHandler == nil {
|
||||||
|
return ctx.JSON(503, map[string]any{
|
||||||
|
"error": "Scheduler service not available",
|
||||||
|
"code": "SERVICE_UNAVAILABLE",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return toHTTPHandler(a.schedulerHandler.DeleteV1SchedulerWorkersWorkerID)(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetV1SchedulerReservations lists active reservations
|
||||||
|
func (a *HandlerAdapter) GetV1SchedulerReservations(ctx echo.Context) error {
|
||||||
|
if a.schedulerHandler == nil {
|
||||||
|
return ctx.JSON(503, map[string]any{
|
||||||
|
"error": "Scheduler service not available",
|
||||||
|
"code": "SERVICE_UNAVAILABLE",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return toHTTPHandler(a.schedulerHandler.GetV1SchedulerReservations)(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostV1SchedulerReservations creates a reservation
|
||||||
|
func (a *HandlerAdapter) PostV1SchedulerReservations(ctx echo.Context) error {
|
||||||
|
if a.schedulerHandler == nil {
|
||||||
|
return ctx.JSON(503, map[string]any{
|
||||||
|
"error": "Scheduler service not available",
|
||||||
|
"code": "SERVICE_UNAVAILABLE",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return toHTTPHandler(a.schedulerHandler.PostV1SchedulerReservations)(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PatchV1SchedulerJobsJobIdPriority updates job priority
|
||||||
|
func (a *HandlerAdapter) PatchV1SchedulerJobsJobIdPriority(ctx echo.Context, jobId string) error {
|
||||||
|
if a.schedulerHandler == nil {
|
||||||
|
return ctx.JSON(503, map[string]any{
|
||||||
|
"error": "Scheduler service not available",
|
||||||
|
"code": "SERVICE_UNAVAILABLE",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return toHTTPHandler(a.schedulerHandler.PatchV1SchedulerJobsJobIDPriority)(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetV1SchedulerJobsJobIdStream gets job progress stream (SSE)
|
||||||
|
func (a *HandlerAdapter) GetV1SchedulerJobsJobIdStream(ctx echo.Context, jobId string) error {
|
||||||
|
if a.schedulerHandler == nil {
|
||||||
|
return ctx.JSON(503, map[string]any{
|
||||||
|
"error": "Scheduler service not available",
|
||||||
|
"code": "SERVICE_UNAVAILABLE",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return toHTTPHandler(a.schedulerHandler.GetV1SchedulerJobsJobIDStream)(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Audit handlers
|
||||||
|
|
||||||
|
// GetV1AuditEvents queries audit events
|
||||||
|
func (a *HandlerAdapter) GetV1AuditEvents(ctx echo.Context, params GetV1AuditEventsParams) error {
|
||||||
|
if a.auditHandler == nil {
|
||||||
|
return ctx.JSON(503, map[string]any{
|
||||||
|
"error": "Audit service not available",
|
||||||
|
"code": "SERVICE_UNAVAILABLE",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return toHTTPHandler(a.auditHandler.GetV1AuditEvents)(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostV1AuditVerify verifies audit chain integrity
|
||||||
|
func (a *HandlerAdapter) PostV1AuditVerify(ctx echo.Context) error {
|
||||||
|
if a.auditHandler == nil {
|
||||||
|
return ctx.JSON(503, map[string]any{
|
||||||
|
"error": "Audit service not available",
|
||||||
|
"code": "SERVICE_UNAVAILABLE",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return toHTTPHandler(a.auditHandler.PostV1AuditVerify)(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetV1AuditChainRoot gets chain root hash
|
||||||
|
func (a *HandlerAdapter) GetV1AuditChainRoot(ctx echo.Context) error {
|
||||||
|
if a.auditHandler == nil {
|
||||||
|
return ctx.JSON(503, map[string]any{
|
||||||
|
"error": "Audit service not available",
|
||||||
|
"code": "SERVICE_UNAVAILABLE",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return toHTTPHandler(a.auditHandler.GetV1AuditChainRoot)(ctx)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,12 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/jfraeys/fetch_ml/internal/api/audit"
|
||||||
"github.com/jfraeys/fetch_ml/internal/api/datasets"
|
"github.com/jfraeys/fetch_ml/internal/api/datasets"
|
||||||
"github.com/jfraeys/fetch_ml/internal/api/jobs"
|
"github.com/jfraeys/fetch_ml/internal/api/jobs"
|
||||||
"github.com/jfraeys/fetch_ml/internal/api/jupyter"
|
"github.com/jfraeys/fetch_ml/internal/api/jupyter"
|
||||||
|
"github.com/jfraeys/fetch_ml/internal/api/plugins"
|
||||||
|
sch "github.com/jfraeys/fetch_ml/internal/api/scheduler"
|
||||||
"github.com/jfraeys/fetch_ml/internal/api/ws"
|
"github.com/jfraeys/fetch_ml/internal/api/ws"
|
||||||
"github.com/jfraeys/fetch_ml/internal/prommetrics"
|
"github.com/jfraeys/fetch_ml/internal/prommetrics"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
|
|
@ -55,8 +58,18 @@ func (s *Server) registerRoutes(mux *http.ServeMux) {
|
||||||
// Register OpenAPI-generated routes with Echo router
|
// Register OpenAPI-generated routes with Echo router
|
||||||
s.registerOpenAPIRoutes(mux, jobsHandler)
|
s.registerOpenAPIRoutes(mux, jobsHandler)
|
||||||
|
|
||||||
|
// Register scheduler API endpoints
|
||||||
|
schedulerHandler := sch.NewHandler(s.schedulerHub, s.logger, s.config.BuildAuthConfig())
|
||||||
|
mux.HandleFunc("GET /api/v1/workers", schedulerHandler.GetV1SchedulerWorkers)
|
||||||
|
mux.HandleFunc("GET /api/v1/workers/{workerId}", schedulerHandler.GetV1SchedulerWorkersWorkerID)
|
||||||
|
mux.HandleFunc("DELETE /api/v1/workers/{workerId}", schedulerHandler.DeleteV1SchedulerWorkersWorkerID)
|
||||||
|
|
||||||
// Register API documentation endpoint
|
// Register API documentation endpoint
|
||||||
s.registerDocsRoutes(mux)
|
s.registerDocsRoutes(mux)
|
||||||
|
|
||||||
|
// Register OpenAPI spec endpoint
|
||||||
|
mux.HandleFunc("GET /api/openapi.yaml", ServeOpenAPISpec)
|
||||||
|
s.logger.Info("OpenAPI spec endpoint registered", "path", "/api/openapi.yaml")
|
||||||
}
|
}
|
||||||
|
|
||||||
// registerDocsRoutes sets up API documentation serving
|
// registerDocsRoutes sets up API documentation serving
|
||||||
|
|
@ -90,16 +103,42 @@ func (s *Server) registerOpenAPIRoutes(mux *http.ServeMux, jobsHandler *jobs.Han
|
||||||
s.config.DataDir,
|
s.config.DataDir,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Create plugins handler
|
||||||
|
pluginsHandler := plugins.NewHandler(
|
||||||
|
s.logger,
|
||||||
|
s.trackingRegistry, // Need to add this to Server
|
||||||
|
nil, // Plugin config - can be loaded from config
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create scheduler handler
|
||||||
|
var schedulerHandler *sch.APIHandler
|
||||||
|
if s.schedulerHub != nil { // Need to add this to Server
|
||||||
|
schedulerHandler = sch.NewAPIHandler(s.logger, s.schedulerHub)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create audit handler
|
||||||
|
auditHandler := audit.NewHandler(s.logger, nil) // Audit store can be added later
|
||||||
|
|
||||||
// Create adapter implementing ServerInterface
|
// Create adapter implementing ServerInterface
|
||||||
handlerAdapter := NewHandlerAdapter(
|
handlerAdapter := NewHandlerAdapter(
|
||||||
jobsHandler,
|
jobsHandler,
|
||||||
jupyterHandler,
|
jupyterHandler,
|
||||||
datasetsHandler,
|
datasetsHandler,
|
||||||
|
pluginsHandler,
|
||||||
|
schedulerHandler,
|
||||||
|
auditHandler,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Register generated OpenAPI routes
|
// Register generated OpenAPI routes
|
||||||
RegisterHandlers(e, handlerAdapter)
|
RegisterHandlers(e, handlerAdapter)
|
||||||
|
|
||||||
|
// Add scheduler workers endpoint directly to main mux (not Echo)
|
||||||
|
if schedulerHandler != nil {
|
||||||
|
mux.HandleFunc("GET /api/v1/workers", schedulerHandler.GetV1SchedulerWorkers)
|
||||||
|
mux.HandleFunc("GET /api/v1/workers/{workerId}", schedulerHandler.GetV1SchedulerWorkersWorkerID)
|
||||||
|
mux.HandleFunc("DELETE /api/v1/workers/{workerId}", schedulerHandler.DeleteV1SchedulerWorkersWorkerID)
|
||||||
|
}
|
||||||
|
|
||||||
// Wrap Echo router to work with existing middleware chain
|
// Wrap Echo router to work with existing middleware chain
|
||||||
echoHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
echoHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
e.ServeHTTP(w, r)
|
e.ServeHTTP(w, r)
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,9 @@ import (
|
||||||
"github.com/jfraeys/fetch_ml/internal/middleware"
|
"github.com/jfraeys/fetch_ml/internal/middleware"
|
||||||
"github.com/jfraeys/fetch_ml/internal/prommetrics"
|
"github.com/jfraeys/fetch_ml/internal/prommetrics"
|
||||||
"github.com/jfraeys/fetch_ml/internal/queue"
|
"github.com/jfraeys/fetch_ml/internal/queue"
|
||||||
|
"github.com/jfraeys/fetch_ml/internal/scheduler"
|
||||||
"github.com/jfraeys/fetch_ml/internal/storage"
|
"github.com/jfraeys/fetch_ml/internal/storage"
|
||||||
|
"github.com/jfraeys/fetch_ml/internal/tracking"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Server represents the API server
|
// Server represents the API server
|
||||||
|
|
@ -32,6 +34,8 @@ type Server struct {
|
||||||
auditLogger *audit.Logger
|
auditLogger *audit.Logger
|
||||||
promMetrics *prommetrics.Metrics
|
promMetrics *prommetrics.Metrics
|
||||||
validationMiddleware *apimiddleware.ValidationMiddleware
|
validationMiddleware *apimiddleware.ValidationMiddleware
|
||||||
|
trackingRegistry *tracking.Registry
|
||||||
|
schedulerHub *scheduler.SchedulerHub
|
||||||
cleanupFuncs []func()
|
cleanupFuncs []func()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/jfraeys/fetch_ml/internal/auth"
|
"github.com/jfraeys/fetch_ml/internal/auth"
|
||||||
"github.com/jfraeys/fetch_ml/internal/config"
|
"github.com/jfraeys/fetch_ml/internal/config"
|
||||||
|
"github.com/jfraeys/fetch_ml/internal/crypto/kms"
|
||||||
"github.com/jfraeys/fetch_ml/internal/fileutil"
|
"github.com/jfraeys/fetch_ml/internal/fileutil"
|
||||||
"github.com/jfraeys/fetch_ml/internal/logging"
|
"github.com/jfraeys/fetch_ml/internal/logging"
|
||||||
"github.com/jfraeys/fetch_ml/internal/storage"
|
"github.com/jfraeys/fetch_ml/internal/storage"
|
||||||
|
|
@ -34,6 +35,7 @@ type ServerConfig struct {
|
||||||
Redis RedisConfig `yaml:"redis"`
|
Redis RedisConfig `yaml:"redis"`
|
||||||
Resources config.ResourceConfig `yaml:"resources"`
|
Resources config.ResourceConfig `yaml:"resources"`
|
||||||
Security SecurityConfig `yaml:"security"`
|
Security SecurityConfig `yaml:"security"`
|
||||||
|
KMS kms.Config `yaml:"kms,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerSection holds server-specific configuration
|
// ServerSection holds server-specific configuration
|
||||||
|
|
@ -204,5 +206,16 @@ func (c *ServerConfig) Validate() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate KMS configuration if set (defaults to memory provider for development)
|
||||||
|
if c.KMS.Provider != "" {
|
||||||
|
if err := c.KMS.Validate(); err != nil {
|
||||||
|
return fmt.Errorf("invalid KMS configuration: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Default to memory provider for development
|
||||||
|
c.KMS.Provider = kms.ProviderTypeMemory
|
||||||
|
c.KMS.Cache = kms.DefaultCacheConfig()
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue