Commit f091ea5e authored by Andy Goldstein's avatar Andy Goldstein

Handle image digests in node status and image GC

Start including Docker image digests in the node status and consider image digests during image garbage collection.
parent 66005063
...@@ -108,7 +108,7 @@ func TestPuller(t *testing.T) { ...@@ -108,7 +108,7 @@ func TestPuller(t *testing.T) {
fakeRecorder := &record.FakeRecorder{} fakeRecorder := &record.FakeRecorder{}
puller := NewImagePuller(fakeRecorder, fakeRuntime, backOff) puller := NewImagePuller(fakeRecorder, fakeRuntime, backOff)
fakeRuntime.ImageList = []Image{{"present_image", nil, 0}} fakeRuntime.ImageList = []Image{{"present_image", nil, nil, 1}}
fakeRuntime.Err = c.pullerErr fakeRuntime.Err = c.pullerErr
fakeRuntime.InspectErr = c.inspectErr fakeRuntime.InspectErr = c.inspectErr
......
...@@ -319,6 +319,8 @@ type Image struct { ...@@ -319,6 +319,8 @@ type Image struct {
ID string ID string
// Other names by which this image is known. // Other names by which this image is known.
RepoTags []string RepoTags []string
// Digests by which this image is known.
RepoDigests []string
// The size of the image in bytes. // The size of the image in bytes.
Size int64 Size int64
} }
......
...@@ -108,7 +108,7 @@ func TestSerializedPuller(t *testing.T) { ...@@ -108,7 +108,7 @@ func TestSerializedPuller(t *testing.T) {
fakeRecorder := &record.FakeRecorder{} fakeRecorder := &record.FakeRecorder{}
puller := NewSerializedImagePuller(fakeRecorder, fakeRuntime, backOff) puller := NewSerializedImagePuller(fakeRecorder, fakeRuntime, backOff)
fakeRuntime.ImageList = []Image{{"present_image", nil, 0}} fakeRuntime.ImageList = []Image{{"present_image", nil, nil, 0}}
fakeRuntime.Err = c.pullerErr fakeRuntime.Err = c.pullerErr
fakeRuntime.InspectErr = c.inspectErr fakeRuntime.InspectErr = c.inspectErr
......
...@@ -76,8 +76,9 @@ func toRuntimeImage(image *dockertypes.Image) (*kubecontainer.Image, error) { ...@@ -76,8 +76,9 @@ func toRuntimeImage(image *dockertypes.Image) (*kubecontainer.Image, error) {
} }
return &kubecontainer.Image{ return &kubecontainer.Image{
ID: image.ID, ID: image.ID,
RepoTags: image.RepoTags, RepoTags: image.RepoTags,
Size: image.VirtualSize, RepoDigests: image.RepoDigests,
Size: image.VirtualSize,
}, nil }, nil
} }
...@@ -72,12 +72,14 @@ func TestToRuntimeImage(t *testing.T) { ...@@ -72,12 +72,14 @@ func TestToRuntimeImage(t *testing.T) {
original := &dockertypes.Image{ original := &dockertypes.Image{
ID: "aeeea", ID: "aeeea",
RepoTags: []string{"abc", "def"}, RepoTags: []string{"abc", "def"},
RepoDigests: []string{"123", "456"},
VirtualSize: 1234, VirtualSize: 1234,
} }
expected := &kubecontainer.Image{ expected := &kubecontainer.Image{
ID: "aeeea", ID: "aeeea",
RepoTags: []string{"abc", "def"}, RepoTags: []string{"abc", "def"},
Size: 1234, RepoDigests: []string{"123", "456"},
Size: 1234,
} }
actual, err := toRuntimeImage(original) actual, err := toRuntimeImage(original)
......
...@@ -164,6 +164,7 @@ func (im *realImageManager) detectImages(detectTime time.Time) error { ...@@ -164,6 +164,7 @@ func (im *realImageManager) detectImages(detectTime time.Time) error {
imagesInUse := sets.NewString() imagesInUse := sets.NewString()
for _, pod := range pods { for _, pod := range pods {
for _, container := range pod.Containers { for _, container := range pod.Containers {
glog.V(5).Infof("Pod %s/%s, container %s uses image %s", pod.Namespace, pod.Name, container.Name, container.Image)
imagesInUse.Insert(container.Image) imagesInUse.Insert(container.Image)
} }
} }
...@@ -174,10 +175,12 @@ func (im *realImageManager) detectImages(detectTime time.Time) error { ...@@ -174,10 +175,12 @@ func (im *realImageManager) detectImages(detectTime time.Time) error {
im.imageRecordsLock.Lock() im.imageRecordsLock.Lock()
defer im.imageRecordsLock.Unlock() defer im.imageRecordsLock.Unlock()
for _, image := range images { for _, image := range images {
glog.V(5).Infof("Adding image ID %s to currentImages", image.ID)
currentImages.Insert(image.ID) currentImages.Insert(image.ID)
// New image, set it as detected now. // New image, set it as detected now.
if _, ok := im.imageRecords[image.ID]; !ok { if _, ok := im.imageRecords[image.ID]; !ok {
glog.V(5).Infof("Image ID %s is new", image.ID)
im.imageRecords[image.ID] = &imageRecord{ im.imageRecords[image.ID] = &imageRecord{
firstDetected: detectTime, firstDetected: detectTime,
} }
...@@ -185,15 +188,18 @@ func (im *realImageManager) detectImages(detectTime time.Time) error { ...@@ -185,15 +188,18 @@ func (im *realImageManager) detectImages(detectTime time.Time) error {
// Set last used time to now if the image is being used. // Set last used time to now if the image is being used.
if isImageUsed(image, imagesInUse) { if isImageUsed(image, imagesInUse) {
glog.V(5).Infof("Setting Image ID %s lastUsed to %v", image.ID, now)
im.imageRecords[image.ID].lastUsed = now im.imageRecords[image.ID].lastUsed = now
} }
glog.V(5).Infof("Image ID %s has size %d", image.ID, image.Size)
im.imageRecords[image.ID].size = image.Size im.imageRecords[image.ID].size = image.Size
} }
// Remove old images from our records. // Remove old images from our records.
for image := range im.imageRecords { for image := range im.imageRecords {
if !currentImages.Has(image) { if !currentImages.Has(image) {
glog.V(5).Infof("Image ID %s is no longer present; removing from imageRecords", image)
delete(im.imageRecords, image) delete(im.imageRecords, image)
} }
} }
...@@ -266,8 +272,10 @@ func (im *realImageManager) freeSpace(bytesToFree int64, freeTime time.Time) (in ...@@ -266,8 +272,10 @@ func (im *realImageManager) freeSpace(bytesToFree int64, freeTime time.Time) (in
var lastErr error var lastErr error
spaceFreed := int64(0) spaceFreed := int64(0)
for _, image := range images { for _, image := range images {
glog.V(5).Infof("Evaluating image ID %s for possible garbage collection", image.id)
// Images that are currently in used were given a newer lastUsed. // Images that are currently in used were given a newer lastUsed.
if image.lastUsed.Equal(freeTime) || image.lastUsed.After(freeTime) { if image.lastUsed.Equal(freeTime) || image.lastUsed.After(freeTime) {
glog.V(5).Infof("Image ID %s has lastUsed=%v which is >= freeTime=%v, not eligible for garbage collection", image.id, image.lastUsed, freeTime)
break break
} }
...@@ -275,6 +283,7 @@ func (im *realImageManager) freeSpace(bytesToFree int64, freeTime time.Time) (in ...@@ -275,6 +283,7 @@ func (im *realImageManager) freeSpace(bytesToFree int64, freeTime time.Time) (in
// In such a case, the image may have just been pulled down, and will be used by a container right away. // In such a case, the image may have just been pulled down, and will be used by a container right away.
if freeTime.Sub(image.firstDetected) < im.policy.MinAge { if freeTime.Sub(image.firstDetected) < im.policy.MinAge {
glog.V(5).Infof("Image ID %s has age %v which is less than the policy's minAge of %v, not eligible for garbage collection", image.id, freeTime.Sub(image.firstDetected), im.policy.MinAge)
continue continue
} }
...@@ -315,11 +324,11 @@ func (ev byLastUsedAndDetected) Less(i, j int) bool { ...@@ -315,11 +324,11 @@ func (ev byLastUsedAndDetected) Less(i, j int) bool {
} }
func isImageUsed(image container.Image, imagesInUse sets.String) bool { func isImageUsed(image container.Image, imagesInUse sets.String) bool {
// Check the image ID and all the RepoTags. // Check the image ID and all the RepoTags and RepoDigests.
if _, ok := imagesInUse[image.ID]; ok { if _, ok := imagesInUse[image.ID]; ok {
return true return true
} }
for _, tag := range image.RepoTags { for _, tag := range append(image.RepoTags, image.RepoDigests...) {
if _, ok := imagesInUse[tag]; ok { if _, ok := imagesInUse[tag]; ok {
return true return true
} }
......
...@@ -337,6 +337,34 @@ func TestFreeSpaceImagesAlsoDoesLookupByRepoTags(t *testing.T) { ...@@ -337,6 +337,34 @@ func TestFreeSpaceImagesAlsoDoesLookupByRepoTags(t *testing.T) {
assert.Len(fakeRuntime.ImageList, 1) assert.Len(fakeRuntime.ImageList, 1)
} }
func TestFreeSpaceImagesAlsoDoesLookupByRepoDigests(t *testing.T) {
manager, fakeRuntime, _ := newRealImageManager(ImageGCPolicy{})
fakeRuntime.ImageList = []container.Image{
makeImage(0, 1024),
{
ID: "5678",
RepoDigests: []string{"potato", "salad"},
Size: 2048,
},
}
fakeRuntime.AllPodList = []*container.Pod{
{
Containers: []*container.Container{
{
ID: container.ContainerID{Type: "test", ID: "c5678"},
Image: "salad",
},
},
},
}
spaceFreed, err := manager.freeSpace(1024, time.Now())
assert := assert.New(t)
require.NoError(t, err)
assert.EqualValues(1024, spaceFreed)
assert.Len(fakeRuntime.ImageList, 1)
}
func TestGarbageCollectBelowLowThreshold(t *testing.T) { func TestGarbageCollectBelowLowThreshold(t *testing.T) {
policy := ImageGCPolicy{ policy := ImageGCPolicy{
HighThresholdPercent: 90, HighThresholdPercent: 90,
......
...@@ -3009,7 +3009,7 @@ func (kl *Kubelet) setNodeStatusImages(node *api.Node) { ...@@ -3009,7 +3009,7 @@ func (kl *Kubelet) setNodeStatusImages(node *api.Node) {
} else { } else {
for _, image := range containerImages { for _, image := range containerImages {
imagesOnNode = append(imagesOnNode, api.ContainerImage{ imagesOnNode = append(imagesOnNode, api.ContainerImage{
Names: image.RepoTags, Names: append(image.RepoTags, image.RepoDigests...),
SizeBytes: image.Size, SizeBytes: image.Size,
}) })
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment