Skip to content

feat(cli): complete CLI with full CRUD operations (#563)#621

Merged
ArangoGutierrez merged 23 commits intoNVIDIA:mainfrom
ArangoGutierrez:feat/563-cli-crud-operations
Feb 6, 2026
Merged

feat(cli): complete CLI with full CRUD operations (#563)#621
ArangoGutierrez merged 23 commits intoNVIDIA:mainfrom
ArangoGutierrez:feat/563-cli-crud-operations

Conversation

@ArangoGutierrez
Copy link
Collaborator

Summary

Implements the complete CLI CRUD operations epic (#563) to unblock 11 dependent PRs. This adds:

  • Logger verbosity levels: quiet/normal/verbose/debug with -q, --verbose, -d flags
  • Output formatting: table/JSON/YAML support via pkg/output package
  • New commands: describe, get kubeconfig, get ssh-config, ssh, scp, update
  • Refactored list: renamed --quiet to --ids-only for clarity
  • Shared utilities: cmd/cli/common package with GetHostURL and ConnectSSH
  • Status command: integrated output formatting with --output flag

Key changes

  • internal/logger/logger.go — verbosity levels (Quiet/Normal/Verbose/Debug)
  • pkg/output/output.go — Formatter, TablePrinter, TableData interface
  • cmd/cli/common/host.go — shared host URL resolution and SSH connection
  • cmd/cli/describe/ — detailed instance info with JSON/YAML/table output
  • cmd/cli/get/ — kubeconfig download and SSH config generation
  • cmd/cli/ssh/ — interactive SSH and remote command execution
  • cmd/cli/scp/ — SFTP file transfer with recursive directory support
  • cmd/cli/update/ — add components, labels, and re-provision instances

Review fixes included

  • C1: Fixed nil map panic when writing labels during --reprovision
  • C2: Fixed SSH remote command quoting (replaced broken containsSpace logic)
  • I1: Extracted duplicate getHostURL/connectSSH into shared common package
  • I2: Use path (POSIX) for remote SFTP paths instead of filepath
  • I3: Log warnings for skipped files during recursive remote copy
  • I4: Use c.IsSet() for flags with defaults to prevent config overwrites

Test plan

  • All existing CLI tests pass (go test ./cmd/cli/...)
  • New unit tests for output formatting (pkg/output)
  • New unit tests for GetHostURL across ssh, get, and common packages
  • New unit tests for parsePath, describe, update, and label handling
  • Regression test for nil Labels map panic (C1)
  • Manual: holodeck list, holodeck describe <id>, holodeck ssh <id>
  • Manual: holodeck scp ./file <id>:/tmp/, holodeck get kubeconfig <id>
  • Manual: holodeck update <id> --add-driver --driver-version 560.35.03

Closes #563

Copilot AI review requested due to automatic review settings February 6, 2026 08:14
Design for issue NVIDIA#563 covering:
- New CLI commands (describe, get, ssh, scp, update)
- Global verbosity flags (-q, -v, -d)
- Output formatting infrastructure
- Implementation tasks and testing strategy

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Detailed task-by-task implementation plan for:
- Global verbosity flags (-q, -v, -d)
- Unit tests for all new CLI commands
- Output formatter tests

11 tasks with TDD approach and commit checkpoints.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Add Verbosity type with four levels (Quiet, Normal, Verbose, Debug) to
control log output. Info, Check, and Warning methods now respect
verbosity settings, while Error always prints. New Debug and Trace
methods provide additional logging granularity for troubleshooting.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Add global flags for controlling output verbosity:
- --quiet, -q: Suppress non-error output
- --verbose: Enable verbose output
- --debug, -d: Enable debug-level logging (also reads DEBUG env var)

Flag precedence: --debug > --verbose > --quiet

Note: -v was not used for --verbose to avoid conflict with built-in --version flag.
Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Rename the --quiet flag to --ids-only to avoid conflict with the new
global --quiet flag that controls verbosity. The --ids-only flag now
only outputs instance IDs, one per line.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Add comprehensive unit tests for the pkg/output package covering:
- ValidFormats() and IsValidFormat() validation functions
- NewFormatter() with empty, valid, and invalid format inputs
- Formatter.Format() accessor method
- PrintJSON() with struct, map, and slice data types
- PrintYAML() with struct, map, and nested struct data
- Print() dispatch logic for JSON, YAML, and table formats
- PrintTable() with mock TableData interface
- TablePrinter fluent interface (Header, Row, Flush)
- SetWriter() for output redirection
- Error message quality for invalid formats

Achieves 97.9% code coverage.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Adds pkg/output with Formatter, TablePrinter, and TableData interface
for consistent output formatting across all CLI commands.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Adds -o flag (json/yaml/table) to holodeck status with structured
output types for JSON/YAML serialization.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
New commands for issue NVIDIA#563:
- describe: detailed instance introspection with JSON/YAML output
- get kubeconfig: download kubeconfig from instance
- get ssh-config: generate SSH config entry
- ssh: SSH into instance or run remote commands
- scp: copy files to/from instance via SFTP
- update: update instance configuration (add components, labels)

All commands include unit tests for core logic.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Updates list_test.go to use --ids-only instead of the old --quiet/-q
flag which was renamed to avoid conflict with the global -q flag.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
C1: Add nil check for env.Labels before writing provisioned label
in update --reprovision. Previously panicked when no --label flags
were provided and the cached environment had no labels.

C2: Replace incorrect containsSpace+fmt.Sprintf("%q") quoting with
simple strings.Join. SSH session.Run always passes through the
remote shell, so Go-style quoting was wrong. Document this behavior.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
- Extract getHostURL and connectSSH into cmd/cli/common package,
  eliminating duplication across ssh, scp, and get commands (I1)
- Use path instead of filepath for remote SFTP paths to ensure
  correct POSIX separators on all platforms (I2)
- Log warnings for skipped files during recursive remote copy (I3)
- Use c.IsSet() for flags with defaults to prevent overwriting
  existing config with default values (I4)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
@ArangoGutierrez ArangoGutierrez force-pushed the feat/563-cli-crud-operations branch from ef73606 to fb19ac0 Compare February 6, 2026 08:17
@coveralls
Copy link

coveralls commented Feb 6, 2026

Pull Request Test Coverage Report for Build 21752525231

Details

  • 85 of 87 (97.7%) changed or added relevant lines in 1 file are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+1.0%) to 45.946%

Changes Missing Coverage Covered Lines Changed/Added Lines %
pkg/output/output.go 85 87 97.7%
Totals Coverage Status
Change from base Build 21708182973: 1.0%
Covered Lines: 2091
Relevant Lines: 4551

💛 - Coveralls

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements a comprehensive set of CLI CRUD operations to complete the Holodeck CLI (#563), adding essential features for managing GPU-ready cloud environments. The changes introduce global verbosity levels, structured output formatting, and critical SSH/file transfer capabilities.

Changes:

  • Added logger verbosity system (Quiet/Normal/Verbose/Debug) with corresponding flags (-q, --verbose, -d)
  • Created pkg/output package for consistent JSON/YAML/table formatting across commands
  • Implemented six new commands: describe, get (kubeconfig/ssh-config), ssh, scp, and update
  • Refactored list command to support output formats and renamed --quiet to --ids-only
  • Created shared cmd/cli/common package with GetHostURL and ConnectSSH utilities

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
internal/logger/logger.go Added verbosity levels (Quiet/Normal/Verbose/Debug) with new Debug() and Trace() methods
internal/logger/logger_test.go Comprehensive unit tests for verbosity filtering and methods
pkg/output/output.go Formatter interface with table/JSON/YAML support and TableData interface
pkg/output/output_test.go 732 lines of tests covering all output formats and edge cases
cmd/cli/common/host.go Shared GetHostURL and ConnectSSH utilities (contains bug)
cmd/cli/describe/describe.go Detailed instance introspection with multi-format output
cmd/cli/get/get.go Kubeconfig download and SSH config generation subcommands
cmd/cli/ssh/ssh.go Interactive SSH and remote command execution
cmd/cli/scp/scp.go SFTP file transfer with recursive directory support, proper POSIX path handling
cmd/cli/update/update.go Add components, labels, and re-provision instances (contains bug)
cmd/cli/list/list.go Refactored with output formatting, renamed --quiet to --ids-only
cmd/cli/status/status.go Added output formatting with --output flag
cmd/cli/main.go Integrated global verbosity flags and new commands

}
}
case v1alpha1.ProviderSSH:
return env.Spec.HostUrl, nil
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The field path should be env.Spec.Instance.HostUrl not env.Spec.HostUrl. The HostUrl field is defined on the Instance struct, not directly on EnvironmentSpec. This will cause a compilation error or runtime panic.

Suggested change
return env.Spec.HostUrl, nil
if env.Spec.Instance != nil {
return env.Spec.Instance.HostUrl, nil
}

Copilot uses AI. Check for mistakes.
}
}
} else if env.Spec.Provider == v1alpha1.ProviderSSH {
hostUrl = env.Spec.HostUrl
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The field path should be env.Spec.Instance.HostUrl not env.Spec.HostUrl. The HostUrl field is defined on the Instance struct, not directly on EnvironmentSpec. This will cause a compilation error or runtime panic.

Suggested change
hostUrl = env.Spec.HostUrl
if env.Spec.Instance == nil {
return fmt.Errorf("instance spec is required for SSH provider")
}
hostUrl = env.Spec.Instance.HostUrl

Copilot uses AI. Check for mistakes.
- Fix gofmt, gosec, staticcheck, unconvert, and errcheck lint issues
- Promote gopkg.in/yaml.v3 from indirect to direct dependency
- Remove planning documents (development artifacts)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
@ArangoGutierrez ArangoGutierrez force-pushed the feat/563-cli-crud-operations branch from 5f98a33 to 8440213 Compare February 6, 2026 10:35
Address CodeQL alert NVIDIA#6 (go/insecure-hostkeycallback) by replacing
the inaccurate comment about "no pre-established host keys". The env
file requires SSH authentication keys (client-to-server), but server
host keys are a separate concern — they are generated at instance boot
with no trusted distribution channel to the client.

The security trade-off is now explicitly documented with CWE reference.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
… (S1)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Move duplicate GetHostURL tests from cmd/cli/get/ and cmd/cli/ssh/
into cmd/cli/common/host_test.go. Merge overlapping test cases and
add a new TestGetHostURL_Cluster_FallbackToFirstNode case that covers
the fallback path when no control-plane node exists.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Verbosity is an iota (0-3) so the int->int32 cast cannot overflow.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
@ArangoGutierrez ArangoGutierrez force-pushed the feat/563-cli-crud-operations branch from 8e9a8eb to 607a329 Compare February 6, 2026 13:38
@ArangoGutierrez ArangoGutierrez merged commit 64d31e8 into NVIDIA:main Feb 6, 2026
19 checks passed
ArangoGutierrez added a commit to ArangoGutierrez/holodeck that referenced this pull request Feb 10, 2026
…A#621)

* docs: add CLI CRUD and verbosity design document

Design for issue NVIDIA#563 covering:
- New CLI commands (describe, get, ssh, scp, update)
- Global verbosity flags (-q, -v, -d)
- Output formatting infrastructure
- Implementation tasks and testing strategy

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* docs: add CLI CRUD implementation plan

Detailed task-by-task implementation plan for:
- Global verbosity flags (-q, -v, -d)
- Unit tests for all new CLI commands
- Output formatter tests

11 tasks with TDD approach and commit checkpoints.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(logger): add verbosity levels with Debug and Trace methods

Add Verbosity type with four levels (Quiet, Normal, Verbose, Debug) to
control log output. Info, Check, and Warning methods now respect
verbosity settings, while Error always prints. New Debug and Trace
methods provide additional logging granularity for troubleshooting.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add global verbosity flags (-q, --verbose, -d)

Add global flags for controlling output verbosity:
- --quiet, -q: Suppress non-error output
- --verbose: Enable verbose output
- --debug, -d: Enable debug-level logging (also reads DEBUG env var)

Flag precedence: --debug > --verbose > --quiet

Note: -v was not used for --verbose to avoid conflict with built-in --version flag.
Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): rename list --quiet to --ids-only

Rename the --quiet flag to --ids-only to avoid conflict with the new
global --quiet flag that controls verbosity. The --ids-only flag now
only outputs instance IDs, one per line.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* test(output): add unit tests for output formatting package

Add comprehensive unit tests for the pkg/output package covering:
- ValidFormats() and IsValidFormat() validation functions
- NewFormatter() with empty, valid, and invalid format inputs
- Formatter.Format() accessor method
- PrintJSON() with struct, map, and slice data types
- PrintYAML() with struct, map, and nested struct data
- Print() dispatch logic for JSON, YAML, and table formats
- PrintTable() with mock TableData interface
- TablePrinter fluent interface (Header, Row, Flush)
- SetWriter() for output redirection
- Error message quality for invalid formats

Achieves 97.9% code coverage.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(output): add output formatting package with table/JSON/YAML support

Adds pkg/output with Formatter, TablePrinter, and TableData interface
for consistent output formatting across all CLI commands.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add output formatting to status command

Adds -o flag (json/yaml/table) to holodeck status with structured
output types for JSON/YAML serialization.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add describe, get, ssh, scp, update commands with tests

New commands for issue NVIDIA#563:
- describe: detailed instance introspection with JSON/YAML output
- get kubeconfig: download kubeconfig from instance
- get ssh-config: generate SSH config entry
- ssh: SSH into instance or run remote commands
- scp: copy files to/from instance via SFTP
- update: update instance configuration (add components, labels)

All commands include unit tests for core logic.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* test(list): update tests for --ids-only flag rename

Updates list_test.go to use --ids-only instead of the old --quiet/-q
flag which was renamed to avoid conflict with the global -q flag.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(cli): fix nil map panic and SSH command quoting

C1: Add nil check for env.Labels before writing provisioned label
in update --reprovision. Previously panicked when no --label flags
were provided and the cached environment had no labels.

C2: Replace incorrect containsSpace+fmt.Sprintf("%q") quoting with
simple strings.Join. SSH session.Run always passes through the
remote shell, so Go-style quoting was wrong. Document this behavior.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): extract shared utilities and fix review issues

- Extract getHostURL and connectSSH into cmd/cli/common package,
  eliminating duplication across ssh, scp, and get commands (I1)
- Use path instead of filepath for remote SFTP paths to ensure
  correct POSIX separators on all platforms (I2)
- Log warnings for skipped files during recursive remote copy (I3)
- Use c.IsSet() for flags with defaults to prevent overwriting
  existing config with default values (I4)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(ci): resolve lint issues, update go.mod, and remove plan docs

- Fix gofmt, gosec, staticcheck, unconvert, and errcheck lint issues
- Promote gopkg.in/yaml.v3 from indirect to direct dependency
- Remove planning documents (development artifacts)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(security): correct misleading host key comments (CWE-322)

Address CodeQL alert NVIDIA#6 (go/insecure-hostkeycallback) by replacing
the inaccurate comment about "no pre-established host keys". The env
file requires SSH authentication keys (client-to-server), but server
host keys are a separate concern — they are generated at instance boot
with no trusted distribution channel to the client.

The security trade-off is now explicitly documented with CWE reference.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): use common.GetHostURL in update runProvision (S4)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(cli): consolidate update command cache file writes (C2)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add -q as alias for --ids-only for backwards compatibility (S1)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): extract SSH retry constants in ConnectSSH (S2)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* docs(cli): document SSH remote command quoting behavior (S3)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(logger): warnings always print regardless of verbosity (M1)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): consolidate GetHostURL tests into common package (M3)

Move duplicate GetHostURL tests from cmd/cli/get/ and cmd/cli/ssh/
into cmd/cli/common/host_test.go. Merge overlapping test cases and
add a new TestGetHostURL_Cluster_FallbackToFirstNode case that covers
the fallback path when no control-plane node exists.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(logger): make Verbosity thread-safe with atomic.Int32 (M6)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(logger): add nolint directive for gosec G115 in SetVerbosity

Verbosity is an iota (0-3) so the int->int32 cast cannot overflow.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

---------

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
ArangoGutierrez added a commit to ArangoGutierrez/holodeck that referenced this pull request Feb 10, 2026
…A#621)

* docs: add CLI CRUD and verbosity design document

Design for issue NVIDIA#563 covering:
- New CLI commands (describe, get, ssh, scp, update)
- Global verbosity flags (-q, -v, -d)
- Output formatting infrastructure
- Implementation tasks and testing strategy

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* docs: add CLI CRUD implementation plan

Detailed task-by-task implementation plan for:
- Global verbosity flags (-q, -v, -d)
- Unit tests for all new CLI commands
- Output formatter tests

11 tasks with TDD approach and commit checkpoints.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(logger): add verbosity levels with Debug and Trace methods

Add Verbosity type with four levels (Quiet, Normal, Verbose, Debug) to
control log output. Info, Check, and Warning methods now respect
verbosity settings, while Error always prints. New Debug and Trace
methods provide additional logging granularity for troubleshooting.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add global verbosity flags (-q, --verbose, -d)

Add global flags for controlling output verbosity:
- --quiet, -q: Suppress non-error output
- --verbose: Enable verbose output
- --debug, -d: Enable debug-level logging (also reads DEBUG env var)

Flag precedence: --debug > --verbose > --quiet

Note: -v was not used for --verbose to avoid conflict with built-in --version flag.
Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): rename list --quiet to --ids-only

Rename the --quiet flag to --ids-only to avoid conflict with the new
global --quiet flag that controls verbosity. The --ids-only flag now
only outputs instance IDs, one per line.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* test(output): add unit tests for output formatting package

Add comprehensive unit tests for the pkg/output package covering:
- ValidFormats() and IsValidFormat() validation functions
- NewFormatter() with empty, valid, and invalid format inputs
- Formatter.Format() accessor method
- PrintJSON() with struct, map, and slice data types
- PrintYAML() with struct, map, and nested struct data
- Print() dispatch logic for JSON, YAML, and table formats
- PrintTable() with mock TableData interface
- TablePrinter fluent interface (Header, Row, Flush)
- SetWriter() for output redirection
- Error message quality for invalid formats

Achieves 97.9% code coverage.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(output): add output formatting package with table/JSON/YAML support

Adds pkg/output with Formatter, TablePrinter, and TableData interface
for consistent output formatting across all CLI commands.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add output formatting to status command

Adds -o flag (json/yaml/table) to holodeck status with structured
output types for JSON/YAML serialization.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add describe, get, ssh, scp, update commands with tests

New commands for issue NVIDIA#563:
- describe: detailed instance introspection with JSON/YAML output
- get kubeconfig: download kubeconfig from instance
- get ssh-config: generate SSH config entry
- ssh: SSH into instance or run remote commands
- scp: copy files to/from instance via SFTP
- update: update instance configuration (add components, labels)

All commands include unit tests for core logic.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* test(list): update tests for --ids-only flag rename

Updates list_test.go to use --ids-only instead of the old --quiet/-q
flag which was renamed to avoid conflict with the global -q flag.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(cli): fix nil map panic and SSH command quoting

C1: Add nil check for env.Labels before writing provisioned label
in update --reprovision. Previously panicked when no --label flags
were provided and the cached environment had no labels.

C2: Replace incorrect containsSpace+fmt.Sprintf("%q") quoting with
simple strings.Join. SSH session.Run always passes through the
remote shell, so Go-style quoting was wrong. Document this behavior.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): extract shared utilities and fix review issues

- Extract getHostURL and connectSSH into cmd/cli/common package,
  eliminating duplication across ssh, scp, and get commands (I1)
- Use path instead of filepath for remote SFTP paths to ensure
  correct POSIX separators on all platforms (I2)
- Log warnings for skipped files during recursive remote copy (I3)
- Use c.IsSet() for flags with defaults to prevent overwriting
  existing config with default values (I4)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(ci): resolve lint issues, update go.mod, and remove plan docs

- Fix gofmt, gosec, staticcheck, unconvert, and errcheck lint issues
- Promote gopkg.in/yaml.v3 from indirect to direct dependency
- Remove planning documents (development artifacts)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(security): correct misleading host key comments (CWE-322)

Address CodeQL alert NVIDIA#6 (go/insecure-hostkeycallback) by replacing
the inaccurate comment about "no pre-established host keys". The env
file requires SSH authentication keys (client-to-server), but server
host keys are a separate concern — they are generated at instance boot
with no trusted distribution channel to the client.

The security trade-off is now explicitly documented with CWE reference.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): use common.GetHostURL in update runProvision (S4)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(cli): consolidate update command cache file writes (C2)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add -q as alias for --ids-only for backwards compatibility (S1)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): extract SSH retry constants in ConnectSSH (S2)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* docs(cli): document SSH remote command quoting behavior (S3)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(logger): warnings always print regardless of verbosity (M1)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): consolidate GetHostURL tests into common package (M3)

Move duplicate GetHostURL tests from cmd/cli/get/ and cmd/cli/ssh/
into cmd/cli/common/host_test.go. Merge overlapping test cases and
add a new TestGetHostURL_Cluster_FallbackToFirstNode case that covers
the fallback path when no control-plane node exists.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(logger): make Verbosity thread-safe with atomic.Int32 (M6)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(logger): add nolint directive for gosec G115 in SetVerbosity

Verbosity is an iota (0-3) so the int->int32 cast cannot overflow.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

---------

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
ArangoGutierrez added a commit to ArangoGutierrez/holodeck that referenced this pull request Feb 10, 2026
…A#621)

* docs: add CLI CRUD and verbosity design document

Design for issue NVIDIA#563 covering:
- New CLI commands (describe, get, ssh, scp, update)
- Global verbosity flags (-q, -v, -d)
- Output formatting infrastructure
- Implementation tasks and testing strategy

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* docs: add CLI CRUD implementation plan

Detailed task-by-task implementation plan for:
- Global verbosity flags (-q, -v, -d)
- Unit tests for all new CLI commands
- Output formatter tests

11 tasks with TDD approach and commit checkpoints.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(logger): add verbosity levels with Debug and Trace methods

Add Verbosity type with four levels (Quiet, Normal, Verbose, Debug) to
control log output. Info, Check, and Warning methods now respect
verbosity settings, while Error always prints. New Debug and Trace
methods provide additional logging granularity for troubleshooting.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add global verbosity flags (-q, --verbose, -d)

Add global flags for controlling output verbosity:
- --quiet, -q: Suppress non-error output
- --verbose: Enable verbose output
- --debug, -d: Enable debug-level logging (also reads DEBUG env var)

Flag precedence: --debug > --verbose > --quiet

Note: -v was not used for --verbose to avoid conflict with built-in --version flag.
Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): rename list --quiet to --ids-only

Rename the --quiet flag to --ids-only to avoid conflict with the new
global --quiet flag that controls verbosity. The --ids-only flag now
only outputs instance IDs, one per line.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* test(output): add unit tests for output formatting package

Add comprehensive unit tests for the pkg/output package covering:
- ValidFormats() and IsValidFormat() validation functions
- NewFormatter() with empty, valid, and invalid format inputs
- Formatter.Format() accessor method
- PrintJSON() with struct, map, and slice data types
- PrintYAML() with struct, map, and nested struct data
- Print() dispatch logic for JSON, YAML, and table formats
- PrintTable() with mock TableData interface
- TablePrinter fluent interface (Header, Row, Flush)
- SetWriter() for output redirection
- Error message quality for invalid formats

Achieves 97.9% code coverage.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(output): add output formatting package with table/JSON/YAML support

Adds pkg/output with Formatter, TablePrinter, and TableData interface
for consistent output formatting across all CLI commands.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add output formatting to status command

Adds -o flag (json/yaml/table) to holodeck status with structured
output types for JSON/YAML serialization.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add describe, get, ssh, scp, update commands with tests

New commands for issue NVIDIA#563:
- describe: detailed instance introspection with JSON/YAML output
- get kubeconfig: download kubeconfig from instance
- get ssh-config: generate SSH config entry
- ssh: SSH into instance or run remote commands
- scp: copy files to/from instance via SFTP
- update: update instance configuration (add components, labels)

All commands include unit tests for core logic.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* test(list): update tests for --ids-only flag rename

Updates list_test.go to use --ids-only instead of the old --quiet/-q
flag which was renamed to avoid conflict with the global -q flag.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(cli): fix nil map panic and SSH command quoting

C1: Add nil check for env.Labels before writing provisioned label
in update --reprovision. Previously panicked when no --label flags
were provided and the cached environment had no labels.

C2: Replace incorrect containsSpace+fmt.Sprintf("%q") quoting with
simple strings.Join. SSH session.Run always passes through the
remote shell, so Go-style quoting was wrong. Document this behavior.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): extract shared utilities and fix review issues

- Extract getHostURL and connectSSH into cmd/cli/common package,
  eliminating duplication across ssh, scp, and get commands (I1)
- Use path instead of filepath for remote SFTP paths to ensure
  correct POSIX separators on all platforms (I2)
- Log warnings for skipped files during recursive remote copy (I3)
- Use c.IsSet() for flags with defaults to prevent overwriting
  existing config with default values (I4)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(ci): resolve lint issues, update go.mod, and remove plan docs

- Fix gofmt, gosec, staticcheck, unconvert, and errcheck lint issues
- Promote gopkg.in/yaml.v3 from indirect to direct dependency
- Remove planning documents (development artifacts)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(security): correct misleading host key comments (CWE-322)

Address CodeQL alert NVIDIA#6 (go/insecure-hostkeycallback) by replacing
the inaccurate comment about "no pre-established host keys". The env
file requires SSH authentication keys (client-to-server), but server
host keys are a separate concern — they are generated at instance boot
with no trusted distribution channel to the client.

The security trade-off is now explicitly documented with CWE reference.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): use common.GetHostURL in update runProvision (S4)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(cli): consolidate update command cache file writes (C2)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add -q as alias for --ids-only for backwards compatibility (S1)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): extract SSH retry constants in ConnectSSH (S2)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* docs(cli): document SSH remote command quoting behavior (S3)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(logger): warnings always print regardless of verbosity (M1)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): consolidate GetHostURL tests into common package (M3)

Move duplicate GetHostURL tests from cmd/cli/get/ and cmd/cli/ssh/
into cmd/cli/common/host_test.go. Merge overlapping test cases and
add a new TestGetHostURL_Cluster_FallbackToFirstNode case that covers
the fallback path when no control-plane node exists.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(logger): make Verbosity thread-safe with atomic.Int32 (M6)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(logger): add nolint directive for gosec G115 in SetVerbosity

Verbosity is an iota (0-3) so the int->int32 cast cannot overflow.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

---------

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
ArangoGutierrez added a commit to ArangoGutierrez/holodeck that referenced this pull request Feb 12, 2026
…A#621)

* docs: add CLI CRUD and verbosity design document

Design for issue NVIDIA#563 covering:
- New CLI commands (describe, get, ssh, scp, update)
- Global verbosity flags (-q, -v, -d)
- Output formatting infrastructure
- Implementation tasks and testing strategy

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* docs: add CLI CRUD implementation plan

Detailed task-by-task implementation plan for:
- Global verbosity flags (-q, -v, -d)
- Unit tests for all new CLI commands
- Output formatter tests

11 tasks with TDD approach and commit checkpoints.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(logger): add verbosity levels with Debug and Trace methods

Add Verbosity type with four levels (Quiet, Normal, Verbose, Debug) to
control log output. Info, Check, and Warning methods now respect
verbosity settings, while Error always prints. New Debug and Trace
methods provide additional logging granularity for troubleshooting.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add global verbosity flags (-q, --verbose, -d)

Add global flags for controlling output verbosity:
- --quiet, -q: Suppress non-error output
- --verbose: Enable verbose output
- --debug, -d: Enable debug-level logging (also reads DEBUG env var)

Flag precedence: --debug > --verbose > --quiet

Note: -v was not used for --verbose to avoid conflict with built-in --version flag.
Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): rename list --quiet to --ids-only

Rename the --quiet flag to --ids-only to avoid conflict with the new
global --quiet flag that controls verbosity. The --ids-only flag now
only outputs instance IDs, one per line.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* test(output): add unit tests for output formatting package

Add comprehensive unit tests for the pkg/output package covering:
- ValidFormats() and IsValidFormat() validation functions
- NewFormatter() with empty, valid, and invalid format inputs
- Formatter.Format() accessor method
- PrintJSON() with struct, map, and slice data types
- PrintYAML() with struct, map, and nested struct data
- Print() dispatch logic for JSON, YAML, and table formats
- PrintTable() with mock TableData interface
- TablePrinter fluent interface (Header, Row, Flush)
- SetWriter() for output redirection
- Error message quality for invalid formats

Achieves 97.9% code coverage.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(output): add output formatting package with table/JSON/YAML support

Adds pkg/output with Formatter, TablePrinter, and TableData interface
for consistent output formatting across all CLI commands.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add output formatting to status command

Adds -o flag (json/yaml/table) to holodeck status with structured
output types for JSON/YAML serialization.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add describe, get, ssh, scp, update commands with tests

New commands for issue NVIDIA#563:
- describe: detailed instance introspection with JSON/YAML output
- get kubeconfig: download kubeconfig from instance
- get ssh-config: generate SSH config entry
- ssh: SSH into instance or run remote commands
- scp: copy files to/from instance via SFTP
- update: update instance configuration (add components, labels)

All commands include unit tests for core logic.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* test(list): update tests for --ids-only flag rename

Updates list_test.go to use --ids-only instead of the old --quiet/-q
flag which was renamed to avoid conflict with the global -q flag.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(cli): fix nil map panic and SSH command quoting

C1: Add nil check for env.Labels before writing provisioned label
in update --reprovision. Previously panicked when no --label flags
were provided and the cached environment had no labels.

C2: Replace incorrect containsSpace+fmt.Sprintf("%q") quoting with
simple strings.Join. SSH session.Run always passes through the
remote shell, so Go-style quoting was wrong. Document this behavior.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): extract shared utilities and fix review issues

- Extract getHostURL and connectSSH into cmd/cli/common package,
  eliminating duplication across ssh, scp, and get commands (I1)
- Use path instead of filepath for remote SFTP paths to ensure
  correct POSIX separators on all platforms (I2)
- Log warnings for skipped files during recursive remote copy (I3)
- Use c.IsSet() for flags with defaults to prevent overwriting
  existing config with default values (I4)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(ci): resolve lint issues, update go.mod, and remove plan docs

- Fix gofmt, gosec, staticcheck, unconvert, and errcheck lint issues
- Promote gopkg.in/yaml.v3 from indirect to direct dependency
- Remove planning documents (development artifacts)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(security): correct misleading host key comments (CWE-322)

Address CodeQL alert NVIDIA#6 (go/insecure-hostkeycallback) by replacing
the inaccurate comment about "no pre-established host keys". The env
file requires SSH authentication keys (client-to-server), but server
host keys are a separate concern — they are generated at instance boot
with no trusted distribution channel to the client.

The security trade-off is now explicitly documented with CWE reference.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): use common.GetHostURL in update runProvision (S4)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(cli): consolidate update command cache file writes (C2)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add -q as alias for --ids-only for backwards compatibility (S1)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): extract SSH retry constants in ConnectSSH (S2)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* docs(cli): document SSH remote command quoting behavior (S3)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(logger): warnings always print regardless of verbosity (M1)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): consolidate GetHostURL tests into common package (M3)

Move duplicate GetHostURL tests from cmd/cli/get/ and cmd/cli/ssh/
into cmd/cli/common/host_test.go. Merge overlapping test cases and
add a new TestGetHostURL_Cluster_FallbackToFirstNode case that covers
the fallback path when no control-plane node exists.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(logger): make Verbosity thread-safe with atomic.Int32 (M6)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(logger): add nolint directive for gosec G115 in SetVerbosity

Verbosity is an iota (0-3) so the int->int32 cast cannot overflow.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

---------

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
ArangoGutierrez added a commit to ArangoGutierrez/holodeck that referenced this pull request Feb 13, 2026
…A#621)

* docs: add CLI CRUD and verbosity design document

Design for issue NVIDIA#563 covering:
- New CLI commands (describe, get, ssh, scp, update)
- Global verbosity flags (-q, -v, -d)
- Output formatting infrastructure
- Implementation tasks and testing strategy

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* docs: add CLI CRUD implementation plan

Detailed task-by-task implementation plan for:
- Global verbosity flags (-q, -v, -d)
- Unit tests for all new CLI commands
- Output formatter tests

11 tasks with TDD approach and commit checkpoints.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(logger): add verbosity levels with Debug and Trace methods

Add Verbosity type with four levels (Quiet, Normal, Verbose, Debug) to
control log output. Info, Check, and Warning methods now respect
verbosity settings, while Error always prints. New Debug and Trace
methods provide additional logging granularity for troubleshooting.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add global verbosity flags (-q, --verbose, -d)

Add global flags for controlling output verbosity:
- --quiet, -q: Suppress non-error output
- --verbose: Enable verbose output
- --debug, -d: Enable debug-level logging (also reads DEBUG env var)

Flag precedence: --debug > --verbose > --quiet

Note: -v was not used for --verbose to avoid conflict with built-in --version flag.
Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): rename list --quiet to --ids-only

Rename the --quiet flag to --ids-only to avoid conflict with the new
global --quiet flag that controls verbosity. The --ids-only flag now
only outputs instance IDs, one per line.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* test(output): add unit tests for output formatting package

Add comprehensive unit tests for the pkg/output package covering:
- ValidFormats() and IsValidFormat() validation functions
- NewFormatter() with empty, valid, and invalid format inputs
- Formatter.Format() accessor method
- PrintJSON() with struct, map, and slice data types
- PrintYAML() with struct, map, and nested struct data
- Print() dispatch logic for JSON, YAML, and table formats
- PrintTable() with mock TableData interface
- TablePrinter fluent interface (Header, Row, Flush)
- SetWriter() for output redirection
- Error message quality for invalid formats

Achieves 97.9% code coverage.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(output): add output formatting package with table/JSON/YAML support

Adds pkg/output with Formatter, TablePrinter, and TableData interface
for consistent output formatting across all CLI commands.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add output formatting to status command

Adds -o flag (json/yaml/table) to holodeck status with structured
output types for JSON/YAML serialization.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add describe, get, ssh, scp, update commands with tests

New commands for issue NVIDIA#563:
- describe: detailed instance introspection with JSON/YAML output
- get kubeconfig: download kubeconfig from instance
- get ssh-config: generate SSH config entry
- ssh: SSH into instance or run remote commands
- scp: copy files to/from instance via SFTP
- update: update instance configuration (add components, labels)

All commands include unit tests for core logic.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* test(list): update tests for --ids-only flag rename

Updates list_test.go to use --ids-only instead of the old --quiet/-q
flag which was renamed to avoid conflict with the global -q flag.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(cli): fix nil map panic and SSH command quoting

C1: Add nil check for env.Labels before writing provisioned label
in update --reprovision. Previously panicked when no --label flags
were provided and the cached environment had no labels.

C2: Replace incorrect containsSpace+fmt.Sprintf("%q") quoting with
simple strings.Join. SSH session.Run always passes through the
remote shell, so Go-style quoting was wrong. Document this behavior.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): extract shared utilities and fix review issues

- Extract getHostURL and connectSSH into cmd/cli/common package,
  eliminating duplication across ssh, scp, and get commands (I1)
- Use path instead of filepath for remote SFTP paths to ensure
  correct POSIX separators on all platforms (I2)
- Log warnings for skipped files during recursive remote copy (I3)
- Use c.IsSet() for flags with defaults to prevent overwriting
  existing config with default values (I4)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(ci): resolve lint issues, update go.mod, and remove plan docs

- Fix gofmt, gosec, staticcheck, unconvert, and errcheck lint issues
- Promote gopkg.in/yaml.v3 from indirect to direct dependency
- Remove planning documents (development artifacts)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(security): correct misleading host key comments (CWE-322)

Address CodeQL alert NVIDIA#6 (go/insecure-hostkeycallback) by replacing
the inaccurate comment about "no pre-established host keys". The env
file requires SSH authentication keys (client-to-server), but server
host keys are a separate concern — they are generated at instance boot
with no trusted distribution channel to the client.

The security trade-off is now explicitly documented with CWE reference.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): use common.GetHostURL in update runProvision (S4)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(cli): consolidate update command cache file writes (C2)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add -q as alias for --ids-only for backwards compatibility (S1)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): extract SSH retry constants in ConnectSSH (S2)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* docs(cli): document SSH remote command quoting behavior (S3)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(logger): warnings always print regardless of verbosity (M1)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): consolidate GetHostURL tests into common package (M3)

Move duplicate GetHostURL tests from cmd/cli/get/ and cmd/cli/ssh/
into cmd/cli/common/host_test.go. Merge overlapping test cases and
add a new TestGetHostURL_Cluster_FallbackToFirstNode case that covers
the fallback path when no control-plane node exists.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(logger): make Verbosity thread-safe with atomic.Int32 (M6)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(logger): add nolint directive for gosec G115 in SetVerbosity

Verbosity is an iota (0-3) so the int->int32 cast cannot overflow.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

---------

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
ArangoGutierrez added a commit to ArangoGutierrez/holodeck that referenced this pull request Feb 13, 2026
…A#621)

* docs: add CLI CRUD and verbosity design document

Design for issue NVIDIA#563 covering:
- New CLI commands (describe, get, ssh, scp, update)
- Global verbosity flags (-q, -v, -d)
- Output formatting infrastructure
- Implementation tasks and testing strategy

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* docs: add CLI CRUD implementation plan

Detailed task-by-task implementation plan for:
- Global verbosity flags (-q, -v, -d)
- Unit tests for all new CLI commands
- Output formatter tests

11 tasks with TDD approach and commit checkpoints.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(logger): add verbosity levels with Debug and Trace methods

Add Verbosity type with four levels (Quiet, Normal, Verbose, Debug) to
control log output. Info, Check, and Warning methods now respect
verbosity settings, while Error always prints. New Debug and Trace
methods provide additional logging granularity for troubleshooting.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add global verbosity flags (-q, --verbose, -d)

Add global flags for controlling output verbosity:
- --quiet, -q: Suppress non-error output
- --verbose: Enable verbose output
- --debug, -d: Enable debug-level logging (also reads DEBUG env var)

Flag precedence: --debug > --verbose > --quiet

Note: -v was not used for --verbose to avoid conflict with built-in --version flag.
Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): rename list --quiet to --ids-only

Rename the --quiet flag to --ids-only to avoid conflict with the new
global --quiet flag that controls verbosity. The --ids-only flag now
only outputs instance IDs, one per line.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* test(output): add unit tests for output formatting package

Add comprehensive unit tests for the pkg/output package covering:
- ValidFormats() and IsValidFormat() validation functions
- NewFormatter() with empty, valid, and invalid format inputs
- Formatter.Format() accessor method
- PrintJSON() with struct, map, and slice data types
- PrintYAML() with struct, map, and nested struct data
- Print() dispatch logic for JSON, YAML, and table formats
- PrintTable() with mock TableData interface
- TablePrinter fluent interface (Header, Row, Flush)
- SetWriter() for output redirection
- Error message quality for invalid formats

Achieves 97.9% code coverage.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(output): add output formatting package with table/JSON/YAML support

Adds pkg/output with Formatter, TablePrinter, and TableData interface
for consistent output formatting across all CLI commands.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add output formatting to status command

Adds -o flag (json/yaml/table) to holodeck status with structured
output types for JSON/YAML serialization.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add describe, get, ssh, scp, update commands with tests

New commands for issue NVIDIA#563:
- describe: detailed instance introspection with JSON/YAML output
- get kubeconfig: download kubeconfig from instance
- get ssh-config: generate SSH config entry
- ssh: SSH into instance or run remote commands
- scp: copy files to/from instance via SFTP
- update: update instance configuration (add components, labels)

All commands include unit tests for core logic.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* test(list): update tests for --ids-only flag rename

Updates list_test.go to use --ids-only instead of the old --quiet/-q
flag which was renamed to avoid conflict with the global -q flag.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(cli): fix nil map panic and SSH command quoting

C1: Add nil check for env.Labels before writing provisioned label
in update --reprovision. Previously panicked when no --label flags
were provided and the cached environment had no labels.

C2: Replace incorrect containsSpace+fmt.Sprintf("%q") quoting with
simple strings.Join. SSH session.Run always passes through the
remote shell, so Go-style quoting was wrong. Document this behavior.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): extract shared utilities and fix review issues

- Extract getHostURL and connectSSH into cmd/cli/common package,
  eliminating duplication across ssh, scp, and get commands (I1)
- Use path instead of filepath for remote SFTP paths to ensure
  correct POSIX separators on all platforms (I2)
- Log warnings for skipped files during recursive remote copy (I3)
- Use c.IsSet() for flags with defaults to prevent overwriting
  existing config with default values (I4)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(ci): resolve lint issues, update go.mod, and remove plan docs

- Fix gofmt, gosec, staticcheck, unconvert, and errcheck lint issues
- Promote gopkg.in/yaml.v3 from indirect to direct dependency
- Remove planning documents (development artifacts)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(security): correct misleading host key comments (CWE-322)

Address CodeQL alert NVIDIA#6 (go/insecure-hostkeycallback) by replacing
the inaccurate comment about "no pre-established host keys". The env
file requires SSH authentication keys (client-to-server), but server
host keys are a separate concern — they are generated at instance boot
with no trusted distribution channel to the client.

The security trade-off is now explicitly documented with CWE reference.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): use common.GetHostURL in update runProvision (S4)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(cli): consolidate update command cache file writes (C2)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* feat(cli): add -q as alias for --ids-only for backwards compatibility (S1)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): extract SSH retry constants in ConnectSSH (S2)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* docs(cli): document SSH remote command quoting behavior (S3)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(logger): warnings always print regardless of verbosity (M1)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* refactor(cli): consolidate GetHostURL tests into common package (M3)

Move duplicate GetHostURL tests from cmd/cli/get/ and cmd/cli/ssh/
into cmd/cli/common/host_test.go. Merge overlapping test cases and
add a new TestGetHostURL_Cluster_FallbackToFirstNode case that covers
the fallback path when no control-plane node exists.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(logger): make Verbosity thread-safe with atomic.Int32 (M6)

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

* fix(logger): add nolint directive for gosec G115 in SetVerbosity

Verbosity is an iota (0-3) so the int->int32 cast cannot overflow.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>

---------

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Epic] Complete CLI with Full CRUD Operations

3 participants