# Taskserv Dependency Validator # Validates taskserv dependencies, conflicts, and requirements use lib_provisioning * use utils.nu * use ../lib_provisioning/config/accessor.nu * # Validate taskserv dependencies from KCL definition export def validate-dependencies [ taskserv_name: string settings: record --verbose (-v) ]: nothing -> record { let taskservs_path = (get-taskservs-path) let taskserv_kcl_path = ($taskservs_path | path join $taskserv_name "kcl") # Check if taskserv has dependencies.k let deps_file = ($taskserv_kcl_path | path join "dependencies.k") if not ($deps_file | path exists) { return { valid: true taskserv: $taskserv_name has_dependencies: false warnings: [] errors: [] } } if $verbose { _print $"Validating dependencies for (_ansi yellow_bold)($taskserv_name)(_ansi reset)..." } # Run KCL to extract dependency information let result = try { kcl run $deps_file --format json | from json } catch { return { valid: false taskserv: $taskserv_name has_dependencies: true warnings: [] errors: [$"Failed to parse dependencies.k: ($in)"] } } # Extract dependency information let deps = ($result | get -o _dependencies) if $deps == null { return { valid: true taskserv: $taskserv_name has_dependencies: false warnings: ["dependencies.k exists but no _dependencies defined"] errors: [] } } let requires = ($deps | get -o requires | default []) let optional = ($deps | get -o optional | default []) let conflicts = ($deps | get -o conflicts | default []) mut warnings = [] mut errors = [] # Validate required dependencies for req in $requires { let req_path = ($taskservs_path | path join $req) if not ($req_path | path exists) { $errors = ($errors | append $"Required dependency not found: ($req)") } else if $verbose { _print $" ✓ Required: ($req)" } } # Check optional dependencies for opt in $optional { let opt_path = ($taskservs_path | path join $opt) if not ($opt_path | path exists) { $warnings = ($warnings | append $"Optional dependency not available: ($opt)") } else if $verbose { _print $" ℹ Optional: ($opt)" } } # Validate conflicts for conf in $conflicts { let conf_path = ($taskservs_path | path join $conf) if ($conf_path | path exists) { $warnings = ($warnings | append $"Conflicting taskserv installed: ($conf)") } else if $verbose { _print $" ✓ No conflict: ($conf)" } } # Validate resource requirements let resource_req = ($deps | get -o resource_requirements) if $resource_req != null { let min_memory = ($resource_req | get -o min_memory | default 0) let min_cores = ($resource_req | get -o min_cores | default 0) let min_disk = ($resource_req | get -o min_disk | default 0) if $verbose { _print $" Resource requirements:" _print $" Memory: ($min_memory) MB" _print $" Cores: ($min_cores)" _print $" Disk: ($min_disk) GB" } # TODO: Could validate against server specs if available in settings } # Validate health check configuration let health_check = ($deps | get -o health_check) if $health_check != null { let endpoint = ($health_check | get -o endpoint | default "") let timeout = ($health_check | get -o timeout | default 30) if $endpoint == "" { $warnings = ($warnings | append "Health check defined but no endpoint specified") } else if $verbose { _print $" Health check: ($endpoint) (timeout: ($timeout)s)" } } return { valid: (($errors | length) == 0) taskserv: $taskserv_name has_dependencies: true requires: $requires optional: $optional conflicts: $conflicts resource_requirements: $resource_req health_check: $health_check warnings: $warnings errors: $errors } } # Validate dependencies for taskserv in infrastructure context export def validate-infra-dependencies [ taskserv_name: string settings: record --verbose (-v) ]: nothing -> record { let validation = (validate-dependencies $taskserv_name $settings --verbose=$verbose) if not $validation.has_dependencies { return $validation } # Check against installed taskservs in infrastructure let installed_taskservs = try { $settings.data.servers | each {|srv| $srv.taskservs | get name} | flatten | uniq } catch { [] } mut infra_errors = [] mut infra_warnings = [] # Check if required dependencies are in infrastructure for req in ($validation.requires | default []) { if $req not-in $installed_taskservs { $infra_errors = ($infra_errors | append $"Required dependency '($req)' not in infrastructure") } } # Check for conflicts in infrastructure for conf in ($validation.conflicts | default []) { if $conf in $installed_taskservs { $infra_errors = ($infra_errors | append $"Conflicting taskserv '($conf)' found in infrastructure") } } return ($validation | merge { infra_validation: true installed_taskservs: $installed_taskservs errors: (($validation.errors | default []) | append $infra_errors) warnings: (($validation.warnings | default []) | append $infra_warnings) valid: ((($validation.errors | default []) | append $infra_errors | length) == 0) }) } # Check dependencies for all taskservs export def check-all-dependencies [ settings: record --verbose (-v) ]: nothing -> table { let taskservs_path = (get-taskservs-path) # Find all taskservs with dependencies.k let all_taskservs = ( ls ($taskservs_path | path join "**/kcl/dependencies.k") | get name | each {|path| $path | path dirname | path dirname | path basename } ) if $verbose { _print $"Found ($all_taskservs | length) taskservs with dependencies" } $all_taskservs | each {|ts| validate-dependencies $ts $settings --verbose=$verbose } } # Print dependency validation report export def print-validation-report [ validation: record ]: nothing -> nothing { _print $"\n(_ansi cyan_bold)Dependency Validation Report(_ansi reset)" _print $"Taskserv: (_ansi yellow_bold)($validation.taskserv)(_ansi reset)" if not $validation.has_dependencies { _print $" (_ansi green)No dependencies defined(_ansi reset)" return } _print $"\nStatus: (if $validation.valid { (_ansi green_bold)VALID(_ansi reset) } else { (_ansi red_bold)INVALID(_ansi reset) })" if ($validation.requires | default [] | length) > 0 { _print $"\n(_ansi cyan)Required Dependencies:(_ansi reset)" for req in $validation.requires { _print $" • ($req)" } } if ($validation.optional | default [] | length) > 0 { _print $"\n(_ansi cyan)Optional Dependencies:(_ansi reset)" for opt in $validation.optional { _print $" • ($opt)" } } if ($validation.conflicts | default [] | length) > 0 { _print $"\n(_ansi cyan)Conflicts:(_ansi reset)" for conf in $validation.conflicts { _print $" • ($conf)" } } if ($validation.warnings | length) > 0 { _print $"\n(_ansi yellow_bold)Warnings:(_ansi reset)" for warn in $validation.warnings { _print $" ⚠ ($warn)" } } if ($validation.errors | length) > 0 { _print $"\n(_ansi red_bold)Errors:(_ansi reset)" for err in $validation.errors { _print $" ✗ ($err)" } } }