Restore --format table support

* system df
* events
  * fix error handling from go routine
  * update tests to use gomega matchers for better error messages
* system info
* version
* volume inspect

Signed-off-by: Jhon Honce <jhonce@redhat.com>
This commit is contained in:
Jhon Honce
2020-10-09 09:13:22 -07:00
parent 7ad631b819
commit eb4a746efc
14 changed files with 166 additions and 412 deletions

View File

@ -11,7 +11,6 @@ import (
"time" "time"
tm "github.com/buger/goterm" tm "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/parse"
"github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/report" "github.com/containers/podman/v2/cmd/podman/report"
@ -93,7 +92,7 @@ func checkFlags(c *cobra.Command) error {
if listOpts.Size || listOpts.Namespace { if listOpts.Size || listOpts.Namespace {
return errors.Errorf("quiet conflicts with size and namespace") return errors.Errorf("quiet conflicts with size and namespace")
} }
if c.Flag("format").Changed && listOpts.Format != formats.JSONString { if c.Flag("format").Changed && !parse.MatchesJSONFormat(listOpts.Format) {
// Quiet is overridden by Go template output. // Quiet is overridden by Go template output.
listOpts.Quiet = false listOpts.Quiet = false
} }

View File

@ -2,7 +2,7 @@ package parse
import "regexp" import "regexp"
var jsonFormatRegex = regexp.MustCompile(`^\s*(json|{{\s*json\s*( \.)?\s*}})\s*$`) var jsonFormatRegex = regexp.MustCompile(`^\s*(json|{{\s*json\s*(\.)?\s*}})\s*$`)
// MatchesJSONFormat test CLI --format string to be a JSON request // MatchesJSONFormat test CLI --format string to be a JSON request
func MatchesJSONFormat(s string) bool { func MatchesJSONFormat(s string) bool {

View File

@ -27,7 +27,7 @@ func TestMatchesJSONFormat(t *testing.T) {
{"json . }}", false}, {"json . }}", false},
{"{{.ID }} json .", false}, {"{{.ID }} json .", false},
{"json .", false}, {"json .", false},
{"{{json.}}", false}, {"{{json.}}", true},
} }
for _, tt := range tests { for _, tt := range tests {

View File

@ -2,15 +2,14 @@ package system
import ( import (
"fmt" "fmt"
"io"
"os" "os"
"strconv"
"strings" "strings"
"text/tabwriter" "text/tabwriter"
"text/template" "text/template"
"time" "time"
"github.com/containers/podman/v2/cmd/podman/registry" "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/cmd/podman/validate"
"github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/domain/entities"
"github.com/docker/go-units" "github.com/docker/go-units"
@ -52,35 +51,21 @@ func df(cmd *cobra.Command, args []string) error {
if err != nil { if err != nil {
return err return err
} }
w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
if dfOptions.Verbose { if dfOptions.Verbose {
return printVerbose(reports) return printVerbose(cmd, w, reports)
} }
return printSummary(reports, dfOptions.Format) return printSummary(w, cmd, reports)
} }
func printSummary(reports *entities.SystemDfReport, userFormat string) error { func printSummary(w *tabwriter.Writer, cmd *cobra.Command, reports *entities.SystemDfReport) error {
var ( var (
dfSummaries []*dfSummary dfSummaries []*dfSummary
active int active int
size, reclaimable int64 size, reclaimable int64
format = "{{.Type}}\t{{.Total}}\t{{.Active}}\t{{.Size}}\t{{.Reclaimable}}\n"
w io.Writer = os.Stdout
) )
// Images
if len(userFormat) > 0 {
if !strings.HasSuffix(userFormat, `\n`) {
userFormat += `\n`
}
// should be Unquoto from cmd line
userFormat, err := strconv.Unquote(`"` + userFormat + `"`)
if err != nil {
return err
}
format = userFormat
}
for _, i := range reports.Images { for _, i := range reports.Images {
if i.Containers > 0 { if i.Containers > 0 {
active++ active++
@ -90,7 +75,6 @@ func printSummary(reports *entities.SystemDfReport, userFormat string) error {
reclaimable += i.Size reclaimable += i.Size
} }
} }
imageSummary := dfSummary{ imageSummary := dfSummary{
Type: "Images", Type: "Images",
Total: len(reports.Images), Total: len(reports.Images),
@ -101,7 +85,6 @@ func printSummary(reports *entities.SystemDfReport, userFormat string) error {
dfSummaries = append(dfSummaries, &imageSummary) dfSummaries = append(dfSummaries, &imageSummary)
// Containers // Containers
var ( var (
conActive int conActive int
conSize, conReclaimable int64 conSize, conReclaimable int64
@ -114,7 +97,6 @@ func printSummary(reports *entities.SystemDfReport, userFormat string) error {
} }
conSize += c.RWSize conSize += c.RWSize
} }
containerSummary := dfSummary{ containerSummary := dfSummary{
Type: "Containers", Type: "Containers",
Total: len(reports.Containers), Total: len(reports.Containers),
@ -122,7 +104,6 @@ func printSummary(reports *entities.SystemDfReport, userFormat string) error {
size: conSize, size: conSize,
reclaimable: conReclaimable, reclaimable: conReclaimable,
} }
dfSummaries = append(dfSummaries, &containerSummary) dfSummaries = append(dfSummaries, &containerSummary)
// Volumes // Volumes
@ -143,78 +124,94 @@ func printSummary(reports *entities.SystemDfReport, userFormat string) error {
size: volumesSize, size: volumesSize,
reclaimable: volumesReclaimable, reclaimable: volumesReclaimable,
} }
dfSummaries = append(dfSummaries, &volumeSummary) dfSummaries = append(dfSummaries, &volumeSummary)
headers := "TYPE\tTOTAL\tACTIVE\tSIZE\tRECLAIMABLE\n" // need to give un-exported fields
format = "{{range . }}" + format + "{{end}}" hdrs := report.Headers(dfSummary{}, map[string]string{
if len(userFormat) == 0 { "Size": "SIZE",
format = headers + format "Reclaimable": "RECLAIMABLE",
})
row := "{{.Type}}\t{{.Total}}\t{{.Active}}\t{{.Size}}\t{{.Reclaimable}}\n"
if cmd.Flags().Changed("format") {
row = report.NormalizeFormat(dfOptions.Format)
} }
return writeTemplate(w, format, dfSummaries) row = "{{range . }}" + row + "{{end}}"
return writeTemplate(cmd, w, hdrs, row, dfSummaries)
} }
func printVerbose(reports *entities.SystemDfReport) error { func printVerbose(cmd *cobra.Command, w *tabwriter.Writer, reports *entities.SystemDfReport) error {
var ( defer w.Flush()
w io.Writer = os.Stdout
)
// Images // Images
fmt.Print("\nImages space usage:\n\n") fmt.Fprint(w, "Images space usage:\n\n")
// convert to dfImage for output // convert to dfImage for output
dfImages := make([]*dfImage, 0, len(reports.Images)) dfImages := make([]*dfImage, 0, len(reports.Images))
for _, d := range reports.Images { for _, d := range reports.Images {
dfImages = append(dfImages, &dfImage{SystemDfImageReport: d}) dfImages = append(dfImages, &dfImage{SystemDfImageReport: d})
} }
imageHeaders := "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tSIZE\tSHARED SIZE\tUNIQUE SIZE\tCONTAINERS\n" hdrs := report.Headers(entities.SystemDfImageReport{}, map[string]string{
"ImageID": "IMAGE ID",
"SharedSize": "SHARED SIZE",
"UniqueSize": "UNIQUE SIZE",
})
imageRow := "{{.Repository}}\t{{.Tag}}\t{{.ImageID}}\t{{.Created}}\t{{.Size}}\t{{.SharedSize}}\t{{.UniqueSize}}\t{{.Containers}}\n" imageRow := "{{.Repository}}\t{{.Tag}}\t{{.ImageID}}\t{{.Created}}\t{{.Size}}\t{{.SharedSize}}\t{{.UniqueSize}}\t{{.Containers}}\n"
format := imageHeaders + "{{range . }}" + imageRow + "{{end}}" format := "{{range . }}" + imageRow + "{{end}}"
if err := writeTemplate(w, format, dfImages); err != nil { if err := writeTemplate(cmd, w, hdrs, format, dfImages); err != nil {
return nil return nil
} }
// Containers // Containers
fmt.Print("\nContainers space usage:\n\n") fmt.Fprint(w, "\nContainers space usage:\n\n")
// convert to dfContainers for output // convert to dfContainers for output
dfContainers := make([]*dfContainer, 0, len(reports.Containers)) dfContainers := make([]*dfContainer, 0, len(reports.Containers))
for _, d := range reports.Containers { for _, d := range reports.Containers {
dfContainers = append(dfContainers, &dfContainer{SystemDfContainerReport: d}) dfContainers = append(dfContainers, &dfContainer{SystemDfContainerReport: d})
} }
containerHeaders := "CONTAINER ID\tIMAGE\tCOMMAND\tLOCAL VOLUMES\tSIZE\tCREATED\tSTATUS\tNAMES\n" hdrs = report.Headers(entities.SystemDfContainerReport{}, map[string]string{
"ContainerID": "CONTAINER ID",
"LocalVolumes": "LOCAL VOLUMES",
"RWSize": "SIZE",
})
containerRow := "{{.ContainerID}}\t{{.Image}}\t{{.Command}}\t{{.LocalVolumes}}\t{{.RWSize}}\t{{.Created}}\t{{.Status}}\t{{.Names}}\n" containerRow := "{{.ContainerID}}\t{{.Image}}\t{{.Command}}\t{{.LocalVolumes}}\t{{.RWSize}}\t{{.Created}}\t{{.Status}}\t{{.Names}}\n"
format = containerHeaders + "{{range . }}" + containerRow + "{{end}}" format = "{{range . }}" + containerRow + "{{end}}"
if err := writeTemplate(w, format, dfContainers); err != nil { if err := writeTemplate(cmd, w, hdrs, format, dfContainers); err != nil {
return nil return nil
} }
// Volumes // Volumes
fmt.Print("\nLocal Volumes space usage:\n\n") fmt.Fprint(w, "\nLocal Volumes space usage:\n\n")
dfVolumes := make([]*dfVolume, 0, len(reports.Volumes)) dfVolumes := make([]*dfVolume, 0, len(reports.Volumes))
// convert to dfVolume for output // convert to dfVolume for output
for _, d := range reports.Volumes { for _, d := range reports.Volumes {
dfVolumes = append(dfVolumes, &dfVolume{SystemDfVolumeReport: d}) dfVolumes = append(dfVolumes, &dfVolume{SystemDfVolumeReport: d})
} }
volumeHeaders := "VOLUME NAME\tLINKS\tSIZE\n" hdrs = report.Headers(entities.SystemDfVolumeReport{}, map[string]string{
"VolumeName": "VOLUME NAME",
})
volumeRow := "{{.VolumeName}}\t{{.Links}}\t{{.Size}}\n" volumeRow := "{{.VolumeName}}\t{{.Links}}\t{{.Size}}\n"
format = volumeHeaders + "{{range . }}" + volumeRow + "{{end}}" format = "{{range . }}" + volumeRow + "{{end}}"
return writeTemplate(w, format, dfVolumes) return writeTemplate(cmd, w, hdrs, format, dfVolumes)
} }
func writeTemplate(w io.Writer, format string, output interface{}) error { func writeTemplate(cmd *cobra.Command, w *tabwriter.Writer, hdrs []map[string]string, format string,
tmpl, err := template.New("dfout").Parse(format) output interface{}) error {
defer w.Flush()
tmpl, err := template.New("df").Parse(format)
if err != nil { if err != nil {
return err return err
} }
w = tabwriter.NewWriter(w, 8, 2, 2, ' ', 0) //nolint
if err := tmpl.Execute(w, output); err != nil { if !cmd.Flags().Changed("format") {
return err if err := tmpl.Execute(w, hdrs); err != nil {
return err
}
} }
if flusher, ok := w.(interface{ Flush() error }); ok { return tmpl.Execute(w, output)
return flusher.Flush()
}
return nil
} }
type dfImage struct { type dfImage struct {

View File

@ -1,13 +1,12 @@
package system package system
import ( import (
"bufio"
"context" "context"
"fmt"
"os" "os"
"strings"
"text/template" "text/template"
"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/registry"
"github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/cmd/podman/validate"
"github.com/containers/podman/v2/libpod/events" "github.com/containers/podman/v2/libpod/events"
@ -28,6 +27,7 @@ var (
RunE: eventsCmd, RunE: eventsCmd,
Example: `podman events Example: `podman events
podman events --filter event=create podman events --filter event=create
podman events --format {{.Image}}
podman events --since 1h30s`, podman events --since 1h30s`,
} }
) )
@ -51,60 +51,54 @@ func init() {
_ = flags.MarkHidden("stream") _ = flags.MarkHidden("stream")
} }
func eventsCmd(cmd *cobra.Command, args []string) error { func eventsCmd(cmd *cobra.Command, _ []string) error {
var (
err error
eventsError error
tmpl *template.Template
)
if strings.Join(strings.Fields(eventFormat), "") == "{{json.}}" {
eventFormat = formats.JSONString
}
if eventFormat != formats.JSONString {
tmpl, err = template.New("events").Parse(eventFormat)
if err != nil {
return err
}
}
if len(eventOptions.Since) > 0 || len(eventOptions.Until) > 0 { if len(eventOptions.Since) > 0 || len(eventOptions.Until) > 0 {
eventOptions.FromStart = true eventOptions.FromStart = true
} }
eventChannel := make(chan *events.Event) eventChannel := make(chan *events.Event, 1)
eventOptions.EventChan = eventChannel eventOptions.EventChan = eventChannel
errChannel := make(chan error)
go func() { var (
eventsError = registry.ContainerEngine().Events(context.Background(), eventOptions) tmpl *template.Template
}() doJSON bool
if eventsError != nil { )
return eventsError
if cmd.Flags().Changed("format") {
doJSON = parse.MatchesJSONFormat(eventFormat)
if !doJSON {
var err error
tmpl, err = template.New("events").Parse(eventFormat)
if err != nil {
return err
}
}
} }
w := bufio.NewWriter(os.Stdout) go func() {
err := registry.ContainerEngine().Events(context.Background(), eventOptions)
errChannel <- err
}()
for event := range eventChannel { for event := range eventChannel {
switch { switch {
case eventFormat == formats.JSONString: case event == nil:
// no-op
case doJSON:
jsonStr, err := event.ToJSONString() jsonStr, err := event.ToJSONString()
if err != nil { if err != nil {
return errors.Wrapf(err, "unable to format json") return errors.Wrapf(err, "unable to format json")
} }
if _, err := w.Write([]byte(jsonStr)); err != nil { fmt.Println(jsonStr)
return err case cmd.Flags().Changed("format"):
} if err := tmpl.Execute(os.Stdout, event); err != nil {
case len(eventFormat) > 0:
if err := tmpl.Execute(w, event); err != nil {
return err return err
} }
fmt.Println("")
default: default:
if _, err := w.Write([]byte(event.ToHumanReadable())); err != nil { fmt.Println(event.ToHumanReadable())
return err
}
}
if _, err := w.Write([]byte("\n")); err != nil {
return err
}
if err := w.Flush(); err != nil {
return err
} }
} }
return nil
return <-errChannel
} }

View File

@ -69,26 +69,25 @@ func info(cmd *cobra.Command, args []string) error {
return err return err
} }
if parse.MatchesJSONFormat(inFormat) { switch {
case parse.MatchesJSONFormat(inFormat):
b, err := json.MarshalIndent(info, "", " ") b, err := json.MarshalIndent(info, "", " ")
if err != nil { if err != nil {
return err return err
} }
fmt.Println(string(b)) fmt.Println(string(b))
return nil case cmd.Flags().Changed("format"):
} tmpl, err := template.New("info").Parse(inFormat)
if !cmd.Flag("format").Changed { if err != nil {
return err
}
return tmpl.Execute(os.Stdout, info)
default:
b, err := yaml.Marshal(info) b, err := yaml.Marshal(info)
if err != nil { if err != nil {
return err return err
} }
fmt.Println(string(b)) fmt.Println(string(b))
return nil
} }
tmpl, err := template.New("info").Parse(inFormat) return nil
if err != nil {
return err
}
err = tmpl.Execute(os.Stdout, info)
return err
} }

View File

@ -6,10 +6,11 @@ import (
"os" "os"
"strings" "strings"
"text/tabwriter" "text/tabwriter"
"text/template"
"github.com/containers/buildah/pkg/formats"
"github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/parse"
"github.com/containers/podman/v2/cmd/podman/registry" "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/cmd/podman/validate"
"github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/domain/entities"
@ -41,31 +42,38 @@ func version(cmd *cobra.Command, args []string) error {
return err return err
} }
switch { if parse.MatchesJSONFormat(versionFormat) {
case parse.MatchesJSONFormat(versionFormat):
s, err := json.MarshalToString(versions) s, err := json.MarshalToString(versions)
if err != nil { if err != nil {
return err return err
} }
_, err = io.WriteString(os.Stdout, s+"\n") fmt.Println(s)
return err
case cmd.Flag("format").Changed:
out := formats.StdoutTemplate{Output: versions, Template: versionFormat}
err := out.Out()
if err != nil {
// On Failure, assume user is using older version of podman version --format and check client
versionFormat = strings.Replace(versionFormat, ".Server.", ".", 1)
out = formats.StdoutTemplate{Output: versions.Client, Template: versionFormat}
if err1 := out.Out(); err1 != nil {
return err
}
}
return nil return nil
} }
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
defer w.Flush() defer w.Flush()
if cmd.Flag("format").Changed {
row := report.NormalizeFormat(versionFormat)
tmpl, err := template.New("version 2.0.0").Parse(row)
if err != nil {
return err
}
if err := tmpl.Execute(w, versions); err != nil {
// On Failure, assume user is using older version of podman version --format and check client
row = strings.Replace(row, ".Server.", ".", 1)
tmpl, err := template.New("version 1.0.0").Parse(row)
if err != nil {
return err
}
if err := tmpl.Execute(w, versions.Client); err != nil {
return err
}
}
return nil
}
if versions.Server != nil { if versions.Server != nil {
if _, err := fmt.Fprintf(w, "Client:\n"); err != nil { if _, err := fmt.Fprintf(w, "Client:\n"); err != nil {
return err return err
@ -81,13 +89,13 @@ func version(cmd *cobra.Command, args []string) error {
return nil return nil
} }
func formatVersion(writer io.Writer, version *define.Version) { func formatVersion(w io.Writer, version *define.Version) {
fmt.Fprintf(writer, "Version:\t%s\n", version.Version) fmt.Fprintf(w, "Version:\t%s\n", version.Version)
fmt.Fprintf(writer, "API Version:\t%s\n", version.APIVersion) fmt.Fprintf(w, "API Version:\t%s\n", version.APIVersion)
fmt.Fprintf(writer, "Go Version:\t%s\n", version.GoVersion) fmt.Fprintf(w, "Go Version:\t%s\n", version.GoVersion)
if version.GitCommit != "" { if version.GitCommit != "" {
fmt.Fprintf(writer, "Git Commit:\t%s\n", version.GitCommit) fmt.Fprintf(w, "Git Commit:\t%s\n", version.GitCommit)
} }
fmt.Fprintf(writer, "Built:\t%s\n", version.BuiltTime) fmt.Fprintf(w, "Built:\t%s\n", version.BuiltTime)
fmt.Fprintf(writer, "OS/Arch:\t%s\n", version.OsArch) fmt.Fprintf(w, "OS/Arch:\t%s\n", version.OsArch)
} }

View File

@ -3,11 +3,11 @@ package volumes
import ( import (
"fmt" "fmt"
"os" "os"
"strings"
"text/template" "text/template"
"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/registry"
"github.com/containers/podman/v2/cmd/podman/report"
"github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/domain/entities"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -53,26 +53,21 @@ func inspect(cmd *cobra.Command, args []string) error {
if err != nil { if err != nil {
return err return err
} }
switch inspectFormat {
case "", formats.JSONString: switch {
case parse.MatchesJSONFormat(inspectFormat), inspectFormat == "":
jsonOut, err := json.MarshalIndent(responses, "", " ") jsonOut, err := json.MarshalIndent(responses, "", " ")
if err != nil { if err != nil {
return errors.Wrapf(err, "error marshalling inspect JSON") return errors.Wrapf(err, "error marshalling inspect JSON")
} }
fmt.Println(string(jsonOut)) fmt.Println(string(jsonOut))
default: default:
if !strings.HasSuffix(inspectFormat, "\n") { row := "{{range . }}" + report.NormalizeFormat(inspectFormat) + "{{end}}"
inspectFormat += "\n" tmpl, err := template.New("volumeInspect").Parse(row)
}
format := "{{range . }}" + inspectFormat + "{{end}}"
tmpl, err := template.New("volumeInspect").Parse(format)
if err != nil { if err != nil {
return err return err
} }
if err := tmpl.Execute(os.Stdout, responses); err != nil { return tmpl.Execute(os.Stdout, responses)
return err
}
} }
return nil return nil
} }

View File

@ -112,11 +112,15 @@ func GetEvents(w http.ResponseWriter, r *http.Request) {
errorChannel <- runtime.Events(r.Context(), readOpts) errorChannel <- runtime.Events(r.Context(), readOpts)
}() }()
var flush = func() {}
if flusher, ok := w.(http.Flusher); ok {
flush = flusher.Flush
}
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
if flusher, ok := w.(http.Flusher); ok { flush()
flusher.Flush()
}
coder := json.NewEncoder(w) coder := json.NewEncoder(w)
coder.SetEscapeHTML(true) coder.SetEscapeHTML(true)
@ -124,6 +128,7 @@ func GetEvents(w http.ResponseWriter, r *http.Request) {
select { select {
case err := <-errorChannel: case err := <-errorChannel:
if err != nil { if err != nil {
// FIXME StatusOK already sent above cannot send 500 here
utils.InternalServerError(w, err) utils.InternalServerError(w, err)
return return
} }
@ -136,9 +141,7 @@ func GetEvents(w http.ResponseWriter, r *http.Request) {
if err := coder.Encode(e); err != nil { if err := coder.Encode(e); err != nil {
logrus.Errorf("unable to write json: %q", err) logrus.Errorf("unable to write json: %q", err)
} }
if flusher, ok := w.(http.Flusher); ok { flush()
flusher.Flush()
}
case <-r.Context().Done(): case <-r.Context().Done():
return return
} }

View File

@ -220,7 +220,6 @@ var _ = Describe("Podman build", func() {
}) })
It("podman build --http_proxy flag", func() { It("podman build --http_proxy flag", func() {
SkipIfRemote("FIXME: This is broken should be fixed") // This is hanging currently.
os.Setenv("http_proxy", "1.2.3.4") os.Setenv("http_proxy", "1.2.3.4")
if IsRemote() { if IsRemote() {
podmanTest.StopRemoteService() podmanTest.StopRemoteService()

View File

@ -10,6 +10,7 @@ import (
. "github.com/containers/podman/v2/test/utils" . "github.com/containers/podman/v2/test/utils"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
. "github.com/onsi/gomega/gexec"
) )
var _ = Describe("Podman events", func() { var _ = Describe("Podman events", func() {
@ -126,26 +127,31 @@ var _ = Describe("Podman events", func() {
SkipIfNotFedora() SkipIfNotFedora()
_, ec, _ := podmanTest.RunLsContainer("") _, ec, _ := podmanTest.RunLsContainer("")
Expect(ec).To(Equal(0)) Expect(ec).To(Equal(0))
test := podmanTest.Podman([]string{"events", "--stream=false", "--format", "json"}) test := podmanTest.Podman([]string{"events", "--stream=false", "--format", "json"})
test.WaitWithDefaultTimeout() test.WaitWithDefaultTimeout()
Expect(test.ExitCode()).To(BeZero()) Expect(test).To(Exit(0))
jsonArr := test.OutputToStringArray() jsonArr := test.OutputToStringArray()
Expect(len(jsonArr)).To(Not(BeZero())) Expect(test.OutputToStringArray()).ShouldNot(BeEmpty())
eventsMap := make(map[string]string) eventsMap := make(map[string]string)
err := json.Unmarshal([]byte(jsonArr[0]), &eventsMap) err := json.Unmarshal([]byte(jsonArr[0]), &eventsMap)
Expect(err).To(BeNil()) Expect(err).ToNot(HaveOccurred())
_, exist := eventsMap["Status"]
Expect(exist).To(BeTrue()) Expect(eventsMap).To(HaveKey("Status"))
test = podmanTest.Podman([]string{"events", "--stream=false", "--format", "{{json.}}"}) test = podmanTest.Podman([]string{"events", "--stream=false", "--format", "{{json.}}"})
test.WaitWithDefaultTimeout() test.WaitWithDefaultTimeout()
Expect(test.ExitCode()).To(BeZero()) Expect(test).To(Exit(0))
jsonArr = test.OutputToStringArray() jsonArr = test.OutputToStringArray()
Expect(len(jsonArr)).To(Not(BeZero())) Expect(test.OutputToStringArray()).ShouldNot(BeEmpty())
eventsMap = make(map[string]string) eventsMap = make(map[string]string)
err = json.Unmarshal([]byte(jsonArr[0]), &eventsMap) err = json.Unmarshal([]byte(jsonArr[0]), &eventsMap)
Expect(err).To(BeNil()) Expect(err).ToNot(HaveOccurred())
_, exist = eventsMap["Status"]
Expect(exist).To(BeTrue()) Expect(eventsMap).To(HaveKey("Status"))
}) })
}) })

View File

@ -1,167 +0,0 @@
package formats
import (
"bytes"
"encoding/json"
"fmt"
"io"
"os"
"strings"
"text/tabwriter"
"text/template"
"github.com/ghodss/yaml"
"github.com/pkg/errors"
"golang.org/x/crypto/ssh/terminal"
)
const (
// JSONString const to save on duplicate variable names
JSONString = "json"
// IDString const to save on duplicates for Go templates
IDString = "{{.ID}}"
parsingErrorStr = "Template parsing error"
)
// Writer interface for outputs
type Writer interface {
Out() error
}
// JSONStructArray for JSON output
type JSONStructArray struct {
Output []interface{}
}
// StdoutTemplateArray for Go template output
type StdoutTemplateArray struct {
Output []interface{}
Template string
Fields map[string]string
}
// JSONStruct for JSON output
type JSONStruct struct {
Output interface{}
}
// StdoutTemplate for Go template output
type StdoutTemplate struct {
Output interface{}
Template string
Fields map[string]string
}
// YAMLStruct for YAML output
type YAMLStruct struct {
Output interface{}
}
func setJSONFormatEncoder(isTerminal bool, w io.Writer) *json.Encoder {
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
if isTerminal {
enc.SetEscapeHTML(false)
}
return enc
}
// Out method for JSON Arrays
func (j JSONStructArray) Out() error {
buf := bytes.NewBuffer(nil)
enc := setJSONFormatEncoder(terminal.IsTerminal(int(os.Stdout.Fd())), buf)
if err := enc.Encode(j.Output); err != nil {
return err
}
data := buf.Bytes()
// JSON returns a byte array with a literal null [110 117 108 108] in it
// if it is passed empty data. We used bytes.Compare to see if that is
// the case.
if diff := bytes.Compare(data, []byte("null")); diff == 0 {
data = []byte("[]")
}
// If the we did get NULL back, we should spit out {} which is
// at least valid JSON for the consumer.
fmt.Printf("%s", data)
humanNewLine()
return nil
}
// Out method for Go templates
func (t StdoutTemplateArray) Out() error {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
if strings.HasPrefix(t.Template, "table") {
// replace any spaces with tabs in template so that tabwriter can align it
t.Template = strings.Replace(strings.TrimSpace(t.Template[5:]), " ", "\t", -1)
headerTmpl, err := template.New("header").Funcs(headerFunctions).Parse(t.Template)
if err != nil {
return errors.Wrapf(err, parsingErrorStr)
}
err = headerTmpl.Execute(w, t.Fields)
if err != nil {
return err
}
fmt.Fprintln(w, "")
}
t.Template = strings.Replace(t.Template, " ", "\t", -1)
tmpl, err := template.New("image").Funcs(basicFunctions).Parse(t.Template)
if err != nil {
return errors.Wrapf(err, parsingErrorStr)
}
for _, raw := range t.Output {
basicTmpl := tmpl.Funcs(basicFunctions)
if err := basicTmpl.Execute(w, raw); err != nil {
return errors.Wrapf(err, parsingErrorStr)
}
fmt.Fprintln(w, "")
}
return w.Flush()
}
// Out method for JSON struct
func (j JSONStruct) Out() error {
data, err := json.MarshalIndent(j.Output, "", " ")
if err != nil {
return err
}
fmt.Printf("%s", data)
humanNewLine()
return nil
}
//Out method for Go templates
func (t StdoutTemplate) Out() error {
tmpl, err := template.New("image").Parse(t.Template)
if err != nil {
return errors.Wrapf(err, "template parsing error")
}
err = tmpl.Execute(os.Stdout, t.Output)
if err != nil {
return err
}
humanNewLine()
return nil
}
// Out method for YAML
func (y YAMLStruct) Out() error {
var buf []byte
var err error
buf, err = yaml.Marshal(y.Output)
if err != nil {
return err
}
fmt.Printf("%s", string(buf))
humanNewLine()
return nil
}
// humanNewLine prints a new line at the end of the output only if stdout is the terminal
func humanNewLine() {
if terminal.IsTerminal(int(os.Stdout.Fd())) {
fmt.Println()
}
}

View File

@ -1,78 +0,0 @@
package formats
import (
"bytes"
"encoding/json"
"strings"
"text/template"
)
// basicFunctions are the set of initial
// functions provided to every template.
var basicFunctions = template.FuncMap{
"json": func(v interface{}) string {
buf := &bytes.Buffer{}
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(false)
_ = enc.Encode(v)
// Remove the trailing new line added by the encoder
return strings.TrimSpace(buf.String())
},
"split": strings.Split,
"join": strings.Join,
"title": strings.Title,
"lower": strings.ToLower,
"upper": strings.ToUpper,
"pad": padWithSpace,
"truncate": truncateWithLength,
}
// HeaderFunctions are used to created headers of a table.
// This is a replacement of basicFunctions for header generation
// because we want the header to remain intact.
// Some functions like `split` are irrelevant so not added.
var headerFunctions = template.FuncMap{
"json": func(v string) string {
return v
},
"title": func(v string) string {
return v
},
"lower": func(v string) string {
return v
},
"upper": func(v string) string {
return v
},
"truncate": func(v string, l int) string {
return v
},
}
// Parse creates a new anonymous template with the basic functions
// and parses the given format.
func Parse(format string) (*template.Template, error) {
return NewParse("", format)
}
// NewParse creates a new tagged template with the basic functions
// and parses the given format.
func NewParse(tag, format string) (*template.Template, error) {
return template.New(tag).Funcs(basicFunctions).Parse(format)
}
// padWithSpace adds whitespace to the input if the input is non-empty
func padWithSpace(source string, prefix, suffix int) string {
if source == "" {
return source
}
return strings.Repeat(" ", prefix) + source + strings.Repeat(" ", suffix)
}
// truncateWithLength truncates the source string up to the length provided by the input
func truncateWithLength(source string, length int) string {
if len(source) < length {
return source
}
return source[:length]
}

1
vendor/modules.txt vendored
View File

@ -78,7 +78,6 @@ github.com/containers/buildah/manifests
github.com/containers/buildah/pkg/blobcache github.com/containers/buildah/pkg/blobcache
github.com/containers/buildah/pkg/chrootuser github.com/containers/buildah/pkg/chrootuser
github.com/containers/buildah/pkg/cli github.com/containers/buildah/pkg/cli
github.com/containers/buildah/pkg/formats
github.com/containers/buildah/pkg/manifests github.com/containers/buildah/pkg/manifests
github.com/containers/buildah/pkg/overlay github.com/containers/buildah/pkg/overlay
github.com/containers/buildah/pkg/parse github.com/containers/buildah/pkg/parse