#!/usr/bin/env nu # Taskserv Creation Helper Tool # Modern Nushell-compatible tool for creating taskservs with guided setup use ../core/nulib/taskservs/discover.nu * # Main command dispatcher def main [ command?: string # Command: create, interactive, template, discover, help name?: string # Taskserv name (for create command) --category: string # Taskserv category --port: int = 8080 # Default port --description: string = "" # Service description --author: string = "Developer" # Author name --template: string = "basic" # Template type --output: string = "provisioning/extensions" # Output directory ] { match $command { "create" => { if ($name | is-empty) { error make { msg: "Taskserv name is required for create command" } } create_taskserv $name $category $port $description $author $output } "interactive" | null => { interactive_create } "template" => { if ($name | is-empty) { create_workspace_template } else { create_workspace_template $name } } "discover" => { show_discovery_info } "help" => { show_help } _ => { print $"Unknown command: ($command)" show_help } } } # Interactive taskserv creation def interactive_create [] { print "๐Ÿš€ Interactive Taskserv Creator" print "" # Get basic information let name = input "๐Ÿ“ Enter taskserv name (kebab-case, e.g., 'my-api'):" if ($name | is-empty) or ($name | str contains " ") { error make { msg: "Taskserv name must be kebab-case without spaces" } } # Select category let categories = [ "container-runtime" "databases" "development" "infrastructure" "kubernetes" "networking" "storage" ] print "" print "๐Ÿ“‚ Available categories:" for i in 0..($categories | length) { let cat = ($categories | get $i) print $" ($i + 1). ($cat)" } let category_choice = input "Select category (1-7):" let category_index = (($category_choice | into int) - 1) if $category_index < 0 or $category_index >= ($categories | length) { error make { msg: "Invalid category selection" } } let category = ($categories | get $category_index) # Get additional info let description = input $"๐Ÿ“„ Enter description for ($name) (optional):" let author = input "๐Ÿ‘ค Enter author name (optional):" --default "Developer" # Service type specific questions let port = match $category { "databases" => { let db_ports = { "postgres": 5432, "redis": 6379, "mysql": 3306, "mongodb": 27017 } if ($name | str contains "postgres") { 5432 } else if ($name | str contains "redis") { 6379 } else if ($name | str contains "mysql") { 3306 } else if ($name | str contains "mongo") { 27017 } else { 5432 } } "development" => { if ($name | str contains "api") or ($name | str contains "web") { 8080 } else { 3000 } } _ => 8080 } let port_input = input $"๐Ÿ”Œ Enter port number (default: ($port)):" let final_port = if ($port_input | is-empty) { $port } else { $port_input | into int } print "" print "๐Ÿ“‹ Creating taskserv with:" print $" Name: ($name)" print $" Category: ($category)" print $" Port: ($final_port)" print $" Description: ($description)" print $" Author: ($author)" print "" let confirm = input "โœ… Proceed with creation? (y/N):" if not ($confirm | str downcase | str starts-with "y") { print "โŒ Creation cancelled" return } create_taskserv $name $category $final_port $description $author "provisioning/extensions" } # Create taskserv with parameters def create_taskserv [ name: string category: string port: int description: string author: string output_dir: string ] { print $"๐Ÿ› ๏ธ Creating taskserv: ($name)" # Validate name format if ($name | str contains " ") or ($name | str contains "_") or ($name != ($name | str downcase)) { error make { msg: "Name must be kebab-case (lowercase with hyphens)" } } # Create directory structure let taskserv_path = ($output_dir | path join "taskservs" $category $name) let kcl_path = ($taskserv_path | path join "kcl") let default_path = ($taskserv_path | path join "default") print $"๐Ÿ“ Creating directories..." mkdir $kcl_path mkdir $default_path # Generate variables for templates let variables = generate_variables $name $category $port $description $author # Create files create_kcl_mod $kcl_path $variables create_main_schema $kcl_path $variables create_version_file $kcl_path $variables create_defaults_toml $default_path $variables create_install_script $default_path $variables create_readme $taskserv_path $variables print $"โœ… Taskserv created successfully at: ($taskserv_path)" print "" print "๐Ÿ”„ Next steps:" print "1. Customize the configuration in the .k files" print "2. Update the installation script" print "3. Test discovery:" print $" nu -c \"use provisioning/core/nulib/taskservs/discover.nu *; get-taskserv-info ($name)\"" print "4. Create a workspace template (optional):" print $" nu provisioning/tools/create-taskserv-helper.nu template ($name)" # Show layer resolution test print "" print "๐Ÿงช Test layer resolution:" print $" nu -c \"use provisioning/workspace/tools/layer-utils.nu *; test_layer_resolution ($name) wuji upcloud\"" } # Generate template variables def generate_variables [ name: string category: string port: int description: string author: string ]: nothing -> record { let name_pascal = ($name | split row "-" | each { |word| $word | str capitalize } | str join "") let name_snake = ($name | str replace -a "-" "_") let display_name = ($name | split row "-" | each { |word| $word | str capitalize } | str join " ") let final_description = if ($description | is-empty) { $"Deployment and management for ($display_name)" } else { $description } { name: $name, name_pascal: $name_pascal, name_snake: $name_snake, display_name: $display_name, category: $category, port: $port, description: $final_description, author: $author, date: (date now | format date "%Y-%m-%d"), version: "1.0.0" } } # Create kcl.mod file def create_kcl_mod [kcl_path: string, vars: record] { let content = $"[package] name = \"($vars.name)\" version = \"($vars.version)\" description = \"($vars.description)\" authors = [\"($vars.author)\"] [dependencies] k8s = { oci = \"oci://ghcr.io/kcl-lang/k8s\", tag = \"1.30\" } " $content | save ($kcl_path | path join "kcl.mod") print $" โœ“ Created kcl.mod" } # Create main KCL schema def create_main_schema [kcl_path: string, vars: record] { let content = $"# ($vars.display_name) Taskserv Configuration # ($vars.description) schema ($vars.name_pascal) { # Service metadata name: str = \"($vars.name)\" version: str = \"latest\" namespace: str = \"default\" # Service configuration replicas: int = 1 port: int = ($vars.port) # Resource requirements resources: { cpu: str = \"100m\" memory: str = \"128Mi\" limits?: { cpu?: str = \"500m\" memory?: str = \"512Mi\" } } = { cpu = \"100m\" memory = \"128Mi\" } # Service specific configuration config?: {str: any} = {} # Health checks health?: { enabled: bool = true path: str = \"/health\" initial_delay: int = 30 period: int = 10 } = { enabled = true path = \"/health\" initial_delay = 30 period = 10 } } # Default configuration ($vars.name_snake)_config: ($vars.name_pascal) = ($vars.name_pascal) { name = \"($vars.name)\" version = \"latest\" replicas = 1 port = ($vars.port) } " $content | save ($kcl_path | path join $"($vars.name).k") print $" โœ“ Created ($vars.name).k" } # Create version file def create_version_file [kcl_path: string, vars: record] { let content = $"# Version information for ($vars.name) taskserv schema ($vars.name_pascal)Version { current: str = \"($vars.version)\" compatible: [str] = [\"($vars.version)\"] deprecated?: [str] = [] changelog?: {str: str} = {} } ($vars.name_snake)_version: ($vars.name_pascal)Version = ($vars.name_pascal)Version { current = \"($vars.version)\" changelog = { \"($vars.version)\" = \"Initial release\" } } " $content | save ($kcl_path | path join "version.k") print $" โœ“ Created version.k" } # Create defaults TOML def create_defaults_toml [default_path: string, vars: record] { let content = $"# Default configuration for ($vars.name) # Generated on ($vars.date) [service] name = \"($vars.name)\" version = \"latest\" port = ($vars.port) replicas = 1 [deployment] strategy = \"RollingUpdate\" max_unavailable = 1 max_surge = 1 [resources] cpu_request = \"100m\" cpu_limit = \"500m\" memory_request = \"128Mi\" memory_limit = \"512Mi\" [health] enabled = true path = \"/health\" initial_delay_seconds = 30 period_seconds = 10 timeout_seconds = 5 [networking] service_type = \"ClusterIP\" expose_metrics = false metrics_port = 9090 " $content | save ($default_path | path join "defs.toml") print $" โœ“ Created defs.toml" } # Create installation script def create_install_script [default_path: string, vars: record] { let content = $"#!/bin/bash set -euo pipefail # ($vars.display_name) Installation Script # Generated on ($vars.date) echo \"๐Ÿš€ Installing ($vars.name)...\" # Configuration SERVICE_NAME=\"${SERVICE_NAME:-($vars.name)}\" SERVICE_VERSION=\"${SERVICE_VERSION:-latest}\" NAMESPACE=\"${NAMESPACE:-default}\" REPLICAS=\"${REPLICAS:-1}\" PORT=\"${PORT:-($vars.port)}\" # Validation if [[ -z \"$SERVICE_NAME\" ]]; then echo \"โŒ SERVICE_NAME is required\" exit 1 fi echo \"๐Ÿ“‹ Configuration:\" echo \" Service: $SERVICE_NAME\" echo \" Version: $SERVICE_VERSION\" echo \" Namespace: $NAMESPACE\" echo \" Port: $PORT\" echo \" Replicas: $REPLICAS\" # Create namespace if it doesn't exist echo \"๐Ÿ—๏ธ Creating namespace...\" kubectl create namespace \"$NAMESPACE\" --dry-run=client -o yaml | kubectl apply -f - # Apply deployment echo \"๐Ÿšข Applying deployment...\" cat < -l app=($vars.name) kubectl describe pod -n ``` ### View Logs ```bash kubectl logs -f deployment/($vars.name) -n ``` ### Debug Configuration ```bash # Test KCL configuration kcl run provisioning/extensions/taskservs/($vars.category)/($vars.name)/kcl/($vars.name).k # Validate deployment provisioning/core/cli/provisioning taskserv create ($vars.name) --infra --check ``` ### Common Issues 1. **Pod not starting**: Check resource requests and image availability 2. **Health check failures**: Verify health check endpoint and timing 3. **Service not accessible**: Check service configuration and network policies ## Development ### Layer Resolution This taskserv supports the 3-layer architecture: 1. **Core Layer**: Base configuration in this directory 2. **Workspace Layer**: Templates in `provisioning/workspace/templates/taskservs/($vars.category)/($vars.name).k` 3. **Infrastructure Layer**: Overrides in `workspace/infra//task-servs/($vars.name).k` ### Testing ```bash # Test layer resolution nu -c \"use provisioning/workspace/tools/layer-utils.nu *; test_layer_resolution ($vars.name) \" # Discover taskserv nu -c \"use provisioning/core/nulib/taskservs/discover.nu *; get-taskserv-info ($vars.name)\" ``` ## Contributing 1. Follow the taskserv development guidelines 2. Update version information when making changes 3. Test across different environments 4. Update this README with any new configuration options ## License MIT License ## Author ($vars.author) ## Generated Created on ($vars.date) using the Taskserv Helper Tool. " $content | save ($taskserv_path | path join "README.md") print $" โœ“ Created README.md" } # Create workspace template def create_workspace_template [name?: string] { if ($name == null) or ($name | is-empty) { print "๐Ÿ“‹ Create workspace template for existing taskserv" let taskservs = (discover-taskservs | select name category) print "Available taskservs:" for i in 0..($taskservs | length | $in - 1) { let ts = ($taskservs | get $i) print $" ($i + 1). ($ts.name) (($ts.category))" } let choice = input "Select taskserv number:" let index = (($choice | into int) - 1) if $index < 0 or $index >= ($taskservs | length) { error make { msg: "Invalid selection" } } let selected = ($taskservs | get $index) create_template_for_taskserv $selected.name $selected.category } else { let taskserv_info = get-taskserv-info $name create_template_for_taskserv $taskserv_info.name $taskserv_info.group } } # Create template for specific taskserv def create_template_for_taskserv [name: string, category: string] { print $"๐Ÿ“ Creating workspace template for ($name)" let template_dir = ("provisioning/workspace/templates/taskservs" | path join $category) let template_file = ($template_dir | path join $"($name).k") mkdir $template_dir let name_snake = ($name | str replace -a "-" "_") let name_pascal = ($name | str replace -a "-" " " | str title-case | str replace -a " " "") let content = $"# Workspace template for ($name) taskserv import taskservs.($category).($name).kcl.($name) as base # Template configuration extending base ($name_snake)_template: base.($name_pascal) = base.($name_snake)_config { # Template customizations for workspace layer # These can be overridden at the infrastructure layer # Common template settings version = \"stable\" # Adjust for template environment replicas = 2 # Default for multi-replica setup # Template resource defaults resources = base.($name_snake)_config.resources { cpu = \"200m\" memory = \"256Mi\" limits = { cpu = \"1000m\" memory = \"1Gi\" } } # Template-level configuration config = { \"environment\" = \"template\" \"log_level\" = \"info\" } } " $content | save $template_file print $"โœ… Created workspace template: ($template_file)" print "" print "๐Ÿงช Test the template:" print $" nu -c \"use provisioning/workspace/tools/layer-utils.nu *; test_layer_resolution ($name) \"" } # Show discovery information def show_discovery_info [] { print "๐Ÿ” Taskserv Discovery Information" print "" let taskservs = discover-taskservs let total = ($taskservs | length) let by_group = ($taskservs | group-by group | transpose group taskservs | each { |row| {group: $row.group, count: ($row.taskservs | length)} }) print $"๐Ÿ“Š Total taskservs discovered: ($total)" print "" print "๐Ÿ“‚ By category:" for group_info in $by_group { print $" ($group_info.group): ($group_info.count) taskservs" let group_taskservs = ($taskservs | where group == $group_info.group | get name) for taskserv in $group_taskservs { print $" โ€ข ($taskserv)" } } print "" print "๐Ÿ› ๏ธ Available commands:" print " nu -c \"use provisioning/core/nulib/taskservs/discover.nu *; discover-taskservs\"" print " nu -c \"use provisioning/core/nulib/taskservs/discover.nu *; search-taskservs \"" print " nu -c \"use provisioning/workspace/tools/layer-utils.nu *; show_layer_stats\"" } # Show help information def show_help [] { print "๐Ÿš€ Taskserv Helper Tool" print "" print "USAGE:" print " nu provisioning/tools/create-taskserv-helper.nu [options]" print "" print "COMMANDS:" print " interactive Launch interactive taskserv creator" print " create Create taskserv with specified name" print " template [name] Create workspace template for taskserv" print " discover Show discovery information" print " help Show this help message" print "" print "CREATE OPTIONS:" print " --category Taskserv category (required for create)" print " --port Service port (default: 8080)" print " --description Service description" print " --author Author name (default: Developer)" print " --output Output directory (default: provisioning/extensions)" print "" print "CATEGORIES:" print " container-runtime Container runtime engines" print " databases Database services" print " development Development tools" print " infrastructure System infrastructure" print " kubernetes Kubernetes orchestration" print " networking Network services" print " storage Storage solutions" print "" print "EXAMPLES:" print " # Interactive creation" print " nu provisioning/tools/create-taskserv-helper.nu interactive" print "" print " # Create API service" print " nu provisioning/tools/create-taskserv-helper.nu create my-api \\" print " --category development \\" print " --port 8080 \\" print " --description \"My REST API service\"" print "" print " # Create database service" print " nu provisioning/tools/create-taskserv-helper.nu create my-db \\" print " --category databases \\" print " --port 5432 \\" print " --author \"Database Team\"" print "" print " # Create workspace template" print " nu provisioning/tools/create-taskserv-helper.nu template my-api" print "" print "๐Ÿ“– For more information, see:" print " docs/development/TASKSERV_DEVELOPER_GUIDE.md" print " docs/development/TASKSERV_QUICK_GUIDE.md" } # String utility functions def "str title-case" [] { let text = $in $text | split row " " | each { |word| if ($word | is-empty) { $word } else { ($word | str substring 0..1 | str upcase) + ($word | str substring 1..) } } | str join " " }