545 lines
17 KiB
Text
545 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
|
|||
|
|
}
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
}
|