Restore --format table...

Following commands:
* systemd generate
* networks inspect
* pod stats
  * Fixed test where format was quoted and then quoted again
  * Fixed bug where output never printed '--' on missed reads
* pod ps

Signed-off-by: Jhon Honce <jhonce@redhat.com>
This commit is contained in:
Jhon Honce
2020-10-08 15:37:44 -07:00
parent 2587cba389
commit f128bff232
6 changed files with 116 additions and 151 deletions

View File

@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"github.com/containers/podman/v2/cmd/podman/parse"
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/utils"
"github.com/containers/podman/v2/pkg/domain/entities"
@ -97,10 +98,10 @@ func systemd(cmd *cobra.Command, args []string) error {
}
}
switch format {
case "json":
switch {
case parse.MatchesJSONFormat(format):
return printJSON(report.Units)
case "":
case format == "":
return printDefault(report.Units)
default:
return errors.Errorf("unknown --format argument: %s", format)

View File

@ -3,12 +3,13 @@ package network
import (
"encoding/json"
"fmt"
"io"
"os"
"strings"
"text/tabwriter"
"text/template"
"github.com/containers/podman/v2/cmd/podman/parse"
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/report"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/spf13/cobra"
)
@ -39,31 +40,32 @@ func init() {
flags.StringVarP(&networkInspectOptions.Format, "format", "f", "", "Pretty-print network to JSON or using a Go template")
}
func networkInspect(cmd *cobra.Command, args []string) error {
func networkInspect(_ *cobra.Command, args []string) error {
responses, err := registry.ContainerEngine().NetworkInspect(registry.Context(), args, entities.NetworkInspectOptions{})
if err != nil {
return err
}
b, err := json.MarshalIndent(responses, "", " ")
if err != nil {
return err
}
if strings.ToLower(networkInspectOptions.Format) == "json" || networkInspectOptions.Format == "" {
fmt.Println(string(b))
} else {
var w io.Writer = os.Stdout
//There can be more than 1 in the inspect output.
format := "{{range . }}" + networkInspectOptions.Format + "{{end}}"
tmpl, err := template.New("inspectNetworks").Parse(format)
switch {
case parse.MatchesJSONFormat(networkInspectOptions.Format) || networkInspectOptions.Format == "":
b, err := json.MarshalIndent(responses, "", " ")
if err != nil {
return err
}
if err := tmpl.Execute(w, responses); err != nil {
fmt.Println(string(b))
default:
row := report.NormalizeFormat(networkInspectOptions.Format)
// There can be more than 1 in the inspect output.
row = "{{range . }}" + row + "{{end}}"
tmpl, err := template.New("inspectNetworks").Parse(row)
if err != nil {
return err
}
if flusher, ok := w.(interface{ Flush() error }); ok {
return flusher.Flush()
}
w := tabwriter.NewWriter(os.Stdout, 8, 2, 0, ' ', 0)
defer w.Flush()
return tmpl.Execute(w, responses)
}
return nil
}

View File

@ -3,7 +3,6 @@ package pods
import (
"context"
"fmt"
"io"
"os"
"sort"
"strings"
@ -11,7 +10,9 @@ import (
"text/template"
"time"
"github.com/containers/podman/v2/cmd/podman/parse"
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/report"
"github.com/containers/podman/v2/cmd/podman/validate"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/docker/go-units"
@ -34,10 +35,9 @@ var (
)
var (
defaultHeaders = "POD ID\tNAME\tSTATUS\tCREATED"
inputFilters []string
noTrunc bool
psInput entities.PodPSOptions
inputFilters []string
noTrunc bool
psInput entities.PodPSOptions
)
func init() {
@ -62,11 +62,6 @@ func init() {
}
func pods(cmd *cobra.Command, _ []string) error {
var (
w io.Writer = os.Stdout
row string
)
if psInput.Quiet && len(psInput.Format) > 0 {
return errors.New("quiet and format cannot be used together")
}
@ -89,80 +84,79 @@ func pods(cmd *cobra.Command, _ []string) error {
return err
}
if psInput.Format == "json" {
switch {
case parse.MatchesJSONFormat(psInput.Format):
b, err := json.MarshalIndent(responses, "", " ")
if err != nil {
return err
}
fmt.Println(string(b))
return nil
case psInput.Quiet:
for _, p := range responses {
fmt.Println(p.Id)
}
return nil
}
// Formatted output below
lpr := make([]ListPodReporter, 0, len(responses))
for _, r := range responses {
lpr = append(lpr, ListPodReporter{r})
}
headers, row := createPodPsOut()
if psInput.Quiet {
row = "{{.Id}}\n"
headers := report.Headers(ListPodReporter{}, map[string]string{
"ContainerIds": "IDS",
"ContainerNames": "NAMES",
"ContainerStatuses": "STATUS",
"Namespace": "NAMESPACES",
"NumberOfContainers": "# OF CONTAINERS",
"InfraId": "INFRA ID",
})
row := podPsFormat()
if cmd.Flags().Changed("format") {
row = report.NormalizeFormat(psInput.Format)
}
if cmd.Flag("format").Changed {
row = psInput.Format
if !strings.HasPrefix(row, "\n") {
row += "\n"
}
}
format := "{{range . }}" + row + "{{end}}"
if !psInput.Quiet && !cmd.Flag("format").Changed {
format = headers + format
}
tmpl, err := template.New("listPods").Parse(format)
row = "{{range . }}" + row + "{{end}}"
tmpl, err := template.New("listPods").Parse(row)
if err != nil {
return err
}
if !psInput.Quiet {
w = tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
defer w.Flush()
if !psInput.Quiet && !cmd.Flag("format").Changed {
if err := tmpl.Execute(w, headers); err != nil {
return err
}
}
if err := tmpl.Execute(w, lpr); err != nil {
return err
}
if flusher, ok := w.(interface{ Flush() error }); ok {
return flusher.Flush()
}
return nil
return tmpl.Execute(w, lpr)
}
func createPodPsOut() (string, string) {
var row string
headers := defaultHeaders
row += "{{.Id}}"
row += "\t{{.Name}}\t{{.Status}}\t{{.Created}}"
func podPsFormat() string {
row := []string{"{{.Id}}", "{{.Name}}", "{{.Status}}", "{{.Created}}}"}
if psInput.CtrIds {
headers += "\tIDS"
row += "\t{{.ContainerIds}}"
row = append(row, "{{.ContainerIds}}")
}
if psInput.CtrNames {
headers += "\tNAMES"
row += "\t{{.ContainerNames}}"
}
if psInput.CtrStatus {
headers += "\tSTATUS"
row += "\t{{.ContainerStatuses}}"
}
if psInput.Namespace {
headers += "\tCGROUP\tNAMESPACES"
row += "\t{{.Cgroup}}\t{{.Namespace}}"
}
if !psInput.CtrStatus && !psInput.CtrNames && !psInput.CtrIds {
headers += "\t# OF CONTAINERS"
row += "\t{{.NumberOfContainers}}"
if psInput.CtrNames {
row = append(row, "{{.ContainerNames}}")
}
headers += "\tINFRA ID\n"
row += "\t{{.InfraId}}\n"
return headers, row
if psInput.CtrStatus {
row = append(row, "{{.ContainerStatuses}}")
}
if psInput.Namespace {
row = append(row, "{{.Cgroup}}", "{{.Namespace}}")
}
if !psInput.CtrStatus && !psInput.CtrNames && !psInput.CtrIds {
row = append(row, "{{.NumberOfContainers}}")
}
return strings.Join(row, "\t") + "\n"
}
// ListPodReporter is a struct for pod ps output
@ -180,7 +174,7 @@ func (l ListPodReporter) Labels() map[string]string {
return l.ListPodsReport.Labels
}
// NumberofContainers returns an int representation for
// NumberOfContainers returns an int representation for
// the number of containers belonging to the pod
func (l ListPodReporter) NumberOfContainers() int {
return len(l.Containers)
@ -192,7 +186,7 @@ func (l ListPodReporter) ID() string {
}
// Id returns the Pod id
func (l ListPodReporter) Id() string { //nolint
func (l ListPodReporter) Id() string { // nolint
if noTrunc {
return l.ListPodsReport.Id
}
@ -206,7 +200,7 @@ func (l ListPodReporter) InfraID() string {
// InfraId returns the infra container id for the pod
// depending on trunc
func (l ListPodReporter) InfraId() string { //nolint
func (l ListPodReporter) InfraId() string { // nolint
if len(l.ListPodsReport.InfraId) == 0 {
return ""
}

View File

@ -4,18 +4,16 @@ import (
"context"
"fmt"
"os"
"reflect"
"strings"
"text/tabwriter"
"text/template"
"time"
"github.com/buger/goterm"
"github.com/containers/buildah/pkg/formats"
"github.com/containers/podman/v2/cmd/podman/parse"
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/report"
"github.com/containers/podman/v2/cmd/podman/validate"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/util/camelcase"
"github.com/spf13/cobra"
)
@ -67,11 +65,18 @@ func stats(cmd *cobra.Command, args []string) error {
return err
}
format := statsOptions.Format
doJSON := strings.ToLower(format) == formats.JSONString
header := getPodStatsHeader(format)
row := report.NormalizeFormat(statsOptions.Format)
doJSON := parse.MatchesJSONFormat(row)
for {
headers := report.Headers(entities.PodStatsReport{}, map[string]string{
"CPU": "CPU %",
"MemUsage": "MEM USAGE/ LIMIT",
"MEM": "MEM %",
"NET IO": "NET IO",
"BlockIO": "BLOCK IO",
})
for ; ; time.Sleep(time.Second) {
reports, err := registry.ContainerEngine().PodStats(context.Background(), args, statsOptions.PodStatsOptions)
if err != nil {
return err
@ -87,16 +92,17 @@ func stats(cmd *cobra.Command, args []string) error {
goterm.MoveCursor(1, 1)
goterm.Flush()
}
if len(format) == 0 {
if cmd.Flags().Changed("format") {
if err := printFormattedPodStatsLines(headers, row, reports); err != nil {
return err
}
} else {
printPodStatsLines(reports)
} else if err := printFormattedPodStatsLines(format, reports, header); err != nil {
return err
}
}
if statsOptions.NoStream {
break
}
time.Sleep(time.Second)
}
return nil
@ -115,72 +121,32 @@ func printPodStatsLines(stats []*entities.PodStatsReport) {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
outFormat := "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n"
fmt.Fprintf(w, outFormat, "POD", "CID", "NAME", "CPU %", "MEM USAGE/ LIMIT", "MEM %", "NET IO", "BLOCK IO", "PIDS")
for _, i := range stats {
if len(stats) == 0 {
fmt.Fprintf(w, outFormat, i.Pod, "--", "--", "--", "--", "--", "--", "--", "--")
} else {
if len(stats) == 0 {
fmt.Fprintf(w, outFormat, "--", "--", "--", "--", "--", "--", "--", "--", "--")
} else {
for _, i := range stats {
fmt.Fprintf(w, outFormat, i.Pod, i.CID, i.Name, i.CPU, i.MemUsage, i.Mem, i.NetIO, i.BlockIO, i.PIDS)
}
}
w.Flush()
}
func printFormattedPodStatsLines(format string, stats []*entities.PodStatsReport, headerNames map[string]string) error {
func printFormattedPodStatsLines(headerNames []map[string]string, row string, stats []*entities.PodStatsReport) error {
if len(stats) == 0 {
return nil
}
// Use a tabwriter to align column format
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
// Spit out the header if "table" is present in the format
if strings.HasPrefix(format, "table") {
hformat := strings.Replace(strings.TrimSpace(format[5:]), " ", "\t", -1)
format = hformat
headerTmpl, err := template.New("header").Parse(hformat)
if err != nil {
return err
}
if err := headerTmpl.Execute(w, headerNames); err != nil {
return err
}
fmt.Fprintln(w, "")
}
row = "{{range .}}" + row + "{{end}}"
// Spit out the data rows now
dataTmpl, err := template.New("data").Parse(format)
tmpl, err := template.New("pod stats").Parse(row)
if err != nil {
return err
}
for _, s := range stats {
if err := dataTmpl.Execute(w, s); err != nil {
return err
}
fmt.Fprintln(w, "")
}
// Flush the writer
return w.Flush()
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
defer w.Flush()
}
// getPodStatsHeader returns the stats header for the specified options.
func getPodStatsHeader(format string) map[string]string {
headerNames := make(map[string]string)
if format == "" {
return headerNames
if err := tmpl.Execute(w, headerNames); err != nil {
return err
}
// Make a map of the field names for the headers
v := reflect.ValueOf(entities.PodStatsReport{})
t := v.Type()
for i := 0; i < t.NumField(); i++ {
split := camelcase.Split(t.Field(i).Name)
value := strings.ToUpper(strings.Join(split, " "))
switch value {
case "CPU", "MEM":
value += " %"
case "MEM USAGE":
value = "MEM USAGE / LIMIT"
}
headerNames[t.Field(i).Name] = value
}
return headerNames
return tmpl.Execute(w, stats)
}

View File

@ -8,6 +8,7 @@ import (
. "github.com/containers/podman/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gexec"
)
var _ = Describe("Podman ps", func() {
@ -63,7 +64,7 @@ var _ = Describe("Podman ps", func() {
result := podmanTest.Podman([]string{"pod", "ps", "-q"})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
Expect(result).To(Exit(0))
Expect(len(result.OutputToStringArray())).Should(BeNumerically(">", 0))
Expect(podid).To(ContainSubstring(result.OutputToStringArray()[0]))
})

View File

@ -6,6 +6,7 @@ import (
. "github.com/containers/podman/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gexec"
)
var _ = Describe("Podman pod stats", func() {
@ -156,9 +157,9 @@ var _ = Describe("Podman pod stats", func() {
session := podmanTest.RunTopContainerInPod("", podid)
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
stats := podmanTest.Podman([]string{"pod", "stats", "-a", "--no-reset", "--no-stream", "--format", "\"table {{.CID}} {{.Pod}} {{.Mem}} {{.MemUsage}} {{.CPU}} {{.NetIO}} {{.BlockIO}} {{.PIDS}} {{.Pod}}\""})
stats := podmanTest.Podman([]string{"pod", "stats", "-a", "--no-reset", "--no-stream", "--format", "table {{.CID}} {{.Pod}} {{.Mem}} {{.MemUsage}} {{.CPU}} {{.NetIO}} {{.BlockIO}} {{.PIDS}} {{.Pod}}"})
stats.WaitWithDefaultTimeout()
Expect(stats.ExitCode()).To(Equal(0))
Expect(stats).To(Exit(0))
})
It("podman stats with invalid GO template", func() {