зеркало из https://github.com/docker/compose.git
Add json output format to several commands
- docker context ls - docker ps - docker compose ls - docker compose ps - docker secret ls - docker volume ls - docker version Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
This commit is contained in:
Родитель
abd6af6386
Коммит
8961805412
|
@ -33,6 +33,7 @@ type composeOptions struct {
|
|||
WorkingDir string
|
||||
ConfigPaths []string
|
||||
Environment []string
|
||||
Format string
|
||||
}
|
||||
|
||||
func (o *composeOptions) toProjectName() (string, error) {
|
||||
|
|
|
@ -21,10 +21,16 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/docker/compose-cli/api/client"
|
||||
"github.com/docker/compose-cli/api/compose"
|
||||
"github.com/docker/compose-cli/errdefs"
|
||||
"github.com/docker/compose-cli/formatter"
|
||||
)
|
||||
|
||||
func listCommand() *cobra.Command {
|
||||
|
@ -35,10 +41,15 @@ func listCommand() *cobra.Command {
|
|||
return runList(cmd.Context(), opts)
|
||||
},
|
||||
}
|
||||
lsCmd.Flags().StringVarP(&opts.Name, "project-name", "p", "", "Project name")
|
||||
addComposeCommonFlags(lsCmd.Flags(), &opts)
|
||||
return lsCmd
|
||||
}
|
||||
|
||||
func addComposeCommonFlags(f *pflag.FlagSet, opts *composeOptions) {
|
||||
f.StringVarP(&opts.Name, "project-name", "p", "", "Project name")
|
||||
f.StringVar(&opts.Format, "format", "", "Format the output. Values: [pretty | json]. (Default: pretty)")
|
||||
}
|
||||
|
||||
func runList(ctx context.Context, opts composeOptions) error {
|
||||
c, err := client.New(ctx)
|
||||
if err != nil {
|
||||
|
@ -49,10 +60,26 @@ func runList(ctx context.Context, opts composeOptions) error {
|
|||
return err
|
||||
}
|
||||
|
||||
err = printSection(os.Stdout, func(w io.Writer) {
|
||||
for _, stack := range stackList {
|
||||
fmt.Fprintf(w, "%s\t%s\n", stack.Name, stack.Status)
|
||||
return printListFormatted(opts.Format, os.Stdout, stackList)
|
||||
}
|
||||
|
||||
func printListFormatted(format string, out io.Writer, stackList []compose.Stack) error {
|
||||
var err error
|
||||
switch strings.ToLower(format) {
|
||||
case formatter.PRETTY, "":
|
||||
err = formatter.PrintPrettySection(out, func(w io.Writer) {
|
||||
for _, stack := range stackList {
|
||||
fmt.Fprintf(w, "%s\t%s\n", stack.Name, stack.Status)
|
||||
}
|
||||
}, "NAME", "STATUS")
|
||||
case formatter.JSON:
|
||||
outJSON, err := formatter.ToStandardJSON(stackList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}, "NAME", "STATUS")
|
||||
_, _ = fmt.Fprint(out, outJSON)
|
||||
default:
|
||||
err = errors.Wrapf(errdefs.ErrParsingFailed, "format value %q could not be parsed", format)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compose
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
"gotest.tools/golden"
|
||||
|
||||
"github.com/docker/compose-cli/api/compose"
|
||||
"github.com/docker/compose-cli/formatter"
|
||||
)
|
||||
|
||||
func TestPrintComposeList(t *testing.T) {
|
||||
secretList := []compose.Stack{
|
||||
{
|
||||
ID: "123",
|
||||
Name: "myName123",
|
||||
Status: "Running",
|
||||
},
|
||||
}
|
||||
out := &bytes.Buffer{}
|
||||
assert.NilError(t, printListFormatted(formatter.PRETTY, out, secretList))
|
||||
golden.Assert(t, out.String(), "compose-list-out.golden")
|
||||
|
||||
out.Reset()
|
||||
assert.NilError(t, printListFormatted(formatter.JSON, out, secretList))
|
||||
golden.Assert(t, out.String(), "compose-list-out-json.golden")
|
||||
}
|
|
@ -22,11 +22,14 @@ import (
|
|||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose-cli/api/client"
|
||||
"github.com/docker/compose-cli/api/compose"
|
||||
"github.com/docker/compose-cli/errdefs"
|
||||
"github.com/docker/compose-cli/formatter"
|
||||
)
|
||||
|
||||
func psCommand() *cobra.Command {
|
||||
|
@ -37,10 +40,9 @@ func psCommand() *cobra.Command {
|
|||
return runPs(cmd.Context(), opts)
|
||||
},
|
||||
}
|
||||
psCmd.Flags().StringVarP(&opts.Name, "project-name", "p", "", "Project name")
|
||||
psCmd.Flags().StringVar(&opts.WorkingDir, "workdir", "", "Work dir")
|
||||
psCmd.Flags().StringArrayVarP(&opts.ConfigPaths, "file", "f", []string{}, "Compose configuration files")
|
||||
|
||||
addComposeCommonFlags(psCmd.Flags(), &opts)
|
||||
return psCmd
|
||||
}
|
||||
|
||||
|
@ -59,17 +61,26 @@ func runPs(ctx context.Context, opts composeOptions) error {
|
|||
return err
|
||||
}
|
||||
|
||||
err = printSection(os.Stdout, func(w io.Writer) {
|
||||
for _, service := range serviceList {
|
||||
fmt.Fprintf(w, "%s\t%s\t%d/%d\t%s\n", service.ID, service.Name, service.Replicas, service.Desired, strings.Join(service.Ports, ", "))
|
||||
}
|
||||
}, "ID", "NAME", "REPLICAS", "PORTS")
|
||||
return err
|
||||
return printPsFormatted(opts.Format, os.Stdout, serviceList)
|
||||
}
|
||||
|
||||
func printSection(out io.Writer, printer func(io.Writer), headers ...string) error {
|
||||
w := tabwriter.NewWriter(out, 20, 1, 3, ' ', 0)
|
||||
fmt.Fprintln(w, strings.Join(headers, "\t"))
|
||||
printer(w)
|
||||
return w.Flush()
|
||||
func printPsFormatted(format string, out io.Writer, serviceList []compose.ServiceStatus) error {
|
||||
var err error
|
||||
switch strings.ToLower(format) {
|
||||
case formatter.PRETTY, "":
|
||||
err = formatter.PrintPrettySection(out, func(w io.Writer) {
|
||||
for _, service := range serviceList {
|
||||
fmt.Fprintf(w, "%s\t%s\t%d/%d\t%s\n", service.ID, service.Name, service.Replicas, service.Desired, strings.Join(service.Ports, ", "))
|
||||
}
|
||||
}, "ID", "NAME", "REPLICAS", "PORTS")
|
||||
case formatter.JSON:
|
||||
outJSON, err := formatter.ToStandardJSON(serviceList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = fmt.Fprint(out, outJSON)
|
||||
default:
|
||||
err = errors.Wrapf(errdefs.ErrParsingFailed, "format value %q could not be parsed", format)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
[
|
||||
{
|
||||
"ID": "123",
|
||||
"Name": "myName123",
|
||||
"Status": "Running"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,2 @@
|
|||
NAME STATUS
|
||||
myName123 Running
|
|
@ -17,18 +17,19 @@
|
|||
package context
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose-cli/cli/mobycli"
|
||||
apicontext "github.com/docker/compose-cli/context"
|
||||
"github.com/docker/compose-cli/context/store"
|
||||
"github.com/docker/compose-cli/errdefs"
|
||||
"github.com/docker/compose-cli/formatter"
|
||||
)
|
||||
|
||||
|
@ -58,7 +59,8 @@ func listCommand() *cobra.Command {
|
|||
}
|
||||
cmd.Flags().BoolVarP(&opts.quiet, "quiet", "q", false, "Only show context names")
|
||||
cmd.Flags().BoolVar(&opts.json, "json", false, "Format output as JSON")
|
||||
cmd.Flags().StringVar(&opts.format, "format", "", "Format output as JSON")
|
||||
cmd.Flags().StringVar(&opts.format, "format", "", "Format the output. Values: [pretty | json | go template]. (Default: pretty)")
|
||||
_ = cmd.Flags().MarkHidden("json")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
@ -68,7 +70,7 @@ func runList(cmd *cobra.Command, opts lsOpts) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if opts.format != "" {
|
||||
if opts.format != "" && opts.format != formatter.JSON && opts.format != formatter.PRETTY {
|
||||
mobycli.Exec(cmd.Root())
|
||||
return nil
|
||||
}
|
||||
|
@ -93,35 +95,41 @@ func runList(cmd *cobra.Command, opts lsOpts) error {
|
|||
}
|
||||
|
||||
if opts.json {
|
||||
j, err := formatter.ToStandardJSON(contexts)
|
||||
opts.format = formatter.JSON
|
||||
}
|
||||
|
||||
return printContextLsFormatted(opts.format, currentContext, os.Stdout, contexts)
|
||||
}
|
||||
|
||||
func printContextLsFormatted(format string, currContext string, out io.Writer, contexts []*store.DockerContext) error {
|
||||
var err error
|
||||
switch strings.ToLower(format) {
|
||||
case formatter.PRETTY, "":
|
||||
err = formatter.PrintPrettySection(out, func(w io.Writer) {
|
||||
for _, c := range contexts {
|
||||
contextName := c.Name
|
||||
if c.Name == currContext {
|
||||
contextName += " *"
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n",
|
||||
contextName,
|
||||
c.Type(),
|
||||
c.Metadata.Description,
|
||||
getEndpoint("docker", c.Endpoints),
|
||||
getEndpoint("kubernetes", c.Endpoints),
|
||||
c.Metadata.StackOrchestrator)
|
||||
}
|
||||
}, "NAME", "TYPE", "DESCRIPTION", "DOCKER ENDPOINT", "KUBERNETES ENDPOINT", "ORCHESTRATOR")
|
||||
case formatter.JSON:
|
||||
out, err := formatter.ToStandardJSON(contexts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(j)
|
||||
return nil
|
||||
fmt.Println(out)
|
||||
default:
|
||||
err = errors.Wrapf(errdefs.ErrParsingFailed, "format value %q could not be parsed", format)
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
|
||||
fmt.Fprintln(w, "NAME\tTYPE\tDESCRIPTION\tDOCKER ENDPOINT\tKUBERNETES ENDPOINT\tORCHESTRATOR")
|
||||
format := "%s\t%s\t%s\t%s\t%s\t%s\n"
|
||||
|
||||
for _, c := range contexts {
|
||||
contextName := c.Name
|
||||
if c.Name == currentContext {
|
||||
contextName += " *"
|
||||
}
|
||||
|
||||
fmt.Fprintf(w,
|
||||
format,
|
||||
contextName,
|
||||
c.Type(),
|
||||
c.Metadata.Description,
|
||||
getEndpoint("docker", c.Endpoints),
|
||||
getEndpoint("kubernetes", c.Endpoints),
|
||||
c.Metadata.StackOrchestrator)
|
||||
}
|
||||
|
||||
return w.Flush()
|
||||
return err
|
||||
}
|
||||
|
||||
func getEndpoint(name string, meta map[string]interface{}) string {
|
||||
|
|
|
@ -19,30 +19,25 @@ package cmd
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose-cli/api/client"
|
||||
"github.com/docker/compose-cli/api/containers"
|
||||
"github.com/docker/compose-cli/errdefs"
|
||||
formatter2 "github.com/docker/compose-cli/formatter"
|
||||
"github.com/docker/compose-cli/utils/formatter"
|
||||
)
|
||||
|
||||
type psOpts struct {
|
||||
all bool
|
||||
quiet bool
|
||||
json bool
|
||||
}
|
||||
|
||||
func (o psOpts) validate() error {
|
||||
if o.quiet && o.json {
|
||||
return errors.New(`cannot combine "quiet" and "json" options`)
|
||||
}
|
||||
return nil
|
||||
all bool
|
||||
quiet bool
|
||||
json bool
|
||||
format string
|
||||
}
|
||||
|
||||
// PsCommand lists containers
|
||||
|
@ -59,50 +54,69 @@ func PsCommand() *cobra.Command {
|
|||
cmd.Flags().BoolVarP(&opts.quiet, "quiet", "q", false, "Only display IDs")
|
||||
cmd.Flags().BoolVarP(&opts.all, "all", "a", false, "Show all containers (default shows just running)")
|
||||
cmd.Flags().BoolVar(&opts.json, "json", false, "Format output as JSON")
|
||||
cmd.Flags().StringVar(&opts.format, "format", "", "Format the output. Values: [pretty | json | go template]. (Default: pretty)")
|
||||
_ = cmd.Flags().MarkHidden("json")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o psOpts) validate() error {
|
||||
if o.quiet && o.json {
|
||||
return errors.New(`cannot combine "quiet" and "json" options`)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runPs(ctx context.Context, opts psOpts) error {
|
||||
err := opts.validate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c, err := client.New(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "cannot connect to backend")
|
||||
}
|
||||
|
||||
containers, err := c.ContainerService().List(ctx, opts.all)
|
||||
containerList, err := c.ContainerService().List(ctx, opts.all)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetch containers")
|
||||
return errors.Wrap(err, "fetch containerList")
|
||||
}
|
||||
|
||||
if opts.quiet {
|
||||
for _, c := range containers {
|
||||
for _, c := range containerList {
|
||||
fmt.Println(c.ID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if opts.json {
|
||||
j, err := formatter2.ToStandardJSON(containers)
|
||||
opts.format = formatter2.JSON
|
||||
}
|
||||
|
||||
return printPsFormatted(opts.format, os.Stdout, containerList)
|
||||
}
|
||||
|
||||
func printPsFormatted(format string, out io.Writer, containers []containers.Container) error {
|
||||
var err error
|
||||
switch strings.ToLower(format) {
|
||||
case formatter2.PRETTY, "":
|
||||
err = formatter2.PrintPrettySection(out, func(w io.Writer) {
|
||||
for _, c := range containers {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", c.ID, c.Image, c.Command, c.Status,
|
||||
strings.Join(formatter.PortsToStrings(c.Ports, fqdn(c)), ", "))
|
||||
}
|
||||
}, "CONTAINER ID", "IMAGE", "COMMAND", "STATUS", "PORTS")
|
||||
case formatter2.JSON:
|
||||
out, err := formatter2.ToStandardJSON(containers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(j)
|
||||
return nil
|
||||
}
|
||||
fmt.Println(out)
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
|
||||
fmt.Fprintf(w, "CONTAINER ID\tIMAGE\tCOMMAND\tSTATUS\tPORTS\n")
|
||||
format := "%s\t%s\t%s\t%s\t%s\n"
|
||||
for _, container := range containers {
|
||||
fmt.Fprintf(w, format, container.ID, container.Image, container.Command, container.Status, strings.Join(formatter.PortsToStrings(container.Ports, fqdn(container)), ", "))
|
||||
default:
|
||||
err = errors.Wrapf(errdefs.ErrParsingFailed, "format value %q could not be parsed", format)
|
||||
}
|
||||
|
||||
return w.Flush()
|
||||
return err
|
||||
}
|
||||
|
||||
func fqdn(container containers.Container) string {
|
||||
|
|
|
@ -21,12 +21,14 @@ import (
|
|||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose-cli/api/client"
|
||||
"github.com/docker/compose-cli/api/secrets"
|
||||
"github.com/docker/compose-cli/errdefs"
|
||||
"github.com/docker/compose-cli/formatter"
|
||||
)
|
||||
|
||||
type createSecretOptions struct {
|
||||
|
@ -105,7 +107,12 @@ func inspectSecret() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
type listSecretsOpts struct {
|
||||
format string
|
||||
}
|
||||
|
||||
func listSecrets() *cobra.Command {
|
||||
var opts listSecretsOpts
|
||||
cmd := &cobra.Command{
|
||||
Use: "list",
|
||||
Aliases: []string{"ls"},
|
||||
|
@ -119,10 +126,10 @@ func listSecrets() *cobra.Command {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(os.Stdout, list)
|
||||
return nil
|
||||
return printSecretList(opts.format, os.Stdout, list)
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVar(&opts.format, "format", "", "Format the output. Values: [pretty | json]. (Default: pretty)")
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
@ -149,17 +156,23 @@ func deleteSecret() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func printList(out io.Writer, secrets []secrets.Secret) {
|
||||
printSection(out, func(w io.Writer) {
|
||||
for _, secret := range secrets {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\n", secret.ID, secret.Name, secret.Description) // nolint:errcheck
|
||||
func printSecretList(format string, out io.Writer, secrets []secrets.Secret) error {
|
||||
var err error
|
||||
switch strings.ToLower(format) {
|
||||
case formatter.PRETTY, "":
|
||||
err = formatter.PrintPrettySection(out, func(w io.Writer) {
|
||||
for _, secret := range secrets {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\n", secret.ID, secret.Name, secret.Description) // nolint:errcheck
|
||||
}
|
||||
}, "ID", "NAME", "DESCRIPTION")
|
||||
case formatter.JSON:
|
||||
outJSON, err := formatter.ToStandardJSON(secrets)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}, "ID", "NAME", "DESCRIPTION")
|
||||
}
|
||||
|
||||
func printSection(out io.Writer, printer func(io.Writer), headers ...string) {
|
||||
w := tabwriter.NewWriter(out, 20, 1, 3, ' ', 0)
|
||||
fmt.Fprintln(w, strings.Join(headers, "\t")) // nolint:errcheck
|
||||
printer(w)
|
||||
w.Flush() // nolint:errcheck
|
||||
_, _ = fmt.Fprint(out, outJSON)
|
||||
default:
|
||||
err = errors.Wrapf(errdefs.ErrParsingFailed, "format value %q could not be parsed", format)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -20,13 +20,15 @@ import (
|
|||
"bytes"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
"gotest.tools/v3/golden"
|
||||
|
||||
"github.com/docker/compose-cli/api/secrets"
|
||||
"github.com/docker/compose-cli/formatter"
|
||||
)
|
||||
|
||||
func TestPrintList(t *testing.T) {
|
||||
secrets := []secrets.Secret{
|
||||
secretList := []secrets.Secret{
|
||||
{
|
||||
ID: "123",
|
||||
Name: "secret123",
|
||||
|
@ -34,6 +36,10 @@ func TestPrintList(t *testing.T) {
|
|||
},
|
||||
}
|
||||
out := &bytes.Buffer{}
|
||||
printList(out, secrets)
|
||||
assert.NilError(t, printSecretList(formatter.PRETTY, out, secretList))
|
||||
golden.Assert(t, out.String(), "secrets-out.golden")
|
||||
|
||||
out.Reset()
|
||||
assert.NilError(t, printSecretList(formatter.JSON, out, secretList))
|
||||
golden.Assert(t, out.String(), "secrets-out-json.golden")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
[
|
||||
{
|
||||
"ID": "123",
|
||||
"Name": "secret123",
|
||||
"Labels": null,
|
||||
"Description": "secret 1,2,3"
|
||||
}
|
||||
]
|
|
@ -18,43 +18,94 @@ package cmd
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose-cli/cli/cmd/mobyflags"
|
||||
"github.com/docker/compose-cli/cli/mobycli"
|
||||
"github.com/docker/compose-cli/formatter"
|
||||
)
|
||||
|
||||
const formatOpt = "format"
|
||||
|
||||
// VersionCommand command to display version
|
||||
func VersionCommand(version string) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Show the Docker version information",
|
||||
Args: cobra.MaximumNArgs(0),
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return runVersion(cmd, version)
|
||||
Run: func(cmd *cobra.Command, _ []string) {
|
||||
runVersion(cmd, version)
|
||||
},
|
||||
}
|
||||
// define flags for backward compatibility with com.docker.cli
|
||||
flags := cmd.Flags()
|
||||
flags.StringP("format", "f", "", "Format the output using the given Go template")
|
||||
flags.StringP(formatOpt, "f", "", "Format the output using the given Go template")
|
||||
// flags.String(&opts.format, "format", "", "Format the output. Values: [pretty | json | go template]. (Default: pretty)")
|
||||
flags.String("kubeconfig", "", "Kubernetes config file")
|
||||
mobyflags.AddMobyFlagsForRetrocompatibility(flags)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runVersion(cmd *cobra.Command, version string) error {
|
||||
func runVersion(cmd *cobra.Command, version string) {
|
||||
var versionString string
|
||||
format := strings.TrimSpace(cmd.Flag(formatOpt).Value.String())
|
||||
displayedVersion := strings.TrimPrefix(version, "v")
|
||||
versionResult, _ := mobycli.ExecSilent(cmd.Context())
|
||||
// Replace is preferred in this case to keep the order.
|
||||
switch format {
|
||||
case formatter.PRETTY, "":
|
||||
versionString = strings.Replace(getOutFromMoby(cmd, fixedPrettyArgs(os.Args[1:])...),
|
||||
"\n Version:", "\n Cloud integration: "+displayedVersion+"\n Version:", 1)
|
||||
case formatter.JSON, "{{json .}}", "{{json . }}", "{{ json .}}", "{{ json . }}": // Try to catch full JSON formats
|
||||
versionString = strings.Replace(getOutFromMoby(cmd, fixedJSONArgs(os.Args[1:])...),
|
||||
`"Version":`, fmt.Sprintf(`"CloudIntegration":%q,"Version":`, displayedVersion), 1)
|
||||
}
|
||||
fmt.Print(versionString)
|
||||
}
|
||||
|
||||
func getOutFromMoby(cmd *cobra.Command, args ...string) string {
|
||||
versionResult, _ := mobycli.ExecSilent(cmd.Context(), args...)
|
||||
// we don't want to fail on error, there is an error if the engine is not available but it displays client version info
|
||||
// Still, technically the [] byte versionResult could be nil, just let the original command display what it has to display
|
||||
if versionResult == nil {
|
||||
mobycli.Exec(cmd.Root())
|
||||
return nil
|
||||
return ""
|
||||
}
|
||||
var s string = string(versionResult)
|
||||
fmt.Print(strings.Replace(s, "\n Version:", "\n Cloud integration "+displayedVersion+"\n Version:", 1))
|
||||
return nil
|
||||
return string(versionResult)
|
||||
}
|
||||
|
||||
func fixedPrettyArgs(oArgs []string) []string {
|
||||
var args []string
|
||||
for i := 0; i < len(oArgs); i++ {
|
||||
if isFormatOpt(oArgs[i]) &&
|
||||
len(oArgs) > i &&
|
||||
(strings.ToLower(oArgs[i+1]) == formatter.PRETTY || oArgs[i+1] == "") {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
args = append(args, oArgs[i])
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func fixedJSONArgs(oArgs []string) []string {
|
||||
var args []string
|
||||
for i := 0; i < len(oArgs); i++ {
|
||||
if isFormatOpt(oArgs[i]) &&
|
||||
len(oArgs) > i &&
|
||||
strings.ToLower(oArgs[i+1]) == formatter.JSON {
|
||||
args = append(args, oArgs[i], "{{json .}}")
|
||||
i++
|
||||
continue
|
||||
}
|
||||
args = append(args, oArgs[i])
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func isFormatOpt(o string) bool {
|
||||
return o == "--format" || o == "-f"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
type caze struct {
|
||||
Actual []string
|
||||
Expected []string
|
||||
}
|
||||
|
||||
func TestVersionFormat(t *testing.T) {
|
||||
jsonCases := []caze{
|
||||
{
|
||||
Actual: fixedJSONArgs([]string{}),
|
||||
Expected: nil,
|
||||
},
|
||||
{
|
||||
Actual: fixedJSONArgs([]string{
|
||||
"docker",
|
||||
"version",
|
||||
}),
|
||||
Expected: []string{
|
||||
"docker",
|
||||
"version",
|
||||
},
|
||||
},
|
||||
{
|
||||
Actual: fixedJSONArgs([]string{
|
||||
"docker",
|
||||
"version",
|
||||
"--format",
|
||||
"json",
|
||||
}),
|
||||
Expected: []string{
|
||||
"docker",
|
||||
"version",
|
||||
"--format",
|
||||
"{{json .}}",
|
||||
},
|
||||
},
|
||||
{
|
||||
Actual: fixedJSONArgs([]string{
|
||||
"docker",
|
||||
"version",
|
||||
"--format",
|
||||
"jSoN",
|
||||
}),
|
||||
Expected: []string{
|
||||
"docker",
|
||||
"version",
|
||||
"--format",
|
||||
"{{json .}}",
|
||||
},
|
||||
},
|
||||
{
|
||||
Actual: fixedJSONArgs([]string{
|
||||
"docker",
|
||||
"version",
|
||||
"--format",
|
||||
"json",
|
||||
"--kubeconfig",
|
||||
"myKubeConfig",
|
||||
}),
|
||||
Expected: []string{
|
||||
"docker",
|
||||
"version",
|
||||
"--format",
|
||||
"{{json .}}",
|
||||
"--kubeconfig",
|
||||
"myKubeConfig",
|
||||
},
|
||||
},
|
||||
{
|
||||
Actual: fixedJSONArgs([]string{
|
||||
"--format",
|
||||
"json",
|
||||
}),
|
||||
Expected: []string{
|
||||
"--format",
|
||||
"{{json .}}",
|
||||
},
|
||||
},
|
||||
}
|
||||
prettyCases := []caze{
|
||||
{
|
||||
Actual: fixedPrettyArgs([]string{}),
|
||||
Expected: nil,
|
||||
},
|
||||
{
|
||||
Actual: fixedPrettyArgs([]string{
|
||||
"docker",
|
||||
"version",
|
||||
}),
|
||||
Expected: []string{
|
||||
"docker",
|
||||
"version",
|
||||
},
|
||||
},
|
||||
{
|
||||
Actual: fixedPrettyArgs([]string{
|
||||
"docker",
|
||||
"version",
|
||||
"--format",
|
||||
"pretty",
|
||||
}),
|
||||
Expected: []string{
|
||||
"docker",
|
||||
"version",
|
||||
},
|
||||
},
|
||||
{
|
||||
Actual: fixedPrettyArgs([]string{
|
||||
"docker",
|
||||
"version",
|
||||
"--format",
|
||||
"pRettY",
|
||||
}),
|
||||
Expected: []string{
|
||||
"docker",
|
||||
"version",
|
||||
},
|
||||
},
|
||||
{
|
||||
Actual: fixedPrettyArgs([]string{
|
||||
"docker",
|
||||
"version",
|
||||
"--format",
|
||||
"",
|
||||
}),
|
||||
Expected: []string{
|
||||
"docker",
|
||||
"version",
|
||||
},
|
||||
},
|
||||
{
|
||||
Actual: fixedPrettyArgs([]string{
|
||||
"docker",
|
||||
"version",
|
||||
"--format",
|
||||
"pretty",
|
||||
"--kubeconfig",
|
||||
"myKubeConfig",
|
||||
}),
|
||||
Expected: []string{
|
||||
"docker",
|
||||
"version",
|
||||
"--kubeconfig",
|
||||
"myKubeConfig",
|
||||
},
|
||||
},
|
||||
{
|
||||
Actual: fixedPrettyArgs([]string{
|
||||
"--format",
|
||||
"pretty",
|
||||
}),
|
||||
Expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("json", func(t *testing.T) {
|
||||
for _, c := range jsonCases {
|
||||
assert.DeepEqual(t, c.Actual, c.Expected)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("pretty", func(t *testing.T) {
|
||||
for _, c := range prettyCases {
|
||||
assert.DeepEqual(t, c.Actual, c.Expected)
|
||||
}
|
||||
})
|
||||
}
|
|
@ -21,15 +21,22 @@ import (
|
|||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose-cli/api/client"
|
||||
"github.com/docker/compose-cli/api/volumes"
|
||||
"github.com/docker/compose-cli/errdefs"
|
||||
"github.com/docker/compose-cli/formatter"
|
||||
)
|
||||
|
||||
type listVolumeOpts struct {
|
||||
format string
|
||||
}
|
||||
|
||||
func listVolume() *cobra.Command {
|
||||
var opts listVolumeOpts
|
||||
cmd := &cobra.Command{
|
||||
Use: "ls",
|
||||
Short: "list available volumes in context.",
|
||||
|
@ -43,24 +50,30 @@ func listVolume() *cobra.Command {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(os.Stdout, vols)
|
||||
return nil
|
||||
return printList(opts.format, os.Stdout, vols)
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVar(&opts.format, "format", formatter.PRETTY, "Format the output. Values: [pretty | json]. (Default: pretty)")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func printList(out io.Writer, volumes []volumes.Volume) {
|
||||
printSection(out, func(w io.Writer) {
|
||||
for _, vol := range volumes {
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\n", vol.ID, vol.Description)
|
||||
func printList(format string, out io.Writer, volumes []volumes.Volume) error {
|
||||
var err error
|
||||
switch strings.ToLower(format) {
|
||||
case formatter.PRETTY, "":
|
||||
_ = formatter.PrintPrettySection(out, func(w io.Writer) {
|
||||
for _, vol := range volumes {
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\n", vol.ID, vol.Description)
|
||||
}
|
||||
}, "ID", "DESCRIPTION")
|
||||
case formatter.JSON:
|
||||
outJSON, err := formatter.ToStandardJSON(volumes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}, "ID", "DESCRIPTION")
|
||||
}
|
||||
|
||||
func printSection(out io.Writer, printer func(io.Writer), headers ...string) {
|
||||
w := tabwriter.NewWriter(out, 20, 1, 3, ' ', 0)
|
||||
_, _ = fmt.Fprintln(w, strings.Join(headers, "\t"))
|
||||
printer(w)
|
||||
_ = w.Flush()
|
||||
_, _ = fmt.Fprint(out, outJSON)
|
||||
default:
|
||||
err = errors.Wrapf(errdefs.ErrParsingFailed, "format value %q could not be parsed", format)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -20,9 +20,11 @@ import (
|
|||
"bytes"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
"gotest.tools/v3/golden"
|
||||
|
||||
"github.com/docker/compose-cli/api/volumes"
|
||||
"github.com/docker/compose-cli/formatter"
|
||||
)
|
||||
|
||||
func TestPrintList(t *testing.T) {
|
||||
|
@ -33,6 +35,11 @@ func TestPrintList(t *testing.T) {
|
|||
},
|
||||
}
|
||||
out := &bytes.Buffer{}
|
||||
printList(out, secrets)
|
||||
assert.NilError(t, printList(formatter.PRETTY, out, secrets))
|
||||
golden.Assert(t, out.String(), "volumes-out.golden")
|
||||
|
||||
out.Reset()
|
||||
assert.NilError(t, printList(formatter.JSON, out, secrets))
|
||||
golden.Assert(t, out.String(), "volumes-out-json.golden")
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
[
|
||||
{
|
||||
"ID": "volume/123",
|
||||
"Description": "volume 123"
|
||||
}
|
||||
]
|
|
@ -112,7 +112,10 @@ func IsDefaultContextCommand(dockerCommand string) bool {
|
|||
}
|
||||
|
||||
// ExecSilent executes a command and do redirect output to stdOut, return output
|
||||
func ExecSilent(ctx context.Context) ([]byte, error) {
|
||||
cmd := exec.CommandContext(ctx, ComDockerCli, os.Args[1:]...)
|
||||
func ExecSilent(ctx context.Context, args ...string) ([]byte, error) {
|
||||
if len(args) == 0 {
|
||||
args = os.Args[1:]
|
||||
}
|
||||
cmd := exec.CommandContext(ctx, ComDockerCli, args...)
|
||||
return cmd.CombinedOutput()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package formatter
|
||||
|
||||
const (
|
||||
// JSON is the constant for Json formats on list commands
|
||||
JSON = "json"
|
||||
// PRETTY is the constant for default formats on list commands
|
||||
PRETTY = "pretty"
|
||||
)
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
)
|
||||
|
||||
// PrintPrettySection prints a tabbed section on the writer parameter
|
||||
func PrintPrettySection(out io.Writer, printer func(writer io.Writer), headers ...string) error {
|
||||
w := tabwriter.NewWriter(out, 20, 1, 3, ' ', 0)
|
||||
fmt.Fprintln(w, strings.Join(headers, "\t"))
|
||||
printer(w)
|
||||
return w.Flush()
|
||||
}
|
1
go.mod
1
go.mod
|
@ -62,5 +62,6 @@ require (
|
|||
google.golang.org/protobuf v1.25.0
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||
gopkg.in/ini.v1 v1.61.0
|
||||
gotest.tools v2.2.0+incompatible
|
||||
gotest.tools/v3 v3.0.2
|
||||
)
|
||||
|
|
|
@ -75,6 +75,12 @@ func TestContextDefault(t *testing.T) {
|
|||
t.Run("ls", func(t *testing.T) {
|
||||
res := c.RunDockerCmd("context", "ls")
|
||||
golden.Assert(t, res.Stdout(), GoldenFile("ls-out-default"))
|
||||
|
||||
res = c.RunDockerCmd("context", "ls", "--format", "pretty")
|
||||
golden.Assert(t, res.Stdout(), GoldenFile("ls-out-default"))
|
||||
|
||||
res = c.RunDockerCmd("context", "ls", "--format", "json")
|
||||
golden.Assert(t, res.Stdout(), GoldenFile("ls-out-json"))
|
||||
})
|
||||
|
||||
t.Run("inspect", func(t *testing.T) {
|
||||
|
@ -407,6 +413,26 @@ func TestVersion(t *testing.T) {
|
|||
res.Assert(t, icmd.Expected{Out: `"Client":`})
|
||||
})
|
||||
|
||||
t.Run("format cloud integration", func(t *testing.T) {
|
||||
res := c.RunDockerCmd("version", "-f", "pretty")
|
||||
res.Assert(t, icmd.Expected{Out: `Cloud integration:`})
|
||||
res = c.RunDockerCmd("version", "-f", "")
|
||||
res.Assert(t, icmd.Expected{Out: `Cloud integration:`})
|
||||
|
||||
res = c.RunDockerCmd("version", "-f", "json")
|
||||
res.Assert(t, icmd.Expected{Out: `"CloudIntegration":`})
|
||||
res = c.RunDockerCmd("version", "-f", "{{ json . }}")
|
||||
res.Assert(t, icmd.Expected{Out: `"CloudIntegration":`})
|
||||
res = c.RunDockerCmd("version", "--format", "{{json .}}")
|
||||
res.Assert(t, icmd.Expected{Out: `"CloudIntegration":`})
|
||||
res = c.RunDockerCmd("version", "--format", "{{json . }}")
|
||||
res.Assert(t, icmd.Expected{Out: `"CloudIntegration":`})
|
||||
res = c.RunDockerCmd("version", "--format", "{{ json .}}")
|
||||
res.Assert(t, icmd.Expected{Out: `"CloudIntegration":`})
|
||||
res = c.RunDockerCmd("version", "--format", "{{ json . }}")
|
||||
res.Assert(t, icmd.Expected{Out: `"CloudIntegration":`})
|
||||
})
|
||||
|
||||
t.Run("delegate version flag", func(t *testing.T) {
|
||||
c.RunDockerCmd("context", "create", "example", "test-example")
|
||||
c.RunDockerCmd("context", "use", "test-example")
|
||||
|
@ -431,6 +457,12 @@ func TestMockBackend(t *testing.T) {
|
|||
t.Run("ps", func(t *testing.T) {
|
||||
res := c.RunDockerCmd("ps")
|
||||
golden.Assert(t, res.Stdout(), "ps-out-example.golden")
|
||||
|
||||
res = c.RunDockerCmd("ps", "--format", "pretty")
|
||||
golden.Assert(t, res.Stdout(), "ps-out-example.golden")
|
||||
|
||||
res = c.RunDockerCmd("ps", "--format", "json")
|
||||
golden.Assert(t, res.Stdout(), "ps-out-example-json.golden")
|
||||
})
|
||||
|
||||
t.Run("ps quiet", func(t *testing.T) {
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
[
|
||||
{
|
||||
"Name": "default",
|
||||
"Metadata": {
|
||||
"Description": "Current DOCKER_HOST based configuration",
|
||||
"StackOrchestrator": "swarm",
|
||||
"Type": "moby"
|
||||
},
|
||||
"Endpoints": {
|
||||
"docker": {
|
||||
"Host": "unix:///var/run/docker.sock"
|
||||
},
|
||||
"kubernetes": {}
|
||||
}
|
||||
}
|
||||
]
|
|
@ -0,0 +1,30 @@
|
|||
[
|
||||
{
|
||||
"ID": "id",
|
||||
"Status": "",
|
||||
"Image": "nginx",
|
||||
"Command": "",
|
||||
"CPUTime": 0,
|
||||
"CPULimit": 0,
|
||||
"MemoryUsage": 0,
|
||||
"MemoryLimit": 0,
|
||||
"PidsCurrent": 0,
|
||||
"PidsLimit": 0,
|
||||
"Platform": "",
|
||||
"RestartPolicyCondition": ""
|
||||
},
|
||||
{
|
||||
"ID": "1234",
|
||||
"Status": "",
|
||||
"Image": "alpine",
|
||||
"Command": "",
|
||||
"CPUTime": 0,
|
||||
"CPULimit": 0,
|
||||
"MemoryUsage": 0,
|
||||
"MemoryLimit": 0,
|
||||
"PidsCurrent": 0,
|
||||
"PidsLimit": 0,
|
||||
"Platform": "",
|
||||
"RestartPolicyCondition": ""
|
||||
}
|
||||
]
|
Загрузка…
Ссылка в новой задаче