Skip to content

Commit 7bece4e

Browse files
committed
rdctl: add rdctl info --field=ip-address
This returns the IP address of the VM; which interface is returned depends on the platform and configuration. Signed-off-by: Mark Yen <[email protected]>
1 parent 318cf67 commit 7bece4e

File tree

3 files changed

+98
-5
lines changed

3 files changed

+98
-5
lines changed

src/go/rdctl/cmd/info.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,37 @@ var infoSettings struct {
3838
var infoCmd = &cobra.Command{
3939
Use: "info",
4040
Short: "Return information about Rancher Desktop",
41-
Long: `Returns information about Rancher Desktop. The command returns all
42-
fields in JSON by default, but a single field can be selected.
43-
`,
44-
RunE: doInfoCommand,
41+
Long: makeLongHelp(),
42+
RunE: doInfoCommand,
4543
}
4644

4745
func init() {
4846
rootCmd.AddCommand(infoCmd)
4947
infoCmd.Flags().StringVarP(&infoSettings.Field, "field", "f", "", "return only a specific field")
5048
}
5149

50+
// Generates help text for each field available.
51+
func makeLongHelp() string {
52+
var builder strings.Builder
53+
54+
_, _ = builder.WriteString("Returns information about Rancher Desktop. The command returns all\n")
55+
_, _ = builder.WriteString("fields in JSON by default, but a single field can be selected.\n")
56+
_, _ = builder.WriteString("\n")
57+
_, _ = builder.WriteString("The available fields are:\n")
58+
59+
typ := reflect.TypeFor[info.Info]()
60+
for i := range typ.NumField() {
61+
field := typ.Field(i)
62+
helpText := field.Tag.Get("help")
63+
if helpText == "" {
64+
continue
65+
}
66+
fieldName := strings.SplitN(field.Tag.Get("json"), ",", 2)[0]
67+
_, _ = fmt.Fprintf(&builder, " %-10s %s\n", fieldName, helpText)
68+
}
69+
return builder.String()
70+
}
71+
5272
func doInfoCommand(cmd *cobra.Command, args []string) error {
5373
var result info.Info
5474
var rdClient client.RDClient

src/go/rdctl/pkg/info/ipaddress.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package info
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"fmt"
8+
"os"
9+
"runtime"
10+
11+
"github.com/rancher-sandbox/rancher-desktop/src/go/rdctl/pkg/client"
12+
"github.com/rancher-sandbox/rancher-desktop/src/go/rdctl/pkg/shell"
13+
)
14+
15+
type interfaceInfo struct {
16+
InterfaceName string `json:"ifname"`
17+
Flags []string
18+
State string `json:"operstate"`
19+
MACAddress string `json:"address"`
20+
Addresses []struct {
21+
Family string
22+
Local string
23+
PrefixLength uint `json:"prefixlen"`
24+
Broadcast string
25+
Scope string
26+
} `json:"addr_info"`
27+
}
28+
29+
func getIPAddress(ctx context.Context, result *Info, _ client.RDClient) error {
30+
cmd, err := shell.SpawnCommand(ctx, "ip", "-json", "address", "show")
31+
if err != nil {
32+
return err
33+
}
34+
var buf bytes.Buffer
35+
cmd.Stdout = &buf
36+
cmd.Stderr = os.Stderr
37+
if err := cmd.Run(); err != nil {
38+
return err
39+
}
40+
41+
var interfaces []interfaceInfo
42+
if err := json.Unmarshal(buf.Bytes(), &interfaces); err != nil {
43+
return err
44+
}
45+
46+
// The list of interface names to try, varying by OS.
47+
interfaceNames := map[string][]string{
48+
"darwin": {"rd0", "vznat", "rd1", "eth0"},
49+
"linux": {"eth0"},
50+
"windows": {"eth0"},
51+
}[runtime.GOOS]
52+
53+
for _, ifaceName := range interfaceNames {
54+
for _, iface := range interfaces {
55+
if iface.InterfaceName != ifaceName {
56+
continue
57+
}
58+
for _, addr := range iface.Addresses {
59+
if addr.Family == "inet" && addr.Scope == "global" {
60+
result.IPAddress = addr.Local
61+
return nil
62+
}
63+
}
64+
}
65+
}
66+
67+
return fmt.Errorf("failed to find IP address")
68+
}
69+
70+
func init() {
71+
register("ip-address", getIPAddress)
72+
}

src/go/rdctl/pkg/info/struct.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import (
99
// An Info describes the output the user will receive when running `rdctl info`
1010
// with no special options.
1111
type Info struct {
12-
Version string `json:"version"`
12+
Version string `json:"version" help:"Rancher Desktop application version"`
13+
IPAddress string `json:"ip-address" help:"IP address to use to contact the VM"`
1314
}
1415

1516
// HandlerFunc is the generic interface to populate the [Info] result structure.

0 commit comments

Comments
 (0)