527 lines
14 KiB
Plaintext
527 lines
14 KiB
Plaintext
#!/usr/bin/env nu
|
|
|
|
# Integration Functions for External Systems
|
|
#
|
|
# Provides integration with:
|
|
# - MCP (Model Context Protocol) servers
|
|
# - Rust installer binary
|
|
# - REST APIs
|
|
# - Webhook notifications
|
|
|
|
# Load configuration from MCP server
|
|
#
|
|
# Queries the MCP server for deployment configuration using
|
|
# the Model Context Protocol.
|
|
#
|
|
# @param mcp_url: MCP server URL
|
|
# @returns: Deployment configuration record
|
|
export def load-config-from-mcp [mcp_url: string]: nothing -> record {
|
|
print $"📡 Loading configuration from MCP server: ($mcp_url)"
|
|
|
|
# MCP request payload
|
|
let request = {
|
|
jsonrpc: "2.0"
|
|
id: 1
|
|
method: "config/get"
|
|
params: {
|
|
type: "deployment"
|
|
include_defaults: true
|
|
}
|
|
}
|
|
|
|
try {
|
|
let response = (
|
|
http post $mcp_url --content-type "application/json" ($request | to json)
|
|
)
|
|
|
|
if "error" in ($response | columns) {
|
|
error make {
|
|
msg: $"MCP error: ($response.error.message)"
|
|
label: {text: $"Code: ($response.error.code)"}
|
|
}
|
|
}
|
|
|
|
if "result" not-in ($response | columns) {
|
|
error make {msg: "Invalid MCP response: missing result"}
|
|
}
|
|
|
|
print "✅ Configuration loaded from MCP server"
|
|
$response.result
|
|
|
|
} catch {|err|
|
|
error make {
|
|
msg: $"Failed to load config from MCP: ($mcp_url)"
|
|
label: {text: $err.msg}
|
|
help: "Ensure MCP server is running and accessible"
|
|
}
|
|
}
|
|
}
|
|
|
|
# Load configuration from REST API
|
|
#
|
|
# Fetches deployment configuration from a REST API endpoint.
|
|
#
|
|
# @param api_url: API endpoint URL
|
|
# @returns: Deployment configuration record
|
|
export def load-config-from-api [api_url: string]: nothing -> record {
|
|
print $"🌐 Loading configuration from API: ($api_url)"
|
|
|
|
try {
|
|
let response = (http get $api_url --max-time 30sec)
|
|
|
|
if "config" not-in ($response | columns) {
|
|
error make {msg: "Invalid API response: missing 'config' field"}
|
|
}
|
|
|
|
print "✅ Configuration loaded from API"
|
|
$response.config
|
|
|
|
} catch {|err|
|
|
error make {
|
|
msg: $"Failed to load config from API: ($api_url)"
|
|
label: {text: $err.msg}
|
|
help: "Check API endpoint and network connectivity"
|
|
}
|
|
}
|
|
}
|
|
|
|
# Send notification to webhook
|
|
#
|
|
# Sends deployment event notifications to a configured webhook URL.
|
|
# Useful for integration with Slack, Discord, Microsoft Teams, etc.
|
|
#
|
|
# @param webhook_url: Webhook URL
|
|
# @param payload: Notification payload record
|
|
# @returns: Nothing
|
|
export def notify-webhook [webhook_url: string, payload: record]: nothing -> nothing {
|
|
try {
|
|
http post $webhook_url --content-type "application/json" ($payload | to json)
|
|
|
|
null
|
|
} catch {|err|
|
|
# Don't fail deployment on webhook errors, just log
|
|
print $"⚠️ Warning: Failed to send webhook notification: ($err.msg)"
|
|
null
|
|
}
|
|
}
|
|
|
|
# Call Rust installer binary with arguments
|
|
#
|
|
# Invokes the Rust installer binary with specified arguments,
|
|
# capturing output and exit code.
|
|
#
|
|
# @param args: List of arguments to pass to installer
|
|
# @returns: Installer execution result record
|
|
export def call-installer [args: list<string>]: nothing -> record {
|
|
let installer_path = get-installer-path
|
|
|
|
print $"🚀 Calling installer: ($installer_path) ($args | str join ' ')"
|
|
|
|
try {
|
|
let output = (^$installer_path ...$args | complete)
|
|
|
|
{
|
|
success: ($output.exit_code == 0)
|
|
exit_code: $output.exit_code
|
|
stdout: $output.stdout
|
|
stderr: $output.stderr
|
|
timestamp: (date now)
|
|
}
|
|
} catch {|err|
|
|
{
|
|
success: false
|
|
exit_code: -1
|
|
error: $err.msg
|
|
timestamp: (date now)
|
|
}
|
|
}
|
|
}
|
|
|
|
# Run installer in headless mode with config file
|
|
#
|
|
# Executes the Rust installer in headless mode using a
|
|
# configuration file.
|
|
#
|
|
# @param config_path: Path to configuration file
|
|
# @param auto_confirm: Auto-confirm prompts
|
|
# @returns: Installer execution result record
|
|
export def run-installer-headless [
|
|
config_path: path
|
|
--auto-confirm
|
|
]: nothing -> record {
|
|
mut args = ["--headless", "--config", $config_path]
|
|
|
|
if $auto_confirm {
|
|
$args = ($args | append "--yes")
|
|
}
|
|
|
|
call-installer $args
|
|
}
|
|
|
|
# Run installer interactively
|
|
#
|
|
# Launches the Rust installer in interactive TUI mode.
|
|
#
|
|
# @returns: Installer execution result record
|
|
export def run-installer-interactive []: nothing -> record {
|
|
let installer_path = get-installer-path
|
|
|
|
print $"🚀 Launching interactive installer: ($installer_path)"
|
|
|
|
try {
|
|
# Run without capturing output (interactive mode)
|
|
^$installer_path
|
|
|
|
{
|
|
success: true
|
|
mode: "interactive"
|
|
message: "Interactive installer completed"
|
|
timestamp: (date now)
|
|
}
|
|
} catch {|err|
|
|
{
|
|
success: false
|
|
mode: "interactive"
|
|
error: $err.msg
|
|
timestamp: (date now)
|
|
}
|
|
}
|
|
}
|
|
|
|
# Pass deployment config to installer via CLI args
|
|
#
|
|
# Converts a deployment configuration record into CLI arguments
|
|
# for the Rust installer binary.
|
|
#
|
|
# @param config: Deployment configuration record
|
|
# @returns: List of CLI arguments
|
|
export def config-to-cli-args [config: record]: nothing -> list<string> {
|
|
mut args = ["--headless"]
|
|
|
|
# Add platform
|
|
$args = ($args | append ["--platform", $config.platform])
|
|
|
|
# Add mode
|
|
$args = ($args | append ["--mode", $config.mode])
|
|
|
|
# Add domain
|
|
$args = ($args | append ["--domain", $config.domain])
|
|
|
|
# Add services (comma-separated)
|
|
let services = $config.services
|
|
| where enabled
|
|
| get name
|
|
| str join ","
|
|
|
|
if $services != "" {
|
|
$args = ($args | append ["--services", $services])
|
|
}
|
|
|
|
$args
|
|
}
|
|
|
|
# Deploy using installer binary
|
|
#
|
|
# High-level function to deploy using the Rust installer binary
|
|
# with the given configuration.
|
|
#
|
|
# @param config: Deployment configuration record
|
|
# @param auto_confirm: Auto-confirm prompts
|
|
# @returns: Deployment result record
|
|
export def deploy-with-installer [
|
|
config: record
|
|
--auto-confirm
|
|
]: nothing -> record {
|
|
print "🚀 Deploying using Rust installer binary..."
|
|
|
|
# Convert config to CLI args
|
|
mut args = (config-to-cli-args $config)
|
|
|
|
if $auto_confirm {
|
|
$args = ($args | append "--yes")
|
|
}
|
|
|
|
# Execute installer
|
|
let result = call-installer $args
|
|
|
|
if $result.success {
|
|
print "✅ Installer deployment successful"
|
|
{
|
|
success: true
|
|
method: "installer_binary"
|
|
config: $config
|
|
timestamp: (date now)
|
|
}
|
|
} else {
|
|
print $"❌ Installer deployment failed: ($result.stderr)"
|
|
{
|
|
success: false
|
|
method: "installer_binary"
|
|
error: $result.stderr
|
|
exit_code: $result.exit_code
|
|
timestamp: (date now)
|
|
}
|
|
}
|
|
}
|
|
|
|
# Query MCP server for deployment status
|
|
#
|
|
# Retrieves deployment status information from MCP server.
|
|
#
|
|
# @param mcp_url: MCP server URL
|
|
# @param deployment_id: Deployment identifier
|
|
# @returns: Deployment status record
|
|
export def query-mcp-status [mcp_url: string, deployment_id: string]: nothing -> record {
|
|
let request = {
|
|
jsonrpc: "2.0"
|
|
id: 1
|
|
method: "deployment/status"
|
|
params: {
|
|
deployment_id: $deployment_id
|
|
}
|
|
}
|
|
|
|
try {
|
|
let response = (
|
|
http post $mcp_url --content-type "application/json" ($request | to json)
|
|
)
|
|
|
|
if "error" in ($response | columns) {
|
|
error make {
|
|
msg: $"MCP error: ($response.error.message)"
|
|
}
|
|
}
|
|
|
|
$response.result
|
|
|
|
} catch {|err|
|
|
error make {
|
|
msg: $"Failed to query MCP status: ($err.msg)"
|
|
}
|
|
}
|
|
}
|
|
|
|
# Register deployment with API
|
|
#
|
|
# Registers a new deployment with the external API and returns
|
|
# a deployment ID for tracking.
|
|
#
|
|
# @param api_url: API endpoint URL
|
|
# @param config: Deployment configuration
|
|
# @returns: Registration result with deployment ID
|
|
export def register-deployment-with-api [api_url: string, config: record]: nothing -> record {
|
|
let payload = {
|
|
platform: $config.platform
|
|
mode: $config.mode
|
|
domain: $config.domain
|
|
services: ($config.services | get name)
|
|
started_at: (date now | format date "%Y-%m-%dT%H:%M:%SZ")
|
|
}
|
|
|
|
try {
|
|
let response = (
|
|
http post $api_url --content-type "application/json" ($payload | to json)
|
|
)
|
|
|
|
if "deployment_id" not-in ($response | columns) {
|
|
error make {msg: "API did not return deployment_id"}
|
|
}
|
|
|
|
print $"✅ Deployment registered with API: ($response.deployment_id)"
|
|
|
|
{
|
|
success: true
|
|
deployment_id: $response.deployment_id
|
|
api_url: $api_url
|
|
}
|
|
|
|
} catch {|err|
|
|
print $"⚠️ Warning: Failed to register with API: ($err.msg)"
|
|
{
|
|
success: false
|
|
error: $err.msg
|
|
}
|
|
}
|
|
}
|
|
|
|
# Update deployment status via API
|
|
#
|
|
# Updates deployment status on external API for tracking and monitoring.
|
|
#
|
|
# @param api_url: API endpoint URL
|
|
# @param deployment_id: Deployment identifier
|
|
# @param status: Status update record
|
|
# @returns: Update result record
|
|
export def update-deployment-status [
|
|
api_url: string
|
|
deployment_id: string
|
|
status: record
|
|
]: nothing -> record {
|
|
let update_url = $"($api_url)/($deployment_id)/status"
|
|
|
|
try {
|
|
http patch $update_url --content-type "application/json" ($status | to json)
|
|
|
|
{success: true}
|
|
|
|
} catch {|err|
|
|
print $"⚠️ Warning: Failed to update deployment status: ($err.msg)"
|
|
{success: false, error: $err.msg}
|
|
}
|
|
}
|
|
|
|
# Send Slack notification
|
|
#
|
|
# Sends formatted notification to Slack webhook.
|
|
#
|
|
# @param webhook_url: Slack webhook URL
|
|
# @param message: Message text
|
|
# @param color: Message color (good, warning, danger)
|
|
# @returns: Nothing
|
|
export def notify-slack [
|
|
webhook_url: string
|
|
message: string
|
|
--color: string = "good"
|
|
]: nothing -> nothing {
|
|
let payload = {
|
|
attachments: [{
|
|
color: $color
|
|
text: $message
|
|
footer: "Provisioning Platform Installer"
|
|
ts: (date now | format date "%s")
|
|
}]
|
|
}
|
|
|
|
notify-webhook $webhook_url $payload
|
|
}
|
|
|
|
# Send Discord notification
|
|
#
|
|
# Sends formatted notification to Discord webhook.
|
|
#
|
|
# @param webhook_url: Discord webhook URL
|
|
# @param message: Message text
|
|
# @param success: Whether deployment was successful
|
|
# @returns: Nothing
|
|
export def notify-discord [
|
|
webhook_url: string
|
|
message: string
|
|
--success
|
|
]: nothing -> nothing {
|
|
let color = if $success { 3066993 } else { 15158332 } # Green or Red
|
|
let emoji = if $success { "✅" } else { "❌" }
|
|
|
|
let payload = {
|
|
embeds: [{
|
|
title: $"($emoji) Provisioning Platform Deployment"
|
|
description: $message
|
|
color: $color
|
|
timestamp: (date now | format date "%Y-%m-%dT%H:%M:%SZ")
|
|
footer: {
|
|
text: "Provisioning Platform Installer"
|
|
}
|
|
}]
|
|
}
|
|
|
|
notify-webhook $webhook_url $payload
|
|
}
|
|
|
|
# Send Microsoft Teams notification
|
|
#
|
|
# Sends formatted notification to Microsoft Teams webhook.
|
|
#
|
|
# @param webhook_url: Teams webhook URL
|
|
# @param title: Notification title
|
|
# @param message: Message text
|
|
# @param success: Whether deployment was successful
|
|
# @returns: Nothing
|
|
export def notify-teams [
|
|
webhook_url: string
|
|
title: string
|
|
message: string
|
|
--success
|
|
]: nothing -> nothing {
|
|
let theme_color = if $success { "00FF00" } else { "FF0000" }
|
|
|
|
let payload = {
|
|
"@type": "MessageCard"
|
|
"@context": "https://schema.org/extensions"
|
|
summary: $title
|
|
themeColor: $theme_color
|
|
title: $title
|
|
text: $message
|
|
}
|
|
|
|
notify-webhook $webhook_url $payload
|
|
}
|
|
|
|
# Execute MCP tool call
|
|
#
|
|
# Executes a tool/function call via MCP server.
|
|
#
|
|
# @param mcp_url: MCP server URL
|
|
# @param tool_name: Name of tool to execute
|
|
# @param arguments: Tool arguments record
|
|
# @returns: Tool execution result
|
|
export def execute-mcp-tool [
|
|
mcp_url: string
|
|
tool_name: string
|
|
arguments: record
|
|
]: nothing -> record {
|
|
let request = {
|
|
jsonrpc: "2.0"
|
|
id: 1
|
|
method: "tools/call"
|
|
params: {
|
|
name: $tool_name
|
|
arguments: $arguments
|
|
}
|
|
}
|
|
|
|
try {
|
|
let response = (
|
|
http post $mcp_url --content-type "application/json" ($request | to json)
|
|
)
|
|
|
|
if "error" in ($response | columns) {
|
|
error make {
|
|
msg: $"MCP tool execution error: ($response.error.message)"
|
|
}
|
|
}
|
|
|
|
$response.result
|
|
|
|
} catch {|err|
|
|
error make {
|
|
msg: $"Failed to execute MCP tool: ($err.msg)"
|
|
}
|
|
}
|
|
}
|
|
|
|
# Get installer binary path (helper function)
|
|
#
|
|
# @returns: Path to installer binary
|
|
def get-installer-path []: nothing -> path {
|
|
let installer_dir = $env.PWD | path dirname
|
|
let installer_name = if $nu.os-info.name == "windows" {
|
|
"provisioning-installer.exe"
|
|
} else {
|
|
"provisioning-installer"
|
|
}
|
|
|
|
# Check target/release first, then target/debug
|
|
let release_path = $installer_dir | path join "target" "release" $installer_name
|
|
let debug_path = $installer_dir | path join "target" "debug" $installer_name
|
|
|
|
if ($release_path | path exists) {
|
|
$release_path
|
|
} else if ($debug_path | path exists) {
|
|
$debug_path
|
|
} else {
|
|
error make {
|
|
msg: "Installer binary not found"
|
|
help: "Build with: cargo build --release"
|
|
}
|
|
}
|
|
}
|