From e18dd5a2030ba1a3b5f5bb6a8d5a1fc3c2f58b85 Mon Sep 17 00:00:00 2001 From: Dmitry Smirnov Date: Wed, 19 Feb 2025 12:58:11 +0300 Subject: [PATCH] improve cli --- lib/cli/auth.sh | 14 ++- lib/cli/config.sh | 21 +++-- lib/cli/env.sh | 153 +++++++++++++++++++++--------- lib/cli/health.sh | 19 ++-- lib/cli/info.sh | 230 ++++++++++++++++++++++++++++++--------------- lib/cli/logs.sh | 20 +++- lib/cli/sbom.sh | 24 +++-- lib/cli/service.sh | 16 +++- lib/env_handler.sh | 32 +++++++ 9 files changed, 374 insertions(+), 155 deletions(-) diff --git a/lib/cli/auth.sh b/lib/cli/auth.sh index 303259c..4dc412c 100644 --- a/lib/cli/auth.sh +++ b/lib/cli/auth.sh @@ -25,7 +25,8 @@ Examples: EOF } -# Show authentication status +# Description: Display authentication status for all cloud providers +# Example: worker auth status show_auth_status() { log_info "Auth" "Checking authentication status..." @@ -39,7 +40,9 @@ show_auth_status() { done } -# Test authentication +# Description: Test authentication for cloud providers +# Options: --provider aws|gcp|azure|bitwarden +# Example: worker auth test --provider aws test_auth() { local provider=$1 @@ -61,7 +64,8 @@ test_auth() { fi } -# Refresh credentials +# Description: Refresh credentials for all configured providers +# Example: worker auth refresh refresh_auth() { log_info "Auth" "Refreshing credentials..." @@ -82,7 +86,9 @@ refresh_auth() { fi } -# Rotate credentials +# Description: Rotate credentials for a specific provider +# Options: --provider aws|gcp|azure|bitwarden +# Example: worker auth rotate --provider aws rotate_auth() { local provider=$1 diff --git a/lib/cli/config.sh b/lib/cli/config.sh index f5e9cea..5557406 100644 --- a/lib/cli/config.sh +++ b/lib/cli/config.sh @@ -29,7 +29,9 @@ Examples: EOF } -# Show current configuration +# Description: Display current worker configuration +# Options: --format yaml|json +# Example: worker config show --format json show_config() { local format=${1:-yaml} log_info "Config" "Current configuration:" @@ -51,7 +53,8 @@ show_config() { esac } -# Edit configuration +# Description: Edit configuration in default editor +# Example: worker config edit edit_config() { local config_file="${HOME}/.config/worker/worker.yaml" @@ -75,7 +78,9 @@ edit_config() { fi } -# Validate configuration +# Description: Validate all configuration files or a specific one +# Options: --file PATH +# Example: worker config validate --file ~/.config/worker/worker.yaml validate_config() { local config_file=${1:-} log_info "Config" "Validating configuration..." @@ -113,7 +118,8 @@ validate_config() { fi } -# Show configuration locations +# Description: Display paths of all configuration files +# Example: worker config locations show_locations() { cat << EOF Configuration Locations: @@ -124,7 +130,8 @@ Configuration Locations: EOF } -# Initialize new configuration +# Description: Initialize a new configuration file with defaults +# Example: worker config init init_config() { local config_dir="${HOME}/.config/worker" local config_file="$config_dir/worker.yaml" @@ -153,7 +160,9 @@ init_config() { fi } -# Show differences between default and current config +# Description: Show differences between default and current configuration +# Options: --format unified|context|git +# Example: worker config diff --format git show_diff() { local default_config="/etc/worker/worker.yaml" local user_config="${HOME}/.config/worker/worker.yaml" diff --git a/lib/cli/env.sh b/lib/cli/env.sh index 84a689d..34032f7 100644 --- a/lib/cli/env.sh +++ b/lib/cli/env.sh @@ -37,49 +37,61 @@ Examples: EOF } -# Show environment variables +# Description: Display environment variables with optional filtering +# Options: --format text|json, --filter PATTERN, --include-secrets +# Example: worker env show --format json --filter AWS_* --include-secrets show_environment() { local format=${1:-text} local filter=$2 local include_secrets=${3:-false} - if [ "$include_secrets" == "true" ]; then - list_env_vars "$format" - else - # Only show non-secret environment variables - if [ -f "$WORKER_ENV_FILE" ]; then - case $format in - json) - { - echo "{" - if [ -n "$filter" ]; then - grep "^export $filter" "$WORKER_ENV_FILE" | sed 's/export \([^=]*\)="\([^"]*\)"/ "\1": "\2",/' - else - grep "^export" "$WORKER_ENV_FILE" | sed 's/export \([^=]*\)="\([^"]*\)"/ "\1": "\2",/' - fi - echo "}" - } | sed 's/,}/}/' | jq '.' - ;; - text) - if [ -n "$filter" ]; then - grep "^export $filter" "$WORKER_ENV_FILE" | sed 's/export \([^=]*\)="\([^"]*\)"/\1=\2/' - else - grep "^export" "$WORKER_ENV_FILE" | sed 's/export \([^=]*\)="\([^"]*\)"/\1=\2/' + # Check if environment file exists + if [ ! -f "$WORKER_ENV_FILE" ]; then + log_error "Env" "Environment file not found" + return 1 + fi + + case $format in + json) + # Get variables and convert to JSON + local vars + if [ -n "$filter" ]; then + vars=$(grep "^export $filter" "$WORKER_ENV_FILE") + else + vars=$(grep "^export" "$WORKER_ENV_FILE") + fi + + # Convert to JSON + local json="{" + while IFS= read -r line; do + if [[ $line =~ ^export[[:space:]]+([^=]+)=\"([^\"]*)\" ]]; then + if [ -n "$json" ] && [ "$json" != "{" ]; then + json="$json," fi - ;; - *) - log_error "Env" "Unknown format: $format" - return 1 - ;; - esac - else - log_error "Env" "Environment file not found" + key=${BASH_REMATCH[1]} + value=${BASH_REMATCH[2]} + json="$json\"$key\":\"$value\"" + fi + done <<< "$vars" + json="$json}" + echo "$json" | jq . + ;; + text) + if [ -n "$filter" ]; then + grep "^export $filter" "$WORKER_ENV_FILE" | sed 's/export \([^=]*\)="\([^"]*\)"/\1=\2/' + else + grep "^export" "$WORKER_ENV_FILE" | sed 's/export \([^=]*\)="\([^"]*\)"/\1=\2/' + fi + ;; + *) + log_error "Env" "Unknown format: $format" return 1 - fi - fi + ;; + esac } -# Set environment variable +# Description: Set a new environment variable or update existing one +# Example: worker env set MY_VAR "my value" set_environment() { local name=$1 local value=$2 @@ -110,7 +122,8 @@ set_environment() { fi } -# Unset environment variable +# Description: Remove an environment variable +# Example: worker env unset MY_VAR unset_environment() { local name=$1 @@ -119,15 +132,32 @@ unset_environment() { return 1 fi - if [ -n "${!name}" ]; then + # Check if variable exists in environment file + if grep -q "^export $name=" "$WORKER_ENV_FILE"; then + # Create a temporary file + local tmpfile + tmpfile=$(mktemp) + + # Remove the variable from environment file + grep -v "^export $name=" "$WORKER_ENV_FILE" > "$tmpfile" + cat "$tmpfile" > "$WORKER_ENV_FILE" + rm -f "$tmpfile" + + # Also remove from current environment unset "$name" + log_success "Env" "Unset $name" + + # Reload environment to ensure consistency + source "$WORKER_ENV_FILE" else log_warn "Env" "Variable $name is not set" fi } -# Export environment variables +# Description: Export environment variables to a file +# Options: --file PATH, --include-secrets +# Example: worker env export --file env.backup --include-secrets export_environment() { local file=$1 local filter=$2 @@ -147,7 +177,9 @@ export_environment() { log_success "Env" "Environment variables exported to $file" } -# Import environment variables +# Description: Import environment variables from a file +# Options: --file PATH +# Example: worker env import --file env.backup import_environment() { local file=$1 @@ -171,7 +203,9 @@ import_environment() { log_success "Env" "Environment variables imported from $file" } -# Validate environment variables +# Description: Validate environment variables against schema +# Options: --format text|json +# Example: worker env validate --format json validate_environment() { local config config=$(load_and_parse_config) @@ -200,7 +234,9 @@ validate_environment() { fi } -# Generate environment template +# Description: Generate a template environment file +# Options: --file PATH +# Example: worker env template --file .env.template generate_template() { local config config=$(load_and_parse_config) @@ -218,7 +254,8 @@ generate_template() { echo "$config" | yq eval '.config.env | to_entries | .[] | "# " + .key + "\n" + .key + "=\"" + .value + "\""' - } -# Reset environment +# Description: Reset environment to default values +# Example: worker env reset reset_environment() { # Clear all non-system variables local system_vars="HOME|USER|PATH|SHELL|TERM|LANG|PWD" @@ -244,21 +281,43 @@ reset_environment() { # Parse command line arguments parse_args() { + # Initialize variables with defaults + format="text" + filter="" + file="" + include_secrets="false" + local args=() while [[ $# -gt 0 ]]; do case $1 in - --format) - format=$2 + --format=*|--type=*) + format="${1#*=}" + shift + ;; + --format|--type) + format="$2" shift 2 ;; + --filter=*) + filter="${1#*=}" + shift + ;; --filter) - filter=$2 + filter="$2" shift 2 ;; + --file=*) + file="${1#*=}" + shift + ;; --file) - file=$2 + file="$2" shift 2 ;; + --include-secrets) + include_secrets="true" + shift + ;; *) args+=("$1") shift @@ -268,7 +327,9 @@ parse_args() { set -- "${args[@]}" } -# Show environment status +# Description: Show environment status and validation results +# Options: --format text|json +# Example: worker env status --format json show_status() { local format=${1:-text} diff --git a/lib/cli/health.sh b/lib/cli/health.sh index 0ecd4fb..39d97be 100644 --- a/lib/cli/health.sh +++ b/lib/cli/health.sh @@ -24,7 +24,8 @@ Examples: EOF } -# Run health check +# Description: Run comprehensive health check of the worker +# Example: worker health check check_health() { log_info "Health" "Running health check..." local failed=0 @@ -49,7 +50,8 @@ check_health() { fi } -# Check system resources +# Description: Check system resource usage (disk, memory, CPU) +# Example: worker health check system check_system_resources() { local failed=0 @@ -86,7 +88,8 @@ check_system_resources() { return $failed } -# Check supervisor status +# Description: Check if supervisor is running and responsive +# Example: worker health check supervisor check_supervisor_status() { if ! pgrep -f supervisord > /dev/null; then log_error "Health" "Supervisor is not running" @@ -102,7 +105,8 @@ check_supervisor_status() { return 0 } -# Check services health +# Description: Check health status of all managed services +# Example: worker health check services check_services_health() { local failed=0 local services_status @@ -129,7 +133,8 @@ check_services_health() { return $failed } -# Check authentication status +# Description: Check authentication status for all providers +# Example: worker health check auth check_auth_status() { local failed=0 @@ -148,7 +153,9 @@ check_auth_status() { return $failed } -# Generate health report +# Description: Generate detailed health report +# Options: --format text|json +# Example: worker health report --format json generate_report() { local format=${1:-text} local report_file diff --git a/lib/cli/info.sh b/lib/cli/info.sh index e2836d4..b5c384c 100644 --- a/lib/cli/info.sh +++ b/lib/cli/info.sh @@ -3,41 +3,135 @@ # shellcheck source=${WORKER_LIB_DIR}/utils.sh disable=SC1091 source "${WORKER_LIB_DIR}/utils.sh" +# Get command metadata by scanning function contents +get_command_metadata() { + local commands={} + declare -A commands + local current_file="${BASH_SOURCE[0]}" + + # Scan the current file for show_* functions and their metadata + while IFS= read -r line; do + # Match show_* function definitions + if [[ $line =~ ^show_([a-z_]+)[[:space:]]*\(\)[[:space:]]*\{[[:space:]]*$ ]]; then + local cmd=${BASH_REMATCH[1]} + local desc="" + local options="" + local example="" + + # Skip internal functions like show_help + [[ $cmd == "help" ]] && continue + + # Look for metadata in comments above function + local temp_file=$(mktemp) + grep -B 10 "^show_${cmd}()" "$current_file" > "$temp_file" + + # Get description + desc=$(grep -B 10 "^show_${cmd}()" "$temp_file" | grep '^# Description:' | tail -n 1 | sed 's/^# Description:[[:space:]]*//') + if [ -z "$desc" ]; then + desc=$(grep -B 10 "^show_${cmd}()" "$temp_file" | grep '^#[[:space:]]' | tail -n 1 | sed 's/^#[[:space:]]*//') + fi + if [ -z "$desc" ]; then + desc="Show ${cmd//_/ } information" + fi + + # Get options + options=$(grep -B 10 "^show_${cmd}()" "$temp_file" | grep '^# Options:' | tail -n 1 | sed 's/^# Options:[[:space:]]*//') + if [ -z "$options" ] && grep -q 'format=' "$temp_file"; then + options="--format text|json" + fi + if grep -q 'verbose=' "$temp_file"; then + [[ -n "$options" ]] && options="$options, " + options+="--verbose" + fi + + # Get example + example=$(grep -B 10 "^show_${cmd}()" "$temp_file" | grep '^# Example:' | tail -n 1 | sed 's/^# Example:[[:space:]]*//') + if [ -z "$example" ] && [ -n "$options" ]; then + if [[ $options == *"format"* ]]; then + example="worker info $cmd --format json" + elif [[ $options == *"verbose"* ]]; then + example="worker info $cmd --verbose" + fi + fi + + rm "$temp_file" + + # Store metadata + commands[$cmd]="$desc|$options|$example" + fi + done < "$current_file" + + echo "$(declare -p commands)" +} + # Show help for info command info_help() { + local -A commands + eval "$(get_command_metadata)" + + # Find the longest command name for proper padding + local max_length=0 + for cmd in "${!commands[@]}"; do + local len=${#cmd} + if ((len > max_length)); then + max_length=$len + fi + done + + # Add padding for alignment + max_length=$((max_length + 2)) + cat << EOF Display information about the UDX Worker image and runtime Usage: worker info [command] Available Commands: - overview Show a high-level overview of the worker image - system Show system information and resource usage - config Show configuration and settings - services Show available services and their status - env Show environment variables and settings - auth Show authentication and credentials status - deps Show installed dependencies and versions - features Show available features and capabilities - paths Show important filesystem paths - logs Show logging configuration and locations - security Show security settings and policies - network Show network configuration and ports - version Show detailed version information - -Options: - --format Output format (text/json) - --verbose Show detailed information - -Examples: - worker info overview # Get a quick overview of the worker - worker info system --verbose # Show detailed system information - worker info deps --format json # List dependencies in JSON format - worker info features # Show available features EOF + + # Sort commands alphabetically and display + local sorted_commands=($(echo "${!commands[@]}" | tr ' ' '\n' | sort)) + for cmd in "${sorted_commands[@]}"; do + IFS='|' read -r desc options example <<< "${commands[$cmd]}" + printf " %-${max_length}s %s\n" "$cmd" "$desc" + done + + # Collect unique options from all commands + local all_options="" + for cmd in "${!commands[@]}"; do + IFS='|' read -r _ options _ <<< "${commands[$cmd]}" + if [ -n "$options" ]; then + all_options="$all_options $options" + fi + done + + # Display unique options + if [ -n "$all_options" ]; then + echo -e "\nOptions:" + echo "$all_options" | tr ',' '\n' | tr ' ' '\n' | sort -u | grep -v '^$' | while read -r opt; do + printf " %s\n" "$opt" + done + fi + + # Display examples for commands that have them + local has_examples=false + for cmd in "${sorted_commands[@]}"; do + IFS='|' read -r desc options example <<< "${commands[$cmd]}" + if [ -n "$example" ]; then + if ! $has_examples; then + echo -e "\nExamples:" + has_examples=true + fi + printf " %-45s # %s\n" "$example" "$desc" + fi + done + + echo } -# Show general information +# Description: Display general information about the worker +# Options: --format text|json +# Example: worker info --format json show_info() { local format=${1:-text} @@ -101,7 +195,9 @@ show_info() { esac } -# Show system information +# Description: Display detailed system information and resource usage +# Options: --format text|json, --verbose +# Example: worker info system --verbose --format json show_system_info() { local format=${1:-text} @@ -197,7 +293,9 @@ get_cli_modules() { echo "$(declare -p modules)" } -# Show overview of the worker +# Description: Display a high-level overview of the worker image and its capabilities +# Options: --format text|json +# Example: worker info overview --format text show_overview() { local format=${1:-text} local -A modules @@ -297,7 +395,9 @@ EOF esac } -# Show available features +# Description: Display available features and their capabilities +# Options: --format text|json +# Example: worker info features --format json show_features() { local format=${1:-text} local -A modules @@ -384,54 +484,30 @@ EOF info_handler() { local command=$1 shift - - case $command in - overview) - show_overview "$@" - ;; - system) - show_system_info "$@" - ;; - features) - show_features "$@" - ;; - deps) - show_deps "$@" - ;; - config) - show_config "$@" - ;; - services) - show_services "$@" - ;; - env) - show_env "$@" - ;; - auth) - show_auth "$@" - ;; - paths) - show_paths "$@" - ;; - logs) - show_logs "$@" - ;; - security) - show_security "$@" - ;; - network) - show_network "$@" - ;; - version) - show_version "$@" - ;; - help|"") - info_help - ;; - *) - log_error "Info" "Unknown command: $command" - info_help - return 1 - ;; - esac + + # If no command provided or help requested, show help + if [ -z "$command" ] || [ "$command" = "help" ]; then + info_help + return + fi + + # Get available commands + local -A commands + eval "$(get_command_metadata)" + + # Check if command exists + if [ -z "${commands[$command]+x}" ]; then + log_error "Info" "Unknown command: $command" + info_help + return 1 + fi + + # Try to execute the command function + local func_name="show_${command}" + if declare -F "$func_name" > /dev/null; then + "$func_name" "$@" + else + log_error "Info" "Command handler not implemented: $command" + return 1 + fi } diff --git a/lib/cli/logs.sh b/lib/cli/logs.sh index 03a92db..7d8d28f 100644 --- a/lib/cli/logs.sh +++ b/lib/cli/logs.sh @@ -57,7 +57,9 @@ get_log_file() { esac } -# Show logs +# Description: Display logs for a specific service +# Options: --service NAME, --type out|err|all, --lines N, --since TIME, --until TIME +# Example: worker logs show --service myapp --type err --lines 100 --since "2024-01-01" show_logs() { local service=$1 local type=${2:-all} @@ -91,7 +93,9 @@ show_logs() { eval "$cmd $log_file" } -# Follow logs +# Description: Follow logs in real-time for a service +# Options: --service NAME, --type out|err|all +# Example: worker logs follow --service myapp --type err follow_logs() { local service=$1 local type=${2:-all} @@ -112,7 +116,9 @@ follow_logs() { tail -f "$log_file" } -# Search logs +# Description: Search logs for a specific pattern +# Options: --pattern PATTERN, --service NAME, --type out|err|all +# Example: worker logs search --pattern "error" --service myapp search_logs() { local pattern=$1 local service=$2 @@ -133,7 +139,9 @@ search_logs() { grep -n "$pattern" $log_file } -# Export logs +# Description: Export logs to a file +# Options: --service NAME, --type out|err|all, --since TIME, --until TIME, --format text|json +# Example: worker logs export --service myapp --since "2024-01-01" --format json export_logs() { local service=$1 local type=${2:-all} @@ -180,7 +188,9 @@ export_logs() { log_success "Logs" "Logs exported to $output_file" } -# Clean old logs +# Description: Clean old log files +# Options: --older-than DURATION +# Example: worker logs clean --older-than 7d clean_logs() { local older_than=${1:-7d} # Default: 7 days diff --git a/lib/cli/sbom.sh b/lib/cli/sbom.sh index 0d3eb57..29c5561 100644 --- a/lib/cli/sbom.sh +++ b/lib/cli/sbom.sh @@ -33,7 +33,9 @@ Examples: EOF } -# Generate SBOM +# Description: Generate Software Bill of Materials in various formats +# Options: --format text|json|cyclonedx|spdx, --type system|python|all, --filter PATTERN +# Example: worker sbom generate --format json --type python --filter requests generate_sbom() { local format=${1:-text} local type=${2:-all} @@ -135,7 +137,9 @@ generate_sbom() { esac } -# Analyze dependencies +# Description: Analyze dependencies for security vulnerabilities +# Options: --type system|python|all, --severity low|medium|high|critical +# Example: worker sbom analyze --type python --severity high analyze_deps() { log_info "SBOM" "Analyzing dependencies for security issues..." @@ -153,7 +157,9 @@ analyze_deps() { fi } -# Export SBOM +# Description: Export SBOM to a file in specified format +# Options: --format text|json|cyclonedx|spdx, --file PATH +# Example: worker sbom export --format cyclonedx --file sbom.xml export_sbom() { local format=$1 local file=$2 @@ -167,7 +173,9 @@ export_sbom() { log_success "SBOM" "SBOM exported to $file" } -# Show dependency tree +# Description: Display dependency tree for installed packages +# Options: --type system|python|all, --format text|json +# Example: worker sbom deps --type python --format json show_deps() { local type=${1:-all} @@ -178,7 +186,9 @@ show_deps() { fi } -# Verify package integrity +# Description: Verify integrity of installed packages +# Options: --type system|python|all +# Example: worker sbom verify --type all verify_packages() { log_info "SBOM" "Verifying package integrity..." @@ -198,7 +208,9 @@ verify_packages() { fi } -# Check for updates +# Description: Check for available package updates +# Options: --type system|python|all, --format text|json +# Example: worker sbom updates --type all --format json check_updates() { log_info "SBOM" "Checking for available updates..." diff --git a/lib/cli/service.sh b/lib/cli/service.sh index 4fb3293..b2baadc 100644 --- a/lib/cli/service.sh +++ b/lib/cli/service.sh @@ -72,7 +72,8 @@ Examples: EOF } -# Function to list all services +# Description: List all configured services and their status +# Example: worker service list list_services() { # Capture the output of supervisorctl status local services_status @@ -94,7 +95,8 @@ list_services() { done } -# Function to check the status of one or all services +# Description: Show detailed status of a specific service +# Example: worker service status my-app check_status() { # Require a service name for this function if [ -z "$1" ]; then @@ -117,7 +119,9 @@ check_status() { echo "$service_status" } -# Function to follow logs for a specific service +# Description: View and follow logs for a service +# Options: --tail N, --follow, --error-only +# Example: worker service logs my-app --tail 100 --follow follow_logs() { local service_name="" local type="out" @@ -180,7 +184,8 @@ follow_logs() { fi } -# Function to show supervisor configuration +# Description: Display current service configuration +# Example: worker service config show_config() { if [ ! -f "/etc/supervisord.conf" ]; then log_error "Service" "Configuration file is not generated since no services are managed." @@ -189,7 +194,8 @@ show_config() { cat /etc/supervisord.conf } -# Function to start, stop, or restart a service +# Description: Start, stop, or restart a service +# Example: worker service restart my-app manage_service() { if [ -z "$2" ]; then log_error "Service" "Error: No service name provided." diff --git a/lib/env_handler.sh b/lib/env_handler.sh index f253cc9..54a4bbc 100644 --- a/lib/env_handler.sh +++ b/lib/env_handler.sh @@ -88,6 +88,38 @@ load_secrets() { fi } +# Format environment variables as JSON or text +format_env_vars() { + local vars=$1 + local format=${2:-text} + + case $format in + json) + # Convert to JSON + local json="{" + while IFS= read -r line; do + if [[ $line =~ ^export[[:space:]]+([^=]+)=\"([^\"]*)\" ]]; then + if [ -n "$json" ] && [ "$json" != "{" ]; then + json="$json," + fi + key=${BASH_REMATCH[1]} + value=${BASH_REMATCH[2]} + json="$json\"$key\":\"$value\"" + fi + done <<< "$vars" + json="$json}" + echo "$json" | jq . + ;; + text) + echo "$vars" | sed 's/export \([^=]*\)=\"\([^\"]*\)"/\1=\2/' + ;; + *) + log_error "Environment" "Unknown format: $format" + return 1 + ;; + esac +} + # Initialize environment init_environment() { generate_env_file