mirror of
https://github.com/docker/compose.git
synced 2025-07-23 13:45:00 +02:00
Merge pull request #893 from docker/add-view-inspect
Add container inspect view
This commit is contained in:
commit
40e6655c2e
@ -20,10 +20,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Azure/go-autorest/autorest/to"
|
||||||
|
"github.com/compose-spec/compose-go/types"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/docker/compose-cli/api/client"
|
"github.com/docker/compose-cli/api/client"
|
||||||
|
"github.com/docker/compose-cli/api/containers"
|
||||||
"github.com/docker/compose-cli/formatter"
|
"github.com/docker/compose-cli/formatter"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -52,7 +55,9 @@ func runInspect(ctx context.Context, id string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
j, err := formatter.ToStandardJSON(container)
|
view := getInspectView(container)
|
||||||
|
|
||||||
|
j, err := formatter.ToStandardJSON(view)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -60,3 +65,75 @@ func runInspect(ctx context.Context, id string) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContainerInspectView inspect view
|
||||||
|
type ContainerInspectView struct {
|
||||||
|
ID string
|
||||||
|
Status string
|
||||||
|
Image string
|
||||||
|
Command string `json:",omitempty"`
|
||||||
|
HostConfig *containers.HostConfig `json:",omitempty"`
|
||||||
|
Ports []containers.Port `json:",omitempty"`
|
||||||
|
Config *containers.RuntimeConfig `json:",omitempty"`
|
||||||
|
Platform string
|
||||||
|
Healthcheck *containerInspectHealthcheck `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type containerInspectHealthcheck struct {
|
||||||
|
// Test is the command to be run to check the health of the container
|
||||||
|
Test []string `json:",omitempty"`
|
||||||
|
// Interval is the period in between the checks
|
||||||
|
Interval *types.Duration `json:",omitempty"`
|
||||||
|
// Retries is the number of attempts before declaring the container as healthy or unhealthy
|
||||||
|
Retries *int `json:",omitempty"`
|
||||||
|
// StartPeriod is the start delay before starting the checks
|
||||||
|
StartPeriod *types.Duration `json:",omitempty"`
|
||||||
|
// Timeout is the timeout in between checks
|
||||||
|
Timeout *types.Duration `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getInspectView(container containers.Container) ContainerInspectView {
|
||||||
|
var (
|
||||||
|
healthcheck *containerInspectHealthcheck
|
||||||
|
test []string
|
||||||
|
retries *int
|
||||||
|
ports []containers.Port
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(container.Ports) > 0 {
|
||||||
|
ports = container.Ports
|
||||||
|
}
|
||||||
|
if !container.Healthcheck.Disable && len(container.Healthcheck.Test) > 0 {
|
||||||
|
test = container.Healthcheck.Test
|
||||||
|
if container.Healthcheck.Retries != 0 {
|
||||||
|
retries = to.IntPtr(container.Healthcheck.Retries)
|
||||||
|
}
|
||||||
|
getDurationPtr := func(d types.Duration) *types.Duration {
|
||||||
|
if d == types.Duration(0) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &d
|
||||||
|
}
|
||||||
|
|
||||||
|
healthcheck = &containerInspectHealthcheck{
|
||||||
|
Test: test,
|
||||||
|
Retries: retries,
|
||||||
|
Interval: getDurationPtr(container.Healthcheck.Interval),
|
||||||
|
StartPeriod: getDurationPtr(container.Healthcheck.StartPeriod),
|
||||||
|
Timeout: getDurationPtr(container.Healthcheck.Timeout),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ContainerInspectView{
|
||||||
|
ID: container.ID,
|
||||||
|
Status: container.Status,
|
||||||
|
Image: container.Image,
|
||||||
|
Command: container.Command,
|
||||||
|
|
||||||
|
Config: container.Config,
|
||||||
|
HostConfig: container.HostConfig,
|
||||||
|
Ports: ports,
|
||||||
|
Platform: container.Platform,
|
||||||
|
Healthcheck: healthcheck,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
15
cli/cmd/testdata/inspect-out-id.golden
vendored
15
cli/cmd/testdata/inspect-out-id.golden
vendored
@ -2,11 +2,6 @@
|
|||||||
"ID": "id",
|
"ID": "id",
|
||||||
"Status": "",
|
"Status": "",
|
||||||
"Image": "nginx",
|
"Image": "nginx",
|
||||||
"Command": "",
|
|
||||||
"CPUTime": 0,
|
|
||||||
"MemoryUsage": 0,
|
|
||||||
"PidsCurrent": 0,
|
|
||||||
"PidsLimit": 0,
|
|
||||||
"HostConfig": {
|
"HostConfig": {
|
||||||
"RestartPolicy": "none",
|
"RestartPolicy": "none",
|
||||||
"CPUReservation": 0,
|
"CPUReservation": 0,
|
||||||
@ -15,13 +10,5 @@
|
|||||||
"MemoryLimit": 0,
|
"MemoryLimit": 0,
|
||||||
"AutoRemove": false
|
"AutoRemove": false
|
||||||
},
|
},
|
||||||
"Platform": "Linux",
|
"Platform": "Linux"
|
||||||
"Healthcheck": {
|
|
||||||
"Disable": false,
|
|
||||||
"Test": null,
|
|
||||||
"Interval": "0s",
|
|
||||||
"Retries": 0,
|
|
||||||
"StartPeriod": "0s",
|
|
||||||
"Timeout": "0s"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -49,6 +51,7 @@ import (
|
|||||||
"github.com/docker/compose-cli/aci/convert"
|
"github.com/docker/compose-cli/aci/convert"
|
||||||
"github.com/docker/compose-cli/aci/login"
|
"github.com/docker/compose-cli/aci/login"
|
||||||
"github.com/docker/compose-cli/api/containers"
|
"github.com/docker/compose-cli/api/containers"
|
||||||
|
"github.com/docker/compose-cli/cli/cmd"
|
||||||
"github.com/docker/compose-cli/context/store"
|
"github.com/docker/compose-cli/context/store"
|
||||||
"github.com/docker/compose-cli/errdefs"
|
"github.com/docker/compose-cli/errdefs"
|
||||||
. "github.com/docker/compose-cli/tests/framework"
|
. "github.com/docker/compose-cli/tests/framework"
|
||||||
@ -271,7 +274,7 @@ func TestRunVolume(t *testing.T) {
|
|||||||
t.Run("inspect", func(t *testing.T) {
|
t.Run("inspect", func(t *testing.T) {
|
||||||
res := c.RunDockerCmd("inspect", container)
|
res := c.RunDockerCmd("inspect", container)
|
||||||
|
|
||||||
containerInspect, err := ParseContainerInspect(res.Stdout())
|
containerInspect, err := parseContainerInspect(res.Stdout())
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Equal(t, containerInspect.Platform, "Linux")
|
assert.Equal(t, containerInspect.Platform, "Linux")
|
||||||
assert.Equal(t, containerInspect.HostConfig.CPULimit, 1.0)
|
assert.Equal(t, containerInspect.HostConfig.CPULimit, 1.0)
|
||||||
@ -418,7 +421,7 @@ func TestContainerRunAttached(t *testing.T) {
|
|||||||
|
|
||||||
inspectRes := c.RunDockerCmd("inspect", container)
|
inspectRes := c.RunDockerCmd("inspect", container)
|
||||||
|
|
||||||
containerInspect, err := ParseContainerInspect(inspectRes.Stdout())
|
containerInspect, err := parseContainerInspect(inspectRes.Stdout())
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Equal(t, containerInspect.Platform, "Linux")
|
assert.Equal(t, containerInspect.Platform, "Linux")
|
||||||
assert.Equal(t, containerInspect.HostConfig.CPULimit, 0.1)
|
assert.Equal(t, containerInspect.HostConfig.CPULimit, 0.1)
|
||||||
@ -549,10 +552,10 @@ func TestUpSecretsResources(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
res := c.RunDockerCmd("inspect", web1)
|
res := c.RunDockerCmd("inspect", web1)
|
||||||
web1Inspect, err := ParseContainerInspect(res.Stdout())
|
web1Inspect, err := parseContainerInspect(res.Stdout())
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
res = c.RunDockerCmd("inspect", web2)
|
res = c.RunDockerCmd("inspect", web2)
|
||||||
web2Inspect, err := ParseContainerInspect(res.Stdout())
|
web2Inspect, err := parseContainerInspect(res.Stdout())
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
t.Run("read secrets in service 1", func(t *testing.T) {
|
t.Run("read secrets in service 1", func(t *testing.T) {
|
||||||
@ -593,11 +596,11 @@ func TestUpSecretsResources(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("check healthchecks inspect", func(t *testing.T) {
|
t.Run("check healthchecks inspect", func(t *testing.T) {
|
||||||
assert.Equal(t, web1Inspect.Healthcheck.Disable, false)
|
assert.Assert(t, web1Inspect.Healthcheck != nil)
|
||||||
assert.Equal(t, time.Duration(web1Inspect.Healthcheck.Interval), 5*time.Second)
|
assert.Equal(t, time.Duration(*web1Inspect.Healthcheck.Interval), 5*time.Second)
|
||||||
assert.DeepEqual(t, web1Inspect.Healthcheck.Test, []string{"curl", "-f", "http://localhost:80/healthz"})
|
assert.DeepEqual(t, web1Inspect.Healthcheck.Test, []string{"curl", "-f", "http://localhost:80/healthz"})
|
||||||
|
|
||||||
assert.Equal(t, web2Inspect.Healthcheck.Disable, true)
|
assert.Assert(t, web2Inspect.Healthcheck == nil)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("healthcheck restart failed app", func(t *testing.T) {
|
t.Run("healthcheck restart failed app", func(t *testing.T) {
|
||||||
@ -623,7 +626,7 @@ func TestUpSecretsResources(t *testing.T) {
|
|||||||
poll.WaitOn(t, checkLogsReset, poll.WithDelay(5*time.Second), poll.WithTimeout(90*time.Second))
|
poll.WaitOn(t, checkLogsReset, poll.WithDelay(5*time.Second), poll.WithTimeout(90*time.Second))
|
||||||
|
|
||||||
res := c.RunDockerCmd("inspect", web1)
|
res := c.RunDockerCmd("inspect", web1)
|
||||||
web1Inspect, err = ParseContainerInspect(res.Stdout())
|
web1Inspect, err = parseContainerInspect(res.Stdout())
|
||||||
assert.Equal(t, web1Inspect.Status, "Running")
|
assert.Equal(t, web1Inspect.Status, "Running")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -697,7 +700,7 @@ func TestUpUpdate(t *testing.T) {
|
|||||||
|
|
||||||
res = c.RunDockerCmd("inspect", serverContainer)
|
res = c.RunDockerCmd("inspect", serverContainer)
|
||||||
|
|
||||||
containerInspect, err := ParseContainerInspect(res.Stdout())
|
containerInspect, err := parseContainerInspect(res.Stdout())
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Assert(t, is.Len(containerInspect.Ports, 1))
|
assert.Assert(t, is.Len(containerInspect.Ports, 1))
|
||||||
endpoint := fmt.Sprintf("http://%s:%d", containerInspect.Ports[0].HostIP, containerInspect.Ports[0].HostPort)
|
endpoint := fmt.Sprintf("http://%s:%d", containerInspect.Ports[0].HostIP, containerInspect.Ports[0].HostPort)
|
||||||
@ -781,7 +784,7 @@ func TestUpUpdate(t *testing.T) {
|
|||||||
for _, cName := range []string{serverContainer, wordsContainer} {
|
for _, cName := range []string{serverContainer, wordsContainer} {
|
||||||
res = c.RunDockerCmd("inspect", cName)
|
res = c.RunDockerCmd("inspect", cName)
|
||||||
|
|
||||||
containerInspect, err := ParseContainerInspect(res.Stdout())
|
containerInspect, err := parseContainerInspect(res.Stdout())
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Assert(t, is.Len(containerInspect.Ports, 1))
|
assert.Assert(t, is.Len(containerInspect.Ports, 1))
|
||||||
endpoint := fmt.Sprintf("http://%s:%d", containerInspect.Ports[0].HostIP, containerInspect.Ports[0].HostPort)
|
endpoint := fmt.Sprintf("http://%s:%d", containerInspect.Ports[0].HostIP, containerInspect.Ports[0].HostPort)
|
||||||
@ -949,7 +952,7 @@ func getContainerName(stdout string) string {
|
|||||||
func waitForStatus(t *testing.T, c *E2eCLI, containerID string, statuses ...string) {
|
func waitForStatus(t *testing.T, c *E2eCLI, containerID string, statuses ...string) {
|
||||||
checkStopped := func(logt poll.LogT) poll.Result {
|
checkStopped := func(logt poll.LogT) poll.Result {
|
||||||
res := c.RunDockerCmd("inspect", containerID)
|
res := c.RunDockerCmd("inspect", containerID)
|
||||||
containerInspect, err := ParseContainerInspect(res.Stdout())
|
containerInspect, err := parseContainerInspect(res.Stdout())
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
for _, status := range statuses {
|
for _, status := range statuses {
|
||||||
if containerInspect.Status == status {
|
if containerInspect.Status == status {
|
||||||
@ -962,6 +965,15 @@ func waitForStatus(t *testing.T, c *E2eCLI, containerID string, statuses ...stri
|
|||||||
poll.WaitOn(t, checkStopped, poll.WithDelay(5*time.Second), poll.WithTimeout(90*time.Second))
|
poll.WaitOn(t, checkStopped, poll.WithDelay(5*time.Second), poll.WithTimeout(90*time.Second))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseContainerInspect(stdout string) (*cmd.ContainerInspectView, error) {
|
||||||
|
var res cmd.ContainerInspectView
|
||||||
|
rdr := bytes.NewReader([]byte(stdout))
|
||||||
|
if err := json.NewDecoder(rdr).Decode(&res); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &res, nil
|
||||||
|
}
|
||||||
|
|
||||||
func waitWithTimeout(blockingCall func(), timeout time.Duration) error {
|
func waitWithTimeout(blockingCall func(), timeout time.Duration) error {
|
||||||
c := make(chan struct{})
|
c := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
|
15
tests/e2e/testdata/inspect-id.golden
vendored
15
tests/e2e/testdata/inspect-id.golden
vendored
@ -2,11 +2,6 @@
|
|||||||
"ID": "id",
|
"ID": "id",
|
||||||
"Status": "",
|
"Status": "",
|
||||||
"Image": "nginx",
|
"Image": "nginx",
|
||||||
"Command": "",
|
|
||||||
"CPUTime": 0,
|
|
||||||
"MemoryUsage": 0,
|
|
||||||
"PidsCurrent": 0,
|
|
||||||
"PidsLimit": 0,
|
|
||||||
"HostConfig": {
|
"HostConfig": {
|
||||||
"RestartPolicy": "none",
|
"RestartPolicy": "none",
|
||||||
"CPUReservation": 0,
|
"CPUReservation": 0,
|
||||||
@ -15,13 +10,5 @@
|
|||||||
"MemoryLimit": 0,
|
"MemoryLimit": 0,
|
||||||
"AutoRemove": false
|
"AutoRemove": false
|
||||||
},
|
},
|
||||||
"Platform": "Linux",
|
"Platform": "Linux"
|
||||||
"Healthcheck": {
|
|
||||||
"Disable": false,
|
|
||||||
"Test": null,
|
|
||||||
"Interval": "0s",
|
|
||||||
"Retries": 0,
|
|
||||||
"StartPeriod": "0s",
|
|
||||||
"Timeout": "0s"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,6 @@
|
|||||||
package framework
|
package framework
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -35,8 +33,6 @@ import (
|
|||||||
is "gotest.tools/v3/assert/cmp"
|
is "gotest.tools/v3/assert/cmp"
|
||||||
"gotest.tools/v3/icmd"
|
"gotest.tools/v3/icmd"
|
||||||
"gotest.tools/v3/poll"
|
"gotest.tools/v3/poll"
|
||||||
|
|
||||||
"github.com/docker/compose-cli/api/containers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -196,17 +192,6 @@ func GoldenFile(name string) string {
|
|||||||
return name + ".golden"
|
return name + ".golden"
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseContainerInspect parses the output of a `docker inspect` command for a
|
|
||||||
// container
|
|
||||||
func ParseContainerInspect(stdout string) (*containers.Container, error) {
|
|
||||||
var res containers.Container
|
|
||||||
rdr := bytes.NewReader([]byte(stdout))
|
|
||||||
if err := json.NewDecoder(rdr).Decode(&res); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPGetWithRetry performs an HTTP GET on an `endpoint`, using retryDelay also as a request timeout.
|
// HTTPGetWithRetry performs an HTTP GET on an `endpoint`, using retryDelay also as a request timeout.
|
||||||
// In the case of an error or the response status is not the expeted one, it retries the same request,
|
// In the case of an error or the response status is not the expeted one, it retries the same request,
|
||||||
// returning the response body as a string (empty if we could not reach it)
|
// returning the response body as a string (empty if we could not reach it)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user