Skip to content

Commit 58b2a0c

Browse files
committed
feat: add crc ssh and crc vm stats commands
Add direct SSH access to the CRC VM via 'crc ssh' with support for interactive shells (syscall.Exec) and remote commands (exec.Command). Add 'crc vm stats' to display comprehensive VM diagnostics including OS info, CPU/memory/disk usage, network config, container stats, service health, and OOM/fault counters. Supports -o json output for scripting. Usage examples: $ crc ssh -- hostname crc $ crc ssh -- cat /etc/redhat-release Red Hat Enterprise Linux release 9.6 (Plow) $ crc ssh # opens interactive shell $ crc vm stats System ──────── OS: Red Hat Enterprise Linux CoreOS 9.6.20260225-1 (Plow) Kernel: 5.14.0-570.94.1.el9_6.aarch64 (aarch64) Uptime: 4h 0m CPU ───── Cores: 4 Load Avg: 2.62 (1m) 2.06 (5m) 1.81 (15m) Memory ──────── RAM: 6.953GB / 10.93GB [############........] 64% Swap: disabled Disk (/sysroot) ───────────────── Usage: 25.51GB / 36.98GB [#############.......] 69% Free: 11.47GB Network ───────── Node IP: 127.0.0.1 Cluster IP: 192.168.127.2 DNS: 192.168.127.1 Workload ────────── Pods: 66 Containers: 104 Images: 66 Top Containers (by memory) ──────────────────────────── kube-apiserver: 1.46GB ovnkube-controller: 262.2MB openshift-apiserver: 255.7MB etcd: 247.4MB kube-controller-manager: 185MB Services ────────── kubelet: ok crio: ok Health ──────── OOM Kills: 0 Major Page Faults: 258166 $ crc vm stats -o json # JSON output for scripting
1 parent 5114f3d commit 58b2a0c

File tree

5 files changed

+559
-0
lines changed

5 files changed

+559
-0
lines changed

cmd/crc/cmd/root.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414

1515
cmdBundle "github.com/crc-org/crc/v2/cmd/crc/cmd/bundle"
1616
cmdConfig "github.com/crc-org/crc/v2/cmd/crc/cmd/config"
17+
cmdVM "github.com/crc-org/crc/v2/cmd/crc/cmd/vm"
1718
crcConfig "github.com/crc-org/crc/v2/pkg/crc/config"
1819
"github.com/crc-org/crc/v2/pkg/crc/constants"
1920
crcErr "github.com/crc-org/crc/v2/pkg/crc/errors"
@@ -71,6 +72,7 @@ func init() {
7172
// subcommands
7273
rootCmd.AddCommand(cmdConfig.GetConfigCmd(config))
7374
rootCmd.AddCommand(cmdBundle.GetBundleCmd(config))
75+
rootCmd.AddCommand(cmdVM.GetVMCmd(config))
7476

7577
logging.AddLogLevelFlag(rootCmd.PersistentFlags())
7678
}

cmd/crc/cmd/root_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,13 @@ func TestCrcManPageGenerator_WhenInvoked_GeneratesManPagesForAllCrcSubCommands(t
3838
"crc-oc-env.1",
3939
"crc-podman-env.1",
4040
"crc-setup.1",
41+
"crc-ssh.1",
4142
"crc-start.1",
4243
"crc-status.1",
4344
"crc-stop.1",
4445
"crc-version.1",
46+
"crc-vm-stats.1",
47+
"crc-vm.1",
4548
"crc.1",
4649
}, manPagesFiles)
4750
}

cmd/crc/cmd/ssh.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/exec"
7+
"strconv"
8+
"syscall"
9+
10+
"github.com/spf13/cobra"
11+
)
12+
13+
func init() {
14+
rootCmd.AddCommand(sshCmd)
15+
}
16+
17+
var sshCmd = &cobra.Command{
18+
Use: "ssh [-- COMMAND...]",
19+
Short: "Open an SSH connection to the OpenShift cluster node",
20+
Long: "Open an SSH connection to the OpenShift cluster node. Pass commands after -- to execute them remotely.",
21+
RunE: func(_ *cobra.Command, args []string) error {
22+
return runSSH(args)
23+
},
24+
}
25+
26+
func runSSH(args []string) error {
27+
client := newMachine()
28+
if err := checkIfMachineMissing(client); err != nil {
29+
return err
30+
}
31+
32+
connectionDetails, err := client.ConnectionDetails()
33+
if err != nil {
34+
return err
35+
}
36+
37+
sshPath, err := exec.LookPath("ssh")
38+
if err != nil {
39+
return fmt.Errorf("cannot find ssh binary: %w", err)
40+
}
41+
42+
var sshKey string
43+
for _, key := range connectionDetails.SSHKeys {
44+
if _, err := os.Stat(key); err == nil {
45+
sshKey = key
46+
break
47+
}
48+
}
49+
if sshKey == "" {
50+
return fmt.Errorf("no SSH key found")
51+
}
52+
53+
sshArgs := []string{
54+
"ssh",
55+
"-o", "StrictHostKeyChecking=no",
56+
"-o", "UserKnownHostsFile=/dev/null",
57+
"-o", "LogLevel=ERROR",
58+
"-i", sshKey,
59+
"-p", strconv.Itoa(connectionDetails.SSHPort),
60+
fmt.Sprintf("%s@%s", connectionDetails.SSHUsername, connectionDetails.IP),
61+
}
62+
63+
if len(args) > 0 {
64+
sshArgs = append(sshArgs, args...)
65+
cmd := exec.Command(sshPath, sshArgs[1:]...)
66+
cmd.Stdin = os.Stdin
67+
cmd.Stdout = os.Stdout
68+
cmd.Stderr = os.Stderr
69+
return cmd.Run()
70+
}
71+
72+
return syscall.Exec(sshPath, sshArgs, os.Environ())
73+
}

0 commit comments

Comments
 (0)