provisioning-core/nulib/cli/handlers/orchestration.nu

295 lines
9.7 KiB
Text

# Orchestration Command Handlers
# Handles: job (orchestrator jobs), workflow (WorkflowDef), batch, orchestrator commands
use cli/flags.nu *
# REMOVED: use ../../lib_provisioning * - causes circular import
use platform/plugins/auth.nu *
use platform/mod.nu *
# Helper to run module commands
def run_module [
args: string
module: string
option?: string
--exec
] {
let use_debug = if ($env.PROVISIONING_DEBUG? | default false) { "-x" } else { "" }
if $exec {
exec $"($env.PROVISIONING_NAME)" $use_debug -mod $module ($option | default "") $args
} else {
^$"($env.PROVISIONING_NAME)" $use_debug -mod $module ($option | default "") $args
}
}
# Main orchestration command dispatcher
export def handle_orchestration_command [
command: string
ops: string
flags: record
] {
set_debug_env $flags
match $command {
"job" => { handle_job $ops $flags }
"workflow" => { handle_workflowdef $ops $flags }
"batch" => { handle_batch $ops $flags }
"orchestrator" => { handle_orchestrator $ops $flags }
_ => {
print $"❌ Unknown orchestration command: ($command)"
print ""
print "Available orchestration commands:"
print " job - Orchestrator job management (list, status, monitor, submit)"
print " workflow - Workspace WorkflowDef management (list, show, run, validate, status)"
print " batch - Batch operations (submit, monitor, rollback, stats)"
print " orchestrator - Orchestrator lifecycle (start, stop, status, health)"
print ""
print "Use 'provisioning help orchestration' for more details"
exit 1
}
}
}
# Job command handler — orchestrator HTTP API jobs
def handle_job [ops: string, flags: record] {
# Authentication check for workflow operations (metadata-driven)
let operation_parts = ($ops | split row " ")
let action = if ($operation_parts | is-empty) { "" } else { $operation_parts | first }
# Determine operation type
let operation_type = match $action {
"rollback" | "cancel" => "delete"
"submit" | "create" => "create"
"update" | "modify" => "modify"
_ => "read"
}
# Check authentication using metadata-driven approach
if not (is-check-mode $flags) and $operation_type != "read" {
let operation_name = $"job ($action)"
check-operation-auth $operation_name $operation_type $flags
}
# Call job management commands directly (avoid -mod routing conflict)
use orchestration/workflows/management.nu *
let orchestrator = ($flags.orchestrator? | default "")
let status_filter = ($flags.status? | default "")
let days = ($flags.days? | default 7 | into int)
let dry_run = ($flags.dry_run? | default false)
# DEBUG
if $action == "browse" {
print $"DEBUG: Handling browse action, ops=($ops)"
}
match $action {
"list" => {
let limit_arg = if ($operation_parts | length) > 1 {
let limit_str = ($operation_parts | get 1 | str trim)
let result = (do { $limit_str | into int } | complete)
if $result.exit_code == 0 { ($result.stdout | str trim | into int) } else { null }
} else {
null
}
if ($limit_arg | is-not-empty) {
job list $limit_arg --orchestrator $orchestrator --status $status_filter
} else {
job list --orchestrator $orchestrator --status $status_filter
}
}
"status" => {
if ($operation_parts | length) < 2 {
print "❌ Error: job status requires a task ID"
return
}
let task_id = ($operation_parts | get 1)
job status $task_id --orchestrator $orchestrator
}
"monitor" => {
if ($operation_parts | length) < 2 {
print "❌ Error: job monitor requires a task ID"
return
}
let task_id = ($operation_parts | get 1)
job monitor $task_id --orchestrator $orchestrator
}
"stats" => { job stats --orchestrator $orchestrator }
"cleanup" => {
if $dry_run {
job cleanup --orchestrator $orchestrator --days $days --dry-run
} else {
job cleanup --orchestrator $orchestrator --days $days
}
}
"orchestrator" => { job orchestrator --orchestrator $orchestrator }
"browse" => {
let limit_arg = if ($operation_parts | length) > 1 {
let limit_str = ($operation_parts | get 1 | str trim)
let result = (do { $limit_str | into int } | complete)
if $result.exit_code == 0 { ($result.stdout | str trim | into int) } else { null }
} else {
null
}
if ($limit_arg | is-not-empty) {
job browse $limit_arg --orchestrator $orchestrator
} else {
job browse --orchestrator $orchestrator
}
}
"submit" => {
if ($operation_parts | length) < 4 {
print "❌ Error: job submit requires: job_type operation target [infra] [settings]"
return
}
let job_type = ($operation_parts | get 1)
let operation_name = ($operation_parts | get 2)
let target = ($operation_parts | get 3)
let infra = if ($operation_parts | length) > 4 { $operation_parts | get 4 } else { "" }
let settings = if ($operation_parts | length) > 5 { $operation_parts | get 5 } else { "" }
let check_mode = (is-check-mode $flags)
let wait = ($flags.wait? | default false)
job submit $job_type $operation_name $target $infra $settings --check=$check_mode --wait=$wait --orchestrator $orchestrator
}
"" => {
print "❌ Error: job subcommand required — use: list, status, monitor, stats, cleanup, browse, submit"
return
}
_ => {
print $"❌ Error: unknown job subcommand '$action'"
return
}
}
}
# WorkflowDef command handler — workspace workflow declarations (workflows/*.ncl)
def handle_workflowdef [ops: string, flags: record] {
let parts = ($ops | split row " ")
let action = if ($parts | is-empty) { "" } else { $parts | first }
let workspace = ($flags.workspace? | default ($flags.ws? | default ""))
let infra = ($flags.infra? | default "")
let dry_run = ($flags.dry_run? | default false)
use ../../cli/workflow.nu *
match $action {
"list" => {
if ($workspace | is-not-empty) {
main workflow list --workspace $workspace
} else {
main workflow list
}
}
"show" => {
if ($parts | length) < 2 {
print "❌ Error: workflow show requires a workflow id"
return
}
let wf_id = ($parts | get 1)
if ($workspace | is-not-empty) {
use ../../cli/ontoref_queries.nu *
main describe workflow $wf_id --workspace $workspace
} else {
use ../../cli/ontoref_queries.nu *
main describe workflow $wf_id
}
}
"run" => {
if ($parts | length) < 2 {
print "❌ Error: workflow run requires a workflow id"
return
}
let wf_id = ($parts | get 1)
if ($workspace | is-not-empty) and $dry_run {
main workflow run $wf_id --workspace $workspace --dry-run
} else if ($workspace | is-not-empty) {
main workflow run $wf_id --workspace $workspace
} else if $dry_run {
main workflow run $wf_id --dry-run
} else {
main workflow run $wf_id
}
}
"validate" => {
if ($workspace | is-not-empty) {
main workflow validate --workspace $workspace
} else {
main workflow validate
}
}
"status" => {
if ($parts | length) < 2 {
print "❌ Error: workflow status requires a workflow id"
return
}
let wf_id = ($parts | get 1)
if ($workspace | is-not-empty) {
main workflow status $wf_id --workspace $workspace
} else {
main workflow status $wf_id
}
}
"" => {
print "❌ Error: workflow subcommand required"
print ""
print " list [--workspace <ws>] List workspace WorkflowDef declarations"
print " show <id> [--workspace <ws>] Show workflow definition + FSM state"
print " run <id> [--workspace <ws>] Execute a WorkflowDef"
print " validate [--workspace <ws>] Cross-validate workflows against components"
print " status <id> [--workspace <ws>] Show FSM dimension state"
}
_ => {
print $"❌ Unknown workflow subcommand: ($action)"
}
}
}
# Batch command handler
def handle_batch [ops: string, flags: record] {
# Authentication check for batch operations (metadata-driven)
let operation_parts = ($ops | split row " ")
let action = if ($operation_parts | is-empty) { "" } else { $operation_parts | first }
# Determine operation type
let operation_type = match $action {
"submit" | "create" => "create"
"rollback" | "cancel" => "delete"
"update" | "modify" => "modify"
_ => "read"
}
# Check authentication using metadata-driven approach
if not (is-check-mode $flags) and $operation_type != "read" {
let operation_name = $"batch ($action)"
check-operation-auth $operation_name $operation_type $flags
}
let args = build_module_args $flags $ops
run_module $args "batch" --exec
}
# Orchestrator command handler
def handle_orchestrator [ops: string, flags: record] {
# Authentication check for orchestrator operations (metadata-driven)
let operation_parts = ($ops | split row " ")
let action = if ($operation_parts | is-empty) { "" } else { $operation_parts | first }
# Determine operation type (orchestrator start/stop are modify operations)
let operation_type = match $action {
"start" => "modify"
"stop" => "modify"
_ => "read"
}
# Check authentication using metadata-driven approach
if not (is-check-mode $flags) and $operation_type != "read" {
let operation_name = $"orchestrator ($action)"
check-operation-auth $operation_name $operation_type $flags
}
# Orchestrator has simpler argument requirements
run_module $ops "orchestrator" --exec
}