pvectl 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.claude/rules/branch-before-changes.md +52 -0
- data/.claude/rules/documentation-updates.md +104 -0
- data/.claude/rules/git-workflow.md +84 -0
- data/.claude/rules/proxmox-api-docs.md +58 -0
- data/.claude/rules/rbs-signatures.md +80 -0
- data/.claude/rules/refactoring-as-design-option.md +35 -0
- data/.claude/scheduled_tasks.lock +1 -0
- data/.claude/settings.json +51 -0
- data/.mcp.json +8 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +138 -0
- data/CLAUDE.md +211 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE.txt +21 -0
- data/README.md +143 -0
- data/Rakefile +8 -0
- data/docs/proxmox-api-update.sh +96 -0
- data/exe/pvectl +5 -0
- data/lib/pvectl/argv_preprocessor.rb +334 -0
- data/lib/pvectl/cli.rb +102 -0
- data/lib/pvectl/commands/apt.rb +389 -0
- data/lib/pvectl/commands/clone_container.rb +230 -0
- data/lib/pvectl/commands/clone_vm.rb +331 -0
- data/lib/pvectl/commands/cloudinit/command.rb +122 -0
- data/lib/pvectl/commands/cloudinit/dump.rb +94 -0
- data/lib/pvectl/commands/cloudinit/pending.rb +137 -0
- data/lib/pvectl/commands/cloudinit/regenerate.rb +79 -0
- data/lib/pvectl/commands/config/command.rb +65 -0
- data/lib/pvectl/commands/config/get_contexts.rb +68 -0
- data/lib/pvectl/commands/config/set_cluster.rb +103 -0
- data/lib/pvectl/commands/config/set_context.rb +136 -0
- data/lib/pvectl/commands/config/set_credentials.rb +181 -0
- data/lib/pvectl/commands/config/use_context.rb +69 -0
- data/lib/pvectl/commands/config/view.rb +67 -0
- data/lib/pvectl/commands/console.rb +93 -0
- data/lib/pvectl/commands/console_ct.rb +187 -0
- data/lib/pvectl/commands/console_vm.rb +187 -0
- data/lib/pvectl/commands/container_lifecycle_command.rb +77 -0
- data/lib/pvectl/commands/create_backup.rb +173 -0
- data/lib/pvectl/commands/create_container.rb +141 -0
- data/lib/pvectl/commands/create_resource_command.rb +244 -0
- data/lib/pvectl/commands/create_snapshot.rb +242 -0
- data/lib/pvectl/commands/create_vm.rb +267 -0
- data/lib/pvectl/commands/delete_backup.rb +139 -0
- data/lib/pvectl/commands/delete_command.rb +119 -0
- data/lib/pvectl/commands/delete_container.rb +30 -0
- data/lib/pvectl/commands/delete_snapshot.rb +248 -0
- data/lib/pvectl/commands/delete_vm.rb +127 -0
- data/lib/pvectl/commands/describe/command.rb +251 -0
- data/lib/pvectl/commands/edit_container.rb +56 -0
- data/lib/pvectl/commands/edit_dns.rb +149 -0
- data/lib/pvectl/commands/edit_hosts.rb +135 -0
- data/lib/pvectl/commands/edit_node.rb +54 -0
- data/lib/pvectl/commands/edit_resource_command.rb +180 -0
- data/lib/pvectl/commands/edit_vm.rb +154 -0
- data/lib/pvectl/commands/edit_volume.rb +189 -0
- data/lib/pvectl/commands/feature_command.rb +230 -0
- data/lib/pvectl/commands/feature_container.rb +21 -0
- data/lib/pvectl/commands/feature_vm.rb +94 -0
- data/lib/pvectl/commands/get/command.rb +360 -0
- data/lib/pvectl/commands/get/handlers/backups.rb +76 -0
- data/lib/pvectl/commands/get/handlers/capabilities.rb +107 -0
- data/lib/pvectl/commands/get/handlers/containers.rb +148 -0
- data/lib/pvectl/commands/get/handlers/disks.rb +107 -0
- data/lib/pvectl/commands/get/handlers/dns.rb +94 -0
- data/lib/pvectl/commands/get/handlers/hosts.rb +94 -0
- data/lib/pvectl/commands/get/handlers/nodes.rb +162 -0
- data/lib/pvectl/commands/get/handlers/services.rb +81 -0
- data/lib/pvectl/commands/get/handlers/snapshots.rb +97 -0
- data/lib/pvectl/commands/get/handlers/storage.rb +118 -0
- data/lib/pvectl/commands/get/handlers/subscription.rb +69 -0
- data/lib/pvectl/commands/get/handlers/tasks.rb +89 -0
- data/lib/pvectl/commands/get/handlers/templates.rb +175 -0
- data/lib/pvectl/commands/get/handlers/time.rb +118 -0
- data/lib/pvectl/commands/get/handlers/vms.rb +145 -0
- data/lib/pvectl/commands/get/handlers/volume.rb +134 -0
- data/lib/pvectl/commands/get/resource_handler.rb +63 -0
- data/lib/pvectl/commands/get/resource_registry.rb +18 -0
- data/lib/pvectl/commands/get/watch_loop.rb +129 -0
- data/lib/pvectl/commands/irreversible_command.rb +265 -0
- data/lib/pvectl/commands/logs/command.rb +275 -0
- data/lib/pvectl/commands/logs/handlers/journal.rb +46 -0
- data/lib/pvectl/commands/logs/handlers/syslog.rb +53 -0
- data/lib/pvectl/commands/logs/handlers/task_detail.rb +52 -0
- data/lib/pvectl/commands/logs/handlers/task_logs.rb +115 -0
- data/lib/pvectl/commands/logs/resource_handler.rb +46 -0
- data/lib/pvectl/commands/logs/resource_registry.rb +22 -0
- data/lib/pvectl/commands/migrate_command.rb +282 -0
- data/lib/pvectl/commands/migrate_container.rb +23 -0
- data/lib/pvectl/commands/migrate_vm.rb +122 -0
- data/lib/pvectl/commands/move_disk_command.rb +239 -0
- data/lib/pvectl/commands/move_disk_container.rb +21 -0
- data/lib/pvectl/commands/move_disk_vm.rb +127 -0
- data/lib/pvectl/commands/ping.rb +249 -0
- data/lib/pvectl/commands/pull.rb +342 -0
- data/lib/pvectl/commands/push.rb +352 -0
- data/lib/pvectl/commands/reset.rb +64 -0
- data/lib/pvectl/commands/resource_lifecycle_command.rb +277 -0
- data/lib/pvectl/commands/resource_registry.rb +73 -0
- data/lib/pvectl/commands/restart.rb +70 -0
- data/lib/pvectl/commands/restart_container.rb +18 -0
- data/lib/pvectl/commands/restore_backup.rb +236 -0
- data/lib/pvectl/commands/resume.rb +57 -0
- data/lib/pvectl/commands/rollback_snapshot.rb +228 -0
- data/lib/pvectl/commands/sendkey_vm.rb +205 -0
- data/lib/pvectl/commands/service.rb +293 -0
- data/lib/pvectl/commands/set_container.rb +50 -0
- data/lib/pvectl/commands/set_node.rb +52 -0
- data/lib/pvectl/commands/set_resource_command.rb +185 -0
- data/lib/pvectl/commands/set_vm.rb +136 -0
- data/lib/pvectl/commands/set_volume.rb +212 -0
- data/lib/pvectl/commands/shared_config_parsers.rb +126 -0
- data/lib/pvectl/commands/shared_flags.rb +155 -0
- data/lib/pvectl/commands/shutdown.rb +73 -0
- data/lib/pvectl/commands/shutdown_container.rb +18 -0
- data/lib/pvectl/commands/start.rb +79 -0
- data/lib/pvectl/commands/start_container.rb +18 -0
- data/lib/pvectl/commands/stop.rb +75 -0
- data/lib/pvectl/commands/stop_container.rb +18 -0
- data/lib/pvectl/commands/suspend.rb +64 -0
- data/lib/pvectl/commands/template_command.rb +205 -0
- data/lib/pvectl/commands/template_container.rb +27 -0
- data/lib/pvectl/commands/template_vm.rb +106 -0
- data/lib/pvectl/commands/top/command.rb +206 -0
- data/lib/pvectl/commands/top/handlers/containers.rb +61 -0
- data/lib/pvectl/commands/top/handlers/nodes.rb +61 -0
- data/lib/pvectl/commands/top/handlers/vms.rb +61 -0
- data/lib/pvectl/commands/top/resource_handler.rb +46 -0
- data/lib/pvectl/commands/top/resource_registry.rb +22 -0
- data/lib/pvectl/commands/unlink_disk_vm.rb +232 -0
- data/lib/pvectl/commands/vm_lifecycle_command.rb +77 -0
- data/lib/pvectl/commands/wakeonlan_node.rb +153 -0
- data/lib/pvectl/config/errors.rb +62 -0
- data/lib/pvectl/config/models/cluster.rb +180 -0
- data/lib/pvectl/config/models/context.rb +100 -0
- data/lib/pvectl/config/models/resolved_config.rb +171 -0
- data/lib/pvectl/config/models/user.rb +133 -0
- data/lib/pvectl/config/provider.rb +297 -0
- data/lib/pvectl/config/service.rb +300 -0
- data/lib/pvectl/config/store.rb +161 -0
- data/lib/pvectl/config/wizard.rb +309 -0
- data/lib/pvectl/config_serializer.rb +1034 -0
- data/lib/pvectl/connection/retry_handler.rb +161 -0
- data/lib/pvectl/connection.rb +157 -0
- data/lib/pvectl/console/terminal_session.rb +449 -0
- data/lib/pvectl/editor_session.rb +157 -0
- data/lib/pvectl/exit_codes.rb +43 -0
- data/lib/pvectl/formatters/base.rb +55 -0
- data/lib/pvectl/formatters/color_support.rb +90 -0
- data/lib/pvectl/formatters/json.rb +45 -0
- data/lib/pvectl/formatters/output_helper.rb +77 -0
- data/lib/pvectl/formatters/registry.rb +72 -0
- data/lib/pvectl/formatters/table.rb +235 -0
- data/lib/pvectl/formatters/wide.rb +93 -0
- data/lib/pvectl/formatters/yaml.rb +49 -0
- data/lib/pvectl/manifest_serializer.rb +142 -0
- data/lib/pvectl/models/apt_package.rb +107 -0
- data/lib/pvectl/models/backup.rb +173 -0
- data/lib/pvectl/models/base.rb +49 -0
- data/lib/pvectl/models/capability.rb +62 -0
- data/lib/pvectl/models/container.rb +205 -0
- data/lib/pvectl/models/container_operation_result.rb +27 -0
- data/lib/pvectl/models/dns_config.rb +54 -0
- data/lib/pvectl/models/hosts_file.rb +47 -0
- data/lib/pvectl/models/journal_entry.rb +16 -0
- data/lib/pvectl/models/network_interface.rb +85 -0
- data/lib/pvectl/models/node.rb +195 -0
- data/lib/pvectl/models/node_operation_result.rb +45 -0
- data/lib/pvectl/models/operation_result.rb +110 -0
- data/lib/pvectl/models/physical_disk.rb +193 -0
- data/lib/pvectl/models/service.rb +80 -0
- data/lib/pvectl/models/snapshot.rb +101 -0
- data/lib/pvectl/models/snapshot_description.rb +39 -0
- data/lib/pvectl/models/storage.rb +180 -0
- data/lib/pvectl/models/subscription.rb +87 -0
- data/lib/pvectl/models/syslog_entry.rb +17 -0
- data/lib/pvectl/models/task.rb +95 -0
- data/lib/pvectl/models/task_entry.rb +52 -0
- data/lib/pvectl/models/task_log_line.rb +17 -0
- data/lib/pvectl/models/time_config.rb +47 -0
- data/lib/pvectl/models/vm.rb +137 -0
- data/lib/pvectl/models/vm_operation_result.rb +27 -0
- data/lib/pvectl/models/volume.rb +133 -0
- data/lib/pvectl/models/volume_operation_result.rb +26 -0
- data/lib/pvectl/parsers/cloud_init_config.rb +92 -0
- data/lib/pvectl/parsers/disk_config.rb +97 -0
- data/lib/pvectl/parsers/lxc_mount_config.rb +98 -0
- data/lib/pvectl/parsers/lxc_net_config.rb +97 -0
- data/lib/pvectl/parsers/net_config.rb +95 -0
- data/lib/pvectl/parsers/smart_text.rb +42 -0
- data/lib/pvectl/plugin_loader.rb +157 -0
- data/lib/pvectl/presenters/apt_package.rb +99 -0
- data/lib/pvectl/presenters/backup.rb +128 -0
- data/lib/pvectl/presenters/base.rb +283 -0
- data/lib/pvectl/presenters/capability.rb +104 -0
- data/lib/pvectl/presenters/config/context.rb +80 -0
- data/lib/pvectl/presenters/container.rb +574 -0
- data/lib/pvectl/presenters/container_operation_result.rb +109 -0
- data/lib/pvectl/presenters/disk.rb +184 -0
- data/lib/pvectl/presenters/dns_config.rb +68 -0
- data/lib/pvectl/presenters/hosts_file.rb +61 -0
- data/lib/pvectl/presenters/journal_entry.rb +20 -0
- data/lib/pvectl/presenters/node.rb +762 -0
- data/lib/pvectl/presenters/node_operation_result.rb +50 -0
- data/lib/pvectl/presenters/operation_result.rb +61 -0
- data/lib/pvectl/presenters/service.rb +76 -0
- data/lib/pvectl/presenters/snapshot.rb +239 -0
- data/lib/pvectl/presenters/snapshot_operation_result.rb +125 -0
- data/lib/pvectl/presenters/storage.rb +329 -0
- data/lib/pvectl/presenters/subscription.rb +189 -0
- data/lib/pvectl/presenters/syslog_entry.rb +20 -0
- data/lib/pvectl/presenters/task_entry.rb +69 -0
- data/lib/pvectl/presenters/task_log_line.rb +20 -0
- data/lib/pvectl/presenters/template.rb +76 -0
- data/lib/pvectl/presenters/time_config.rb +86 -0
- data/lib/pvectl/presenters/top_container.rb +112 -0
- data/lib/pvectl/presenters/top_node.rb +115 -0
- data/lib/pvectl/presenters/top_presenter.rb +59 -0
- data/lib/pvectl/presenters/top_vm.rb +105 -0
- data/lib/pvectl/presenters/vm.rb +853 -0
- data/lib/pvectl/presenters/vm_operation_result.rb +109 -0
- data/lib/pvectl/presenters/volume.rb +136 -0
- data/lib/pvectl/presenters/volume_operation_result.rb +58 -0
- data/lib/pvectl/repositories/apt.rb +93 -0
- data/lib/pvectl/repositories/backup.rb +186 -0
- data/lib/pvectl/repositories/base.rb +110 -0
- data/lib/pvectl/repositories/capabilities.rb +96 -0
- data/lib/pvectl/repositories/container.rb +503 -0
- data/lib/pvectl/repositories/disk.rb +87 -0
- data/lib/pvectl/repositories/dns.rb +54 -0
- data/lib/pvectl/repositories/hosts.rb +63 -0
- data/lib/pvectl/repositories/journal.rb +23 -0
- data/lib/pvectl/repositories/node.rb +537 -0
- data/lib/pvectl/repositories/service.rb +139 -0
- data/lib/pvectl/repositories/snapshot.rb +133 -0
- data/lib/pvectl/repositories/storage.rb +302 -0
- data/lib/pvectl/repositories/subscription.rb +77 -0
- data/lib/pvectl/repositories/syslog.rb +25 -0
- data/lib/pvectl/repositories/task.rb +82 -0
- data/lib/pvectl/repositories/task_list.rb +30 -0
- data/lib/pvectl/repositories/task_log.rb +31 -0
- data/lib/pvectl/repositories/time_config.rb +53 -0
- data/lib/pvectl/repositories/vm.rb +616 -0
- data/lib/pvectl/repositories/volume.rb +306 -0
- data/lib/pvectl/selectors/base.rb +201 -0
- data/lib/pvectl/selectors/container.rb +116 -0
- data/lib/pvectl/selectors/disk.rb +59 -0
- data/lib/pvectl/selectors/vm.rb +116 -0
- data/lib/pvectl/selectors/volume.rb +59 -0
- data/lib/pvectl/services/backup.rb +209 -0
- data/lib/pvectl/services/clone_container.rb +260 -0
- data/lib/pvectl/services/clone_vm.rb +265 -0
- data/lib/pvectl/services/cloudinit.rb +96 -0
- data/lib/pvectl/services/console.rb +152 -0
- data/lib/pvectl/services/container_lifecycle.rb +124 -0
- data/lib/pvectl/services/create_container.rb +179 -0
- data/lib/pvectl/services/create_vm.rb +191 -0
- data/lib/pvectl/services/edit_container.rb +125 -0
- data/lib/pvectl/services/edit_dns.rb +159 -0
- data/lib/pvectl/services/edit_hosts.rb +78 -0
- data/lib/pvectl/services/edit_node.rb +147 -0
- data/lib/pvectl/services/edit_vm.rb +125 -0
- data/lib/pvectl/services/edit_volume.rb +224 -0
- data/lib/pvectl/services/get/resource_service.rb +98 -0
- data/lib/pvectl/services/move_disk.rb +132 -0
- data/lib/pvectl/services/pull_config.rb +94 -0
- data/lib/pvectl/services/push_config.rb +524 -0
- data/lib/pvectl/services/resize_volume.rb +253 -0
- data/lib/pvectl/services/resource_delete.rb +169 -0
- data/lib/pvectl/services/resource_migration.rb +170 -0
- data/lib/pvectl/services/sendkey.rb +108 -0
- data/lib/pvectl/services/service_lifecycle.rb +89 -0
- data/lib/pvectl/services/set_container.rb +128 -0
- data/lib/pvectl/services/set_node.rb +236 -0
- data/lib/pvectl/services/set_vm.rb +128 -0
- data/lib/pvectl/services/set_volume.rb +126 -0
- data/lib/pvectl/services/snapshot.rb +261 -0
- data/lib/pvectl/services/task_listing.rb +75 -0
- data/lib/pvectl/services/unlink_disk.rb +86 -0
- data/lib/pvectl/services/vm_lifecycle.rb +124 -0
- data/lib/pvectl/services/wakeonlan.rb +79 -0
- data/lib/pvectl/utils/resource_resolver.rb +80 -0
- data/lib/pvectl/version.rb +13 -0
- data/lib/pvectl/wizards/create_container.rb +105 -0
- data/lib/pvectl/wizards/create_vm.rb +98 -0
- data/lib/pvectl.rb +439 -0
- data/sig/external/gli.rbs +16 -0
- data/sig/external/proxmox_api.rbs +10 -0
- data/sig/pvectl/argv_preprocessor.rbs +53 -0
- data/sig/pvectl/cli.rbs +26 -0
- data/sig/pvectl/commands/apt.rbs +47 -0
- data/sig/pvectl/commands/clone_container.rbs +31 -0
- data/sig/pvectl/commands/clone_vm.rbs +33 -0
- data/sig/pvectl/commands/cloudinit/command.rbs +13 -0
- data/sig/pvectl/commands/cloudinit/dump.rbs +13 -0
- data/sig/pvectl/commands/cloudinit/pending.rbs +17 -0
- data/sig/pvectl/commands/cloudinit/regenerate.rbs +11 -0
- data/sig/pvectl/commands/config/command.rbs +9 -0
- data/sig/pvectl/commands/config/get_contexts.rbs +11 -0
- data/sig/pvectl/commands/config/set_cluster.rbs +11 -0
- data/sig/pvectl/commands/config/set_context.rbs +15 -0
- data/sig/pvectl/commands/config/set_credentials.rbs +15 -0
- data/sig/pvectl/commands/config/use_context.rbs +11 -0
- data/sig/pvectl/commands/config/view.rbs +11 -0
- data/sig/pvectl/commands/console.rbs +9 -0
- data/sig/pvectl/commands/console_ct.rbs +27 -0
- data/sig/pvectl/commands/console_vm.rbs +27 -0
- data/sig/pvectl/commands/container_lifecycle_command.rbs +25 -0
- data/sig/pvectl/commands/create_backup.rbs +29 -0
- data/sig/pvectl/commands/create_container.rbs +30 -0
- data/sig/pvectl/commands/create_resource_command.rbs +53 -0
- data/sig/pvectl/commands/create_snapshot.rbs +35 -0
- data/sig/pvectl/commands/create_vm.rbs +30 -0
- data/sig/pvectl/commands/delete_backup.rbs +25 -0
- data/sig/pvectl/commands/delete_command.rbs +45 -0
- data/sig/pvectl/commands/delete_container.rbs +11 -0
- data/sig/pvectl/commands/delete_snapshot.rbs +35 -0
- data/sig/pvectl/commands/delete_vm.rbs +13 -0
- data/sig/pvectl/commands/describe/command.rbs +27 -0
- data/sig/pvectl/commands/edit_container.rbs +17 -0
- data/sig/pvectl/commands/edit_dns.rbs +25 -0
- data/sig/pvectl/commands/edit_hosts.rbs +23 -0
- data/sig/pvectl/commands/edit_node.rbs +17 -0
- data/sig/pvectl/commands/edit_resource_command.rbs +35 -0
- data/sig/pvectl/commands/edit_vm.rbs +19 -0
- data/sig/pvectl/commands/edit_volume.rbs +24 -0
- data/sig/pvectl/commands/feature_command.rbs +43 -0
- data/sig/pvectl/commands/feature_container.rbs +10 -0
- data/sig/pvectl/commands/feature_vm.rbs +12 -0
- data/sig/pvectl/commands/get/command.rbs +42 -0
- data/sig/pvectl/commands/get/handlers/backups.rbs +23 -0
- data/sig/pvectl/commands/get/handlers/capabilities.rbs +29 -0
- data/sig/pvectl/commands/get/handlers/containers.rbs +35 -0
- data/sig/pvectl/commands/get/handlers/disks.rbs +27 -0
- data/sig/pvectl/commands/get/handlers/dns.rbs +25 -0
- data/sig/pvectl/commands/get/handlers/hosts.rbs +25 -0
- data/sig/pvectl/commands/get/handlers/nodes.rbs +33 -0
- data/sig/pvectl/commands/get/handlers/services.rbs +23 -0
- data/sig/pvectl/commands/get/handlers/snapshots.rbs +27 -0
- data/sig/pvectl/commands/get/handlers/storage.rbs +25 -0
- data/sig/pvectl/commands/get/handlers/subscription.rbs +25 -0
- data/sig/pvectl/commands/get/handlers/tasks.rbs +28 -0
- data/sig/pvectl/commands/get/handlers/templates.rbs +35 -0
- data/sig/pvectl/commands/get/handlers/time.rbs +29 -0
- data/sig/pvectl/commands/get/handlers/vms.rbs +35 -0
- data/sig/pvectl/commands/get/handlers/volume.rbs +27 -0
- data/sig/pvectl/commands/get/resource_handler.rbs +13 -0
- data/sig/pvectl/commands/get/resource_registry.rbs +8 -0
- data/sig/pvectl/commands/get/watch_loop.rbs +33 -0
- data/sig/pvectl/commands/irreversible_command.rbs +32 -0
- data/sig/pvectl/commands/logs/command.rbs +35 -0
- data/sig/pvectl/commands/logs/handlers/journal.rbs +21 -0
- data/sig/pvectl/commands/logs/handlers/syslog.rbs +21 -0
- data/sig/pvectl/commands/logs/handlers/task_detail.rbs +21 -0
- data/sig/pvectl/commands/logs/handlers/task_logs.rbs +35 -0
- data/sig/pvectl/commands/logs/resource_handler.rbs +11 -0
- data/sig/pvectl/commands/logs/resource_registry.rbs +8 -0
- data/sig/pvectl/commands/migrate_command.rbs +45 -0
- data/sig/pvectl/commands/migrate_container.rbs +11 -0
- data/sig/pvectl/commands/migrate_vm.rbs +13 -0
- data/sig/pvectl/commands/move_disk_command.rbs +43 -0
- data/sig/pvectl/commands/move_disk_container.rbs +11 -0
- data/sig/pvectl/commands/move_disk_vm.rbs +13 -0
- data/sig/pvectl/commands/ping.rbs +39 -0
- data/sig/pvectl/commands/pull.rbs +33 -0
- data/sig/pvectl/commands/push.rbs +32 -0
- data/sig/pvectl/commands/reset.rbs +11 -0
- data/sig/pvectl/commands/resource_lifecycle_command.rbs +55 -0
- data/sig/pvectl/commands/resource_registry.rbs +19 -0
- data/sig/pvectl/commands/restart.rbs +11 -0
- data/sig/pvectl/commands/restart_container.rbs +9 -0
- data/sig/pvectl/commands/restore_backup.rbs +27 -0
- data/sig/pvectl/commands/resume.rbs +11 -0
- data/sig/pvectl/commands/rollback_snapshot.rbs +31 -0
- data/sig/pvectl/commands/sendkey_vm.rbs +25 -0
- data/sig/pvectl/commands/service.rbs +38 -0
- data/sig/pvectl/commands/set_container.rbs +13 -0
- data/sig/pvectl/commands/set_node.rbs +13 -0
- data/sig/pvectl/commands/set_resource_command.rbs +25 -0
- data/sig/pvectl/commands/set_vm.rbs +15 -0
- data/sig/pvectl/commands/set_volume.rbs +24 -0
- data/sig/pvectl/commands/shared_config_parsers.rbs +19 -0
- data/sig/pvectl/commands/shared_flags.rbs +10 -0
- data/sig/pvectl/commands/shutdown.rbs +11 -0
- data/sig/pvectl/commands/shutdown_container.rbs +9 -0
- data/sig/pvectl/commands/start.rbs +11 -0
- data/sig/pvectl/commands/start_container.rbs +9 -0
- data/sig/pvectl/commands/stop.rbs +11 -0
- data/sig/pvectl/commands/stop_container.rbs +9 -0
- data/sig/pvectl/commands/suspend.rbs +11 -0
- data/sig/pvectl/commands/template_command.rbs +21 -0
- data/sig/pvectl/commands/template_container.rbs +10 -0
- data/sig/pvectl/commands/template_vm.rbs +12 -0
- data/sig/pvectl/commands/top/command.rbs +31 -0
- data/sig/pvectl/commands/top/handlers/containers.rbs +21 -0
- data/sig/pvectl/commands/top/handlers/nodes.rbs +21 -0
- data/sig/pvectl/commands/top/handlers/vms.rbs +21 -0
- data/sig/pvectl/commands/top/resource_handler.rbs +11 -0
- data/sig/pvectl/commands/top/resource_registry.rbs +8 -0
- data/sig/pvectl/commands/unlink_disk_vm.rbs +27 -0
- data/sig/pvectl/commands/vm_lifecycle_command.rbs +25 -0
- data/sig/pvectl/commands/wakeonlan_node.rbs +21 -0
- data/sig/pvectl/config/errors.rbs +24 -0
- data/sig/pvectl/config/models/cluster.rbs +39 -0
- data/sig/pvectl/config/models/context.rbs +23 -0
- data/sig/pvectl/config/models/resolved_config.rbs +51 -0
- data/sig/pvectl/config/models/user.rbs +31 -0
- data/sig/pvectl/config/provider.rbs +40 -0
- data/sig/pvectl/config/service.rbs +65 -0
- data/sig/pvectl/config/store.rbs +14 -0
- data/sig/pvectl/config/wizard.rbs +48 -0
- data/sig/pvectl/config_serializer.rbs +121 -0
- data/sig/pvectl/connection/retry_handler.rbs +31 -0
- data/sig/pvectl/connection.rbs +35 -0
- data/sig/pvectl/console/terminal_session.rbs +63 -0
- data/sig/pvectl/editor_session.rbs +33 -0
- data/sig/pvectl/exit_codes.rbs +19 -0
- data/sig/pvectl/formatters/base.rbs +13 -0
- data/sig/pvectl/formatters/color_support.rbs +13 -0
- data/sig/pvectl/formatters/json.rbs +7 -0
- data/sig/pvectl/formatters/output_helper.rbs +9 -0
- data/sig/pvectl/formatters/registry.rbs +13 -0
- data/sig/pvectl/formatters/table.rbs +25 -0
- data/sig/pvectl/formatters/wide.rbs +15 -0
- data/sig/pvectl/formatters/yaml.rbs +7 -0
- data/sig/pvectl/manifest_serializer.rbs +18 -0
- data/sig/pvectl/models/apt_package.rbs +26 -0
- data/sig/pvectl/models/backup.rbs +31 -0
- data/sig/pvectl/models/base.rbs +11 -0
- data/sig/pvectl/models/capability.rbs +16 -0
- data/sig/pvectl/models/container.rbs +44 -0
- data/sig/pvectl/models/container_operation_result.rbs +9 -0
- data/sig/pvectl/models/dns_config.rbs +15 -0
- data/sig/pvectl/models/hosts_file.rbs +13 -0
- data/sig/pvectl/models/journal_entry.rbs +10 -0
- data/sig/pvectl/models/network_interface.rbs +20 -0
- data/sig/pvectl/models/node.rbs +47 -0
- data/sig/pvectl/models/node_operation_result.rbs +12 -0
- data/sig/pvectl/models/operation_result.rbs +21 -0
- data/sig/pvectl/models/physical_disk.rbs +35 -0
- data/sig/pvectl/models/service.rbs +18 -0
- data/sig/pvectl/models/snapshot.rbs +21 -0
- data/sig/pvectl/models/snapshot_description.rbs +18 -0
- data/sig/pvectl/models/storage.rbs +39 -0
- data/sig/pvectl/models/subscription.rbs +24 -0
- data/sig/pvectl/models/syslog_entry.rbs +10 -0
- data/sig/pvectl/models/task.rbs +22 -0
- data/sig/pvectl/models/task_entry.rbs +24 -0
- data/sig/pvectl/models/task_log_line.rbs +10 -0
- data/sig/pvectl/models/time_config.rbs +12 -0
- data/sig/pvectl/models/vm.rbs +32 -0
- data/sig/pvectl/models/vm_operation_result.rbs +9 -0
- data/sig/pvectl/models/volume.rbs +29 -0
- data/sig/pvectl/models/volume_operation_result.rbs +9 -0
- data/sig/pvectl/parsers/cloud_init_config.rbs +15 -0
- data/sig/pvectl/parsers/disk_config.rbs +19 -0
- data/sig/pvectl/parsers/lxc_mount_config.rbs +19 -0
- data/sig/pvectl/parsers/lxc_net_config.rbs +19 -0
- data/sig/pvectl/parsers/net_config.rbs +19 -0
- data/sig/pvectl/parsers/smart_text.rbs +7 -0
- data/sig/pvectl/plugin_loader.rbs +25 -0
- data/sig/pvectl/presenters/apt_package.rbs +19 -0
- data/sig/pvectl/presenters/backup.rbs +25 -0
- data/sig/pvectl/presenters/base.rbs +41 -0
- data/sig/pvectl/presenters/capability.rbs +19 -0
- data/sig/pvectl/presenters/config/context.rbs +17 -0
- data/sig/pvectl/presenters/container.rbs +78 -0
- data/sig/pvectl/presenters/container_operation_result.rbs +19 -0
- data/sig/pvectl/presenters/disk.rbs +31 -0
- data/sig/pvectl/presenters/dns_config.rbs +13 -0
- data/sig/pvectl/presenters/hosts_file.rbs +13 -0
- data/sig/pvectl/presenters/journal_entry.rbs +11 -0
- data/sig/pvectl/presenters/node.rbs +118 -0
- data/sig/pvectl/presenters/node_operation_result.rbs +11 -0
- data/sig/pvectl/presenters/operation_result.rbs +17 -0
- data/sig/pvectl/presenters/service.rbs +15 -0
- data/sig/pvectl/presenters/snapshot.rbs +35 -0
- data/sig/pvectl/presenters/snapshot_operation_result.rbs +27 -0
- data/sig/pvectl/presenters/storage.rbs +59 -0
- data/sig/pvectl/presenters/subscription.rbs +36 -0
- data/sig/pvectl/presenters/syslog_entry.rbs +11 -0
- data/sig/pvectl/presenters/task_entry.rbs +21 -0
- data/sig/pvectl/presenters/task_log_line.rbs +11 -0
- data/sig/pvectl/presenters/template.rbs +15 -0
- data/sig/pvectl/presenters/time_config.rbs +19 -0
- data/sig/pvectl/presenters/top_container.rbs +17 -0
- data/sig/pvectl/presenters/top_node.rbs +17 -0
- data/sig/pvectl/presenters/top_presenter.rbs +13 -0
- data/sig/pvectl/presenters/top_vm.rbs +17 -0
- data/sig/pvectl/presenters/vm.rbs +91 -0
- data/sig/pvectl/presenters/vm_operation_result.rbs +19 -0
- data/sig/pvectl/presenters/volume.rbs +23 -0
- data/sig/pvectl/presenters/volume_operation_result.rbs +11 -0
- data/sig/pvectl/repositories/apt.rbs +17 -0
- data/sig/pvectl/repositories/backup.rbs +27 -0
- data/sig/pvectl/repositories/base.rbs +23 -0
- data/sig/pvectl/repositories/capabilities.rbs +20 -0
- data/sig/pvectl/repositories/container.rbs +63 -0
- data/sig/pvectl/repositories/disk.rbs +17 -0
- data/sig/pvectl/repositories/dns.rbs +13 -0
- data/sig/pvectl/repositories/hosts.rbs +13 -0
- data/sig/pvectl/repositories/journal.rbs +7 -0
- data/sig/pvectl/repositories/node.rbs +68 -0
- data/sig/pvectl/repositories/service.rbs +27 -0
- data/sig/pvectl/repositories/snapshot.rbs +19 -0
- data/sig/pvectl/repositories/storage.rbs +37 -0
- data/sig/pvectl/repositories/subscription.rbs +17 -0
- data/sig/pvectl/repositories/syslog.rbs +7 -0
- data/sig/pvectl/repositories/task.rbs +19 -0
- data/sig/pvectl/repositories/task_list.rbs +7 -0
- data/sig/pvectl/repositories/task_log.rbs +11 -0
- data/sig/pvectl/repositories/time_config.rbs +13 -0
- data/sig/pvectl/repositories/vm.rbs +85 -0
- data/sig/pvectl/repositories/volume.rbs +43 -0
- data/sig/pvectl/selectors/base.rbs +37 -0
- data/sig/pvectl/selectors/container.rbs +19 -0
- data/sig/pvectl/selectors/disk.rbs +13 -0
- data/sig/pvectl/selectors/vm.rbs +19 -0
- data/sig/pvectl/selectors/volume.rbs +13 -0
- data/sig/pvectl/services/backup.rbs +27 -0
- data/sig/pvectl/services/clone_container.rbs +35 -0
- data/sig/pvectl/services/clone_vm.rbs +35 -0
- data/sig/pvectl/services/cloudinit.rbs +19 -0
- data/sig/pvectl/services/console.rbs +23 -0
- data/sig/pvectl/services/container_lifecycle.rbs +26 -0
- data/sig/pvectl/services/create_container.rbs +64 -0
- data/sig/pvectl/services/create_vm.rbs +72 -0
- data/sig/pvectl/services/edit_container.rbs +17 -0
- data/sig/pvectl/services/edit_dns.rbs +23 -0
- data/sig/pvectl/services/edit_hosts.rbs +13 -0
- data/sig/pvectl/services/edit_node.rbs +21 -0
- data/sig/pvectl/services/edit_vm.rbs +17 -0
- data/sig/pvectl/services/edit_volume.rbs +18 -0
- data/sig/pvectl/services/get/resource_service.rbs +23 -0
- data/sig/pvectl/services/move_disk.rbs +21 -0
- data/sig/pvectl/services/pull_config.rbs +18 -0
- data/sig/pvectl/services/push_config.rbs +37 -0
- data/sig/pvectl/services/resize_volume.rbs +47 -0
- data/sig/pvectl/services/resource_delete.rbs +27 -0
- data/sig/pvectl/services/resource_migration.rbs +29 -0
- data/sig/pvectl/services/sendkey.rbs +19 -0
- data/sig/pvectl/services/service_lifecycle.rbs +17 -0
- data/sig/pvectl/services/set_container.rbs +14 -0
- data/sig/pvectl/services/set_node.rbs +23 -0
- data/sig/pvectl/services/set_vm.rbs +14 -0
- data/sig/pvectl/services/set_volume.rbs +12 -0
- data/sig/pvectl/services/snapshot.rbs +35 -0
- data/sig/pvectl/services/task_listing.rbs +13 -0
- data/sig/pvectl/services/unlink_disk.rbs +17 -0
- data/sig/pvectl/services/vm_lifecycle.rbs +26 -0
- data/sig/pvectl/services/wakeonlan.rbs +17 -0
- data/sig/pvectl/utils/resource_resolver.rbs +17 -0
- data/sig/pvectl/wizards/create_container.rbs +21 -0
- data/sig/pvectl/wizards/create_vm.rbs +21 -0
- data/sig/pvectl.rbs +9 -0
- metadata +675 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
# Command line argument preprocessor.
|
|
5
|
+
#
|
|
6
|
+
# Normalizes ARGV arguments before passing to GLI by reordering flags
|
|
7
|
+
# to appear before positional arguments, using GLI command metadata
|
|
8
|
+
# for dynamic flag discovery.
|
|
9
|
+
#
|
|
10
|
+
# GLI with `subcommand_option_handling :normal` requires flags before
|
|
11
|
+
# positional arguments. This preprocessor allows kubectl-style flag
|
|
12
|
+
# placement anywhere on the command line.
|
|
13
|
+
#
|
|
14
|
+
# @example Global flags moved to beginning
|
|
15
|
+
# ArgvPreprocessor.process(["get", "nodes", "-o", "json"], cli_app: CLI)
|
|
16
|
+
# #=> ["-o", "json", "get", "nodes"]
|
|
17
|
+
#
|
|
18
|
+
# @example Command flags moved before positional args
|
|
19
|
+
# ArgvPreprocessor.process(["delete", "vm", "103", "--yes"], cli_app: CLI)
|
|
20
|
+
# #=> ["delete", "--yes", "vm", "103"]
|
|
21
|
+
#
|
|
22
|
+
# @example Passthrough mode for --help
|
|
23
|
+
# ArgvPreprocessor.process(["--help", "get"], cli_app: CLI)
|
|
24
|
+
# #=> ["--help", "get"] # unchanged
|
|
25
|
+
#
|
|
26
|
+
class ArgvPreprocessor
|
|
27
|
+
# @return [Integer] Maximum number of arguments (DoS protection)
|
|
28
|
+
MAX_ARGUMENTS = 10_000
|
|
29
|
+
|
|
30
|
+
# @return [Integer] Maximum length of single argument in bytes
|
|
31
|
+
MAX_ARGUMENT_LENGTH = 4096
|
|
32
|
+
|
|
33
|
+
# @return [Array<String>] Flags passed through without processing
|
|
34
|
+
PASSTHROUGH_FLAGS = %w[--help -h --version].freeze
|
|
35
|
+
|
|
36
|
+
# Error raised when the same global flag is provided with different values.
|
|
37
|
+
class DuplicateFlagError < Pvectl::Error
|
|
38
|
+
# Creates a new flag duplication error.
|
|
39
|
+
#
|
|
40
|
+
# @param flag_name [Symbol] flag name (e.g., :output)
|
|
41
|
+
# @param existing_value [String, Boolean] first flag value
|
|
42
|
+
# @param new_value [String, Boolean] second (conflicting) flag value
|
|
43
|
+
def initialize(flag_name, existing_value, new_value)
|
|
44
|
+
super("Duplicate global flag --#{flag_name} with different values: #{existing_value}, #{new_value}")
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Processes command line arguments.
|
|
49
|
+
#
|
|
50
|
+
# @param argv [Array<String>] command line arguments
|
|
51
|
+
# @param cli_app [GLI::App] CLI application with registered commands
|
|
52
|
+
# @return [Array<String>] normalized arguments
|
|
53
|
+
# @raise [ArgumentError] when input limits are exceeded
|
|
54
|
+
# @raise [DuplicateFlagError] when global flag has different values
|
|
55
|
+
def self.process(argv, cli_app: Pvectl::CLI)
|
|
56
|
+
new(argv, cli_app: cli_app).call
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Initializes preprocessor with a copy of arguments.
|
|
60
|
+
#
|
|
61
|
+
# @param argv [Array<String>] command line arguments
|
|
62
|
+
# @param cli_app [GLI::App] CLI application with registered commands
|
|
63
|
+
def initialize(argv, cli_app: Pvectl::CLI)
|
|
64
|
+
@argv = argv.dup
|
|
65
|
+
@cli_app = cli_app
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Executes argument processing.
|
|
69
|
+
#
|
|
70
|
+
# @return [Array<String>] normalized arguments
|
|
71
|
+
# @raise [ArgumentError] when input limits are exceeded
|
|
72
|
+
# @raise [DuplicateFlagError] when global flag has different values
|
|
73
|
+
def call
|
|
74
|
+
validate_input_limits!
|
|
75
|
+
return @argv if passthrough_mode?
|
|
76
|
+
return [] if @argv.empty?
|
|
77
|
+
|
|
78
|
+
reorder_all_flags
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
# Validates input limits for DoS attack protection.
|
|
84
|
+
#
|
|
85
|
+
# @raise [ArgumentError] when argument count or length exceeds limits
|
|
86
|
+
# @return [void]
|
|
87
|
+
def validate_input_limits!
|
|
88
|
+
raise ArgumentError, "Too many arguments (max #{MAX_ARGUMENTS})" if @argv.length > MAX_ARGUMENTS
|
|
89
|
+
|
|
90
|
+
@argv.each do |arg|
|
|
91
|
+
raise ArgumentError, "Argument too long (max #{MAX_ARGUMENT_LENGTH})" if arg.length > MAX_ARGUMENT_LENGTH
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Checks if arguments contain flags requiring passthrough.
|
|
96
|
+
#
|
|
97
|
+
# @return [Boolean] true if --help, -h or --version detected
|
|
98
|
+
def passthrough_mode?
|
|
99
|
+
(@argv & PASSTHROUGH_FLAGS).any?
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Main reordering logic. Three phases:
|
|
103
|
+
# 1. Extract global flags to front
|
|
104
|
+
# 2. Identify command (and optional subcommand)
|
|
105
|
+
# 3. Reorder command/subcommand flags before positional args
|
|
106
|
+
#
|
|
107
|
+
# @return [Array<String>] reordered arguments
|
|
108
|
+
def reorder_all_flags
|
|
109
|
+
global_flags, rest = extract_global_flags(@argv)
|
|
110
|
+
return global_flags + rest if rest.empty?
|
|
111
|
+
|
|
112
|
+
command_name, command_tokens, after_command = identify_command(rest)
|
|
113
|
+
return global_flags + rest unless command_name
|
|
114
|
+
|
|
115
|
+
cmd = find_gli_command(command_name)
|
|
116
|
+
return global_flags + rest unless cmd
|
|
117
|
+
|
|
118
|
+
# Check for subcommand
|
|
119
|
+
if after_command.any? && cmd.commands.any?
|
|
120
|
+
sub_name = after_command.first
|
|
121
|
+
sub_cmd = find_gli_subcommand(cmd, sub_name)
|
|
122
|
+
if sub_cmd
|
|
123
|
+
subcommand_tokens = [after_command.shift]
|
|
124
|
+
reordered = reorder_command_flags(after_command, sub_cmd)
|
|
125
|
+
return global_flags + command_tokens + subcommand_tokens + reordered
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
reordered = reorder_command_flags(after_command, cmd)
|
|
130
|
+
global_flags + command_tokens + reordered
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Extracts global flags from anywhere in the argument list.
|
|
134
|
+
#
|
|
135
|
+
# @param args [Array<String>] arguments
|
|
136
|
+
# @return [Array(Array<String>, Array<String>)] [extracted_global_flags, remaining_args]
|
|
137
|
+
def extract_global_flags(args)
|
|
138
|
+
global_flags_collected = {}
|
|
139
|
+
global_result = []
|
|
140
|
+
remaining = []
|
|
141
|
+
index = 0
|
|
142
|
+
|
|
143
|
+
while index < args.length
|
|
144
|
+
arg = args[index]
|
|
145
|
+
|
|
146
|
+
if arg == "--"
|
|
147
|
+
remaining.concat(args[index..])
|
|
148
|
+
break
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
flag_info = find_global_flag(arg)
|
|
152
|
+
if flag_info
|
|
153
|
+
name, has_value = flag_info
|
|
154
|
+
if arg.include?("=")
|
|
155
|
+
_, value = arg.split("=", 2)
|
|
156
|
+
validate_value!(value, name)
|
|
157
|
+
store_global_flag(global_flags_collected, name, value)
|
|
158
|
+
global_result << arg
|
|
159
|
+
elsif has_value
|
|
160
|
+
raise ArgumentError, "Missing value for flag #{arg}" if index + 1 >= args.length
|
|
161
|
+
|
|
162
|
+
value = args[index + 1]
|
|
163
|
+
validate_value!(value, name)
|
|
164
|
+
store_global_flag(global_flags_collected, name, value)
|
|
165
|
+
global_result << arg << value
|
|
166
|
+
index += 1
|
|
167
|
+
else
|
|
168
|
+
store_global_flag(global_flags_collected, name, true)
|
|
169
|
+
global_result << arg
|
|
170
|
+
end
|
|
171
|
+
else
|
|
172
|
+
remaining << arg
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
index += 1
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
[global_result, remaining]
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Finds a global flag definition matching the given argument.
|
|
182
|
+
#
|
|
183
|
+
# @param arg [String] argument to check
|
|
184
|
+
# @return [Array(Symbol, Boolean), nil] [flag_name, has_value] or nil
|
|
185
|
+
def find_global_flag(arg)
|
|
186
|
+
flag_part = arg.split("=", 2).first
|
|
187
|
+
|
|
188
|
+
@cli_app.flags.each_value do |flag|
|
|
189
|
+
return [flag_display_name(flag), true] if flag_matches?(flag, flag_part)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
@cli_app.switches.each_value do |sw|
|
|
193
|
+
return [flag_display_name(sw), false] if flag_matches?(sw, flag_part)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
nil
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Checks if a GLI flag/switch matches the given argument string.
|
|
200
|
+
#
|
|
201
|
+
# @param gli_flag [GLI::Flag, GLI::Switch] GLI flag or switch object
|
|
202
|
+
# @param flag_part [String] argument to match (e.g., "-o", "--output")
|
|
203
|
+
# @return [Boolean] true if matches
|
|
204
|
+
def flag_matches?(gli_flag, flag_part)
|
|
205
|
+
all_names = [gli_flag.name] + (gli_flag.aliases || [])
|
|
206
|
+
all_names.any? do |name|
|
|
207
|
+
prefix = name.to_s.length == 1 ? "-" : "--"
|
|
208
|
+
"#{prefix}#{name}" == flag_part
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Returns the display name for a GLI flag/switch (prefers long name).
|
|
213
|
+
#
|
|
214
|
+
# @param gli_flag [GLI::Flag, GLI::Switch] GLI flag or switch object
|
|
215
|
+
# @return [Symbol] display name (long form if available)
|
|
216
|
+
def flag_display_name(gli_flag)
|
|
217
|
+
all_names = [gli_flag.name] + (gli_flag.aliases || [])
|
|
218
|
+
long_name = all_names.find { |n| n.to_s.length > 1 }
|
|
219
|
+
long_name || gli_flag.name
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Stores a global flag value with duplicate detection.
|
|
223
|
+
#
|
|
224
|
+
# @param store [Hash] flag storage
|
|
225
|
+
# @param name [Symbol] flag name
|
|
226
|
+
# @param value [String, Boolean] flag value
|
|
227
|
+
# @raise [DuplicateFlagError] when flag already exists with different value
|
|
228
|
+
# @return [void]
|
|
229
|
+
def store_global_flag(store, name, value)
|
|
230
|
+
if store.key?(name)
|
|
231
|
+
existing = store[name]
|
|
232
|
+
raise DuplicateFlagError.new(name, existing, value) if existing != value
|
|
233
|
+
else
|
|
234
|
+
store[name] = value
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Validates flag value for security.
|
|
239
|
+
#
|
|
240
|
+
# @param value [String, Boolean] value to validate
|
|
241
|
+
# @param flag_name [Symbol] flag name (for error message)
|
|
242
|
+
# @raise [ArgumentError] when value contains null byte
|
|
243
|
+
# @return [void]
|
|
244
|
+
def validate_value!(value, flag_name)
|
|
245
|
+
return if value == true
|
|
246
|
+
|
|
247
|
+
raise ArgumentError, "Invalid null byte in value for --#{flag_name}" if value.to_s.include?("\x00")
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Identifies the command name from remaining args (after global flag extraction).
|
|
251
|
+
#
|
|
252
|
+
# @param args [Array<String>] arguments without global flags
|
|
253
|
+
# @return [Array(String, Array<String>, Array<String>)] [command_name, command_tokens, rest]
|
|
254
|
+
def identify_command(args)
|
|
255
|
+
return [nil, [], args] if args.empty? || args.first.start_with?("-")
|
|
256
|
+
|
|
257
|
+
command_name = args.first
|
|
258
|
+
[command_name, [command_name], args[1..]]
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# Finds a GLI command by name (supports aliases and dash-to-underscore).
|
|
262
|
+
#
|
|
263
|
+
# @param name [String] command name
|
|
264
|
+
# @return [GLI::Command, nil] command object or nil
|
|
265
|
+
def find_gli_command(name)
|
|
266
|
+
@cli_app.commands[name.to_sym] || @cli_app.commands[name.tr("-", "_").to_sym]
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Finds a GLI subcommand by name (supports aliases and dash-to-underscore).
|
|
270
|
+
#
|
|
271
|
+
# @param cmd [GLI::Command] parent command
|
|
272
|
+
# @param name [String] subcommand name
|
|
273
|
+
# @return [GLI::Command, nil] subcommand object or nil
|
|
274
|
+
def find_gli_subcommand(cmd, name)
|
|
275
|
+
cmd.commands[name.to_sym] || cmd.commands[name.tr("-", "_").to_sym]
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# Reorders command flags to appear before positional arguments.
|
|
279
|
+
#
|
|
280
|
+
# @param args [Array<String>] arguments after command name
|
|
281
|
+
# @param cmd [GLI::Command] GLI command with flag/switch metadata
|
|
282
|
+
# @return [Array<String>] reordered arguments
|
|
283
|
+
def reorder_command_flags(args, cmd)
|
|
284
|
+
flags = []
|
|
285
|
+
positional = []
|
|
286
|
+
index = 0
|
|
287
|
+
|
|
288
|
+
while index < args.length
|
|
289
|
+
arg = args[index]
|
|
290
|
+
|
|
291
|
+
if arg == "--"
|
|
292
|
+
positional.concat(args[index..])
|
|
293
|
+
break
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
flag_info = find_command_flag(arg, cmd)
|
|
297
|
+
unless flag_info.nil?
|
|
298
|
+
has_value = flag_info
|
|
299
|
+
if has_value && !arg.include?("=") && index + 1 < args.length
|
|
300
|
+
flags << arg << args[index + 1]
|
|
301
|
+
index += 1
|
|
302
|
+
else
|
|
303
|
+
flags << arg
|
|
304
|
+
end
|
|
305
|
+
else
|
|
306
|
+
positional << arg
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
index += 1
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
flags + positional
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# Finds a command flag/switch definition matching the given argument.
|
|
316
|
+
#
|
|
317
|
+
# @param arg [String] argument to check
|
|
318
|
+
# @param cmd [GLI::Command] command to search in
|
|
319
|
+
# @return [Boolean, nil] has_value (true for flags, false for switches) or nil if not found
|
|
320
|
+
def find_command_flag(arg, cmd)
|
|
321
|
+
flag_part = arg.split("=", 2).first
|
|
322
|
+
|
|
323
|
+
cmd.flags.each_value do |flag|
|
|
324
|
+
return true if flag_matches?(flag, flag_part)
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
cmd.switches.each_value do |sw|
|
|
328
|
+
return false if flag_matches?(sw, flag_part)
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
nil
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
end
|
data/lib/pvectl/cli.rb
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "gli"
|
|
4
|
+
|
|
5
|
+
module Pvectl
|
|
6
|
+
# Main CLI class using the GLI framework.
|
|
7
|
+
#
|
|
8
|
+
# The CLI class is the entry point for the pvectl command line interface.
|
|
9
|
+
# It is responsible for:
|
|
10
|
+
# - Defining global flags and options
|
|
11
|
+
# - Loading commands via PluginLoader (built-in + plugins)
|
|
12
|
+
# - Error handling and system signal handling
|
|
13
|
+
#
|
|
14
|
+
# Uses the GLI (Git-Like Interface) framework to create commands
|
|
15
|
+
# in kubectl/git style.
|
|
16
|
+
#
|
|
17
|
+
# @example Running CLI
|
|
18
|
+
# Pvectl::CLI.run(ARGV)
|
|
19
|
+
#
|
|
20
|
+
# @example Typical command line usage
|
|
21
|
+
# pvectl get nodes # List nodes
|
|
22
|
+
# pvectl get vms -o json # List VMs in JSON format
|
|
23
|
+
# pvectl describe vm 100 --verbose # VM details with debugging
|
|
24
|
+
#
|
|
25
|
+
# @see https://github.com/davetron5000/gli GLI documentation
|
|
26
|
+
# @see Pvectl::ExitCodes Exit codes used by CLI
|
|
27
|
+
#
|
|
28
|
+
class CLI
|
|
29
|
+
extend GLI::App
|
|
30
|
+
|
|
31
|
+
# Program configuration - description and version shown in --help and --version
|
|
32
|
+
program_desc "CLI tool for managing Proxmox clusters with kubectl-like syntax"
|
|
33
|
+
version Pvectl::VERSION
|
|
34
|
+
|
|
35
|
+
# Help formatting: preserve whitespace for code examples in long_desc
|
|
36
|
+
wrap_help_text :verbatim
|
|
37
|
+
|
|
38
|
+
# Display commands in declaration order (not alphabetical)
|
|
39
|
+
sort_help :manually
|
|
40
|
+
|
|
41
|
+
# Enable normal flag processing in subcommands
|
|
42
|
+
# Note: We do NOT use 'arguments :strict' to allow flexible flag/argument ordering
|
|
43
|
+
subcommand_option_handling :normal
|
|
44
|
+
|
|
45
|
+
# @!group Global flags
|
|
46
|
+
|
|
47
|
+
desc "Output format (table, json, yaml, wide)"
|
|
48
|
+
arg_name "FORMAT"
|
|
49
|
+
default_value "table"
|
|
50
|
+
flag [:o, :output], must_match: %w[table json yaml wide]
|
|
51
|
+
|
|
52
|
+
desc "Enable verbose output for debugging"
|
|
53
|
+
switch [:v, :verbose], negatable: false
|
|
54
|
+
|
|
55
|
+
desc "Path to configuration file"
|
|
56
|
+
arg_name "FILE"
|
|
57
|
+
flag [:c, :config]
|
|
58
|
+
|
|
59
|
+
desc "Force colored output (even when not TTY)"
|
|
60
|
+
switch [:color], negatable: true, default_value: nil
|
|
61
|
+
|
|
62
|
+
# @!endgroup
|
|
63
|
+
|
|
64
|
+
# Error handling - maps exceptions to appropriate exit codes.
|
|
65
|
+
#
|
|
66
|
+
# @param exception [Exception] caught exception
|
|
67
|
+
# @return [void]
|
|
68
|
+
on_error do |exception|
|
|
69
|
+
case exception
|
|
70
|
+
when SystemExit
|
|
71
|
+
# Re-raise SystemExit to preserve the exit code
|
|
72
|
+
raise
|
|
73
|
+
when GLI::BadCommandLine, GLI::UnknownCommand
|
|
74
|
+
$stderr.puts "Error: #{exception.message}"
|
|
75
|
+
exit ExitCodes::USAGE_ERROR
|
|
76
|
+
when Pvectl::ArgvPreprocessor::DuplicateFlagError
|
|
77
|
+
$stderr.puts "Error: #{exception.message}"
|
|
78
|
+
exit ExitCodes::USAGE_ERROR
|
|
79
|
+
when Pvectl::Config::ContextNotFoundError,
|
|
80
|
+
Pvectl::Config::ClusterNotFoundError,
|
|
81
|
+
Pvectl::Config::UserNotFoundError,
|
|
82
|
+
Pvectl::Config::ConfigNotFoundError,
|
|
83
|
+
Pvectl::Config::InvalidConfigError
|
|
84
|
+
$stderr.puts "Error: #{exception.message}"
|
|
85
|
+
exit ExitCodes::CONFIG_ERROR
|
|
86
|
+
else
|
|
87
|
+
$stderr.puts "Error: #{exception.message}"
|
|
88
|
+
$stderr.puts exception.backtrace.join("\n") if ENV["GLI_DEBUG"] == "true"
|
|
89
|
+
exit ExitCodes::GENERAL_ERROR
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# SIGINT (Ctrl+C) handling - clean exit with code 130
|
|
94
|
+
trap("INT") do
|
|
95
|
+
$stderr.puts "\nInterrupted"
|
|
96
|
+
exit ExitCodes::INTERRUPTED
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# --- Load all commands (built-in + plugins) ---
|
|
100
|
+
Pvectl::PluginLoader.load_all(self)
|
|
101
|
+
end
|
|
102
|
+
end
|