Merge pull request #653 from baude/images_json

cmd/kpod/images.go: Add JSON output option
This commit is contained in:
Mrunal Patel 2017-08-13 07:19:04 -07:00 committed by GitHub
commit 4311020c36
6 changed files with 155 additions and 60 deletions

View file

@ -0,0 +1,54 @@
package formats
import (
"encoding/json"
"fmt"
"github.com/pkg/errors"
"os"
"text/template"
)
// Writer interface for outputs
type Writer interface {
Out() error
}
// JSONstruct for JSON output
type JSONstruct struct {
Output []interface{}
}
// StdoutTemplate for Go template output
type StdoutTemplate struct {
Output []interface{}
Template string
}
// Out method for JSON
func (j JSONstruct) Out() error {
data, err := json.MarshalIndent(j.Output, "", " ")
if err != nil {
return err
}
fmt.Printf("%s\n", data)
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")
}
for _, img := range t.Output {
err = tmpl.Execute(os.Stdout, img)
if err != nil {
return err
}
fmt.Println()
}
return nil
}

View file

@ -2,10 +2,8 @@ package main
import ( import (
"fmt" "fmt"
"os"
"text/template"
"github.com/containers/storage" "github.com/containers/storage"
"github.com/kubernetes-incubator/cri-o/cmd/kpod/formats"
libkpodimage "github.com/kubernetes-incubator/cri-o/libkpod/image" libkpodimage "github.com/kubernetes-incubator/cri-o/libkpod/image"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -13,11 +11,11 @@ import (
) )
type imageOutputParams struct { type imageOutputParams struct {
ID string ID string `json:"id"`
Name string Name string `json:"names"`
Digest digest.Digest Digest digest.Digest `json:"digest"`
CreatedAt string CreatedAt string `json:"created"`
Size string Size string `json:"size"`
} }
var ( var (
@ -40,7 +38,7 @@ var (
}, },
cli.StringFlag{ cli.StringFlag{
Name: "format", Name: "format",
Usage: "pretty-print images using a Go template. will override --quiet", Usage: "Change the output format.",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "filter, f", Name: "filter, f",
@ -59,6 +57,47 @@ var (
} }
) )
type stdoutstruct struct {
output []imageOutputParams
truncate, digests, quiet, noheading bool
}
func (so stdoutstruct) Out() error {
if len(so.output) > 0 && !so.noheading && !so.quiet {
outputHeader(so.truncate, so.digests)
}
lastID := ""
for _, img := range so.output {
if so.quiet {
if lastID == img.ID {
continue // quiet should not show the same ID multiple times.
}
fmt.Printf("%-64s\n", img.ID)
continue
}
if so.truncate {
fmt.Printf("%-20.12s %-56s", img.ID, img.Name)
} else {
fmt.Printf("%-64s %-56s", img.ID, img.Name)
}
if so.digests {
fmt.Printf(" %-64s", img.Digest)
}
fmt.Printf(" %-22s %s\n", img.CreatedAt, img.Size)
}
return nil
}
func toGeneric(params []imageOutputParams) []interface{} {
genericParams := make([]interface{}, len(params))
for i, v := range params {
genericParams[i] = interface{}(v)
}
return genericParams
}
func imagesCmd(c *cli.Context) error { func imagesCmd(c *cli.Context) error {
config, err := getConfig(c) config, err := getConfig(c)
if err != nil { if err != nil {
@ -85,11 +124,9 @@ func imagesCmd(c *cli.Context) error {
if c.IsSet("digests") { if c.IsSet("digests") {
digests = c.Bool("digests") digests = c.Bool("digests")
} }
formatString := "" outputFormat := ""
hasTemplate := false
if c.IsSet("format") { if c.IsSet("format") {
formatString = c.String("format") outputFormat = c.String("format")
hasTemplate = true
} }
name := "" name := ""
@ -113,11 +150,8 @@ func imagesCmd(c *cli.Context) error {
if err != nil { if err != nil {
return errors.Wrapf(err, "could not get list of images matching filter") return errors.Wrapf(err, "could not get list of images matching filter")
} }
if len(imageList) > 0 && !noheading && !quiet && !hasTemplate {
outputHeader(truncate, digests)
}
return outputImages(store, imageList, formatString, hasTemplate, truncate, digests, quiet) return outputImages(store, imageList, truncate, digests, quiet, outputFormat, noheading)
} }
func outputHeader(truncate, digests bool) { func outputHeader(truncate, digests bool) {
@ -134,7 +168,9 @@ func outputHeader(truncate, digests bool) {
fmt.Printf("%-22s %s\n", "CREATED AT", "SIZE") fmt.Printf("%-22s %s\n", "CREATED AT", "SIZE")
} }
func outputImages(store storage.Store, images []storage.Image, format string, hasTemplate, truncate, digests, quiet bool) error { func outputImages(store storage.Store, images []storage.Image, truncate, digests, quiet bool, outputFormat string, noheading bool) error {
imageOutput := []imageOutputParams{}
for _, img := range images { for _, img := range images {
createdTime := img.Created createdTime := img.Created
@ -148,12 +184,6 @@ func outputImages(store storage.Store, images []storage.Image, format string, ha
createdTime = info.Created createdTime = info.Created
} }
if quiet {
fmt.Printf("%-64s\n", img.ID)
// We only want to print each id once
continue
}
params := imageOutputParams{ params := imageOutputParams{
ID: img.ID, ID: img.ID,
Name: name, Name: name,
@ -161,40 +191,25 @@ func outputImages(store storage.Store, images []storage.Image, format string, ha
CreatedAt: createdTime.Format("Jan 2, 2006 15:04"), CreatedAt: createdTime.Format("Jan 2, 2006 15:04"),
Size: libkpodimage.FormattedSize(size), Size: libkpodimage.FormattedSize(size),
} }
if hasTemplate { imageOutput = append(imageOutput, params)
if err := outputUsingTemplate(format, params); err != nil {
return err
}
continue
}
outputUsingFormatString(truncate, digests, params)
}
return nil
}
func outputUsingTemplate(format string, params imageOutputParams) error {
tmpl, err := template.New("image").Parse(format)
if err != nil {
return errors.Wrapf(err, "Template parsing error")
} }
err = tmpl.Execute(os.Stdout, params) var out formats.Writer
if err != nil {
return err
}
fmt.Println()
return nil
}
func outputUsingFormatString(truncate, digests bool, params imageOutputParams) { if outputFormat != "" {
if truncate { switch outputFormat {
fmt.Printf("%-20.12s %-56s", params.ID, params.Name) case "json":
out = formats.JSONstruct{Output: toGeneric(imageOutput)}
default:
// Assuming Go-template
out = formats.StdoutTemplate{Output: toGeneric(imageOutput), Template: outputFormat}
}
} else { } else {
fmt.Printf("%-64s %-56s", params.ID, params.Name) out = stdoutstruct{output: imageOutput, digests: digests, truncate: truncate, quiet: quiet, noheading: noheading}
} }
if digests { formats.Writer(out).Out()
fmt.Printf(" %-64s", params.Digest)
} return nil
fmt.Printf(" %-22s %s\n", params.CreatedAt, params.Size)
} }

View file

@ -14,7 +14,6 @@ _kpod_history() {
--human -H --human -H
--no-trunc --no-trunc
--quiet -q --quiet -q
--json
" "
_complete_ "$options_with_args" "$boolean_options" _complete_ "$options_with_args" "$boolean_options"
@ -38,11 +37,11 @@ _kpod_images() {
-n -n
--no-trunc --no-trunc
--digests --digests
--format
--filter --filter
-f -f
" "
local options_with_args=" local options_with_args="
--format
" "
local all_options="$options_with_args $boolean_options" local all_options="$options_with_args $boolean_options"

View file

@ -21,9 +21,10 @@ Show image digests
Filter output based on conditions provided (default []) Filter output based on conditions provided (default [])
**--format="TEMPLATE"** **--format**
Pretty-print images using a Go template. Will override --quiet Change the default output format. This can be of a supported type like 'json'
or a Go template.
**--noheading, -n** **--noheading, -n**
@ -46,6 +47,10 @@ kpod images --quiet
kpod images -q --noheading --notruncate kpod images -q --noheading --notruncate
kpod images --format json
kpod images --format "{{.ID}}"
## SEE ALSO ## SEE ALSO
kpod(1) kpod(1)

View file

@ -0,0 +1 @@
package common

View file

@ -201,6 +201,7 @@ function teardown() {
} }
run ${KPOD_BINARY} $KPOD_OPTIONS rmi redis:alpine run ${KPOD_BINARY} $KPOD_OPTIONS rmi redis:alpine
@test "kpod inspect non-existent container" { @test "kpod inspect non-existent container" {
run ${KPOD_BINARY} $KPOD_OPTIONS inspect 14rcole/non-existent run ${KPOD_BINARY} $KPOD_OPTIONS inspect 14rcole/non-existent
echo "$output" echo "$output"
@ -227,3 +228,23 @@ function teardown() {
[ "$status" -eq 0] [ "$status" -eq 0]
run ${KPOD_BINARY} $KPOD_OPTIONS rmi redis:alpine run ${KPOD_BINARY} $KPOD_OPTIONS rmi redis:alpine
} }
@test "kpod images" {
run ${KPOD_BINARY} $KPOD_OPTIONS pull debian:6.0.10
run ${KPOD_BINARY} $KPOD_OPTIONS images
[ "$status" -eq 0 ]
}
@test "kpod images test valid json" {
run ${KPOD_BINARY} $KPOD_OPTIONS pull debian:6.0.10
run ${KPOD_BINARY} $KPOD_OPTIONS images --format json
echo "$output" | python -m json.tool
[ "$status" -eq 0 ]
}
@test "kpod images check name json output" {
run ${KPOD_BINARY} $KPOD_OPTIONS pull debian:6.0.10
run ${KPOD_BINARY} $KPOD_OPTIONS images --format json
name=$(echo $output | python -c 'import sys; import json; print(json.loads(sys.stdin.read())[0])["names"][0]')
[ "$name" == "docker.io/library/debian:6.0.10" ]
}