Add CreatedAt filed to volume. Display when volume is inspected.

Closes #32663 by adding CreatedAt field when volume is created.
Displaying CreatedAt value when volume is inspected
Adding tests to verfiy the new field is correctly populated

Signed-off-by: Marianna <mtesselh@gmail.com>

Moving CreatedAt tests from the CLI

Moving the tests added for the newly added CreatedAt field for Volume, from CLI to API tests

Signed-off-by: Marianna <mtesselh@gmail.com>
This commit is contained in:
Marianna 2017-05-17 14:19:13 -07:00
parent fa54c94b9d
commit a46f757c40
9 changed files with 68 additions and 3 deletions

View file

@ -1047,6 +1047,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: |
@ -1100,6 +1104,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"

View file

@ -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"`

View file

@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"strings"
"time"
"github.com/Sirupsen/logrus"
dockererrors "github.com/docker/docker/api/errors"
@ -27,9 +28,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()

View file

@ -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)
}
}

View file

@ -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 {

View file

@ -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
}

View file

@ -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
}

View file

@ -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

View file

@ -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{}
}