11package main
22
33import (
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
3732func 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\t STATUS\t SSH\t VMTYPE\t ARCH\t CPUS\t MEMORY\t DISK\t DIR" )
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
210161func listBashComplete (cmd * cobra.Command , args []string , toComplete string ) ([]string , cobra.ShellCompDirective ) {
0 commit comments