Bläddra i källkod

Merge pull request #33257 from mtesselH/master

Add CreatedAt filed to volume. Display when volume is inspected.
Sebastiaan van Stijn 8 år sedan
förälder
incheckning
79b19c2e16

+ 5 - 0
api/swagger.yaml

@@ -1048,6 +1048,10 @@ definitions:
         type: "string"
         description: "Mount path of the volume on the host."
         x-nullable: false
+      CreatedAt:
+        type: "string"
+        format: "dateTime"
+        description: "Time volume was created."
       Status:
         type: "object"
         description: |
@@ -1101,6 +1105,7 @@ definitions:
         com.example.some-label: "some-value"
         com.example.some-other-label: "some-other-value"
       Scope: "local"
+      CreatedAt: "2016-06-07T20:31:11.853781916Z"
 
   Network:
     type: "object"

+ 3 - 0
api/types/volume.go

@@ -7,6 +7,9 @@ package types
 // swagger:model Volume
 type Volume struct {
 
+	// Time volume was created.
+	CreatedAt string `json:"CreatedAt,omitempty"`
+
 	// Name of the volume driver used by the volume.
 	// Required: true
 	Driver string `json:"Driver"`

+ 5 - 2
daemon/volumes.go

@@ -7,6 +7,7 @@ import (
 	"path/filepath"
 	"reflect"
 	"strings"
+	"time"
 
 	"github.com/Sirupsen/logrus"
 	dockererrors "github.com/docker/docker/api/errors"
@@ -28,9 +29,11 @@ type mounts []container.Mount
 
 // volumeToAPIType converts a volume.Volume to the type used by the Engine API
 func volumeToAPIType(v volume.Volume) *types.Volume {
+	createdAt, _ := v.CreatedAt()
 	tv := &types.Volume{
-		Name:   v.Name(),
-		Driver: v.DriverName(),
+		Name:      v.Name(),
+		Driver:    v.DriverName(),
+		CreatedAt: createdAt.Format(time.RFC3339),
 	}
 	if v, ok := v.(volume.DetailedVolume); ok {
 		tv.Labels = v.Labels()

+ 13 - 0
integration-cli/docker_api_volumes_test.go

@@ -2,8 +2,11 @@ package main
 
 import (
 	"encoding/json"
+	"fmt"
 	"net/http"
 	"path/filepath"
+	"strings"
+	"time"
 
 	"github.com/docker/docker/api/types"
 	volumetypes "github.com/docker/docker/api/types/volume"
@@ -69,6 +72,8 @@ func (s *DockerSuite) TestVolumesAPIInspect(c *check.C) {
 	config := volumetypes.VolumesCreateBody{
 		Name: "test",
 	}
+	// sampling current time minus a minute so to now have false positive in case of delays
+	now := time.Now().Truncate(time.Minute)
 	status, b, err := request.SockRequest("POST", "/volumes/create", config, daemonHost())
 	c.Assert(err, check.IsNil)
 	c.Assert(status, check.Equals, http.StatusCreated, check.Commentf(string(b)))
@@ -87,4 +92,12 @@ func (s *DockerSuite) TestVolumesAPIInspect(c *check.C) {
 	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf(string(b)))
 	c.Assert(json.Unmarshal(b, &vol), checker.IsNil)
 	c.Assert(vol.Name, checker.Equals, config.Name)
+
+	// comparing CreatedAt field time for the new volume to now. Removing a minute from both to avoid false positive
+	testCreatedAt, err := time.Parse(time.RFC3339, strings.TrimSpace(vol.CreatedAt))
+	c.Assert(err, check.IsNil)
+	testCreatedAt = testCreatedAt.Truncate(time.Minute)
+	if !testCreatedAt.Equal(now) {
+		c.Assert(fmt.Errorf("Time Volume is CreatedAt not equal to current time"), check.NotNil)
+	}
 }

+ 8 - 1
volume/drivers/adapter.go

@@ -4,6 +4,7 @@ import (
 	"errors"
 	"path/filepath"
 	"strings"
+	"time"
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/volume"
@@ -82,6 +83,7 @@ func (a *volumeDriverAdapter) Get(name string) (volume.Volume, error) {
 		name:         v.Name,
 		driverName:   a.Name(),
 		eMount:       v.Mountpoint,
+		createdAt:    v.CreatedAt,
 		status:       v.Status,
 		baseHostPath: a.baseHostPath,
 	}, nil
@@ -124,13 +126,15 @@ type volumeAdapter struct {
 	name         string
 	baseHostPath string
 	driverName   string
-	eMount       string // ephemeral host volume path
+	eMount       string    // ephemeral host volume path
+	createdAt    time.Time // time the directory was created
 	status       map[string]interface{}
 }
 
 type proxyVolume struct {
 	Name       string
 	Mountpoint string
+	CreatedAt  time.Time
 	Status     map[string]interface{}
 }
 
@@ -168,6 +172,9 @@ func (a *volumeAdapter) Unmount(id string) error {
 	return err
 }
 
+func (a *volumeAdapter) CreatedAt() (time.Time, error) {
+	return a.createdAt, nil
+}
 func (a *volumeAdapter) Status() map[string]interface{} {
 	out := make(map[string]interface{}, len(a.status))
 	for k, v := range a.status {

+ 12 - 0
volume/local/local_unix.go

@@ -8,8 +8,11 @@ package local
 import (
 	"fmt"
 	"net"
+	"os"
 	"path/filepath"
 	"strings"
+	"syscall"
+	"time"
 
 	"github.com/pkg/errors"
 
@@ -85,3 +88,12 @@ func (v *localVolume) mount() error {
 	err := mount.Mount(v.opts.MountDevice, v.path, v.opts.MountType, mountOpts)
 	return errors.Wrapf(err, "error while mounting volume with options: %s", v.opts)
 }
+
+func (v *localVolume) CreatedAt() (time.Time, error) {
+	fileInfo, err := os.Stat(v.path)
+	if err != nil {
+		return time.Time{}, err
+	}
+	sec, nsec := fileInfo.Sys().(*syscall.Stat_t).Ctim.Unix()
+	return time.Unix(sec, nsec), nil
+}

+ 12 - 0
volume/local/local_windows.go

@@ -5,8 +5,11 @@ package local
 
 import (
 	"fmt"
+	"os"
 	"path/filepath"
 	"strings"
+	"syscall"
+	"time"
 )
 
 type optsConfig struct{}
@@ -32,3 +35,12 @@ func setOpts(v *localVolume, opts map[string]string) error {
 func (v *localVolume) mount() error {
 	return nil
 }
+
+func (v *localVolume) CreatedAt() (time.Time, error) {
+	fileInfo, err := os.Stat(v.path)
+	if err != nil {
+		return time.Time{}, err
+	}
+	ft := fileInfo.Sys().(*syscall.Win32FileAttributeData).CreationTime
+	return time.Unix(0, ft.Nanoseconds()), nil
+}

+ 7 - 0
volume/testutils/testutils.go

@@ -2,6 +2,7 @@ package testutils
 
 import (
 	"fmt"
+	"time"
 
 	"github.com/docker/docker/volume"
 )
@@ -27,6 +28,9 @@ func (NoopVolume) Unmount(_ string) error { return nil }
 // Status proivdes low-level details about the volume
 func (NoopVolume) Status() map[string]interface{} { return nil }
 
+// CreatedAt provides the time the volume (directory) was created at
+func (NoopVolume) CreatedAt() (time.Time, error) { return time.Now(), nil }
+
 // FakeVolume is a fake volume with a random name
 type FakeVolume struct {
 	name       string
@@ -56,6 +60,9 @@ func (FakeVolume) Unmount(_ string) error { return nil }
 // Status proivdes low-level details about the volume
 func (FakeVolume) Status() map[string]interface{} { return nil }
 
+// CreatedAt provides the time the volume (directory) was created at
+func (FakeVolume) CreatedAt() (time.Time, error) { return time.Now(), nil }
+
 // FakeDriver is a driver that generates fake volumes
 type FakeDriver struct {
 	name string

+ 3 - 0
volume/volume.go

@@ -6,6 +6,7 @@ import (
 	"path/filepath"
 	"strings"
 	"syscall"
+	"time"
 
 	mounttypes "github.com/docker/docker/api/types/mount"
 	"github.com/docker/docker/pkg/idtools"
@@ -64,6 +65,8 @@ type Volume interface {
 	Mount(id string) (string, error)
 	// Unmount unmounts the volume when it is no longer in use.
 	Unmount(id string) error
+	// CreatedAt returns Volume Creation time
+	CreatedAt() (time.Time, error)
 	// Status returns low-level status information about a volume
 	Status() map[string]interface{}
 }