Skip to content

Commit 3b5529f

Browse files
authored
Merge pull request #1249 from deitch/limactl-inspect
add inspect CLI option
2 parents 5a2dea0 + 75594fa commit 3b5529f

File tree

4 files changed

+192
-98
lines changed

4 files changed

+192
-98
lines changed

cmd/limactl/inspect.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/lima-vm/lima/pkg/store"
7+
"github.com/sirupsen/logrus"
8+
"github.com/spf13/cobra"
9+
)
10+
11+
func newInspectCommand() *cobra.Command {
12+
inspectCommand := &cobra.Command{
13+
Use: "inspect [flags] [INSTANCE]...",
14+
Short: "Inspect instances of Lima.",
15+
Long: `Inspect instances of Lima.
16+
Includes both the current state, as well as the combined configuration used to create the instance.
17+
The output can be presented in one of several formats, using the --format <format> flag.
18+
19+
--format json - output in json format
20+
--format yaml - output in yaml format
21+
--format table - output in table format
22+
--format '{{ <go template> }}' - if the format begins and ends with '{{ }}', then it is used as a go template.
23+
`,
24+
Args: cobra.ArbitraryArgs,
25+
RunE: inspectAction,
26+
ValidArgsFunction: inspectBashComplete,
27+
}
28+
29+
inspectCommand.Flags().StringP("format", "f", "json", "output format, one of: 'json', 'yaml', a go-template")
30+
31+
return inspectCommand
32+
}
33+
34+
func inspectAction(cmd *cobra.Command, args []string) error {
35+
format, err := cmd.Flags().GetString("format")
36+
if err != nil {
37+
return err
38+
}
39+
40+
allinstances, err := store.Instances()
41+
if err != nil {
42+
return err
43+
}
44+
45+
instanceNames := []string{}
46+
if len(args) > 0 {
47+
for _, arg := range args {
48+
matches := instanceMatches(arg, allinstances)
49+
if len(matches) > 0 {
50+
instanceNames = append(instanceNames, matches...)
51+
} else {
52+
logrus.Warnf("No instance matching %v found.", arg)
53+
}
54+
}
55+
} else {
56+
instanceNames = allinstances
57+
}
58+
59+
// get the state and config for all the requested instances
60+
var instances []*store.Instance
61+
for _, instanceName := range instanceNames {
62+
instance, err := store.Inspect(instanceName)
63+
if err != nil {
64+
return fmt.Errorf("unable to load instance %s: %w", instanceName, err)
65+
}
66+
instances = append(instances, instance)
67+
}
68+
69+
return store.PrintInstances(cmd.OutOrStdout(), instances, format)
70+
}
71+
72+
func inspectBashComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
73+
return bashCompleteInstanceNames(cmd)
74+
}

cmd/limactl/list.go

Lines changed: 49 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
package main
22

33
import (
4-
"encoding/json"
54
"errors"
65
"fmt"
7-
"os/user"
86
"reflect"
97
"sort"
108
"strings"
11-
"text/tabwriter"
12-
"text/template"
139

14-
"github.com/docker/go-units"
1510
"github.com/lima-vm/lima/pkg/store"
1611
"github.com/sirupsen/logrus"
1712
"github.com/spf13/cobra"
@@ -36,15 +31,26 @@ func fieldNames() []string {
3631

3732
func newListCommand() *cobra.Command {
3833
listCommand := &cobra.Command{
39-
Use: "list [flags] [INSTANCE]...",
40-
Aliases: []string{"ls"},
41-
Short: "List instances of Lima.",
34+
Use: "list [flags] [INSTANCE]...",
35+
Aliases: []string{"ls"},
36+
Short: "List instances of Lima.",
37+
Long: `List instances of Lima.
38+
The output can be presented in one of several formats, using the --format <format> flag.
39+
40+
--format json - output in json format
41+
--format yaml - output in yaml format
42+
--format table - output in table format
43+
--format '{{ <go template> }}' - if the format begins and ends with '{{ }}', then it is used as a go template.
44+
45+
The following legacy flags continue to function:
46+
--json - equal to '--format json'
47+
`,
4248
Args: cobra.ArbitraryArgs,
4349
RunE: listAction,
4450
ValidArgsFunction: listBashComplete,
4551
}
4652

47-
listCommand.Flags().StringP("format", "f", "", "Format the output using the given Go template")
53+
listCommand.Flags().StringP("format", "f", "table", "output format, one of: json, yaml, table, go-template")
4854
listCommand.Flags().Bool("list-fields", false, "List fields available for format")
4955
listCommand.Flags().Bool("json", false, "JSONify output")
5056
listCommand.Flags().BoolP("quiet", "q", false, "Only show names")
@@ -67,7 +73,7 @@ func listAction(cmd *cobra.Command, args []string) error {
6773
if err != nil {
6874
return err
6975
}
70-
goFormat, err := cmd.Flags().GetString("format")
76+
format, err := cmd.Flags().GetString("format")
7177
if err != nil {
7278
return err
7379
}
@@ -80,131 +86,76 @@ func listAction(cmd *cobra.Command, args []string) error {
8086
return err
8187
}
8288

83-
if goFormat != "" && listFields {
84-
return errors.New("option --format conflicts with --list-fields")
89+
if jsonFormat {
90+
format = "json"
91+
}
92+
93+
// conflicts
94+
if jsonFormat && cmd.Flags().Changed("format") {
95+
return errors.New("option --json conflicts with option --format")
8596
}
86-
if jsonFormat && listFields {
87-
return errors.New("option --json conflicts with --list-fields")
97+
if listFields && cmd.Flags().Changed("format") {
98+
return errors.New("option --list-fields conflicts with option --format")
99+
}
100+
101+
if quiet && format != "table" {
102+
return errors.New("option --quiet can only be used with '--format table'")
88103
}
104+
89105
if listFields {
90106
names := fieldNames()
91107
sort.Strings(names)
92108
fmt.Println(strings.Join(names, "\n"))
93109
return nil
94110
}
95-
if quiet && jsonFormat {
96-
return errors.New("option --quiet conflicts with --json")
97-
}
98-
if goFormat != "" && jsonFormat {
99-
return errors.New("option --format conflicts with --json")
100-
}
101111

102112
allinstances, err := store.Instances()
103113
if err != nil {
104114
return err
105115
}
116+
if len(allinstances) == 0 {
117+
logrus.Warn("No instance found. Run `limactl start` to create an instance.")
118+
return nil
119+
}
106120

107-
instances := []string{}
121+
instanceNames := []string{}
108122
if len(args) > 0 {
109123
for _, arg := range args {
110124
matches := instanceMatches(arg, allinstances)
111125
if len(matches) > 0 {
112-
instances = append(instances, matches...)
126+
instanceNames = append(instanceNames, matches...)
113127
} else {
114128
logrus.Warnf("No instance matching %v found.", arg)
115129
}
116130
}
117131
} else {
118-
instances = allinstances
132+
instanceNames = allinstances
119133
}
120134

121135
if quiet {
122-
for _, instName := range instances {
136+
for _, instName := range instanceNames {
123137
fmt.Fprintln(cmd.OutOrStdout(), instName)
124138
}
125139
return nil
126140
}
127141

128-
if goFormat != "" {
129-
tmpl, err := template.New("format").Parse(goFormat)
142+
// get the state and config for all the requested instances
143+
var instances []*store.Instance
144+
for _, instanceName := range instanceNames {
145+
instance, err := store.Inspect(instanceName)
130146
if err != nil {
131-
return err
132-
}
133-
for _, instName := range instances {
134-
inst, err := store.Inspect(instName)
135-
if err != nil {
136-
logrus.WithError(err).Errorf("instance %q does not exist?", instName)
137-
continue
138-
}
139-
data, err := store.AddGlobalFields(inst)
140-
if err != nil {
141-
logrus.WithError(err).Error("Cannot add global fields to instance data")
142-
continue
143-
}
144-
err = tmpl.Execute(cmd.OutOrStdout(), data)
145-
if err != nil {
146-
return err
147-
}
148-
fmt.Fprintln(cmd.OutOrStdout())
149-
}
150-
return nil
151-
}
152-
if jsonFormat {
153-
for _, instName := range instances {
154-
inst, err := store.Inspect(instName)
155-
if err != nil {
156-
logrus.WithError(err).Errorf("instance %q does not exist?", instName)
157-
continue
158-
}
159-
b, err := json.Marshal(inst)
160-
if err != nil {
161-
return err
162-
}
163-
fmt.Fprintln(cmd.OutOrStdout(), string(b))
147+
return fmt.Errorf("unable to load instance %s: %w", instanceName, err)
164148
}
165-
return nil
166-
}
167-
168-
w := tabwriter.NewWriter(cmd.OutOrStdout(), 4, 8, 4, ' ', 0)
169-
fmt.Fprintln(w, "NAME\tSTATUS\tSSH\tVMTYPE\tARCH\tCPUS\tMEMORY\tDISK\tDIR")
170-
171-
if len(allinstances) == 0 {
172-
logrus.Warn("No instance found. Run `limactl start` to create an instance.")
149+
instances = append(instances, instance)
173150
}
174151

175-
u, err := user.Current()
176-
if err != nil {
177-
return err
152+
for _, instance := range instances {
153+
if len(instance.Errors) > 0 {
154+
logrus.WithField("errors", instance.Errors).Warnf("instance %q has errors", instance.Name)
155+
}
178156
}
179-
homeDir := u.HomeDir
180157

181-
for _, instName := range instances {
182-
inst, err := store.Inspect(instName)
183-
if err != nil {
184-
logrus.WithError(err).Errorf("instance %q does not exist?", instName)
185-
continue
186-
}
187-
if len(inst.Errors) > 0 {
188-
logrus.WithField("errors", inst.Errors).Warnf("instance %q has errors", instName)
189-
}
190-
dir := inst.Dir
191-
if strings.HasPrefix(dir, homeDir) {
192-
dir = strings.Replace(dir, homeDir, "~", 1)
193-
}
194-
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%d\t%s\t%s\t%s\n",
195-
inst.Name,
196-
inst.Status,
197-
fmt.Sprintf("127.0.0.1:%d", inst.SSHLocalPort),
198-
inst.VMType,
199-
inst.Arch,
200-
inst.CPUs,
201-
units.BytesSize(float64(inst.Memory)),
202-
units.BytesSize(float64(inst.Disk)),
203-
dir,
204-
)
205-
}
206-
207-
return w.Flush()
158+
return store.PrintInstances(cmd.OutOrStdout(), instances, format)
208159
}
209160

210161
func listBashComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {

cmd/limactl/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ func newApp() *cobra.Command {
9393
newEditCommand(),
9494
newFactoryResetCommand(),
9595
newDiskCommand(),
96+
newInspectCommand(),
9697
)
9798
return rootCmd
9899
}

0 commit comments

Comments
 (0)