mirror of
https://github.com/docker/compose.git
synced 2025-07-28 16:14:06 +02:00
add support for attributes exposed by docker ps
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
parent
1054792b47
commit
41682acc77
@ -17,6 +17,9 @@
|
|||||||
package formatter
|
package formatter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/cli/cli/command/formatter"
|
"github.com/docker/cli/cli/command/formatter"
|
||||||
@ -141,6 +144,22 @@ func (c *ContainerContext) Name() string {
|
|||||||
return c.c.Name
|
return c.c.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Names returns a comma-separated string of the container's names, with their
|
||||||
|
// slash (/) prefix stripped. Additional names for the container (related to the
|
||||||
|
// legacy `--link` feature) are omitted.
|
||||||
|
func (c *ContainerContext) Names() string {
|
||||||
|
names := formatter.StripNamePrefix(c.c.Names)
|
||||||
|
if c.trunc {
|
||||||
|
for _, name := range names {
|
||||||
|
if len(strings.Split(name, "/")) == 1 {
|
||||||
|
names = []string{name}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Join(names, ",")
|
||||||
|
}
|
||||||
|
|
||||||
func (c *ContainerContext) Service() string {
|
func (c *ContainerContext) Service() string {
|
||||||
return c.c.Service
|
return c.c.Service
|
||||||
}
|
}
|
||||||
@ -150,7 +169,11 @@ func (c *ContainerContext) Image() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *ContainerContext) Command() string {
|
func (c *ContainerContext) Command() string {
|
||||||
return c.c.Command
|
command := c.c.Command
|
||||||
|
if c.trunc {
|
||||||
|
command = formatter.Ellipsis(command, 20)
|
||||||
|
}
|
||||||
|
return strconv.Quote(command)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ContainerContext) CreatedAt() string {
|
func (c *ContainerContext) CreatedAt() string {
|
||||||
@ -194,3 +217,65 @@ func (c *ContainerContext) Ports() string {
|
|||||||
}
|
}
|
||||||
return formatter.DisplayablePorts(ports)
|
return formatter.DisplayablePorts(ports)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Labels returns a comma-separated string of labels present on the container.
|
||||||
|
func (c *ContainerContext) Labels() string {
|
||||||
|
if c.c.Labels == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var joinLabels []string
|
||||||
|
for k, v := range c.c.Labels {
|
||||||
|
joinLabels = append(joinLabels, fmt.Sprintf("%s=%s", k, v))
|
||||||
|
}
|
||||||
|
return strings.Join(joinLabels, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Label returns the value of the label with the given name or an empty string
|
||||||
|
// if the given label does not exist.
|
||||||
|
func (c *ContainerContext) Label(name string) string {
|
||||||
|
if c.c.Labels == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return c.c.Labels[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mounts returns a comma-separated string of mount names present on the container.
|
||||||
|
// If the trunc option is set, names can be truncated (ellipsized).
|
||||||
|
func (c *ContainerContext) Mounts() string {
|
||||||
|
var mounts []string
|
||||||
|
for _, name := range c.c.Mounts {
|
||||||
|
if c.trunc {
|
||||||
|
name = formatter.Ellipsis(name, 15)
|
||||||
|
}
|
||||||
|
mounts = append(mounts, name)
|
||||||
|
}
|
||||||
|
return strings.Join(mounts, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalVolumes returns the number of volumes using the "local" volume driver.
|
||||||
|
func (c *ContainerContext) LocalVolumes() string {
|
||||||
|
return fmt.Sprintf("%d", c.c.LocalVolumes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Networks returns a comma-separated string of networks that the container is
|
||||||
|
// attached to.
|
||||||
|
func (c *ContainerContext) Networks() string {
|
||||||
|
return strings.Join(c.c.Networks, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size returns the container's size and virtual size (e.g. "2B (virtual 21.5MB)")
|
||||||
|
func (c *ContainerContext) Size() string {
|
||||||
|
if c.FieldsUsed == nil {
|
||||||
|
c.FieldsUsed = map[string]interface{}{}
|
||||||
|
}
|
||||||
|
c.FieldsUsed["Size"] = struct{}{}
|
||||||
|
srw := units.HumanSizeWithPrecision(float64(c.c.SizeRw), 3)
|
||||||
|
sv := units.HumanSizeWithPrecision(float64(c.c.SizeRootFs), 3)
|
||||||
|
|
||||||
|
sf := srw
|
||||||
|
if c.c.SizeRootFs > 0 {
|
||||||
|
sf = fmt.Sprintf("%s (virtual %s)", srw, sv)
|
||||||
|
}
|
||||||
|
return sf
|
||||||
|
}
|
||||||
|
@ -390,18 +390,25 @@ type PortPublisher struct {
|
|||||||
|
|
||||||
// ContainerSummary hold high-level description of a container
|
// ContainerSummary hold high-level description of a container
|
||||||
type ContainerSummary struct {
|
type ContainerSummary struct {
|
||||||
ID string
|
ID string
|
||||||
Name string
|
Name string
|
||||||
Image string
|
Names []string
|
||||||
Command string
|
Image string
|
||||||
Project string
|
Command string
|
||||||
Service string
|
Project string
|
||||||
Created int64
|
Service string
|
||||||
State string
|
Created int64
|
||||||
Status string
|
State string
|
||||||
Health string
|
Status string
|
||||||
ExitCode int
|
Health string
|
||||||
Publishers PortPublishers
|
ExitCode int
|
||||||
|
Publishers PortPublishers
|
||||||
|
Labels map[string]string
|
||||||
|
SizeRw int64 `json:",omitempty"`
|
||||||
|
SizeRootFs int64 `json:",omitempty"`
|
||||||
|
Mounts []string
|
||||||
|
Networks []string
|
||||||
|
LocalVolumes int
|
||||||
}
|
}
|
||||||
|
|
||||||
// PortPublishers is a slice of PortPublisher
|
// PortPublishers is a slice of PortPublisher
|
||||||
|
@ -78,19 +78,48 @@ func (s *composeService) Ps(ctx context.Context, projectName string, options api
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
local int
|
||||||
|
mounts []string
|
||||||
|
)
|
||||||
|
for _, m := range container.Mounts {
|
||||||
|
name := m.Name
|
||||||
|
if name == "" {
|
||||||
|
name = m.Source
|
||||||
|
}
|
||||||
|
if m.Driver == "local" {
|
||||||
|
local++
|
||||||
|
}
|
||||||
|
mounts = append(mounts, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
var networks []string
|
||||||
|
if container.NetworkSettings != nil {
|
||||||
|
for k := range container.NetworkSettings.Networks {
|
||||||
|
networks = append(networks, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
summary[i] = api.ContainerSummary{
|
summary[i] = api.ContainerSummary{
|
||||||
ID: container.ID,
|
ID: container.ID,
|
||||||
Name: getCanonicalContainerName(container),
|
Name: getCanonicalContainerName(container),
|
||||||
Image: container.Image,
|
Names: container.Names,
|
||||||
Project: container.Labels[api.ProjectLabel],
|
Image: container.Image,
|
||||||
Service: container.Labels[api.ServiceLabel],
|
Project: container.Labels[api.ProjectLabel],
|
||||||
Command: container.Command,
|
Service: container.Labels[api.ServiceLabel],
|
||||||
State: container.State,
|
Command: container.Command,
|
||||||
Status: container.Status,
|
State: container.State,
|
||||||
Created: container.Created,
|
Status: container.Status,
|
||||||
Health: health,
|
Created: container.Created,
|
||||||
ExitCode: exitCode,
|
Labels: container.Labels,
|
||||||
Publishers: publishers,
|
SizeRw: container.SizeRw,
|
||||||
|
SizeRootFs: container.SizeRootFs,
|
||||||
|
Mounts: mounts,
|
||||||
|
LocalVolumes: local,
|
||||||
|
Networks: networks,
|
||||||
|
Health: health,
|
||||||
|
ExitCode: exitCode,
|
||||||
|
Publishers: publishers,
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
@ -54,13 +54,34 @@ func TestPs(t *testing.T) {
|
|||||||
containers, err := tested.Ps(ctx, strings.ToLower(testProject), compose.PsOptions{})
|
containers, err := tested.Ps(ctx, strings.ToLower(testProject), compose.PsOptions{})
|
||||||
|
|
||||||
expected := []compose.ContainerSummary{
|
expected := []compose.ContainerSummary{
|
||||||
{ID: "123", Name: "123", Image: "foo", Project: strings.ToLower(testProject), Service: "service1",
|
{ID: "123", Name: "123", Names: []string{"/123"}, Image: "foo", Project: strings.ToLower(testProject), Service: "service1",
|
||||||
State: "running", Health: "healthy", Publishers: nil},
|
State: "running", Health: "healthy", Publishers: nil,
|
||||||
{ID: "456", Name: "456", Image: "foo", Project: strings.ToLower(testProject), Service: "service1",
|
Labels: map[string]string{
|
||||||
|
compose.ProjectLabel: strings.ToLower(testProject),
|
||||||
|
compose.ConfigFilesLabel: "/src/pkg/compose/testdata/compose.yaml",
|
||||||
|
compose.WorkingDirLabel: "/src/pkg/compose/testdata",
|
||||||
|
compose.ServiceLabel: "service1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ID: "456", Name: "456", Names: []string{"/456"}, Image: "foo", Project: strings.ToLower(testProject), Service: "service1",
|
||||||
State: "running", Health: "",
|
State: "running", Health: "",
|
||||||
Publishers: []compose.PortPublisher{{URL: "localhost", TargetPort: 90, PublishedPort: 80}}},
|
Publishers: []compose.PortPublisher{{URL: "localhost", TargetPort: 90, PublishedPort: 80}},
|
||||||
{ID: "789", Name: "789", Image: "foo", Project: strings.ToLower(testProject), Service: "service2",
|
Labels: map[string]string{
|
||||||
State: "exited", Health: "", ExitCode: 130, Publishers: nil},
|
compose.ProjectLabel: strings.ToLower(testProject),
|
||||||
|
compose.ConfigFilesLabel: "/src/pkg/compose/testdata/compose.yaml",
|
||||||
|
compose.WorkingDirLabel: "/src/pkg/compose/testdata",
|
||||||
|
compose.ServiceLabel: "service1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ID: "789", Name: "789", Names: []string{"/789"}, Image: "foo", Project: strings.ToLower(testProject), Service: "service2",
|
||||||
|
State: "exited", Health: "", ExitCode: 130, Publishers: nil,
|
||||||
|
Labels: map[string]string{
|
||||||
|
compose.ProjectLabel: strings.ToLower(testProject),
|
||||||
|
compose.ConfigFilesLabel: "/src/pkg/compose/testdata/compose.yaml",
|
||||||
|
compose.WorkingDirLabel: "/src/pkg/compose/testdata",
|
||||||
|
compose.ServiceLabel: "service2",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.DeepEqual(t, containers, expected)
|
assert.DeepEqual(t, containers, expected)
|
||||||
|
@ -62,10 +62,15 @@ func TestPs(t *testing.T) {
|
|||||||
t.Run("json", func(t *testing.T) {
|
t.Run("json", func(t *testing.T) {
|
||||||
res = c.RunDockerComposeCmd(t, "-f", "./fixtures/ps-test/compose.yaml", "--project-name", projectName, "ps",
|
res = c.RunDockerComposeCmd(t, "-f", "./fixtures/ps-test/compose.yaml", "--project-name", projectName, "ps",
|
||||||
"--format", "json")
|
"--format", "json")
|
||||||
var output []api.ContainerSummary
|
type element struct {
|
||||||
dec := json.NewDecoder(strings.NewReader(res.Stdout()))
|
Name string
|
||||||
|
Publishers api.PortPublishers
|
||||||
|
}
|
||||||
|
var output []element
|
||||||
|
out := res.Stdout()
|
||||||
|
dec := json.NewDecoder(strings.NewReader(out))
|
||||||
for dec.More() {
|
for dec.More() {
|
||||||
var s api.ContainerSummary
|
var s element
|
||||||
require.NoError(t, dec.Decode(&s), "Failed to unmarshal ps JSON output")
|
require.NoError(t, dec.Decode(&s), "Failed to unmarshal ps JSON output")
|
||||||
output = append(output, s)
|
output = append(output, s)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user