From 1c3cb2d31ea722e2c174bf78eda62fec6949fb8b Mon Sep 17 00:00:00 2001 From: David Calavera Date: Wed, 3 Jun 2015 12:21:38 -0700 Subject: [PATCH] Expose new mount points structs in inspect. Keep old hashes around for old api version calls. Signed-off-by: David Calavera --- api/server/server.go | 4 +- api/types/types.go | 19 ++++++-- daemon/inspect.go | 35 +++++++++------ docs/reference/api/docker_remote_api_v1.20.md | 38 +++++++++++----- integration-cli/docker_api_containers_test.go | 20 ++++----- integration-cli/docker_api_inspect_test.go | 45 ++++++++++++------- integration-cli/docker_cli_create_test.go | 2 +- integration-cli/docker_cli_daemon_test.go | 16 +++---- .../docker_cli_inspect_experimental_test.go | 44 ++++++++++++++++++ integration-cli/docker_cli_inspect_test.go | 43 +++++++++++++++++- integration-cli/docker_cli_restart_test.go | 12 ++--- integration-cli/docker_cli_run_test.go | 24 +++++----- integration-cli/docker_utils.go | 41 +++++++++++++++++ man/docker-inspect.1.md | 10 ++++- 14 files changed, 264 insertions(+), 89 deletions(-) create mode 100644 integration-cli/docker_cli_inspect_experimental_test.go diff --git a/api/server/server.go b/api/server/server.go index 0c165d024e..6a2e9ee8f7 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -1193,8 +1193,8 @@ func (s *Server) getContainersByName(version version.Version, w http.ResponseWri return fmt.Errorf("Missing parameter") } - if version.LessThan("1.19") { - containerJSONRaw, err := s.daemon.ContainerInspectRaw(vars["name"]) + if version.LessThan("1.20") { + containerJSONRaw, err := s.daemon.ContainerInspectPre120(vars["name"]) if err != nil { return err } diff --git a/api/types/types.go b/api/types/types.go index a27755f69f..9eb28dee7a 100644 --- a/api/types/types.go +++ b/api/types/types.go @@ -225,8 +225,6 @@ type ContainerJSONBase struct { ExecDriver string MountLabel string ProcessLabel string - Volumes map[string]string - VolumesRW map[string]bool AppArmorProfile string ExecIDs []string HostConfig *runconfig.HostConfig @@ -235,13 +233,16 @@ type ContainerJSONBase struct { type ContainerJSON struct { *ContainerJSONBase + Mounts []MountPoint Config *runconfig.Config } // backcompatibility struct along with ContainerConfig -type ContainerJSONRaw struct { +type ContainerJSONPre120 struct { *ContainerJSONBase - Config *ContainerConfig + Volumes map[string]string + VolumesRW map[string]bool + Config *ContainerConfig } type ContainerConfig struct { @@ -253,3 +254,13 @@ type ContainerConfig struct { CpuShares int64 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 +} diff --git a/daemon/inspect.go b/daemon/inspect.go index 7e3edaef98..1fb73c43ae 100644 --- a/daemon/inspect.go +++ b/daemon/inspect.go @@ -20,10 +20,22 @@ func (daemon *Daemon) ContainerInspect(name string) (*types.ContainerJSON, error 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) if err != nil { return nil, err @@ -37,6 +49,13 @@ func (daemon *Daemon) ContainerInspectRaw(name string) (*types.ContainerJSONRaw, 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{ container.Config, container.hostConfig.Memory, @@ -45,7 +64,7 @@ func (daemon *Daemon) ContainerInspectRaw(name string) (*types.ContainerJSONRaw, 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) { @@ -76,14 +95,6 @@ func (daemon *Daemon) getInspectData(container *Container) (*types.ContainerJSON 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{ Id: container.ID, Created: container.Created, @@ -102,8 +113,6 @@ func (daemon *Daemon) getInspectData(container *Container) (*types.ContainerJSON ExecDriver: container.ExecDriver, MountLabel: container.MountLabel, ProcessLabel: container.ProcessLabel, - Volumes: volumes, - VolumesRW: volumesRW, AppArmorProfile: container.AppArmorProfile, ExecIDs: container.GetExecIDs(), HostConfig: &hostConfig, diff --git a/docs/reference/api/docker_remote_api_v1.20.md b/docs/reference/api/docker_remote_api_v1.20.md index b29649a0e9..1058d35b97 100644 --- a/docs/reference/api/docker_remote_api_v1.20.md +++ b/docs/reference/api/docker_remote_api_v1.20.md @@ -140,9 +140,14 @@ Create a container "com.example.license": "GPL", "com.example.version": "1.0" }, - "Volumes": { - "/tmp": {} - }, + "Mounts": [ + { + "Source": "/data", + "Destination": "/data", + "Mode": "ro,Z", + "RW": false + } + ], "WorkingDir": "", "NetworkDisabled": false, "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 of strings. - **Image** - A string specifying the image name to use for the container. -- **Volumes** – An object mapping mount point paths (strings) inside the - container to empty objects. +- **Mounts** - An array of mount points in the container. - **WorkingDir** - A string specifying the working directory for commands to run in. - **NetworkDisabled** - Boolean value, when true disables networking for the @@ -420,8 +424,14 @@ Return low-level information on the container `id` "Running": false, "StartedAt": "2015-01-06T15:47:32.072697474Z" }, - "Volumes": {}, - "VolumesRW": {} + "Mounts": [ + { + "Source": "/data", + "Destination": "/data", + "Mode": "ro,Z", + "RW": false + } + ] } Status Codes: @@ -1694,9 +1704,14 @@ Create a new image from a container's changes "Cmd": [ "date" ], - "Volumes": { - "/tmp": {} - }, + "Mounts": [ + { + "Source": "/data", + "Destination": "/data", + "Mode": "ro,Z", + "RW": false + } + ], "Labels": { "key1": "value1", "key2": "value2" @@ -2082,8 +2097,7 @@ Return low-level information about the `exec` command `id`. "ProcessLabel" : "", "AppArmorProfile" : "", "RestartCount" : 0, - "Volumes" : {}, - "VolumesRW" : {} + "Mounts" : [], } } diff --git a/integration-cli/docker_api_containers_test.go b/integration-cli/docker_api_containers_test.go index 9236f88409..8d85d16842 100644 --- a/integration-cli/docker_api_containers_test.go +++ b/integration-cli/docker_api_containers_test.go @@ -190,7 +190,7 @@ func (s *DockerSuite) TestContainerApiStartVolumeBinds(c *check.C) { c.Assert(err, check.IsNil) c.Assert(status, check.Equals, http.StatusNoContent) - pth, err := inspectFieldMap(name, "Volumes", "/tmp") + pth, err := inspectMountSourceField(name, "/tmp") if err != nil { c.Fatal(err) } @@ -233,7 +233,7 @@ func (s *DockerSuite) TestContainerApiStartVolumesFrom(c *check.C) { dockerCmd(c, "run", "-d", "--name", volName, "-v", volPath, "busybox") - name := "TestContainerApiStartDupVolumeBinds" + name := "TestContainerApiStartVolumesFrom" config := map[string]interface{}{ "Image": "busybox", "Volumes": map[string]struct{}{volPath: {}}, @@ -250,11 +250,11 @@ func (s *DockerSuite) TestContainerApiStartVolumesFrom(c *check.C) { c.Assert(err, check.IsNil) c.Assert(status, check.Equals, http.StatusNoContent) - pth, err := inspectFieldMap(name, "Volumes", volPath) + pth, err := inspectMountSourceField(name, volPath) if err != nil { c.Fatal(err) } - pth2, err := inspectFieldMap(volName, "Volumes", volPath) + pth2, err := inspectMountSourceField(volName, volPath) if err != nil { c.Fatal(err) } @@ -705,7 +705,7 @@ func (s *DockerSuite) TestBuildApiDockerfileSymlink(c *check.C) { func (s *DockerSuite) TestPostContainerBindNormalVolume(c *check.C) { dockerCmd(c, "create", "-v", "/foo", "--name=one", "busybox") - fooDir, err := inspectFieldMap("one", "Volumes", "/foo") + fooDir, err := inspectMountSourceField("one", "/foo") if err != nil { c.Fatal(err) } @@ -717,7 +717,7 @@ func (s *DockerSuite) TestPostContainerBindNormalVolume(c *check.C) { c.Assert(err, check.IsNil) c.Assert(status, check.Equals, http.StatusNoContent) - fooDir2, err := inspectFieldMap("two", "Volumes", "/foo") + fooDir2, err := inspectMountSourceField("two", "/foo") if err != nil { c.Fatal(err) } @@ -1467,17 +1467,15 @@ func (s *DockerSuite) TestContainerApiDeleteRemoveVolume(c *check.C) { id := strings.TrimSpace(out) c.Assert(waitRun(id), check.IsNil) - vol, err := inspectFieldMap(id, "Volumes", "/testvolume") - c.Assert(err, check.IsNil) - - _, err = os.Stat(vol) + source, err := inspectMountSourceField(id, "/testvolume") + _, err = os.Stat(source) c.Assert(err, check.IsNil) status, _, err := sockRequest("DELETE", "/containers/"+id+"?v=1&force=1", nil) c.Assert(err, check.IsNil) 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) } } diff --git a/integration-cli/docker_api_inspect_test.go b/integration-cli/docker_api_inspect_test.go index b97dba3293..2287e7d42d 100644 --- a/integration-cli/docker_api_inspect_test.go +++ b/integration-cli/docker_api_inspect_test.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "fmt" "net/http" "strings" @@ -12,28 +13,38 @@ func (s *DockerSuite) TestInspectApiContainerResponse(c *check.C) { out, _ := dockerCmd(c, "run", "-d", "busybox", "true") 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" - status, body, err := sockRequest("GET", endpoint, nil) - c.Assert(status, check.Equals, http.StatusOK) - c.Assert(err, check.IsNil) - - var inspectJSON map[string]interface{} - if err = json.Unmarshal(body, &inspectJSON); err != nil { - c.Fatalf("unable to unmarshal body for latest version: %v", err) + cases := []struct { + version string + keys []string + }{ + {"1.20", append(keysBase, "Mounts")}, + {"1.19", append(keysBase, "Volumes", "VolumesRW")}, } - 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 { - if _, ok := inspectJSON[key]; !ok { - c.Fatalf("%s does not exist in response for latest version", key) + var inspectJSON map[string]interface{} + if err = json.Unmarshal(body, &inspectJSON); err != nil { + 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") - } } diff --git a/integration-cli/docker_cli_create_test.go b/integration-cli/docker_cli_create_test.go index bcd8ac76ef..9f5f033732 100644 --- a/integration-cli/docker_cli_create_test.go +++ b/integration-cli/docker_cli_create_test.go @@ -184,7 +184,7 @@ func (s *DockerSuite) TestCreateVolumesCreated(c *check.C) { name := "test_create_volume" dockerCmd(c, "create", "--name", name, "-v", "/foo", "busybox") - dir, err := inspectFieldMap(name, "Volumes", "/foo") + dir, err := inspectMountSourceField(name, "/foo") if err != nil { c.Fatalf("Error getting volume host path: %q", err) } diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index 9b77d918e4..05308055e4 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -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 { c.Fatal(err, out) } + if err := s.d.Restart(); err != nil { c.Fatal(err) } if _, err := s.d.Cmd("run", "-d", "--volumes-from", "volrestarttest1", "--name", "volrestarttest2", "busybox", "top"); err != nil { c.Fatal(err) } + if out, err := s.d.Cmd("rm", "-fv", "volrestarttest2"); err != nil { c.Fatal(err, out) } - v, err := s.d.Cmd("inspect", "--format", "{{ json .Volumes }}", "volrestarttest1") - if err != nil { - c.Fatal(err) - } - volumes := make(map[string]string) - json.Unmarshal([]byte(v), &volumes) - if _, err := os.Stat(volumes["/foo"]); err != nil { - c.Fatalf("Expected volume to exist: %s - %s", volumes["/foo"], err) + + out, err := s.d.Cmd("inspect", "-f", "{{json .Mounts}}", "volrestarttest1") + c.Assert(err, check.IsNil) + + if _, err := inspectMountPointJSON(out, "/foo"); err != nil { + c.Fatalf("Expected volume to exist: /foo, error: %v\n", err) } } diff --git a/integration-cli/docker_cli_inspect_experimental_test.go b/integration-cli/docker_cli_inspect_experimental_test.go new file mode 100644 index 0000000000..fada86e6ff --- /dev/null +++ b/integration-cli/docker_cli_inspect_experimental_test.go @@ -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) + } +} diff --git a/integration-cli/docker_cli_inspect_test.go b/integration-cli/docker_cli_inspect_test.go index 64a660d39e..343bb0b2ce 100644 --- a/integration-cli/docker_cli_inspect_test.go +++ b/integration-cli/docker_cli_inspect_test.go @@ -6,6 +6,7 @@ import ( "strconv" "strings" + "github.com/docker/docker/api/types" "github.com/go-check/check" ) @@ -18,7 +19,6 @@ func (s *DockerSuite) TestInspectImage(c *check.C) { if id != imageTestID { c.Fatalf("Expected id: %s for image: %s but received id: %s", imageTestID, imageTest, id) } - } 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) } } + +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") + } +} diff --git a/integration-cli/docker_cli_restart_test.go b/integration-cli/docker_cli_restart_test.go index c8bb0b1bcc..647bf023fd 100644 --- a/integration-cli/docker_cli_restart_test.go +++ b/integration-cli/docker_cli_restart_test.go @@ -54,27 +54,27 @@ func (s *DockerSuite) TestRestartWithVolumes(c *check.C) { out, _ := dockerCmd(c, "run", "-d", "-v", "/test", "busybox", "top") 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" { c.Errorf("expect 1 volume received %s", out) } - volumes, err := inspectField(cleanedContainerID, "Volumes") + source, err := inspectMountSourceField(cleanedContainerID, "/test") c.Assert(err, check.IsNil) 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" { 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) - if volumes != volumesAfterRestart { - c.Errorf("expected volume path: %s Actual path: %s", volumes, volumesAfterRestart) + if source != sourceAfterRestart { + c.Errorf("expected volume path: %s Actual path: %s", source, sourceAfterRestart) } } diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 957c1a2ad4..b505a7c3d9 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -2302,24 +2302,23 @@ func (s *DockerSuite) TestRunVolumesCleanPaths(c *check.C) { c.Fatal(err, out) } - out, err := inspectFieldMap("dark_helmet", "Volumes", "/foo/") - c.Assert(err, check.IsNil) - if out != "" { + out, err := inspectMountSourceField("dark_helmet", "/foo/") + if err != mountNotFound { 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) if !strings.Contains(out, volumesConfigPath) { c.Fatalf("Volume was not defined for /foo\n%q", out) } - out, err = inspectFieldMap("dark_helmet", "Volumes", "/bar/") - c.Assert(err, check.IsNil) - if out != "" { + out, err = inspectMountSourceField("dark_helmet", "/bar/") + if err != mountNotFound { 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) if !strings.Contains(out, volumesConfigPath) { 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: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) - if testRO != "false" { + if mRO.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) - if testRW != "true" { + if !mRW.RW { c.Fatalf("Expected RW volume was RO") } } diff --git a/integration-cli/docker_utils.go b/integration-cli/docker_utils.go index 70831cf073..99f4ceb775 100644 --- a/integration-cli/docker_utils.go +++ b/integration-cli/docker_utils.go @@ -20,6 +20,7 @@ import ( "strings" "time" + "github.com/docker/docker/api/types" "github.com/docker/docker/opts" "github.com/docker/docker/pkg/ioutils" "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)) } +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) { return inspectField(name, "Id") } diff --git a/man/docker-inspect.1.md b/man/docker-inspect.1.md index 1860480ea8..d423431944 100644 --- a/man/docker-inspect.1.md +++ b/man/docker-inspect.1.md @@ -95,8 +95,14 @@ To get information on a container use its ID or instance name: "ExecDriver": "native-0.2", "MountLabel": "", "ProcessLabel": "", - "Volumes": {}, - "VolumesRW": {}, + "Mounts": [ + { + "Source": "/data", + "Destination": "/data", + "Mode": "ro,Z", + "RW": false + } + ], "AppArmorProfile": "", "ExecIDs": null, "HostConfig": {