544 lines
17 KiB
Text
544 lines
17 KiB
Text
# provctl Integration Module
|
||
# Optional integration with provctl for enhanced service management
|
||
# Graceful fallback when provctl is not installed
|
||
# Follows Nushell guidelines: explicit types, single purpose, no try-catch
|
||
|
||
# Selective imports (ADR-025 Phase 3 Layer 2).
|
||
# setup/detection star-import was dead — dropped.
|
||
use platform/setup/mod.nu [
|
||
get-timestamp-iso8601 load-config-toml print-setup-error print-setup-header
|
||
print-setup-info print-setup-success print-setup-warning save-config-toml
|
||
]
|
||
|
||
# ============================================================================
|
||
# PROVCTL DETECTION
|
||
# ============================================================================
|
||
|
||
# Check if provctl is installed
|
||
export def has-provctl [] {
|
||
let which_result = (do { which provctl } | complete)
|
||
($which_result.exit_code == 0)
|
||
}
|
||
|
||
# Check if provctl is accessible and functional
|
||
export def provctl-available [] {
|
||
let installed = (has-provctl)
|
||
if not $installed {
|
||
return false
|
||
}
|
||
|
||
# Verify provctl can run
|
||
let version_check = (do { provctl --version o> /dev/null e> /dev/null } | complete)
|
||
($version_check.exit_code == 0)
|
||
}
|
||
|
||
# Get provctl version
|
||
export def get-provctl-version [] {
|
||
let result = (do { provctl --version } | complete)
|
||
if ($result.exit_code == 0) {
|
||
$result.stdout | str trim
|
||
} else {
|
||
"unknown"
|
||
}
|
||
}
|
||
|
||
# Get provctl configuration directory
|
||
export def get-provctl-config-dir [] {
|
||
match $nu.os-info.name {
|
||
"macos" => {
|
||
let home = ($env.HOME? | default "~" | path expand)
|
||
$"($home)/Library/Application Support/provctl"
|
||
}
|
||
"windows" => {
|
||
let appdata = ($env.APPDATA? | default "~" | path expand)
|
||
$"($appdata)/provctl"
|
||
}
|
||
_ => {
|
||
let home = ($env.HOME? | default "~" | path expand)
|
||
$"($home)/.config/provctl"
|
||
}
|
||
}
|
||
}
|
||
|
||
# ============================================================================
|
||
# PROVCTL CONFIGURATION GENERATION
|
||
# ============================================================================
|
||
|
||
# Generate provctl configuration from provisioning config
|
||
export def generate-provctl-config [
|
||
config_base: string
|
||
] {
|
||
let provisioning_config = (load-config-toml $"($config_base)/system.toml")
|
||
let platform_config = (load-config-toml $"($config_base)/platform/deployment.toml")
|
||
|
||
{
|
||
version: "1.0.0"
|
||
provisioning: {
|
||
path: $config_base
|
||
system_config: $"($config_base)/system.toml"
|
||
platform_config: $"($config_base)/platform"
|
||
providers: $"($config_base)/providers"
|
||
}
|
||
deployment: {
|
||
mode: ($platform_config.deployment.mode? | default "docker-compose")
|
||
location_type: ($platform_config.deployment.location_type? | default "local")
|
||
}
|
||
services: {
|
||
orchestrator: {
|
||
endpoint: ($platform_config.services.orchestrator.endpoint? | default "http://localhost:9090")
|
||
}
|
||
control_center: {
|
||
endpoint: ($platform_config.services.control_center.endpoint? | default "http://localhost:3000")
|
||
}
|
||
kms_service: {
|
||
endpoint: ($platform_config.services.kms_service.endpoint? | default "http://localhost:3001")
|
||
}
|
||
}
|
||
generated_at: (get-timestamp-iso8601)
|
||
}
|
||
}
|
||
|
||
# ============================================================================
|
||
# PROVCTL SETUP
|
||
# ============================================================================
|
||
|
||
# Initialize provctl configuration directory
|
||
export def setup-provctl-config-dir [] {
|
||
let provctl_dir = (get-provctl-config-dir)
|
||
let mkdir_result = (do { mkdir $provctl_dir } | complete)
|
||
($mkdir_result.exit_code == 0)
|
||
}
|
||
|
||
# Write provisioning configuration to provctl
|
||
export def write-provctl-config [
|
||
config_base: string
|
||
] {
|
||
if not (setup-provctl-config-dir) {
|
||
return false
|
||
}
|
||
|
||
let provctl_dir = (get-provctl-config-dir)
|
||
let provctl_config = (generate-provctl-config $config_base)
|
||
let config_path = $"($provctl_dir)/provisioning-config.toml"
|
||
|
||
save-config-toml $config_path $provctl_config
|
||
}
|
||
|
||
# Register platform services with provctl
|
||
export def register-services-with-provctl [
|
||
--verbose = false
|
||
] {
|
||
if not (provctl-available) {
|
||
return {
|
||
success: false
|
||
reason: "provctl not available"
|
||
services_registered: []
|
||
}
|
||
}
|
||
|
||
let services = ["orchestrator", "control-center", "kms-service"]
|
||
mut registered = []
|
||
mut errors = []
|
||
|
||
for service in $services {
|
||
if $verbose {
|
||
print-setup-info $"Registering ($service) with provctl..."
|
||
}
|
||
|
||
let result = (do {
|
||
provctl service register $service o> /dev/null e> /dev/null
|
||
} | complete)
|
||
|
||
if ($result.exit_code == 0) {
|
||
$registered = ($registered | append $service)
|
||
if $verbose {
|
||
print-setup-success $"Registered ($service)"
|
||
}
|
||
} else {
|
||
$errors = ($errors | append $service)
|
||
if $verbose {
|
||
print-setup-warning $"Failed to register ($service)"
|
||
}
|
||
}
|
||
}
|
||
|
||
let success_status = ($errors | length) == 0
|
||
let timestamp_value = (get-timestamp-iso8601)
|
||
{
|
||
success: $success_status
|
||
services_registered: $registered
|
||
registration_errors: $errors
|
||
timestamp: $timestamp_value
|
||
}
|
||
}
|
||
|
||
# ============================================================================
|
||
# FALLBACK DETECTION
|
||
# ============================================================================
|
||
|
||
# Determine if provctl fallback is needed
|
||
export def needs-provctl-fallback [] {
|
||
not (provctl-available)
|
||
}
|
||
|
||
# Get fallback deployment method
|
||
export def get-fallback-method [
|
||
detection_report: record
|
||
] {
|
||
let caps = $detection_report.capabilities
|
||
|
||
if ($caps.docker_available and $caps.docker_compose_available) {
|
||
"docker-compose"
|
||
} else if ($caps.kubectl_available) {
|
||
"kubernetes"
|
||
} else if ($caps.ssh_available) {
|
||
"remote-ssh"
|
||
} else if ($caps.systemd_available) {
|
||
"systemd"
|
||
} else {
|
||
"unknown"
|
||
}
|
||
}
|
||
|
||
# ============================================================================
|
||
# DEPLOYMENT MODE ENHANCEMENT
|
||
# ============================================================================
|
||
|
||
# Enhance deployment configuration with provctl features (if available)
|
||
export def enhance-deployment-with-provctl [
|
||
config_base: string
|
||
--verbose = false
|
||
] {
|
||
if not (provctl-available) {
|
||
if $verbose {
|
||
print-setup-info "provctl not available - using standard deployment"
|
||
}
|
||
return {
|
||
enhanced: false
|
||
method: "standard"
|
||
reason: "provctl not installed"
|
||
}
|
||
}
|
||
|
||
if $verbose {
|
||
print-setup-info $"Using provctl v(get-provctl-version) for enhanced deployment"
|
||
}
|
||
|
||
# Write provctl configuration
|
||
let config_result = (write-provctl-config $config_base)
|
||
if not $config_result {
|
||
if $verbose {
|
||
print-setup-warning "Failed to write provctl configuration"
|
||
}
|
||
return {
|
||
enhanced: false
|
||
method: "fallback"
|
||
reason: "Failed to configure provctl"
|
||
}
|
||
}
|
||
|
||
# Register services with provctl
|
||
let registration_result = (register-services-with-provctl --verbose=$verbose)
|
||
if not $registration_result.success {
|
||
if $verbose {
|
||
print-setup-warning "Failed to register services with provctl"
|
||
}
|
||
return {
|
||
enhanced: true
|
||
method: "partial"
|
||
registered_services: $registration_result.services_registered
|
||
reason: "Partial provctl integration"
|
||
}
|
||
}
|
||
|
||
{
|
||
enhanced: true
|
||
method: "provctl"
|
||
registered_services: $registration_result.services_registered
|
||
provctl_version: (get-provctl-version)
|
||
provctl_config_dir: (get-provctl-config-dir)
|
||
timestamp: (get-timestamp-iso8601)
|
||
}
|
||
}
|
||
|
||
# ============================================================================
|
||
# SERVICE LIFECYCLE WITH PROVCTL
|
||
# ============================================================================
|
||
|
||
# Start services using provctl if available, fallback to direct commands
|
||
export def start-services-optimized [
|
||
deployment_mode: string
|
||
--use_provctl = true
|
||
--verbose = false
|
||
] {
|
||
# Check if provctl should/can be used
|
||
let provctl_ok = ($use_provctl and (provctl-available))
|
||
|
||
if $provctl_ok {
|
||
if $verbose {
|
||
print-setup-info "Starting services via provctl..."
|
||
}
|
||
|
||
let result = (do {
|
||
provctl service deploy --mode $deployment_mode o> /dev/null e> /dev/null
|
||
} | complete)
|
||
|
||
if ($result.exit_code == 0) {
|
||
if $verbose {
|
||
print-setup-success "Services started via provctl"
|
||
}
|
||
|
||
return {
|
||
success: true
|
||
method: "provctl"
|
||
deployment_mode: $deployment_mode
|
||
timestamp: (get-timestamp-iso8601)
|
||
}
|
||
} else {
|
||
if $verbose {
|
||
print-setup-warning "provctl deployment failed - using fallback"
|
||
}
|
||
}
|
||
}
|
||
|
||
# Fallback: Direct deployment without provctl
|
||
if $verbose {
|
||
print-setup-info "Starting services directly (no provctl)..."
|
||
}
|
||
|
||
{
|
||
success: true
|
||
method: "direct"
|
||
deployment_mode: $deployment_mode
|
||
timestamp: (get-timestamp-iso8601)
|
||
}
|
||
}
|
||
|
||
# ============================================================================
|
||
# PROVCTL STATUS AND MONITORING
|
||
# ============================================================================
|
||
|
||
# Get status of services via provctl
|
||
export def get-provctl-service-status [] {
|
||
if not (provctl-available) {
|
||
return {
|
||
provctl_available: false
|
||
services: []
|
||
}
|
||
}
|
||
|
||
let result = (do {
|
||
provctl service status --output json
|
||
} | complete)
|
||
|
||
if ($result.exit_code == 0) {
|
||
let services = (do { $result.stdout | from json } | complete)
|
||
{
|
||
provctl_available: true
|
||
services: ($services.stdout? | default [])
|
||
}
|
||
} else {
|
||
{
|
||
provctl_available: true
|
||
services: []
|
||
error: "Failed to query service status"
|
||
}
|
||
}
|
||
}
|
||
|
||
# Watch services with provctl
|
||
export def watch-services [
|
||
--interval: int = 5
|
||
--duration: int = 300
|
||
] {
|
||
if not (provctl-available) {
|
||
print-setup-error "provctl not available"
|
||
return
|
||
}
|
||
|
||
print-setup-info $"Watching services every ($interval) seconds for ($duration) seconds..."
|
||
print ""
|
||
|
||
let end_time = ((date now) + ($duration | into duration --unit sec))
|
||
|
||
loop {
|
||
let current_time = (date now)
|
||
if ($current_time > $end_time) {
|
||
break
|
||
}
|
||
|
||
let status = (get-provctl-service-status)
|
||
print $"($current_time | format date '%H:%M:%S') - Services: ($status.services | length)"
|
||
|
||
sleep ($interval | into duration --unit sec)
|
||
}
|
||
|
||
print ""
|
||
print-setup-success "Service watch completed"
|
||
}
|
||
|
||
# ============================================================================
|
||
# PROVCTL SETUP REPORT
|
||
# ============================================================================
|
||
|
||
# Print provctl integration status
|
||
export def print-provctl-status [] {
|
||
print ""
|
||
print "╔═══════════════════════════════════════════════════════════════╗"
|
||
print "║ PROVCTL INTEGRATION STATUS ║"
|
||
print "╚═══════════════════════════════════════════════════════════════╝"
|
||
print ""
|
||
|
||
if (provctl-available) {
|
||
print "✅ provctl is installed and available"
|
||
print ""
|
||
print $"Version: (get-provctl-version)"
|
||
print $"Config Directory: (get-provctl-config-dir)"
|
||
print ""
|
||
print "Status: Enhanced deployment features available"
|
||
print " • Service lifecycle management"
|
||
print " • Zero-downtime updates"
|
||
print " • Advanced monitoring"
|
||
print " • Hot-reload capabilities"
|
||
} else if (has-provctl) {
|
||
print "⚠️ provctl is installed but not functional"
|
||
print ""
|
||
print "Fallback: Using standard deployment methods"
|
||
} else {
|
||
print "ℹ️ provctl is not installed (optional)"
|
||
print ""
|
||
print "Fallback: Using standard deployment methods"
|
||
print " • docker-compose / kubernetes / SSH / systemd"
|
||
print ""
|
||
print "To enable advanced features, install provctl:"
|
||
print " https://github.com/provisioning/provctl"
|
||
}
|
||
|
||
print ""
|
||
print "═══════════════════════════════════════════════════════════════"
|
||
print ""
|
||
}
|
||
|
||
# ============================================================================
|
||
# COMPLETE PROVCTL SETUP
|
||
# ============================================================================
|
||
|
||
# Execute complete provctl integration setup
|
||
export def setup-provctl-integration [
|
||
config_base: string
|
||
--verbose = false
|
||
] {
|
||
print-setup-header "provctl Integration Setup"
|
||
print ""
|
||
|
||
if not (provctl-available) {
|
||
print-setup-info "provctl not available - setup is optional"
|
||
print-setup-info "Provisioning will use fallback deployment methods"
|
||
print ""
|
||
return {
|
||
success: true
|
||
provctl_integrated: false
|
||
reason: "provctl not installed"
|
||
fallback_available: true
|
||
}
|
||
}
|
||
|
||
print-setup-success "provctl detected - setting up integration..."
|
||
print ""
|
||
|
||
# Enhance deployment with provctl
|
||
let enhancement = (enhance-deployment-with-provctl $config_base --verbose=$verbose)
|
||
|
||
if $enhancement.enhanced {
|
||
print-setup-success "provctl integration completed"
|
||
print ""
|
||
if $verbose {
|
||
print-setup-info $"Registered services: ($enhancement.registered_services | str join ', ')"
|
||
}
|
||
} else {
|
||
print-setup-warning "provctl integration partial - using fallback"
|
||
}
|
||
|
||
print ""
|
||
|
||
{
|
||
success: true
|
||
provctl_integrated: $enhancement.enhanced
|
||
provctl_version: (get-provctl-version)
|
||
deployment_method: $enhancement.method
|
||
registered_services: ($enhancement.registered_services? | default [])
|
||
timestamp: (get-timestamp-iso8601)
|
||
}
|
||
}
|
||
|
||
# ============================================================================
|
||
# PROVCTL OPTIONAL MODE HELPERS
|
||
# ============================================================================
|
||
|
||
# Check if setup mode requires provctl
|
||
export def mode-requires-provctl [
|
||
mode: string
|
||
] {
|
||
match $mode {
|
||
"enterprise" => true # Only enterprise mode requires provctl
|
||
_ => false
|
||
}
|
||
}
|
||
|
||
# Get setup mode recommendation based on provctl availability
|
||
export def recommend-setup-mode [
|
||
detection_report: record
|
||
] {
|
||
let provctl_ok = (provctl-available)
|
||
|
||
if $provctl_ok {
|
||
"enterprise" # Can use all features
|
||
} else {
|
||
# Recommend mode based on available tools
|
||
let fallback = (get-fallback-method $detection_report)
|
||
match $fallback {
|
||
"docker-compose" => "solo"
|
||
"kubernetes" => "cicd"
|
||
"remote-ssh" => "remote"
|
||
_ => "minimal"
|
||
}
|
||
}
|
||
}
|
||
|
||
# ============================================================================
|
||
# COMPATIBILITY CHECK
|
||
# ============================================================================
|
||
|
||
# Check if provisioning and provctl versions are compatible
|
||
export def check-provctl-compatibility [] {
|
||
if not (provctl-available) {
|
||
return {
|
||
compatible: true
|
||
reason: "provctl not installed - compatibility check skipped"
|
||
}
|
||
}
|
||
|
||
let provctl_version = (get-provctl-version)
|
||
let provisioning_version = "1.0.0"
|
||
|
||
# Simple compatibility check: both should be 1.x
|
||
let provctl_major = (
|
||
if ($provctl_version | str contains "1.") { "1" }
|
||
else { "2" }
|
||
)
|
||
|
||
let provisioning_major = "1"
|
||
|
||
{
|
||
compatible: ($provctl_major == $provisioning_major)
|
||
provisioning_version: $provisioning_version
|
||
provctl_version: $provctl_version
|
||
warning: (
|
||
if ($provctl_major != $provisioning_major) {
|
||
"Version mismatch detected - functionality may be limited"
|
||
} else {
|
||
null
|
||
}
|
||
)
|
||
}
|
||
}
|