зеркало из https://github.com/microsoft/docker.git
Expose new mount points structs in inspect.
Keep old hashes around for old api version calls. Signed-off-by: David Calavera <david.calavera@gmail.com>
This commit is contained in:
Родитель
48a01a317c
Коммит
1c3cb2d31e
|
@ -1193,8 +1193,8 @@ func (s *Server) getContainersByName(version version.Version, w http.ResponseWri
|
||||||
return fmt.Errorf("Missing parameter")
|
return fmt.Errorf("Missing parameter")
|
||||||
}
|
}
|
||||||
|
|
||||||
if version.LessThan("1.19") {
|
if version.LessThan("1.20") {
|
||||||
containerJSONRaw, err := s.daemon.ContainerInspectRaw(vars["name"])
|
containerJSONRaw, err := s.daemon.ContainerInspectPre120(vars["name"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -225,8 +225,6 @@ type ContainerJSONBase struct {
|
||||||
ExecDriver string
|
ExecDriver string
|
||||||
MountLabel string
|
MountLabel string
|
||||||
ProcessLabel string
|
ProcessLabel string
|
||||||
Volumes map[string]string
|
|
||||||
VolumesRW map[string]bool
|
|
||||||
AppArmorProfile string
|
AppArmorProfile string
|
||||||
ExecIDs []string
|
ExecIDs []string
|
||||||
HostConfig *runconfig.HostConfig
|
HostConfig *runconfig.HostConfig
|
||||||
|
@ -235,13 +233,16 @@ type ContainerJSONBase struct {
|
||||||
|
|
||||||
type ContainerJSON struct {
|
type ContainerJSON struct {
|
||||||
*ContainerJSONBase
|
*ContainerJSONBase
|
||||||
|
Mounts []MountPoint
|
||||||
Config *runconfig.Config
|
Config *runconfig.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// backcompatibility struct along with ContainerConfig
|
// backcompatibility struct along with ContainerConfig
|
||||||
type ContainerJSONRaw struct {
|
type ContainerJSONPre120 struct {
|
||||||
*ContainerJSONBase
|
*ContainerJSONBase
|
||||||
Config *ContainerConfig
|
Volumes map[string]string
|
||||||
|
VolumesRW map[string]bool
|
||||||
|
Config *ContainerConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
type ContainerConfig struct {
|
type ContainerConfig struct {
|
||||||
|
@ -253,3 +254,13 @@ type ContainerConfig struct {
|
||||||
CpuShares int64
|
CpuShares int64
|
||||||
Cpuset string
|
Cpuset string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MountPoint represents a mount point configuration inside the container.
|
||||||
|
type MountPoint struct {
|
||||||
|
Name string `json:",omitempty"`
|
||||||
|
Source string
|
||||||
|
Destination string
|
||||||
|
Driver string `json:",omitempty"`
|
||||||
|
Mode string // this is internally named `Relabel`
|
||||||
|
RW bool
|
||||||
|
}
|
||||||
|
|
|
@ -20,10 +20,22 @@ func (daemon *Daemon) ContainerInspect(name string) (*types.ContainerJSON, error
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &types.ContainerJSON{base, container.Config}, nil
|
mountPoints := make([]types.MountPoint, 0, len(container.MountPoints))
|
||||||
|
for _, m := range container.MountPoints {
|
||||||
|
mountPoints = append(mountPoints, types.MountPoint{
|
||||||
|
Name: m.Name,
|
||||||
|
Source: m.Path(),
|
||||||
|
Destination: m.Destination,
|
||||||
|
Driver: m.Driver,
|
||||||
|
Mode: m.Relabel,
|
||||||
|
RW: m.RW,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.ContainerJSON{base, mountPoints, container.Config}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) ContainerInspectRaw(name string) (*types.ContainerJSONRaw, error) {
|
func (daemon *Daemon) ContainerInspectPre120(name string) (*types.ContainerJSONPre120, error) {
|
||||||
container, err := daemon.Get(name)
|
container, err := daemon.Get(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -37,6 +49,13 @@ func (daemon *Daemon) ContainerInspectRaw(name string) (*types.ContainerJSONRaw,
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
volumes := make(map[string]string)
|
||||||
|
volumesRW := make(map[string]bool)
|
||||||
|
for _, m := range container.MountPoints {
|
||||||
|
volumes[m.Destination] = m.Path()
|
||||||
|
volumesRW[m.Destination] = m.RW
|
||||||
|
}
|
||||||
|
|
||||||
config := &types.ContainerConfig{
|
config := &types.ContainerConfig{
|
||||||
container.Config,
|
container.Config,
|
||||||
container.hostConfig.Memory,
|
container.hostConfig.Memory,
|
||||||
|
@ -45,7 +64,7 @@ func (daemon *Daemon) ContainerInspectRaw(name string) (*types.ContainerJSONRaw,
|
||||||
container.hostConfig.CpusetCpus,
|
container.hostConfig.CpusetCpus,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &types.ContainerJSONRaw{base, config}, nil
|
return &types.ContainerJSONPre120{base, volumes, volumesRW, config}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) getInspectData(container *Container) (*types.ContainerJSONBase, error) {
|
func (daemon *Daemon) getInspectData(container *Container) (*types.ContainerJSONBase, error) {
|
||||||
|
@ -76,14 +95,6 @@ func (daemon *Daemon) getInspectData(container *Container) (*types.ContainerJSON
|
||||||
FinishedAt: container.State.FinishedAt,
|
FinishedAt: container.State.FinishedAt,
|
||||||
}
|
}
|
||||||
|
|
||||||
volumes := make(map[string]string)
|
|
||||||
volumesRW := make(map[string]bool)
|
|
||||||
|
|
||||||
for _, m := range container.MountPoints {
|
|
||||||
volumes[m.Destination] = m.Path()
|
|
||||||
volumesRW[m.Destination] = m.RW
|
|
||||||
}
|
|
||||||
|
|
||||||
contJSONBase := &types.ContainerJSONBase{
|
contJSONBase := &types.ContainerJSONBase{
|
||||||
Id: container.ID,
|
Id: container.ID,
|
||||||
Created: container.Created,
|
Created: container.Created,
|
||||||
|
@ -102,8 +113,6 @@ func (daemon *Daemon) getInspectData(container *Container) (*types.ContainerJSON
|
||||||
ExecDriver: container.ExecDriver,
|
ExecDriver: container.ExecDriver,
|
||||||
MountLabel: container.MountLabel,
|
MountLabel: container.MountLabel,
|
||||||
ProcessLabel: container.ProcessLabel,
|
ProcessLabel: container.ProcessLabel,
|
||||||
Volumes: volumes,
|
|
||||||
VolumesRW: volumesRW,
|
|
||||||
AppArmorProfile: container.AppArmorProfile,
|
AppArmorProfile: container.AppArmorProfile,
|
||||||
ExecIDs: container.GetExecIDs(),
|
ExecIDs: container.GetExecIDs(),
|
||||||
HostConfig: &hostConfig,
|
HostConfig: &hostConfig,
|
||||||
|
|
|
@ -140,9 +140,14 @@ Create a container
|
||||||
"com.example.license": "GPL",
|
"com.example.license": "GPL",
|
||||||
"com.example.version": "1.0"
|
"com.example.version": "1.0"
|
||||||
},
|
},
|
||||||
"Volumes": {
|
"Mounts": [
|
||||||
"/tmp": {}
|
{
|
||||||
},
|
"Source": "/data",
|
||||||
|
"Destination": "/data",
|
||||||
|
"Mode": "ro,Z",
|
||||||
|
"RW": false
|
||||||
|
}
|
||||||
|
],
|
||||||
"WorkingDir": "",
|
"WorkingDir": "",
|
||||||
"NetworkDisabled": false,
|
"NetworkDisabled": false,
|
||||||
"MacAddress": "12:34:56:78:9a:bc",
|
"MacAddress": "12:34:56:78:9a:bc",
|
||||||
|
@ -223,8 +228,7 @@ Json Parameters:
|
||||||
- **Entrypoint** - Set the entry point for the container as a string or an array
|
- **Entrypoint** - Set the entry point for the container as a string or an array
|
||||||
of strings.
|
of strings.
|
||||||
- **Image** - A string specifying the image name to use for the container.
|
- **Image** - A string specifying the image name to use for the container.
|
||||||
- **Volumes** – An object mapping mount point paths (strings) inside the
|
- **Mounts** - An array of mount points in the container.
|
||||||
container to empty objects.
|
|
||||||
- **WorkingDir** - A string specifying the working directory for commands to
|
- **WorkingDir** - A string specifying the working directory for commands to
|
||||||
run in.
|
run in.
|
||||||
- **NetworkDisabled** - Boolean value, when true disables networking for the
|
- **NetworkDisabled** - Boolean value, when true disables networking for the
|
||||||
|
@ -420,8 +424,14 @@ Return low-level information on the container `id`
|
||||||
"Running": false,
|
"Running": false,
|
||||||
"StartedAt": "2015-01-06T15:47:32.072697474Z"
|
"StartedAt": "2015-01-06T15:47:32.072697474Z"
|
||||||
},
|
},
|
||||||
"Volumes": {},
|
"Mounts": [
|
||||||
"VolumesRW": {}
|
{
|
||||||
|
"Source": "/data",
|
||||||
|
"Destination": "/data",
|
||||||
|
"Mode": "ro,Z",
|
||||||
|
"RW": false
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
Status Codes:
|
Status Codes:
|
||||||
|
@ -1694,9 +1704,14 @@ Create a new image from a container's changes
|
||||||
"Cmd": [
|
"Cmd": [
|
||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Volumes": {
|
"Mounts": [
|
||||||
"/tmp": {}
|
{
|
||||||
},
|
"Source": "/data",
|
||||||
|
"Destination": "/data",
|
||||||
|
"Mode": "ro,Z",
|
||||||
|
"RW": false
|
||||||
|
}
|
||||||
|
],
|
||||||
"Labels": {
|
"Labels": {
|
||||||
"key1": "value1",
|
"key1": "value1",
|
||||||
"key2": "value2"
|
"key2": "value2"
|
||||||
|
@ -2082,8 +2097,7 @@ Return low-level information about the `exec` command `id`.
|
||||||
"ProcessLabel" : "",
|
"ProcessLabel" : "",
|
||||||
"AppArmorProfile" : "",
|
"AppArmorProfile" : "",
|
||||||
"RestartCount" : 0,
|
"RestartCount" : 0,
|
||||||
"Volumes" : {},
|
"Mounts" : [],
|
||||||
"VolumesRW" : {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -190,7 +190,7 @@ func (s *DockerSuite) TestContainerApiStartVolumeBinds(c *check.C) {
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
c.Assert(status, check.Equals, http.StatusNoContent)
|
c.Assert(status, check.Equals, http.StatusNoContent)
|
||||||
|
|
||||||
pth, err := inspectFieldMap(name, "Volumes", "/tmp")
|
pth, err := inspectMountSourceField(name, "/tmp")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Fatal(err)
|
c.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,7 @@ func (s *DockerSuite) TestContainerApiStartVolumesFrom(c *check.C) {
|
||||||
|
|
||||||
dockerCmd(c, "run", "-d", "--name", volName, "-v", volPath, "busybox")
|
dockerCmd(c, "run", "-d", "--name", volName, "-v", volPath, "busybox")
|
||||||
|
|
||||||
name := "TestContainerApiStartDupVolumeBinds"
|
name := "TestContainerApiStartVolumesFrom"
|
||||||
config := map[string]interface{}{
|
config := map[string]interface{}{
|
||||||
"Image": "busybox",
|
"Image": "busybox",
|
||||||
"Volumes": map[string]struct{}{volPath: {}},
|
"Volumes": map[string]struct{}{volPath: {}},
|
||||||
|
@ -250,11 +250,11 @@ func (s *DockerSuite) TestContainerApiStartVolumesFrom(c *check.C) {
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
c.Assert(status, check.Equals, http.StatusNoContent)
|
c.Assert(status, check.Equals, http.StatusNoContent)
|
||||||
|
|
||||||
pth, err := inspectFieldMap(name, "Volumes", volPath)
|
pth, err := inspectMountSourceField(name, volPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Fatal(err)
|
c.Fatal(err)
|
||||||
}
|
}
|
||||||
pth2, err := inspectFieldMap(volName, "Volumes", volPath)
|
pth2, err := inspectMountSourceField(volName, volPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Fatal(err)
|
c.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -705,7 +705,7 @@ func (s *DockerSuite) TestBuildApiDockerfileSymlink(c *check.C) {
|
||||||
func (s *DockerSuite) TestPostContainerBindNormalVolume(c *check.C) {
|
func (s *DockerSuite) TestPostContainerBindNormalVolume(c *check.C) {
|
||||||
dockerCmd(c, "create", "-v", "/foo", "--name=one", "busybox")
|
dockerCmd(c, "create", "-v", "/foo", "--name=one", "busybox")
|
||||||
|
|
||||||
fooDir, err := inspectFieldMap("one", "Volumes", "/foo")
|
fooDir, err := inspectMountSourceField("one", "/foo")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Fatal(err)
|
c.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -717,7 +717,7 @@ func (s *DockerSuite) TestPostContainerBindNormalVolume(c *check.C) {
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
c.Assert(status, check.Equals, http.StatusNoContent)
|
c.Assert(status, check.Equals, http.StatusNoContent)
|
||||||
|
|
||||||
fooDir2, err := inspectFieldMap("two", "Volumes", "/foo")
|
fooDir2, err := inspectMountSourceField("two", "/foo")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Fatal(err)
|
c.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -1467,17 +1467,15 @@ func (s *DockerSuite) TestContainerApiDeleteRemoveVolume(c *check.C) {
|
||||||
id := strings.TrimSpace(out)
|
id := strings.TrimSpace(out)
|
||||||
c.Assert(waitRun(id), check.IsNil)
|
c.Assert(waitRun(id), check.IsNil)
|
||||||
|
|
||||||
vol, err := inspectFieldMap(id, "Volumes", "/testvolume")
|
source, err := inspectMountSourceField(id, "/testvolume")
|
||||||
c.Assert(err, check.IsNil)
|
_, err = os.Stat(source)
|
||||||
|
|
||||||
_, err = os.Stat(vol)
|
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
status, _, err := sockRequest("DELETE", "/containers/"+id+"?v=1&force=1", nil)
|
status, _, err := sockRequest("DELETE", "/containers/"+id+"?v=1&force=1", nil)
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
c.Assert(status, check.Equals, http.StatusNoContent)
|
c.Assert(status, check.Equals, http.StatusNoContent)
|
||||||
|
|
||||||
if _, err := os.Stat(vol); !os.IsNotExist(err) {
|
if _, err := os.Stat(source); !os.IsNotExist(err) {
|
||||||
c.Fatalf("expected to get ErrNotExist error, got %v", err)
|
c.Fatalf("expected to get ErrNotExist error, got %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -12,28 +13,38 @@ func (s *DockerSuite) TestInspectApiContainerResponse(c *check.C) {
|
||||||
out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
|
out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
|
||||||
|
|
||||||
cleanedContainerID := strings.TrimSpace(out)
|
cleanedContainerID := strings.TrimSpace(out)
|
||||||
|
keysBase := []string{"Id", "State", "Created", "Path", "Args", "Config", "Image", "NetworkSettings",
|
||||||
|
"ResolvConfPath", "HostnamePath", "HostsPath", "LogPath", "Name", "Driver", "ExecDriver", "MountLabel", "ProcessLabel", "GraphDriver"}
|
||||||
|
|
||||||
endpoint := "/containers/" + cleanedContainerID + "/json"
|
cases := []struct {
|
||||||
status, body, err := sockRequest("GET", endpoint, nil)
|
version string
|
||||||
c.Assert(status, check.Equals, http.StatusOK)
|
keys []string
|
||||||
c.Assert(err, check.IsNil)
|
}{
|
||||||
|
{"1.20", append(keysBase, "Mounts")},
|
||||||
var inspectJSON map[string]interface{}
|
{"1.19", append(keysBase, "Volumes", "VolumesRW")},
|
||||||
if err = json.Unmarshal(body, &inspectJSON); err != nil {
|
|
||||||
c.Fatalf("unable to unmarshal body for latest version: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
keys := []string{"State", "Created", "Path", "Args", "Config", "Image", "NetworkSettings", "ResolvConfPath", "HostnamePath", "HostsPath", "LogPath", "Name", "Driver", "ExecDriver", "MountLabel", "ProcessLabel", "Volumes", "VolumesRW", "GraphDriver"}
|
for _, cs := range cases {
|
||||||
|
endpoint := fmt.Sprintf("/v%s/containers/%s/json", cs.version, cleanedContainerID)
|
||||||
|
|
||||||
keys = append(keys, "Id")
|
status, body, err := sockRequest("GET", endpoint, nil)
|
||||||
|
c.Assert(status, check.Equals, http.StatusOK)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
for _, key := range keys {
|
var inspectJSON map[string]interface{}
|
||||||
if _, ok := inspectJSON[key]; !ok {
|
if err = json.Unmarshal(body, &inspectJSON); err != nil {
|
||||||
c.Fatalf("%s does not exist in response for latest version", key)
|
c.Fatalf("unable to unmarshal body for version %s: %v", cs.version, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range cs.keys {
|
||||||
|
if _, ok := inspectJSON[key]; !ok {
|
||||||
|
c.Fatalf("%s does not exist in response for version %s", key, cs.version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Issue #6830: type not properly converted to JSON/back
|
||||||
|
if _, ok := inspectJSON["Path"].(bool); ok {
|
||||||
|
c.Fatalf("Path of `true` should not be converted to boolean `true` via JSON marshalling")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Issue #6830: type not properly converted to JSON/back
|
|
||||||
if _, ok := inspectJSON["Path"].(bool); ok {
|
|
||||||
c.Fatalf("Path of `true` should not be converted to boolean `true` via JSON marshalling")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,7 +184,7 @@ func (s *DockerSuite) TestCreateVolumesCreated(c *check.C) {
|
||||||
name := "test_create_volume"
|
name := "test_create_volume"
|
||||||
dockerCmd(c, "create", "--name", name, "-v", "/foo", "busybox")
|
dockerCmd(c, "create", "--name", name, "-v", "/foo", "busybox")
|
||||||
|
|
||||||
dir, err := inspectFieldMap(name, "Volumes", "/foo")
|
dir, err := inspectMountSourceField(name, "/foo")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Fatalf("Error getting volume host path: %q", err)
|
c.Fatalf("Error getting volume host path: %q", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,23 +67,23 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithVolumesRefs(c *check.C) {
|
||||||
if out, err := s.d.Cmd("run", "-d", "--name", "volrestarttest1", "-v", "/foo", "busybox"); err != nil {
|
if out, err := s.d.Cmd("run", "-d", "--name", "volrestarttest1", "-v", "/foo", "busybox"); err != nil {
|
||||||
c.Fatal(err, out)
|
c.Fatal(err, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.d.Restart(); err != nil {
|
if err := s.d.Restart(); err != nil {
|
||||||
c.Fatal(err)
|
c.Fatal(err)
|
||||||
}
|
}
|
||||||
if _, err := s.d.Cmd("run", "-d", "--volumes-from", "volrestarttest1", "--name", "volrestarttest2", "busybox", "top"); err != nil {
|
if _, err := s.d.Cmd("run", "-d", "--volumes-from", "volrestarttest1", "--name", "volrestarttest2", "busybox", "top"); err != nil {
|
||||||
c.Fatal(err)
|
c.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if out, err := s.d.Cmd("rm", "-fv", "volrestarttest2"); err != nil {
|
if out, err := s.d.Cmd("rm", "-fv", "volrestarttest2"); err != nil {
|
||||||
c.Fatal(err, out)
|
c.Fatal(err, out)
|
||||||
}
|
}
|
||||||
v, err := s.d.Cmd("inspect", "--format", "{{ json .Volumes }}", "volrestarttest1")
|
|
||||||
if err != nil {
|
out, err := s.d.Cmd("inspect", "-f", "{{json .Mounts}}", "volrestarttest1")
|
||||||
c.Fatal(err)
|
c.Assert(err, check.IsNil)
|
||||||
}
|
|
||||||
volumes := make(map[string]string)
|
if _, err := inspectMountPointJSON(out, "/foo"); err != nil {
|
||||||
json.Unmarshal([]byte(v), &volumes)
|
c.Fatalf("Expected volume to exist: /foo, error: %v\n", err)
|
||||||
if _, err := os.Stat(volumes["/foo"]); err != nil {
|
|
||||||
c.Fatalf("Expected volume to exist: %s - %s", volumes["/foo"], err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
// +build experimental
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/go-check/check"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *DockerSuite) TestInspectNamedMountPoint(c *check.C) {
|
||||||
|
dockerCmd(c, "run", "-d", "--name", "test", "-v", "data:/data", "busybox", "cat")
|
||||||
|
|
||||||
|
vol, err := inspectFieldJSON("test", "Mounts")
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
|
var mp []types.MountPoint
|
||||||
|
err = unmarshalJSON([]byte(vol), &mp)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
|
if len(mp) != 1 {
|
||||||
|
c.Fatalf("Expected 1 mount point, was %v\n", len(mp))
|
||||||
|
}
|
||||||
|
|
||||||
|
m := mp[0]
|
||||||
|
if m.Name != "data" {
|
||||||
|
c.Fatalf("Expected name data, was %s\n", m.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Driver != "local" {
|
||||||
|
c.Fatalf("Expected driver local, was %s\n", m.Driver)
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Source == "" {
|
||||||
|
c.Fatalf("Expected source to not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.RW != true {
|
||||||
|
c.Fatalf("Expected rw to be true")
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Destination != "/data" {
|
||||||
|
c.Fatalf("Expected destination /data, was %s\n", m.Destination)
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/go-check/check"
|
"github.com/go-check/check"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,7 +19,6 @@ func (s *DockerSuite) TestInspectImage(c *check.C) {
|
||||||
if id != imageTestID {
|
if id != imageTestID {
|
||||||
c.Fatalf("Expected id: %s for image: %s but received id: %s", imageTestID, imageTest, id)
|
c.Fatalf("Expected id: %s for image: %s but received id: %s", imageTestID, imageTest, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerSuite) TestInspectInt64(c *check.C) {
|
func (s *DockerSuite) TestInspectInt64(c *check.C) {
|
||||||
|
@ -265,3 +265,44 @@ func (s *DockerSuite) TestInspectContainerGraphDriver(c *check.C) {
|
||||||
c.Fatalf("failed to inspect DeviceSize of the image: %s, %v", deviceSize, err)
|
c.Fatalf("failed to inspect DeviceSize of the image: %s, %v", deviceSize, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DockerSuite) TestInspectBindMountPoint(c *check.C) {
|
||||||
|
dockerCmd(c, "run", "-d", "--name", "test", "-v", "/data:/data:ro,z", "busybox", "cat")
|
||||||
|
|
||||||
|
vol, err := inspectFieldJSON("test", "Mounts")
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
|
var mp []types.MountPoint
|
||||||
|
err = unmarshalJSON([]byte(vol), &mp)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
|
if len(mp) != 1 {
|
||||||
|
c.Fatalf("Expected 1 mount point, was %v\n", len(mp))
|
||||||
|
}
|
||||||
|
|
||||||
|
m := mp[0]
|
||||||
|
|
||||||
|
if m.Name != "" {
|
||||||
|
c.Fatal("Expected name to be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Driver != "" {
|
||||||
|
c.Fatal("Expected driver to be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Source != "/data" {
|
||||||
|
c.Fatalf("Expected source /data, was %s\n", m.Source)
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Destination != "/data" {
|
||||||
|
c.Fatalf("Expected destination /data, was %s\n", m.Destination)
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Mode != "ro,z" {
|
||||||
|
c.Fatalf("Expected mode `ro,z`, was %s\n", m.Mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.RW != false {
|
||||||
|
c.Fatalf("Expected rw to be false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -54,27 +54,27 @@ func (s *DockerSuite) TestRestartWithVolumes(c *check.C) {
|
||||||
out, _ := dockerCmd(c, "run", "-d", "-v", "/test", "busybox", "top")
|
out, _ := dockerCmd(c, "run", "-d", "-v", "/test", "busybox", "top")
|
||||||
|
|
||||||
cleanedContainerID := strings.TrimSpace(out)
|
cleanedContainerID := strings.TrimSpace(out)
|
||||||
out, _ = dockerCmd(c, "inspect", "--format", "{{ len .Volumes }}", cleanedContainerID)
|
out, _ = dockerCmd(c, "inspect", "--format", "{{ len .Mounts }}", cleanedContainerID)
|
||||||
|
|
||||||
if out = strings.Trim(out, " \n\r"); out != "1" {
|
if out = strings.Trim(out, " \n\r"); out != "1" {
|
||||||
c.Errorf("expect 1 volume received %s", out)
|
c.Errorf("expect 1 volume received %s", out)
|
||||||
}
|
}
|
||||||
|
|
||||||
volumes, err := inspectField(cleanedContainerID, "Volumes")
|
source, err := inspectMountSourceField(cleanedContainerID, "/test")
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
dockerCmd(c, "restart", cleanedContainerID)
|
dockerCmd(c, "restart", cleanedContainerID)
|
||||||
|
|
||||||
out, _ = dockerCmd(c, "inspect", "--format", "{{ len .Volumes }}", cleanedContainerID)
|
out, _ = dockerCmd(c, "inspect", "--format", "{{ len .Mounts }}", cleanedContainerID)
|
||||||
if out = strings.Trim(out, " \n\r"); out != "1" {
|
if out = strings.Trim(out, " \n\r"); out != "1" {
|
||||||
c.Errorf("expect 1 volume after restart received %s", out)
|
c.Errorf("expect 1 volume after restart received %s", out)
|
||||||
}
|
}
|
||||||
|
|
||||||
volumesAfterRestart, err := inspectField(cleanedContainerID, "Volumes")
|
sourceAfterRestart, err := inspectMountSourceField(cleanedContainerID, "/test")
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
if volumes != volumesAfterRestart {
|
if source != sourceAfterRestart {
|
||||||
c.Errorf("expected volume path: %s Actual path: %s", volumes, volumesAfterRestart)
|
c.Errorf("expected volume path: %s Actual path: %s", source, sourceAfterRestart)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2302,24 +2302,23 @@ func (s *DockerSuite) TestRunVolumesCleanPaths(c *check.C) {
|
||||||
c.Fatal(err, out)
|
c.Fatal(err, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err := inspectFieldMap("dark_helmet", "Volumes", "/foo/")
|
out, err := inspectMountSourceField("dark_helmet", "/foo/")
|
||||||
c.Assert(err, check.IsNil)
|
if err != mountNotFound {
|
||||||
if out != "" {
|
|
||||||
c.Fatalf("Found unexpected volume entry for '/foo/' in volumes\n%q", out)
|
c.Fatalf("Found unexpected volume entry for '/foo/' in volumes\n%q", out)
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err = inspectFieldMap("dark_helmet", "Volumes", "/foo")
|
out, err = inspectMountSourceField("dark_helmet", "/foo")
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
if !strings.Contains(out, volumesConfigPath) {
|
if !strings.Contains(out, volumesConfigPath) {
|
||||||
c.Fatalf("Volume was not defined for /foo\n%q", out)
|
c.Fatalf("Volume was not defined for /foo\n%q", out)
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err = inspectFieldMap("dark_helmet", "Volumes", "/bar/")
|
out, err = inspectMountSourceField("dark_helmet", "/bar/")
|
||||||
c.Assert(err, check.IsNil)
|
if err != mountNotFound {
|
||||||
if out != "" {
|
|
||||||
c.Fatalf("Found unexpected volume entry for '/bar/' in volumes\n%q", out)
|
c.Fatalf("Found unexpected volume entry for '/bar/' in volumes\n%q", out)
|
||||||
}
|
}
|
||||||
out, err = inspectFieldMap("dark_helmet", "Volumes", "/bar")
|
|
||||||
|
out, err = inspectMountSourceField("dark_helmet", "/bar")
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
if !strings.Contains(out, volumesConfigPath) {
|
if !strings.Contains(out, volumesConfigPath) {
|
||||||
c.Fatalf("Volume was not defined for /bar\n%q", out)
|
c.Fatalf("Volume was not defined for /bar\n%q", out)
|
||||||
|
@ -3107,14 +3106,15 @@ func (s *DockerSuite) TestVolumeFromMixedRWOptions(c *check.C) {
|
||||||
dockerCmd(c, "run", "--volumes-from", "parent:ro", "--name", "test-volumes-1", "busybox", "true")
|
dockerCmd(c, "run", "--volumes-from", "parent:ro", "--name", "test-volumes-1", "busybox", "true")
|
||||||
dockerCmd(c, "run", "--volumes-from", "parent:rw", "--name", "test-volumes-2", "busybox", "true")
|
dockerCmd(c, "run", "--volumes-from", "parent:rw", "--name", "test-volumes-2", "busybox", "true")
|
||||||
|
|
||||||
testRO, err := inspectFieldMap("test-volumes-1", ".VolumesRW", "/test")
|
mRO, err := inspectMountPoint("test-volumes-1", "/test")
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
if testRO != "false" {
|
if mRO.RW {
|
||||||
c.Fatalf("Expected RO volume was RW")
|
c.Fatalf("Expected RO volume was RW")
|
||||||
}
|
}
|
||||||
testRW, err := inspectFieldMap("test-volumes-2", ".VolumesRW", "/test")
|
|
||||||
|
mRW, err := inspectMountPoint("test-volumes-2", "/test")
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
if testRW != "true" {
|
if !mRW.RW {
|
||||||
c.Fatalf("Expected RW volume was RO")
|
c.Fatalf("Expected RW volume was RO")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/opts"
|
"github.com/docker/docker/opts"
|
||||||
"github.com/docker/docker/pkg/ioutils"
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
"github.com/docker/docker/pkg/stringutils"
|
"github.com/docker/docker/pkg/stringutils"
|
||||||
|
@ -874,6 +875,46 @@ func inspectFieldMap(name, path, field string) (string, error) {
|
||||||
return inspectFilter(name, fmt.Sprintf("index .%s %q", path, field))
|
return inspectFilter(name, fmt.Sprintf("index .%s %q", path, field))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func inspectMountSourceField(name, destination string) (string, error) {
|
||||||
|
m, err := inspectMountPoint(name, destination)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return m.Source, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func inspectMountPoint(name, destination string) (types.MountPoint, error) {
|
||||||
|
out, err := inspectFieldJSON(name, "Mounts")
|
||||||
|
if err != nil {
|
||||||
|
return types.MountPoint{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return inspectMountPointJSON(out, destination)
|
||||||
|
}
|
||||||
|
|
||||||
|
var mountNotFound = errors.New("mount point not found")
|
||||||
|
|
||||||
|
func inspectMountPointJSON(j, destination string) (types.MountPoint, error) {
|
||||||
|
var mp []types.MountPoint
|
||||||
|
if err := unmarshalJSON([]byte(j), &mp); err != nil {
|
||||||
|
return types.MountPoint{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var m *types.MountPoint
|
||||||
|
for _, c := range mp {
|
||||||
|
if c.Destination == destination {
|
||||||
|
m = &c
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if m == nil {
|
||||||
|
return types.MountPoint{}, mountNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return *m, nil
|
||||||
|
}
|
||||||
|
|
||||||
func getIDByName(name string) (string, error) {
|
func getIDByName(name string) (string, error) {
|
||||||
return inspectField(name, "Id")
|
return inspectField(name, "Id")
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,8 +95,14 @@ To get information on a container use its ID or instance name:
|
||||||
"ExecDriver": "native-0.2",
|
"ExecDriver": "native-0.2",
|
||||||
"MountLabel": "",
|
"MountLabel": "",
|
||||||
"ProcessLabel": "",
|
"ProcessLabel": "",
|
||||||
"Volumes": {},
|
"Mounts": [
|
||||||
"VolumesRW": {},
|
{
|
||||||
|
"Source": "/data",
|
||||||
|
"Destination": "/data",
|
||||||
|
"Mode": "ro,Z",
|
||||||
|
"RW": false
|
||||||
|
}
|
||||||
|
],
|
||||||
"AppArmorProfile": "",
|
"AppArmorProfile": "",
|
||||||
"ExecIDs": null,
|
"ExecIDs": null,
|
||||||
"HostConfig": {
|
"HostConfig": {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче