From c71d82d6f6b4d474d4692ca9c899794441db3e6e Mon Sep 17 00:00:00 2001 From: Berger Eugene Date: Mon, 6 May 2024 23:12:44 +0300 Subject: [PATCH] Issue #180: Add output options to processes list --- src/client/client.go | 2 +- src/client/processes.go | 4 +- src/cmd/list.go | 84 +++++++++++++++++++++++++++++++++++++++-- src/config/Flags.go | 2 + 4 files changed, 86 insertions(+), 6 deletions(-) diff --git a/src/client/client.go b/src/client/client.go index 5eff355..39e9ff0 100644 --- a/src/client/client.go +++ b/src/client/client.go @@ -105,7 +105,7 @@ func (p *PcClient) GetProcessState(name string) (*types.ProcessState, error) { } func (p *PcClient) GetProcessesState() (*types.ProcessesState, error) { - return p.getProcessesState() + return p.GetRemoteProcessesState() } func (p *PcClient) StopProcess(name string) error { diff --git a/src/client/processes.go b/src/client/processes.go index 04363da..13ffd04 100644 --- a/src/client/processes.go +++ b/src/client/processes.go @@ -9,7 +9,7 @@ import ( ) func (p *PcClient) GetProcessesName() ([]string, error) { - states, err := p.getProcessesState() + states, err := p.GetRemoteProcessesState() if err != nil { return nil, err } @@ -21,7 +21,7 @@ func (p *PcClient) GetProcessesName() ([]string, error) { return procs, nil } -func (p *PcClient) getProcessesState() (*types.ProcessesState, error) { +func (p *PcClient) GetRemoteProcessesState() (*types.ProcessesState, error) { url := fmt.Sprintf("http://%s/processes", p.address) resp, err := p.client.Get(url) if err != nil { diff --git a/src/cmd/list.go b/src/cmd/list.go index 57e50d9..2150170 100644 --- a/src/cmd/list.go +++ b/src/cmd/list.go @@ -1,8 +1,12 @@ package cmd import ( + "encoding/json" "fmt" + "github.com/f1bonacc1/process-compose/src/types" "github.com/rs/zerolog/log" + "os" + "sort" "github.com/spf13/cobra" ) @@ -13,16 +17,90 @@ var listCmd = &cobra.Command{ Short: "List available processes", Aliases: []string{"ls"}, Run: func(cmd *cobra.Command, args []string) { - processNames, err := getClient().GetProcessesName() + states, err := getClient().GetRemoteProcessesState() if err != nil { log.Fatal().Err(err).Msg("failed to list processes") } - for _, proc := range processNames { - fmt.Println(proc) + //sort states by name + sort.Slice(states.States, func(i, j int) bool { + return states.States[i].Name < states.States[j].Name + }) + switch *pcFlags.OutputFormat { + case "json": + b, err := json.MarshalIndent(states.States, "", "\t") + if err != nil { + log.Fatal().Err(err).Msg("failed to marshal processes") + } + os.Stdout.Write(b) + case "wide": + printStatesAsTable(states.States) + case "": + for _, state := range states.States { + fmt.Println(state.Name) + } + default: + log.Fatal().Err(err).Msgf("unknown output format %s", *pcFlags.OutputFormat) } }, } +func printStatesAsTable(states []types.ProcessState) { + + // Create a table + table := []string{"PID", "NAME", "NAMESPACE", "STATUS", "AGE", "HEALTH", "RESTARTS", "EXITCODE"} + tableColWidth := make([]int, len(table)) + + for _, state := range states { + if len(fmt.Sprintf("%d", state.Pid)) > tableColWidth[0] { + tableColWidth[0] = len(fmt.Sprintf("%d", state.Pid)) + } + if len(state.Name) > tableColWidth[1] { + tableColWidth[1] = len(state.Name) + } + if len(state.Namespace) > tableColWidth[2] { + tableColWidth[2] = len(state.Namespace) + } + if len(state.Status) > tableColWidth[3] { + tableColWidth[3] = len(state.Status) + } + if len(state.SystemTime) > tableColWidth[4] { + tableColWidth[4] = len(state.SystemTime) + } + if len(state.Health) > tableColWidth[5] { + tableColWidth[5] = len(state.Health) + } + if len(fmt.Sprintf("%d", state.Restarts)) > tableColWidth[6] { + tableColWidth[6] = len(fmt.Sprintf("%d", state.Restarts)) + } + if len(fmt.Sprintf("%d", state.ExitCode)) > tableColWidth[7] { + tableColWidth[7] = len(fmt.Sprintf("%d", state.ExitCode)) + } + } + for i, col := range table { + if len(col) > tableColWidth[i] { + tableColWidth[i] = len(col) + } + } + for i, col := range table { + fmt.Printf("%-*s ", tableColWidth[i], col) + } + fmt.Println() + for _, state := range states { + fmt.Printf("%-*d ", tableColWidth[0], state.Pid) + fmt.Printf("%-*s ", tableColWidth[1], state.Name) + fmt.Printf("%-*s ", tableColWidth[2], state.Namespace) + fmt.Printf("%-*s ", tableColWidth[3], state.Status) + fmt.Printf("%-*s ", tableColWidth[4], state.SystemTime) + fmt.Printf("%-*s ", tableColWidth[5], state.Health) + fmt.Printf("%-*d ", tableColWidth[6], state.Restarts) + fmt.Printf("%-*d ", tableColWidth[7], state.ExitCode) + fmt.Println() + } + +} + func init() { processCmd.AddCommand(listCmd) + + listCmd.Flags().StringVarP(pcFlags.OutputFormat, "output", "o", *pcFlags.OutputFormat, "Output format. One of: (json, wide)") } diff --git a/src/config/Flags.go b/src/config/Flags.go index bb4ae4e..7ca0d0e 100644 --- a/src/config/Flags.go +++ b/src/config/Flags.go @@ -62,6 +62,7 @@ type Flags struct { UnixSocketPath *string IsUnixSocket *bool IsReadOnlyMode *bool + OutputFormat *string } // NewFlags returns new configuration flags. @@ -87,6 +88,7 @@ func NewFlags() *Flags { UnixSocketPath: toPtr(""), IsUnixSocket: toPtr(false), IsReadOnlyMode: toPtr(getReadOnlyDefault()), + OutputFormat: toPtr(""), } }