1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #31497 from dnephin/engine-local-image-data

Add a LastTagTime for images
This commit is contained in:
Sebastiaan van Stijn 2017-06-26 18:51:09 -07:00 committed by GitHub
commit 8f3c526e78
7 changed files with 61 additions and 0 deletions

View file

@ -949,6 +949,12 @@ definitions:
type: "string" type: "string"
BaseLayer: BaseLayer:
type: "string" type: "string"
Metadata:
type: "object"
properties:
LastTagTime:
type: "string"
format: "dateTime"
ImageSummary: ImageSummary:
type: "object" type: "object"

View file

@ -45,6 +45,12 @@ type ImageInspect struct {
VirtualSize int64 VirtualSize int64
GraphDriver GraphDriverData GraphDriver GraphDriverData
RootFS RootFS RootFS RootFS
Metadata ImageMetadata
}
// ImageMetadata contains engine-local data about the image
type ImageMetadata struct {
LastTagTime time.Time `json:",omitempty"`
} }
// Container contains response of Engine API: // Container contains response of Engine API:

View file

@ -61,6 +61,11 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
comment = img.History[len(img.History)-1].Comment comment = img.History[len(img.History)-1].Comment
} }
lastUpdated, err := daemon.stores[platform].imageStore.GetLastUpdated(img.ID())
if err != nil {
return nil, err
}
imageInspect := &types.ImageInspect{ imageInspect := &types.ImageInspect{
ID: img.ID().String(), ID: img.ID().String(),
RepoTags: repoTags, RepoTags: repoTags,
@ -79,6 +84,9 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
Size: size, Size: size,
VirtualSize: size, // TODO: field unused, deprecate VirtualSize: size, // TODO: field unused, deprecate
RootFS: rootFSToAPIType(img.RootFS), RootFS: rootFSToAPIType(img.RootFS),
Metadata: types.ImageMetadata{
LastTagTime: lastUpdated,
},
} }
imageInspect.GraphDriver.Name = daemon.GraphDriverName(platform) imageInspect.GraphDriver.Name = daemon.GraphDriverName(platform)

View file

@ -32,6 +32,9 @@ func (daemon *Daemon) TagImageWithReference(imageID image.ID, platform string, n
return err return err
} }
if err := daemon.stores[platform].imageStore.SetLastUpdated(imageID); err != nil {
return err
}
daemon.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), "tag") daemon.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), "tag")
return nil return nil
} }

View file

@ -25,6 +25,7 @@ keywords: "API, Docker, rcli, REST, documentation"
* `POST /session` is a new endpoint that can be used for running interactive long-running protocols between client and * `POST /session` is a new endpoint that can be used for running interactive long-running protocols between client and
the daemon. This endpoint is experimental and only available if the daemon is started with experimental features the daemon. This endpoint is experimental and only available if the daemon is started with experimental features
enabled. enabled.
* `GET /images/(name)/get` now includes an `ImageMetadata` field which contains image metadata that is local to the engine and not part of the image config.
## v1.30 API changes ## v1.30 API changes

View file

@ -6,6 +6,7 @@ import (
"runtime" "runtime"
"strings" "strings"
"sync" "sync"
"time"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/docker/distribution/digestset" "github.com/docker/distribution/digestset"
@ -23,6 +24,8 @@ type Store interface {
Search(partialID string) (ID, error) Search(partialID string) (ID, error)
SetParent(id ID, parent ID) error SetParent(id ID, parent ID) error
GetParent(id ID) (ID, error) GetParent(id ID) (ID, error)
SetLastUpdated(id ID) error
GetLastUpdated(id ID) (time.Time, error)
Children(id ID) []ID Children(id ID) []ID
Map() map[ID]*Image Map() map[ID]*Image
Heads() map[ID]*Image Heads() map[ID]*Image
@ -259,6 +262,22 @@ func (is *store) GetParent(id ID) (ID, error) {
return ID(d), nil // todo: validate? return ID(d), nil // todo: validate?
} }
// SetLastUpdated time for the image ID to the current time
func (is *store) SetLastUpdated(id ID) error {
lastUpdated := []byte(time.Now().Format(time.RFC3339Nano))
return is.fs.SetMetadata(id.Digest(), "lastUpdated", lastUpdated)
}
// GetLastUpdated time for the image ID
func (is *store) GetLastUpdated(id ID) (time.Time, error) {
bytes, err := is.fs.GetMetadata(id.Digest(), "lastUpdated")
if err != nil || len(bytes) == 0 {
// No lastUpdated time
return time.Time{}, nil
}
return time.Parse(time.RFC3339Nano, string(bytes))
}
func (is *store) Children(id ID) []ID { func (is *store) Children(id ID) []ID {
is.RLock() is.RLock()
defer is.RUnlock() defer is.RUnlock()

View file

@ -149,6 +149,24 @@ func defaultImageStore(t *testing.T) (Store, func()) {
return store, cleanup return store, cleanup
} }
func TestGetAndSetLastUpdated(t *testing.T) {
store, cleanup := defaultImageStore(t)
defer cleanup()
id, err := store.Create([]byte(`{"comment": "abc1", "rootfs": {"type": "layers"}}`))
assert.NoError(t, err)
updated, err := store.GetLastUpdated(id)
assert.NoError(t, err)
assert.Equal(t, updated.IsZero(), true)
assert.NoError(t, store.SetLastUpdated(id))
updated, err = store.GetLastUpdated(id)
assert.NoError(t, err)
assert.Equal(t, updated.IsZero(), false)
}
type mockLayerGetReleaser struct{} type mockLayerGetReleaser struct{}
func (ls *mockLayerGetReleaser) Get(layer.ChainID) (layer.Layer, error) { func (ls *mockLayerGetReleaser) Get(layer.ChainID) (layer.Layer, error) {