Modify kpod diff --json to --format json
We want all kpod subcommands to use the formats code to output formats like json. Altering kpod diff --json to kpod diff --format json like the kpod images command. Signed-off-by: baude <bbaude@redhat.com>
This commit is contained in:
parent
f82fe5691a
commit
78c6151519
13 changed files with 267 additions and 237 deletions
|
@ -1,10 +1,9 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/containers/storage/pkg/archive"
|
"github.com/containers/storage/pkg/archive"
|
||||||
|
"github.com/kubernetes-incubator/cri-o/cmd/kpod/formats"
|
||||||
"github.com/kubernetes-incubator/cri-o/libkpod"
|
"github.com/kubernetes-incubator/cri-o/libkpod"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
@ -16,6 +15,22 @@ type diffJSONOutput struct {
|
||||||
Deleted []string `json:"deleted,omitempty"`
|
Deleted []string `json:"deleted,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type diffOutputParams struct {
|
||||||
|
Change archive.ChangeType
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
type stdoutStruct struct {
|
||||||
|
output []diffOutputParams
|
||||||
|
}
|
||||||
|
|
||||||
|
func (so stdoutStruct) Out() error {
|
||||||
|
for _, d := range so.output {
|
||||||
|
fmt.Printf("%s %s\n", d.Change, d.Path)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
diffFlags = []cli.Flag{
|
diffFlags = []cli.Flag{
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
|
@ -23,9 +38,9 @@ var (
|
||||||
Usage: "Save the diff as a tar archive",
|
Usage: "Save the diff as a tar archive",
|
||||||
Hidden: true,
|
Hidden: true,
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.StringFlag{
|
||||||
Name: "json",
|
Name: "format",
|
||||||
Usage: "Format output as JSON",
|
Usage: "Change the output format.",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
diffDescription = fmt.Sprint(`Displays changes on a container or image's filesystem. The
|
diffDescription = fmt.Sprint(`Displays changes on a container or image's filesystem. The
|
||||||
|
@ -41,6 +56,23 @@ var (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func formatJSON(output []diffOutputParams) (diffJSONOutput, error) {
|
||||||
|
jsonStruct := diffJSONOutput{}
|
||||||
|
for _, output := range output {
|
||||||
|
switch output.Change {
|
||||||
|
case archive.ChangeModify:
|
||||||
|
jsonStruct.Changed = append(jsonStruct.Changed, output.Path)
|
||||||
|
case archive.ChangeAdd:
|
||||||
|
jsonStruct.Added = append(jsonStruct.Added, output.Path)
|
||||||
|
case archive.ChangeDelete:
|
||||||
|
jsonStruct.Deleted = append(jsonStruct.Deleted, output.Path)
|
||||||
|
default:
|
||||||
|
return jsonStruct, errors.Errorf("output kind %q not recognized", output.Change.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return jsonStruct, nil
|
||||||
|
}
|
||||||
|
|
||||||
func diffCmd(c *cli.Context) error {
|
func diffCmd(c *cli.Context) error {
|
||||||
if len(c.Args()) != 1 {
|
if len(c.Args()) != 1 {
|
||||||
return errors.Errorf("container, layer, or image name must be specified: kpod diff [options [...]] ID-NAME")
|
return errors.Errorf("container, layer, or image name must be specified: kpod diff [options [...]] ID-NAME")
|
||||||
|
@ -61,29 +93,35 @@ func diffCmd(c *cli.Context) error {
|
||||||
return errors.Wrapf(err, "could not get changes for %q", to)
|
return errors.Wrapf(err, "could not get changes for %q", to)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Bool("json") {
|
diffOutput := []diffOutputParams{}
|
||||||
jsonStruct := diffJSONOutput{}
|
outputFormat := c.String("format")
|
||||||
for _, change := range changes {
|
|
||||||
if change.Kind == archive.ChangeModify {
|
for _, change := range changes {
|
||||||
jsonStruct.Changed = append(jsonStruct.Changed, change.Path)
|
|
||||||
} else if change.Kind == archive.ChangeAdd {
|
params := diffOutputParams{
|
||||||
jsonStruct.Added = append(jsonStruct.Added, change.Path)
|
Change: change.Kind,
|
||||||
} else if change.Kind == archive.ChangeDelete {
|
Path: change.Path,
|
||||||
jsonStruct.Deleted = append(jsonStruct.Deleted, change.Path)
|
|
||||||
} else {
|
|
||||||
return errors.Errorf("change kind %q not recognized", change.Kind.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b, err := json.MarshalIndent(jsonStruct, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "could not marshal json for %+v", jsonStruct)
|
|
||||||
}
|
|
||||||
fmt.Println(string(b))
|
|
||||||
} else {
|
|
||||||
for _, change := range changes {
|
|
||||||
fmt.Println(change.String())
|
|
||||||
}
|
}
|
||||||
|
diffOutput = append(diffOutput, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var out formats.Writer
|
||||||
|
|
||||||
|
if outputFormat != "" {
|
||||||
|
switch outputFormat {
|
||||||
|
case formats.JSONString:
|
||||||
|
data, err := formatJSON(diffOutput)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
out = formats.JSONStruct{Output: data}
|
||||||
|
default:
|
||||||
|
return errors.New("only valid format for diff is 'json'")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out = stdoutStruct{output: diffOutput}
|
||||||
|
}
|
||||||
|
formats.Writer(out).Out()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package formats
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/ghodss/yaml"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
@ -10,25 +11,45 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// JSONString const to save on duplicate variable names
|
||||||
|
const JSONString string = "json"
|
||||||
|
|
||||||
// Writer interface for outputs
|
// Writer interface for outputs
|
||||||
type Writer interface {
|
type Writer interface {
|
||||||
Out() error
|
Out() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSONstruct for JSON output
|
// JSONStructArray for JSON output
|
||||||
type JSONstruct struct {
|
type JSONStructArray struct {
|
||||||
Output []interface{}
|
Output []interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// StdoutTemplate for Go template output
|
// StdoutTemplateArray for Go template output
|
||||||
type StdoutTemplate struct {
|
type StdoutTemplateArray struct {
|
||||||
Output []interface{}
|
Output []interface{}
|
||||||
Template string
|
Template string
|
||||||
Fields map[string]string
|
Fields map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Out method for JSON
|
// JSONStruct for JSON output
|
||||||
func (j JSONstruct) Out() error {
|
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{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Out method for JSON Arrays
|
||||||
|
func (j JSONStructArray) Out() error {
|
||||||
data, err := json.MarshalIndent(j.Output, "", " ")
|
data, err := json.MarshalIndent(j.Output, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -38,7 +59,7 @@ func (j JSONstruct) Out() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Out method for Go templates
|
// Out method for Go templates
|
||||||
func (t StdoutTemplate) Out() error {
|
func (t StdoutTemplateArray) Out() error {
|
||||||
if strings.HasPrefix(t.Template, "table") {
|
if strings.HasPrefix(t.Template, "table") {
|
||||||
t.Template = strings.TrimSpace(t.Template[5:])
|
t.Template = strings.TrimSpace(t.Template[5:])
|
||||||
headerTmpl, err := template.New("header").Funcs(headerFunctions).Parse(t.Template)
|
headerTmpl, err := template.New("header").Funcs(headerFunctions).Parse(t.Template)
|
||||||
|
@ -67,3 +88,39 @@ func (t StdoutTemplate) Out() error {
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Out method for JSON struct
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
err = tmpl.Execute(os.Stdout, t.Output)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
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.Println(string(buf))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -3,17 +3,15 @@ package main
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"reflect"
|
||||||
"text/template"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
is "github.com/containers/image/storage"
|
is "github.com/containers/image/storage"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
units "github.com/docker/go-units"
|
units "github.com/docker/go-units"
|
||||||
|
"github.com/kubernetes-incubator/cri-o/cmd/kpod/formats"
|
||||||
"github.com/kubernetes-incubator/cri-o/libkpod/common"
|
"github.com/kubernetes-incubator/cri-o/libkpod/common"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
@ -27,20 +25,20 @@ const (
|
||||||
|
|
||||||
// historyOutputParams stores info about each layer
|
// historyOutputParams stores info about each layer
|
||||||
type historyOutputParams struct {
|
type historyOutputParams struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Created *time.Time `json:"created"`
|
Created string `json:"created"`
|
||||||
CreatedBy string `json:"createdby"`
|
CreatedBy string `json:"createdby"`
|
||||||
Size int64 `json:"size"`
|
Size string `json:"size"`
|
||||||
Comment string `json:"comment"`
|
Comment string `json:"comment"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// historyOptions stores cli flag values
|
// historyOptions stores cli flag values
|
||||||
type historyOptions struct {
|
type historyOptions struct {
|
||||||
image string
|
image string
|
||||||
human bool
|
human bool
|
||||||
noTrunc bool
|
truncate bool
|
||||||
quiet bool
|
quiet bool
|
||||||
format string
|
format string
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -50,7 +48,7 @@ var (
|
||||||
Usage: "Display sizes and dates in human readable format",
|
Usage: "Display sizes and dates in human readable format",
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "no-trunc",
|
Name: "no-trunc, notruncate",
|
||||||
Usage: "Do not truncate the output",
|
Usage: "Do not truncate the output",
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
|
@ -59,11 +57,7 @@ var (
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "format",
|
Name: "format",
|
||||||
Usage: "Pretty-print history of the image using a Go template",
|
Usage: "Change the output to JSON or a Go template",
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "json",
|
|
||||||
Usage: "Print the history in JSON format",
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,23 +87,19 @@ func historyCmd(c *cli.Context) error {
|
||||||
if c.IsSet("human") {
|
if c.IsSet("human") {
|
||||||
human = c.Bool("human")
|
human = c.Bool("human")
|
||||||
}
|
}
|
||||||
noTruncate := false
|
|
||||||
|
truncate := true
|
||||||
if c.IsSet("no-trunc") {
|
if c.IsSet("no-trunc") {
|
||||||
noTruncate = c.Bool("no-trunc")
|
truncate = !c.Bool("no-trunc")
|
||||||
}
|
}
|
||||||
quiet := false
|
quiet := false
|
||||||
if c.IsSet("quiet") {
|
if c.IsSet("quiet") {
|
||||||
quiet = c.Bool("quiet")
|
quiet = c.Bool("quiet")
|
||||||
}
|
}
|
||||||
json := false
|
|
||||||
if c.IsSet("json") {
|
|
||||||
json = c.Bool("json")
|
|
||||||
}
|
|
||||||
format := ""
|
format := ""
|
||||||
if c.IsSet("format") {
|
if c.IsSet("format") {
|
||||||
format = c.String("format")
|
format = c.String("format")
|
||||||
}
|
}
|
||||||
|
|
||||||
args := c.Args()
|
args := c.Args()
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
logrus.Errorf("an image name must be specified")
|
logrus.Errorf("an image name must be specified")
|
||||||
|
@ -122,98 +112,29 @@ func historyCmd(c *cli.Context) error {
|
||||||
imgName := args[0]
|
imgName := args[0]
|
||||||
|
|
||||||
opts := historyOptions{
|
opts := historyOptions{
|
||||||
image: imgName,
|
image: imgName,
|
||||||
human: human,
|
human: human,
|
||||||
noTrunc: noTruncate,
|
truncate: truncate,
|
||||||
quiet: quiet,
|
quiet: quiet,
|
||||||
format: format,
|
format: format,
|
||||||
}
|
|
||||||
|
|
||||||
var history []byte
|
|
||||||
if json {
|
|
||||||
history, err = createJSON(store, opts)
|
|
||||||
fmt.Println(string(history))
|
|
||||||
} else {
|
|
||||||
if format == "" && !quiet {
|
|
||||||
outputHeading(noTruncate)
|
|
||||||
}
|
|
||||||
err = outputHistory(store, opts)
|
|
||||||
}
|
}
|
||||||
|
err = outputHistory(store, opts)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// outputHeader outputs the heading
|
func genHistoryFormat(quiet, truncate, human bool) (format string) {
|
||||||
func outputHeading(noTrunc bool) {
|
if quiet {
|
||||||
if !noTrunc {
|
return "{{.ID}}"
|
||||||
fmt.Printf("%-12s\t\t%-16s\t\t%-45s\t\t", "IMAGE", "CREATED", "CREATED BY")
|
}
|
||||||
fmt.Printf("%-16s\t\t%s\n", "SIZE", "COMMENT")
|
|
||||||
|
if truncate {
|
||||||
|
format = "table {{ .ID | printf \"%-12.12s\" }} {{ .Created | printf \"%-16s\" }} {{ .CreatedBy | " +
|
||||||
|
"printf \"%-45.45s\" }} {{ .Size | printf \"%-16s\" }} {{ .Comment | printf \"%s\" }}"
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("%-64s\t%-18s\t%-60s\t", "IMAGE", "CREATED", "CREATED BY")
|
format = "table {{ .ID | printf \"%-64s\" }} {{ .Created | printf \"%-18s\" }} {{ .CreatedBy | " +
|
||||||
fmt.Printf("%-16s\t%s\n", "SIZE", "COMMENT")
|
"printf \"%-60s\" }} {{ .Size | printf \"%-16s\" }} {{ .Comment | printf \"%s\"}}"
|
||||||
}
|
}
|
||||||
}
|
return
|
||||||
|
|
||||||
// outputString outputs the information in historyOutputParams
|
|
||||||
func outputString(noTrunc, human bool, params historyOutputParams) {
|
|
||||||
var (
|
|
||||||
createdTime string
|
|
||||||
outputSize string
|
|
||||||
)
|
|
||||||
|
|
||||||
if human {
|
|
||||||
createdTime = outputHumanTime(params.Created) + " ago"
|
|
||||||
outputSize = units.HumanSize(float64(params.Size))
|
|
||||||
} else {
|
|
||||||
createdTime = outputTime(params.Created)
|
|
||||||
outputSize = strconv.FormatInt(params.Size, 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !noTrunc {
|
|
||||||
fmt.Printf("%-12.12s\t\t%-16s\t\t%-45.45s\t\t", params.ID, createdTime, params.CreatedBy)
|
|
||||||
fmt.Printf("%-16s\t\t%s\n", outputSize, params.Comment)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("%-64s\t%-18s\t%-60s\t", params.ID, createdTime, params.CreatedBy)
|
|
||||||
fmt.Printf("%-16s\t%s\n\n", outputSize, params.Comment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// outputWithTemplate is called when --format is given a template
|
|
||||||
func outputWithTemplate(format string, params historyOutputParams, human bool) error {
|
|
||||||
templ, err := template.New("history").Parse(format)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "error parsing template")
|
|
||||||
}
|
|
||||||
|
|
||||||
createdTime := outputTime(params.Created)
|
|
||||||
outputSize := strconv.FormatInt(params.Size, 10)
|
|
||||||
|
|
||||||
if human {
|
|
||||||
createdTime = outputHumanTime(params.Created) + " ago"
|
|
||||||
outputSize = units.HumanSize(float64(params.Size))
|
|
||||||
}
|
|
||||||
|
|
||||||
// templParams is used to store the info from params and the time and
|
|
||||||
// size that have been converted to type string for when the human flag
|
|
||||||
// is set
|
|
||||||
templParams := struct {
|
|
||||||
ID string
|
|
||||||
Created string
|
|
||||||
CreatedBy string
|
|
||||||
Size string
|
|
||||||
Comment string
|
|
||||||
}{
|
|
||||||
params.ID,
|
|
||||||
createdTime,
|
|
||||||
params.CreatedBy,
|
|
||||||
outputSize,
|
|
||||||
params.Comment,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = templ.Execute(os.Stdout, templParams); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Println()
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// outputTime displays the time stamp in "2017-06-20T20:24:10Z" format
|
// outputTime displays the time stamp in "2017-06-20T20:24:10Z" format
|
||||||
|
@ -229,10 +150,12 @@ func outputHumanTime(tm *time.Time) string {
|
||||||
// createJSON retrieves the history of the image and returns a JSON object
|
// createJSON retrieves the history of the image and returns a JSON object
|
||||||
func createJSON(store storage.Store, opts historyOptions) ([]byte, error) {
|
func createJSON(store storage.Store, opts historyOptions) ([]byte, error) {
|
||||||
var (
|
var (
|
||||||
size int64
|
size int64
|
||||||
img *storage.Image
|
img *storage.Image
|
||||||
imageID string
|
imageID string
|
||||||
layerAll []historyOutputParams
|
layerAll []historyOutputParams
|
||||||
|
createdTime string
|
||||||
|
outputSize string
|
||||||
)
|
)
|
||||||
|
|
||||||
ref, err := is.Transport.ParseStoreReference(store, opts.image)
|
ref, err := is.Transport.ParseStoreReference(store, opts.image)
|
||||||
|
@ -275,11 +198,18 @@ func createJSON(store storage.Store, opts historyOptions) ([]byte, error) {
|
||||||
size = 0
|
size = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.human {
|
||||||
|
createdTime = outputHumanTime(history[i].Created) + " ago"
|
||||||
|
outputSize = units.HumanSize(float64(size))
|
||||||
|
} else {
|
||||||
|
createdTime = outputTime(history[i].Created)
|
||||||
|
outputSize = strconv.FormatInt(size, 10)
|
||||||
|
}
|
||||||
params := historyOutputParams{
|
params := historyOutputParams{
|
||||||
ID: imageID,
|
ID: imageID,
|
||||||
Created: history[i].Created,
|
Created: createdTime,
|
||||||
CreatedBy: history[i].CreatedBy,
|
CreatedBy: history[i].CreatedBy,
|
||||||
Size: size,
|
Size: outputSize,
|
||||||
Comment: history[i].Comment,
|
Comment: history[i].Comment,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,6 +228,15 @@ func createJSON(store storage.Store, opts historyOptions) ([]byte, error) {
|
||||||
return output, nil
|
return output, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// historyToGeneric makes an empty array of interfaces for output
|
||||||
|
func historyToGeneric(params []historyOutputParams) []interface{} {
|
||||||
|
genericParams := make([]interface{}, len(params))
|
||||||
|
for i, v := range params {
|
||||||
|
genericParams[i] = interface{}(v)
|
||||||
|
}
|
||||||
|
return genericParams
|
||||||
|
}
|
||||||
|
|
||||||
// outputHistory gets the history of the image from the JSON object
|
// outputHistory gets the history of the image from the JSON object
|
||||||
// and pretty prints it to the screen
|
// and pretty prints it to the screen
|
||||||
func outputHistory(store storage.Store, opts historyOptions) error {
|
func outputHistory(store storage.Store, opts historyOptions) error {
|
||||||
|
@ -316,27 +255,25 @@ func outputHistory(store storage.Store, opts historyOptions) error {
|
||||||
return errors.Errorf("error Unmarshalling JSON: %v", err)
|
return errors.Errorf("error Unmarshalling JSON: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
historyOutput := []historyOutputParams{}
|
||||||
|
|
||||||
|
historyFormat := opts.format
|
||||||
|
if historyFormat == "" {
|
||||||
|
historyFormat = genHistoryFormat(opts.quiet, opts.truncate, opts.human)
|
||||||
|
}
|
||||||
|
|
||||||
for i := 0; i < len(history); i++ {
|
for i := 0; i < len(history); i++ {
|
||||||
imageID = history[i].ID
|
imageID = history[i].ID
|
||||||
|
|
||||||
outputCreatedBy = strings.Join(strings.Fields(history[i].CreatedBy), " ")
|
outputCreatedBy = strings.Join(strings.Fields(history[i].CreatedBy), " ")
|
||||||
if !opts.noTrunc && len(outputCreatedBy) > createdByTruncLength {
|
if opts.truncate && len(outputCreatedBy) > createdByTruncLength {
|
||||||
outputCreatedBy = outputCreatedBy[:createdByTruncLength-3] + "..."
|
outputCreatedBy = outputCreatedBy[:createdByTruncLength-3] + "..."
|
||||||
}
|
}
|
||||||
|
|
||||||
if !opts.noTrunc && i == 0 {
|
if opts.truncate && i == 0 {
|
||||||
imageID = history[i].ID[:idTruncLength]
|
imageID = history[i].ID[:idTruncLength]
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.quiet {
|
|
||||||
if !opts.noTrunc {
|
|
||||||
fmt.Printf("%-12.12s\n", imageID)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("%-s\n", imageID)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
params := historyOutputParams{
|
params := historyOutputParams{
|
||||||
ID: imageID,
|
ID: imageID,
|
||||||
Created: history[i].Created,
|
Created: history[i].Created,
|
||||||
|
@ -344,15 +281,28 @@ func outputHistory(store storage.Store, opts historyOptions) error {
|
||||||
Size: history[i].Size,
|
Size: history[i].Size,
|
||||||
Comment: history[i].Comment,
|
Comment: history[i].Comment,
|
||||||
}
|
}
|
||||||
|
historyOutput = append(historyOutput, params)
|
||||||
if len(opts.format) > 0 {
|
|
||||||
if err = outputWithTemplate(opts.format, params, opts.human); err != nil {
|
|
||||||
return errors.Errorf("error outputing with template: %v", err)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
outputString(opts.noTrunc, opts.human, params)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var out formats.Writer
|
||||||
|
switch opts.format {
|
||||||
|
case formats.JSONString:
|
||||||
|
out = formats.JSONStructArray{Output: historyToGeneric(historyOutput)}
|
||||||
|
default:
|
||||||
|
out = formats.StdoutTemplateArray{Output: historyToGeneric(historyOutput), Template: historyFormat, Fields: historyOutput[0].headerMap()}
|
||||||
|
|
||||||
|
}
|
||||||
|
formats.Writer(out).Out()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *historyOutputParams) headerMap() map[string]string {
|
||||||
|
v := reflect.Indirect(reflect.ValueOf(h))
|
||||||
|
values := make(map[string]string)
|
||||||
|
for h := 0; h < v.NumField(); h++ {
|
||||||
|
key := v.Type().Field(h).Name
|
||||||
|
value := key
|
||||||
|
values[key] = fmt.Sprintf("%s ", strings.ToUpper(splitCamelCase(value)))
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ var (
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "format",
|
Name: "format",
|
||||||
Usage: "Change the output format.",
|
Usage: "Change the output format to JSON or a Go template",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "filter, f",
|
Name: "filter, f",
|
||||||
|
@ -160,10 +160,10 @@ func outputImages(store storage.Store, images []storage.Image, truncate, digests
|
||||||
var out formats.Writer
|
var out formats.Writer
|
||||||
|
|
||||||
switch outputFormat {
|
switch outputFormat {
|
||||||
case "json":
|
case formats.JSONString:
|
||||||
out = formats.JSONstruct{Output: toGeneric(imageOutput)}
|
out = formats.JSONStructArray{Output: toGeneric(imageOutput)}
|
||||||
default:
|
default:
|
||||||
out = formats.StdoutTemplate{Output: toGeneric(imageOutput), Template: outputFormat, Fields: imageOutput[0].headerMap()}
|
out = formats.StdoutTemplateArray{Output: toGeneric(imageOutput), Template: outputFormat, Fields: imageOutput[0].headerMap()}
|
||||||
}
|
}
|
||||||
|
|
||||||
formats.Writer(out).Out()
|
formats.Writer(out).Out()
|
||||||
|
|
|
@ -2,14 +2,13 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/system"
|
"github.com/docker/docker/pkg/system"
|
||||||
"github.com/ghodss/yaml"
|
"github.com/kubernetes-incubator/cri-o/cmd/kpod/formats"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
@ -29,9 +28,9 @@ var (
|
||||||
Name: "debug, D",
|
Name: "debug, D",
|
||||||
Usage: "display additional debug information",
|
Usage: "display additional debug information",
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.StringFlag{
|
||||||
Name: "json",
|
Name: "format",
|
||||||
Usage: "output as JSON instead of the default YAML",
|
Usage: "Change the output format to JSON or a Go template",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -57,17 +56,18 @@ func infoCmd(c *cli.Context) error {
|
||||||
info[thisName] = thisInfo
|
info[thisName] = thisInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf []byte
|
var out formats.Writer
|
||||||
var err error
|
infoOutputFormat := c.String("format")
|
||||||
if c.Bool("json") {
|
switch infoOutputFormat {
|
||||||
buf, err = json.MarshalIndent(info, "", " ")
|
case formats.JSONString:
|
||||||
} else {
|
out = formats.JSONStruct{Output: info}
|
||||||
buf, err = yaml.Marshal(info)
|
case "":
|
||||||
|
out = formats.YAMLStruct{Output: info}
|
||||||
|
default:
|
||||||
|
out = formats.StdoutTemplate{Output: info, Template: infoOutputFormat}
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
return err
|
formats.Writer(out).Out()
|
||||||
}
|
|
||||||
fmt.Println(string(buf))
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"github.com/kubernetes-incubator/cri-o/cmd/kpod/formats"
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"github.com/kubernetes-incubator/cri-o/libkpod"
|
"github.com/kubernetes-incubator/cri-o/libkpod"
|
||||||
libkpodimage "github.com/kubernetes-incubator/cri-o/libkpod/image"
|
libkpodimage "github.com/kubernetes-incubator/cri-o/libkpod/image"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -13,9 +9,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultFormat = `Container: {{.Container}}
|
|
||||||
ID: {{.ContainerID}}
|
|
||||||
`
|
|
||||||
inspectTypeContainer = "container"
|
inspectTypeContainer = "container"
|
||||||
inspectTypeImage = "image"
|
inspectTypeImage = "image"
|
||||||
inspectAll = "all"
|
inspectAll = "all"
|
||||||
|
@ -30,8 +23,7 @@ var (
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "format, f",
|
Name: "format, f",
|
||||||
Value: defaultFormat,
|
Usage: "Change the output format to a Go template",
|
||||||
Usage: "Format the output using the given go template",
|
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "size",
|
Name: "size",
|
||||||
|
@ -60,10 +52,6 @@ func inspectCmd(c *cli.Context) error {
|
||||||
|
|
||||||
itemType := c.String("type")
|
itemType := c.String("type")
|
||||||
size := c.Bool("size")
|
size := c.Bool("size")
|
||||||
format := defaultFormat
|
|
||||||
if c.String("format") != "" {
|
|
||||||
format = c.String("format")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch itemType {
|
switch itemType {
|
||||||
case inspectTypeContainer:
|
case inspectTypeContainer:
|
||||||
|
@ -73,8 +61,6 @@ func inspectCmd(c *cli.Context) error {
|
||||||
return errors.Errorf("the only recognized types are %q, %q, and %q", inspectTypeContainer, inspectTypeImage, inspectAll)
|
return errors.Errorf("the only recognized types are %q, %q, and %q", inspectTypeContainer, inspectTypeImage, inspectAll)
|
||||||
}
|
}
|
||||||
|
|
||||||
t := template.Must(template.New("format").Parse(format))
|
|
||||||
|
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
config, err := getConfig(c)
|
config, err := getConfig(c)
|
||||||
|
@ -89,6 +75,7 @@ func inspectCmd(c *cli.Context) error {
|
||||||
return errors.Wrapf(err, "could not update list of containers")
|
return errors.Wrapf(err, "could not update list of containers")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outputFormat := c.String("format")
|
||||||
var data interface{}
|
var data interface{}
|
||||||
switch itemType {
|
switch itemType {
|
||||||
case inspectTypeContainer:
|
case inspectTypeContainer:
|
||||||
|
@ -115,18 +102,15 @@ func inspectCmd(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.IsSet("format") {
|
var out formats.Writer
|
||||||
if err = t.Execute(os.Stdout, data); err != nil {
|
if outputFormat != "" && outputFormat != formats.JSONString {
|
||||||
return err
|
//template
|
||||||
}
|
out = formats.StdoutTemplate{Output: data, Template: outputFormat}
|
||||||
fmt.Println()
|
} else {
|
||||||
return nil
|
// default is json output
|
||||||
|
out = formats.JSONStruct{Output: data}
|
||||||
}
|
}
|
||||||
|
|
||||||
d, err := json.MarshalIndent(data, "", " ")
|
formats.Writer(out).Out()
|
||||||
if err != nil {
|
return nil
|
||||||
return errors.Wrapf(err, "error encoding build container as json")
|
|
||||||
}
|
|
||||||
_, err = fmt.Println(string(d))
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
js "encoding/json"
|
js "encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
of "github.com/kubernetes-incubator/cri-o/cmd/kpod/formats"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
@ -28,7 +29,7 @@ var (
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "format",
|
Name: "format",
|
||||||
Usage: "Print mounted containers in specified format",
|
Usage: "Change the output format to Go template",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
mountCommand = cli.Command{
|
mountCommand = cli.Command{
|
||||||
|
@ -55,7 +56,7 @@ func mountCmd(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
args := c.Args()
|
args := c.Args()
|
||||||
json := c.String("format") == "json"
|
json := c.String("format") == of.JSONString
|
||||||
if !formats[c.String("format")] {
|
if !formats[c.String("format")] {
|
||||||
return errors.Errorf("%q is not a supported format", c.String("format"))
|
return errors.Errorf("%q is not a supported format", c.String("format"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,10 +35,10 @@ _kpod_info() {
|
||||||
local boolean_options="
|
local boolean_options="
|
||||||
--help
|
--help
|
||||||
-h
|
-h
|
||||||
--json
|
|
||||||
--debug
|
--debug
|
||||||
"
|
"
|
||||||
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"
|
||||||
|
|
|
@ -13,9 +13,9 @@ Displays changes on a container or image's filesystem. The container or image w
|
||||||
|
|
||||||
## OPTIONS
|
## OPTIONS
|
||||||
|
|
||||||
**--json**
|
**--format**
|
||||||
|
|
||||||
Format output as json
|
Alter the output into a different format. The only valid format for diff is `json`.
|
||||||
|
|
||||||
|
|
||||||
## EXAMPLE
|
## EXAMPLE
|
||||||
|
@ -26,7 +26,7 @@ C /usr/local
|
||||||
C /usr/local/bin
|
C /usr/local/bin
|
||||||
A /usr/local/bin/docker-entrypoint.sh
|
A /usr/local/bin/docker-entrypoint.sh
|
||||||
|
|
||||||
kpod diff --json redis:alpine
|
kpod diff --format json redis:alpine
|
||||||
{
|
{
|
||||||
"changed": [
|
"changed": [
|
||||||
"/usr",
|
"/usr",
|
||||||
|
|
|
@ -47,10 +47,8 @@ Valid placeholders for the Go template are listed below:
|
||||||
Print the numeric IDs only
|
Print the numeric IDs only
|
||||||
|
|
||||||
**--format**
|
**--format**
|
||||||
Pretty-print history of the image using a Go template
|
Alter the output for a format like 'json' or a Go template.
|
||||||
|
|
||||||
**--json**
|
|
||||||
Print the history in JSON form
|
|
||||||
|
|
||||||
## COMMANDS
|
## COMMANDS
|
||||||
|
|
||||||
|
@ -60,6 +58,8 @@ Valid placeholders for the Go template are listed below:
|
||||||
|
|
||||||
**kpod history --format "{{.ID}} {{.Created}}" debian**
|
**kpod history --format "{{.ID}} {{.Created}}" debian**
|
||||||
|
|
||||||
|
**kpod history --format json debian**
|
||||||
|
|
||||||
## history
|
## history
|
||||||
Show the history of an image
|
Show the history of an image
|
||||||
|
|
||||||
|
|
|
@ -21,16 +21,16 @@ Information display here pertain to the host, current storage stats, and build o
|
||||||
|
|
||||||
Show additional information
|
Show additional information
|
||||||
|
|
||||||
**--json**
|
**--format**
|
||||||
|
|
||||||
Output as JSON instead of the default YAML",
|
Change output format to "json" or a Go template.
|
||||||
|
|
||||||
|
|
||||||
## EXAMPLE
|
## EXAMPLE
|
||||||
|
|
||||||
`kpod info`
|
`kpod info`
|
||||||
|
|
||||||
`kpod info --debug --json | jq .host.kernel`
|
`kpod info --debug --format json| jq .host.kernel`
|
||||||
|
|
||||||
## SEE ALSO
|
## SEE ALSO
|
||||||
crio(8), crio.conf(5)
|
crio(8), crio.conf(5)
|
||||||
|
|
|
@ -73,7 +73,7 @@ function teardown() {
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "kpod history with format" {
|
@test "kpod history with Go template format" {
|
||||||
run ${KPOD_BINARY} ${KPOD_OPTIONS} pull $IMAGE
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} pull $IMAGE
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
run ${KPOD_BINARY} ${KPOD_OPTIONS} history --format "{{.ID}} {{.Created}}" $IMAGE
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} history --format "{{.ID}} {{.Created}}" $IMAGE
|
||||||
|
@ -116,7 +116,7 @@ function teardown() {
|
||||||
@test "kpod history json flag" {
|
@test "kpod history json flag" {
|
||||||
run ${KPOD_BINARY} ${KPOD_OPTIONS} pull $IMAGE
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} pull $IMAGE
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
run bash -c "${KPOD_BINARY} ${KPOD_OPTIONS} history --json $IMAGE | python -m json.tool"
|
run bash -c "${KPOD_BINARY} ${KPOD_OPTIONS} history --format json $IMAGE | python -m json.tool"
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
run ${KPOD_BINARY} $KPOD_OPTIONS rmi $IMAGE
|
run ${KPOD_BINARY} $KPOD_OPTIONS rmi $IMAGE
|
||||||
|
|
|
@ -29,8 +29,8 @@ function teardown() {
|
||||||
@test "test diff with json output" {
|
@test "test diff with json output" {
|
||||||
run ${KPOD_BINARY} $KPOD_OPTIONS pull $IMAGE
|
run ${KPOD_BINARY} $KPOD_OPTIONS pull $IMAGE
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
# run bash -c "${KPOD_BINARY} ${KPOD_OPTIONS} diff --json $IMAGE | python -m json.tool"
|
# run bash -c "${KPOD_BINARY} ${KPOD_OPTIONS} diff --format json $IMAGE | python -m json.tool"
|
||||||
run ${KPOD_BINARY} $KPOD_OPTIONS diff --json $IMAGE
|
run ${KPOD_BINARY} $KPOD_OPTIONS diff --format json $IMAGE
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
echo "$output"
|
echo "$output"
|
||||||
run ${KKPOD_BINARY} $KPOD_OPTIONS rmi $IMAGE
|
run ${KKPOD_BINARY} $KPOD_OPTIONS rmi $IMAGE
|
||||||
|
|
Loading…
Reference in a new issue