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,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Formatters
|
|
5
|
+
# Manages color output based on TTY detection and user flags.
|
|
6
|
+
#
|
|
7
|
+
# Implements color flag priority:
|
|
8
|
+
# 1. --no-color flag (highest) -> disabled
|
|
9
|
+
# 2. --color flag -> enabled
|
|
10
|
+
# 3. NO_COLOR env var -> disabled (see https://no-color.org/)
|
|
11
|
+
# 4. TTY detection (lowest) -> $stdout.tty?
|
|
12
|
+
#
|
|
13
|
+
# @example Check if colors are enabled
|
|
14
|
+
# ColorSupport.enabled?(explicit_flag: nil) # TTY auto-detect
|
|
15
|
+
# ColorSupport.enabled?(explicit_flag: true) # forced on
|
|
16
|
+
# ColorSupport.enabled?(explicit_flag: false) # forced off
|
|
17
|
+
#
|
|
18
|
+
# @example Get Pastel instance
|
|
19
|
+
# pastel = ColorSupport.pastel(explicit_flag: global_options[:color])
|
|
20
|
+
# puts pastel.green("Success!")
|
|
21
|
+
#
|
|
22
|
+
# @example Colorize status value
|
|
23
|
+
# pastel = ColorSupport.pastel(explicit_flag: true)
|
|
24
|
+
# ColorSupport.colorize_status("running", pastel) #=> "\e[32mrunning\e[0m"
|
|
25
|
+
#
|
|
26
|
+
module ColorSupport
|
|
27
|
+
# Status color mapping following kubectl conventions.
|
|
28
|
+
# @return [Hash<String, Symbol>] mapping of status to Pastel color method
|
|
29
|
+
STATUS_COLORS = {
|
|
30
|
+
"running" => :green,
|
|
31
|
+
"stopped" => :red,
|
|
32
|
+
"paused" => :yellow
|
|
33
|
+
}.freeze
|
|
34
|
+
|
|
35
|
+
class << self
|
|
36
|
+
# Determines if color output should be enabled.
|
|
37
|
+
#
|
|
38
|
+
# Priority order:
|
|
39
|
+
# 1. explicit_flag: false (--no-color) -> disabled
|
|
40
|
+
# 2. explicit_flag: true (--color) -> enabled
|
|
41
|
+
# 3. NO_COLOR env var present -> disabled
|
|
42
|
+
# 4. TTY detection -> $stdout.tty?
|
|
43
|
+
#
|
|
44
|
+
# @param explicit_flag [Boolean, nil] value from --color / --no-color flag
|
|
45
|
+
# - true: --color was passed
|
|
46
|
+
# - false: --no-color was passed
|
|
47
|
+
# - nil: no flag passed, use auto-detection
|
|
48
|
+
# @return [Boolean] true if colors should be used
|
|
49
|
+
def enabled?(explicit_flag: nil)
|
|
50
|
+
return false if explicit_flag == false
|
|
51
|
+
return true if explicit_flag == true
|
|
52
|
+
return false if ENV.key?("NO_COLOR")
|
|
53
|
+
|
|
54
|
+
$stdout.tty?
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Returns a Pastel instance configured based on color settings.
|
|
58
|
+
#
|
|
59
|
+
# @param explicit_flag [Boolean, nil] value from --color / --no-color flag
|
|
60
|
+
# @return [Pastel] pastel instance with appropriate enabled state
|
|
61
|
+
#
|
|
62
|
+
# @example
|
|
63
|
+
# pastel = ColorSupport.pastel(explicit_flag: true)
|
|
64
|
+
# pastel.green("text") #=> "\e[32mtext\e[0m"
|
|
65
|
+
def pastel(explicit_flag: nil)
|
|
66
|
+
require "pastel"
|
|
67
|
+
Pastel.new(enabled: enabled?(explicit_flag: explicit_flag))
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Colors status text according to kubectl conventions.
|
|
71
|
+
#
|
|
72
|
+
# Status colors:
|
|
73
|
+
# - running -> green
|
|
74
|
+
# - stopped -> red
|
|
75
|
+
# - paused -> yellow
|
|
76
|
+
# - unknown status -> dim (gray)
|
|
77
|
+
#
|
|
78
|
+
# @param status [String, nil] status value
|
|
79
|
+
# @param pastel_instance [Pastel] pastel instance
|
|
80
|
+
# @return [String] colored text (or "-" if nil, or dim if unknown status)
|
|
81
|
+
def colorize_status(status, pastel_instance)
|
|
82
|
+
return "-" if status.nil?
|
|
83
|
+
|
|
84
|
+
color = STATUS_COLORS[status.to_s.downcase]
|
|
85
|
+
color ? pastel_instance.public_send(color, status) : pastel_instance.dim(status)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Pvectl
|
|
6
|
+
module Formatters
|
|
7
|
+
# Formats data as JSON output.
|
|
8
|
+
#
|
|
9
|
+
# Collections are rendered as JSON arrays.
|
|
10
|
+
# Single resources are rendered as JSON objects.
|
|
11
|
+
# Empty collections return "[]".
|
|
12
|
+
# Nil values are rendered as JSON null.
|
|
13
|
+
#
|
|
14
|
+
# @example JSON output for collection
|
|
15
|
+
# [
|
|
16
|
+
# {"name": "vm-100", "status": "running"},
|
|
17
|
+
# {"name": "vm-101", "status": "stopped"}
|
|
18
|
+
# ]
|
|
19
|
+
#
|
|
20
|
+
# @example JSON output for single resource
|
|
21
|
+
# {"name": "vm-100", "status": "running", "node": "pve1"}
|
|
22
|
+
#
|
|
23
|
+
class Json < Base
|
|
24
|
+
# Formats data as JSON output.
|
|
25
|
+
#
|
|
26
|
+
# @param data [Array, Object] collection of models or single model
|
|
27
|
+
# @param presenter [Presenters::Base] presenter for hash conversion
|
|
28
|
+
# @param color_enabled [Boolean] ignored for JSON (always plain text)
|
|
29
|
+
# @param describe [Boolean] whether this is a describe command
|
|
30
|
+
# @param context [Hash] ignored for JSON output
|
|
31
|
+
# @return [String] formatted JSON string (pretty-printed)
|
|
32
|
+
def format(data, presenter, color_enabled: true, describe: false, **context)
|
|
33
|
+
if describe && !collection?(data)
|
|
34
|
+
# Use to_description for describe mode
|
|
35
|
+
JSON.pretty_generate(presenter.to_description(data))
|
|
36
|
+
elsif collection?(data)
|
|
37
|
+
hashes = data.map { |model| presenter.to_hash(model) }
|
|
38
|
+
JSON.pretty_generate(hashes)
|
|
39
|
+
else
|
|
40
|
+
JSON.pretty_generate(presenter.to_hash(data))
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Formatters
|
|
5
|
+
# Facade for output formatting in commands.
|
|
6
|
+
#
|
|
7
|
+
# Coordinates Formatter and Presenter to produce formatted output.
|
|
8
|
+
# Handles color flag interpretation and prints to stdout.
|
|
9
|
+
#
|
|
10
|
+
# @example Usage in a command
|
|
11
|
+
# def self.execute(global_options)
|
|
12
|
+
# service = Pvectl::Services::Vm.new
|
|
13
|
+
# vms = service.list
|
|
14
|
+
# presenter = Pvectl::Presenters::Vm.new
|
|
15
|
+
#
|
|
16
|
+
# OutputHelper.print(
|
|
17
|
+
# data: vms,
|
|
18
|
+
# presenter: presenter,
|
|
19
|
+
# format: global_options[:output],
|
|
20
|
+
# color_flag: global_options[:color]
|
|
21
|
+
# )
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# @example Rendering without printing (for testing)
|
|
25
|
+
# output = OutputHelper.render(
|
|
26
|
+
# data: vms,
|
|
27
|
+
# presenter: presenter,
|
|
28
|
+
# format: "json"
|
|
29
|
+
# )
|
|
30
|
+
#
|
|
31
|
+
module OutputHelper
|
|
32
|
+
class << self
|
|
33
|
+
# Formats data and prints to stdout.
|
|
34
|
+
#
|
|
35
|
+
# @param data [Array, Object] collection of models or single model
|
|
36
|
+
# @param presenter [Presenters::Base] presenter for the resource type
|
|
37
|
+
# @param format [String] output format ("table", "json", "yaml", "wide")
|
|
38
|
+
# @param color_flag [Boolean, nil] color flag from CLI
|
|
39
|
+
# - true: --color was passed
|
|
40
|
+
# - false: --no-color was passed
|
|
41
|
+
# - nil: auto-detect based on TTY
|
|
42
|
+
# @param describe [Boolean] whether this is a describe command (default: false)
|
|
43
|
+
# @param context [Hash] additional context passed to presenter
|
|
44
|
+
# @return [void]
|
|
45
|
+
def print(data:, presenter:, format: "table", color_flag: nil, describe: false, **context)
|
|
46
|
+
output = render(data: data, presenter: presenter, format: format, color_flag: color_flag, describe: describe, **context)
|
|
47
|
+
puts output
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Returns formatted string without printing.
|
|
51
|
+
#
|
|
52
|
+
# Useful for testing or when you need to manipulate the output
|
|
53
|
+
# before displaying.
|
|
54
|
+
#
|
|
55
|
+
# @param data [Array, Object] collection of models or single model
|
|
56
|
+
# @param presenter [Presenters::Base] presenter for the resource type
|
|
57
|
+
# @param format [String] output format ("table", "json", "yaml", "wide")
|
|
58
|
+
# @param color_flag [Boolean, nil] color flag from CLI
|
|
59
|
+
# @param describe [Boolean] whether this is a describe command
|
|
60
|
+
# @param context [Hash] additional context passed to presenter
|
|
61
|
+
# @return [String] formatted output
|
|
62
|
+
def render(data:, presenter:, format: "table", color_flag: nil, describe: false, **context)
|
|
63
|
+
formatter = Registry.for(format)
|
|
64
|
+
color_enabled = ColorSupport.enabled?(explicit_flag: color_flag)
|
|
65
|
+
|
|
66
|
+
formatter.format(
|
|
67
|
+
data,
|
|
68
|
+
presenter,
|
|
69
|
+
color_enabled: color_enabled,
|
|
70
|
+
describe: describe,
|
|
71
|
+
**context
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Formatters
|
|
5
|
+
# Registry for looking up formatters by name.
|
|
6
|
+
#
|
|
7
|
+
# Implements the Registry Pattern to map format names
|
|
8
|
+
# ("table", "json", "yaml", "wide") to formatter classes.
|
|
9
|
+
#
|
|
10
|
+
# @example Getting a formatter
|
|
11
|
+
# formatter = Registry.for("json")
|
|
12
|
+
# output = formatter.format(data, presenter)
|
|
13
|
+
#
|
|
14
|
+
# @example Listing available formats
|
|
15
|
+
# Registry.available_formats #=> ["table", "wide", "json", "yaml"]
|
|
16
|
+
#
|
|
17
|
+
# @example Checking if format is supported
|
|
18
|
+
# Registry.supported?("json") #=> true
|
|
19
|
+
# Registry.supported?("xml") #=> false
|
|
20
|
+
#
|
|
21
|
+
class Registry
|
|
22
|
+
# Mapping of format names to formatter classes.
|
|
23
|
+
# @return [Hash<String, Class>] frozen hash of format name to class
|
|
24
|
+
FORMATS = {
|
|
25
|
+
"table" => Table,
|
|
26
|
+
"wide" => Wide,
|
|
27
|
+
"json" => Json,
|
|
28
|
+
"yaml" => Yaml
|
|
29
|
+
}.freeze
|
|
30
|
+
|
|
31
|
+
class << self
|
|
32
|
+
# Gets a formatter instance for the specified format.
|
|
33
|
+
#
|
|
34
|
+
# @param format_name [String, Symbol] format name (table, wide, json, yaml)
|
|
35
|
+
# @return [Base] formatter instance
|
|
36
|
+
# @raise [ArgumentError] if format is not found
|
|
37
|
+
#
|
|
38
|
+
# @example
|
|
39
|
+
# formatter = Registry.for("json")
|
|
40
|
+
# formatter.format(data, presenter)
|
|
41
|
+
def for(format_name)
|
|
42
|
+
formatter_class = FORMATS[format_name.to_s]
|
|
43
|
+
raise ArgumentError, "Unknown format: #{format_name}" unless formatter_class
|
|
44
|
+
|
|
45
|
+
formatter_class.new
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Returns list of available format names.
|
|
49
|
+
#
|
|
50
|
+
# @return [Array<String>] available format names
|
|
51
|
+
#
|
|
52
|
+
# @example
|
|
53
|
+
# Registry.available_formats #=> ["table", "wide", "json", "yaml"]
|
|
54
|
+
def available_formats
|
|
55
|
+
FORMATS.keys
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Checks if a format is supported.
|
|
59
|
+
#
|
|
60
|
+
# @param format_name [String, Symbol] format name
|
|
61
|
+
# @return [Boolean] true if format is supported
|
|
62
|
+
#
|
|
63
|
+
# @example
|
|
64
|
+
# Registry.supported?("json") #=> true
|
|
65
|
+
# Registry.supported?("xml") #=> false
|
|
66
|
+
def supported?(format_name)
|
|
67
|
+
FORMATS.key?(format_name.to_s)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tty-table"
|
|
4
|
+
|
|
5
|
+
module Pvectl
|
|
6
|
+
module Formatters
|
|
7
|
+
# Formats data as a table using tty-table gem.
|
|
8
|
+
#
|
|
9
|
+
# For collections: renders standard horizontal table with headers.
|
|
10
|
+
# For single resources (describe): renders vertical key-value layout.
|
|
11
|
+
#
|
|
12
|
+
# @example Table output for collection
|
|
13
|
+
# NAME STATUS NODE
|
|
14
|
+
# vm-100 running pve1
|
|
15
|
+
# vm-101 stopped pve2
|
|
16
|
+
#
|
|
17
|
+
# @example Vertical output for single resource (describe)
|
|
18
|
+
# Name: vm-100
|
|
19
|
+
# Status: running
|
|
20
|
+
# Node: pve1
|
|
21
|
+
#
|
|
22
|
+
# @see Pvectl::Formatters::Wide for extended column output
|
|
23
|
+
#
|
|
24
|
+
class Table < Base
|
|
25
|
+
# Formats data as table output.
|
|
26
|
+
#
|
|
27
|
+
# @param data [Array, Object] collection of models or single model
|
|
28
|
+
# @param presenter [Presenters::Base] presenter for column/row definitions
|
|
29
|
+
# @param color_enabled [Boolean] whether to apply color formatting
|
|
30
|
+
# @param describe [Boolean] whether this is a describe command (single resource)
|
|
31
|
+
# @param context [Hash] additional context passed to presenter
|
|
32
|
+
# @return [String] formatted table string
|
|
33
|
+
def format(data, presenter, color_enabled: true, describe: false, **context)
|
|
34
|
+
pastel = ColorSupport.pastel(explicit_flag: color_enabled)
|
|
35
|
+
|
|
36
|
+
if describe || !collection?(data)
|
|
37
|
+
format_describe(data, presenter, pastel)
|
|
38
|
+
else
|
|
39
|
+
format_table(data, presenter, pastel, **context)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
# Formats collection as horizontal table.
|
|
46
|
+
#
|
|
47
|
+
# @param data [Array] collection of models
|
|
48
|
+
# @param presenter [Presenters::Base] presenter
|
|
49
|
+
# @param pastel [Pastel] pastel instance for coloring
|
|
50
|
+
# @param context [Hash] context passed to presenter
|
|
51
|
+
# @return [String] formatted table
|
|
52
|
+
def format_table(data, presenter, pastel, **context)
|
|
53
|
+
headers = presenter.columns
|
|
54
|
+
rows = data.map do |model|
|
|
55
|
+
row = presenter.to_row(model, **context)
|
|
56
|
+
colorize_row(row, headers, pastel)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
render_table(headers, rows)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Formats single resource as vertical key-value layout.
|
|
63
|
+
# Supports nested Hashes (sections) and Arrays of Hashes (tables).
|
|
64
|
+
#
|
|
65
|
+
# @param model [Object] single model
|
|
66
|
+
# @param presenter [Presenters::Base] presenter
|
|
67
|
+
# @param pastel [Pastel] pastel instance for coloring
|
|
68
|
+
# @return [String] formatted vertical layout
|
|
69
|
+
def format_describe(model, presenter, pastel)
|
|
70
|
+
hash = presenter.to_description(model)
|
|
71
|
+
format_describe_hash(hash, pastel, indent: 0)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Colorizes status columns in a row.
|
|
75
|
+
#
|
|
76
|
+
# @param row [Array] row values
|
|
77
|
+
# @param headers [Array<String>] column headers
|
|
78
|
+
# @param pastel [Pastel] pastel instance
|
|
79
|
+
# @return [Array] row with colorized values
|
|
80
|
+
def colorize_row(row, headers, pastel)
|
|
81
|
+
row.each_with_index.map do |value, idx|
|
|
82
|
+
header = headers[idx].to_s.downcase
|
|
83
|
+
if header == "status"
|
|
84
|
+
ColorSupport.colorize_status(value, pastel)
|
|
85
|
+
else
|
|
86
|
+
normalize_nil(value)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Recursively formats a hash for describe output.
|
|
92
|
+
#
|
|
93
|
+
# At indent 0 (top level), once a section (Hash/Array) has been rendered,
|
|
94
|
+
# all subsequent entries get a blank line separator for consistent spacing.
|
|
95
|
+
#
|
|
96
|
+
# @param hash [Hash] hash to format
|
|
97
|
+
# @param pastel [Pastel] pastel instance
|
|
98
|
+
# @param indent [Integer] current indentation level
|
|
99
|
+
# @return [String] formatted output
|
|
100
|
+
def format_describe_hash(hash, pastel, indent: 0)
|
|
101
|
+
return "-" if hash.nil?
|
|
102
|
+
|
|
103
|
+
lines = []
|
|
104
|
+
prefix = " " * indent
|
|
105
|
+
|
|
106
|
+
# Calculate max key length for alignment (only for non-nested values)
|
|
107
|
+
simple_keys = hash.select { |_, v| !v.is_a?(Hash) && !(v.is_a?(Array) && v.first.is_a?(Hash)) && !v.to_s.include?("\n") }
|
|
108
|
+
max_key_length = simple_keys.keys.map { |k| k.to_s.length }.max || 0
|
|
109
|
+
|
|
110
|
+
# Track whether we've passed a nested section (Hash/Array).
|
|
111
|
+
# Once a section appears, all subsequent simple entries get blank-line separators.
|
|
112
|
+
prev_was_section = false
|
|
113
|
+
|
|
114
|
+
hash.each do |key, value|
|
|
115
|
+
human_key = humanize_key(key)
|
|
116
|
+
|
|
117
|
+
if value.is_a?(Hash)
|
|
118
|
+
# Nested section
|
|
119
|
+
lines << ""
|
|
120
|
+
lines << "#{prefix}#{human_key}:"
|
|
121
|
+
lines << format_describe_hash(value, pastel, indent: indent + 1)
|
|
122
|
+
prev_was_section = true
|
|
123
|
+
elsif value.is_a?(Array) && !value.empty? && value.first.is_a?(Hash)
|
|
124
|
+
# Array of hashes -> inline table
|
|
125
|
+
lines << ""
|
|
126
|
+
lines << "#{prefix}#{human_key}:"
|
|
127
|
+
lines << format_describe_table(value, indent: indent + 1)
|
|
128
|
+
prev_was_section = true
|
|
129
|
+
elsif value.is_a?(Array) && value.empty?
|
|
130
|
+
# Empty array -> show as "-"
|
|
131
|
+
lines << "" if prev_was_section
|
|
132
|
+
formatted_key = "#{human_key}:".ljust(max_key_length + 2)
|
|
133
|
+
lines << "#{prefix}#{formatted_key}-"
|
|
134
|
+
else
|
|
135
|
+
formatted_value = format_describe_value(value, key.to_s, pastel)
|
|
136
|
+
if formatted_value.include?("\n")
|
|
137
|
+
# Multi-line value: render as block section
|
|
138
|
+
lines << ""
|
|
139
|
+
lines << "#{prefix}#{human_key}:"
|
|
140
|
+
formatted_value.each_line do |line|
|
|
141
|
+
lines << "#{prefix} #{line.chomp}"
|
|
142
|
+
end
|
|
143
|
+
prev_was_section = true
|
|
144
|
+
else
|
|
145
|
+
# Simple key-value — add separator after sections at any indent level
|
|
146
|
+
lines << "" if prev_was_section
|
|
147
|
+
formatted_key = "#{human_key}:".ljust(max_key_length + 2)
|
|
148
|
+
lines << "#{prefix}#{formatted_key}#{formatted_value}"
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
lines.join("\n").sub(/\A\n+/, "").rstrip
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Formats array of hashes as inline table.
|
|
157
|
+
#
|
|
158
|
+
# @param array [Array<Hash>] array of hashes
|
|
159
|
+
# @param indent [Integer] indentation level
|
|
160
|
+
# @return [String] formatted table
|
|
161
|
+
def format_describe_table(array, indent: 0)
|
|
162
|
+
return " " * indent + "-" if array.empty?
|
|
163
|
+
|
|
164
|
+
prefix = " " * indent
|
|
165
|
+
headers = array.first.keys.map { |k| k.to_s.upcase }
|
|
166
|
+
rows = array.map { |item| item.values.map(&:to_s) }
|
|
167
|
+
|
|
168
|
+
# Calculate column widths
|
|
169
|
+
widths = headers.each_with_index.map do |h, i|
|
|
170
|
+
[h.length, *rows.map { |r| r[i]&.length || 0 }].max
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
lines = []
|
|
174
|
+
# Header
|
|
175
|
+
header_line = headers.each_with_index.map { |h, i| h.ljust(widths[i]) }.join(" ")
|
|
176
|
+
lines << "#{prefix}#{header_line}"
|
|
177
|
+
|
|
178
|
+
# Rows
|
|
179
|
+
rows.each do |row|
|
|
180
|
+
row_line = row.each_with_index.map { |v, i| (v || "-").ljust(widths[i]) }.join(" ")
|
|
181
|
+
lines << "#{prefix}#{row_line}"
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
lines.join("\n")
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Formats a value for describe output.
|
|
188
|
+
#
|
|
189
|
+
# @param value [Object] value to format
|
|
190
|
+
# @param key [String] key name (used for status detection)
|
|
191
|
+
# @param pastel [Pastel] pastel instance
|
|
192
|
+
# @return [String] formatted value
|
|
193
|
+
def format_describe_value(value, key, pastel)
|
|
194
|
+
return "-" if value.nil?
|
|
195
|
+
|
|
196
|
+
if key.downcase == "status"
|
|
197
|
+
ColorSupport.colorize_status(value, pastel)
|
|
198
|
+
else
|
|
199
|
+
value.to_s
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Converts snake_case or kebab-case key to Title Case.
|
|
204
|
+
# Preserves already-formatted keys (e.g., "CPU", "DNS", "Cloud-Init").
|
|
205
|
+
#
|
|
206
|
+
# @param key [String, Symbol] key to humanize
|
|
207
|
+
# @return [String] humanized key
|
|
208
|
+
def humanize_key(key)
|
|
209
|
+
str = key.to_s
|
|
210
|
+
# If the key is already formatted (contains spaces, is all caps,
|
|
211
|
+
# or has uppercase letters indicating intentional formatting), return as-is
|
|
212
|
+
return str if str.include?(" ") || str == str.upcase || str.match?(/[A-Z]/)
|
|
213
|
+
|
|
214
|
+
str.split(/[_-]/).map(&:capitalize).join(" ")
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Renders table using tty-table.
|
|
218
|
+
#
|
|
219
|
+
# Uses :basic renderer for clean kubectl-like output (no borders).
|
|
220
|
+
# Handles non-TTY environments gracefully by rescuing ioctl errors.
|
|
221
|
+
#
|
|
222
|
+
# @param headers [Array<String>] column headers
|
|
223
|
+
# @param rows [Array<Array>] row data
|
|
224
|
+
# @return [String] rendered table
|
|
225
|
+
def render_table(headers, rows)
|
|
226
|
+
table = TTY::Table.new(header: headers, rows: rows)
|
|
227
|
+
table.render(:basic, padding: [0, 2]) || headers.join("\t")
|
|
228
|
+
rescue NoMethodError
|
|
229
|
+
# TTY::Screen may call ioctl on non-TTY streams (e.g., StringIO in tests)
|
|
230
|
+
# Fall back to simple tab-separated output
|
|
231
|
+
([headers] + rows).map { |row| row.join("\t") }.join("\n")
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tty-table"
|
|
4
|
+
|
|
5
|
+
module Pvectl
|
|
6
|
+
module Formatters
|
|
7
|
+
# Formats data as a wide table with extended columns.
|
|
8
|
+
#
|
|
9
|
+
# For collections: uses wide_columns and to_wide_row from presenter.
|
|
10
|
+
# For single resources (describe): delegates to Table (no wide variant).
|
|
11
|
+
#
|
|
12
|
+
# @example Wide table output
|
|
13
|
+
# NAME STATUS NODE MEMORY CPU UPTIME
|
|
14
|
+
# vm-100 running pve1 2048 2 3d 5h
|
|
15
|
+
#
|
|
16
|
+
# @see Pvectl::Formatters::Table for standard table output
|
|
17
|
+
# @see Pvectl::Presenters::Base#wide_columns for wide column definitions
|
|
18
|
+
#
|
|
19
|
+
class Wide < Base
|
|
20
|
+
# Formats data as wide table output.
|
|
21
|
+
#
|
|
22
|
+
# @param data [Array, Object] collection of models or single model
|
|
23
|
+
# @param presenter [Presenters::Base] presenter for column/row definitions
|
|
24
|
+
# @param color_enabled [Boolean] whether to apply color formatting
|
|
25
|
+
# @param describe [Boolean] whether this is a describe command
|
|
26
|
+
# @param context [Hash] additional context passed to presenter
|
|
27
|
+
# @return [String] formatted wide table string
|
|
28
|
+
def format(data, presenter, color_enabled: true, describe: false, **context)
|
|
29
|
+
# For describe (single resource), delegate to Table formatter
|
|
30
|
+
# Wide format has no meaning for vertical key-value layout
|
|
31
|
+
if describe || !collection?(data)
|
|
32
|
+
Table.new.format(data, presenter, color_enabled: color_enabled, describe: true, **context)
|
|
33
|
+
else
|
|
34
|
+
format_wide_table(data, presenter, color_enabled, **context)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
# Formats collection as wide table.
|
|
41
|
+
#
|
|
42
|
+
# @param data [Array] collection of models
|
|
43
|
+
# @param presenter [Presenters::Base] presenter
|
|
44
|
+
# @param color_enabled [Boolean] whether to apply color
|
|
45
|
+
# @param context [Hash] context passed to presenter
|
|
46
|
+
# @return [String] formatted wide table
|
|
47
|
+
def format_wide_table(data, presenter, color_enabled, **context)
|
|
48
|
+
pastel = ColorSupport.pastel(explicit_flag: color_enabled)
|
|
49
|
+
headers = presenter.wide_columns
|
|
50
|
+
rows = data.map do |model|
|
|
51
|
+
row = presenter.to_wide_row(model, **context)
|
|
52
|
+
colorize_row(row, headers, pastel)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
render_table(headers, rows)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Colorizes status columns in a row.
|
|
59
|
+
#
|
|
60
|
+
# @param row [Array] row values
|
|
61
|
+
# @param headers [Array<String>] column headers
|
|
62
|
+
# @param pastel [Pastel] pastel instance
|
|
63
|
+
# @return [Array] row with colorized values
|
|
64
|
+
def colorize_row(row, headers, pastel)
|
|
65
|
+
row.each_with_index.map do |value, idx|
|
|
66
|
+
header = headers[idx].to_s.downcase
|
|
67
|
+
if header == "status"
|
|
68
|
+
ColorSupport.colorize_status(value, pastel)
|
|
69
|
+
else
|
|
70
|
+
value.nil? ? "-" : value
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Renders table using tty-table.
|
|
76
|
+
#
|
|
77
|
+
# Uses :basic renderer for clean kubectl-like output (no borders).
|
|
78
|
+
# Handles non-TTY environments gracefully by rescuing ioctl errors.
|
|
79
|
+
#
|
|
80
|
+
# @param headers [Array<String>] column headers
|
|
81
|
+
# @param rows [Array<Array>] row data
|
|
82
|
+
# @return [String] rendered table
|
|
83
|
+
def render_table(headers, rows)
|
|
84
|
+
table = TTY::Table.new(header: headers, rows: rows)
|
|
85
|
+
table.render(:basic, padding: [0, 2]) || headers.join("\t")
|
|
86
|
+
rescue NoMethodError
|
|
87
|
+
# TTY::Screen may call ioctl on non-TTY streams (e.g., StringIO in tests)
|
|
88
|
+
# Fall back to simple tab-separated output
|
|
89
|
+
([headers] + rows).map { |row| row.join("\t") }.join("\n")
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
|
|
5
|
+
module Pvectl
|
|
6
|
+
module Formatters
|
|
7
|
+
# Formats data as YAML output.
|
|
8
|
+
#
|
|
9
|
+
# Collections are rendered as YAML arrays.
|
|
10
|
+
# Single resources are rendered as YAML mappings.
|
|
11
|
+
# Empty collections return "--- []\n".
|
|
12
|
+
# Nil values are rendered as YAML null (~).
|
|
13
|
+
#
|
|
14
|
+
# @example YAML output for collection
|
|
15
|
+
# ---
|
|
16
|
+
# - name: vm-100
|
|
17
|
+
# status: running
|
|
18
|
+
# - name: vm-101
|
|
19
|
+
# status: stopped
|
|
20
|
+
#
|
|
21
|
+
# @example YAML output for single resource
|
|
22
|
+
# ---
|
|
23
|
+
# name: vm-100
|
|
24
|
+
# status: running
|
|
25
|
+
# node: pve1
|
|
26
|
+
#
|
|
27
|
+
class Yaml < Base
|
|
28
|
+
# Formats data as YAML output.
|
|
29
|
+
#
|
|
30
|
+
# @param data [Array, Object] collection of models or single model
|
|
31
|
+
# @param presenter [Presenters::Base] presenter for hash conversion
|
|
32
|
+
# @param color_enabled [Boolean] ignored for YAML (always plain text)
|
|
33
|
+
# @param describe [Boolean] whether this is a describe command
|
|
34
|
+
# @param context [Hash] ignored for YAML output
|
|
35
|
+
# @return [String] formatted YAML string
|
|
36
|
+
def format(data, presenter, color_enabled: true, describe: false, **context)
|
|
37
|
+
if describe && !collection?(data)
|
|
38
|
+
# Use to_description for describe mode
|
|
39
|
+
presenter.to_description(data).to_yaml
|
|
40
|
+
elsif collection?(data)
|
|
41
|
+
hashes = data.map { |model| presenter.to_hash(model) }
|
|
42
|
+
hashes.to_yaml
|
|
43
|
+
else
|
|
44
|
+
presenter.to_hash(data).to_yaml
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|