Skip to content

Commit ca5dfd3

Browse files
author
Michal Minář
committed
handling image change events
Signed-off-by: Michal Minář <[email protected]>
1 parent f6444e5 commit ca5dfd3

File tree

3 files changed

+93
-38
lines changed

3 files changed

+93
-38
lines changed

pkg/oc/admin/prune/imageprune/prune.go

+49-5
Original file line numberDiff line numberDiff line change
@@ -147,11 +147,13 @@ type PrunerOptions struct {
147147
// Images is the entire list of images in OpenShift. An image must be in this
148148
// list to be a candidate for pruning.
149149
Images *imageapi.ImageList
150+
// ImageWatcher watches for image changes.
151+
ImageWatcher watch.Interface
150152
// Streams is the entire list of image streams across all namespaces in the
151153
// cluster.
152154
Streams *imageapi.ImageStreamList
153-
// StreamsWatcher watches for stream changes.
154-
StreamsWatcher watch.Interface
155+
// StreamWatcher watches for stream changes.
156+
StreamWatcher watch.Interface
155157
// Pods is the entire list of pods across all namespaces in the cluster.
156158
Pods *kapi.PodList
157159
// RCs is the entire list of replication controllers across all namespaces in
@@ -205,6 +207,7 @@ type pruner struct {
205207
algorithm pruneAlgorithm
206208
registryClientFactory RegistryClientFactoryFunc
207209
registryURL *url.URL
210+
imageWatcher watch.Interface
208211
imageStreamWatcher watch.Interface
209212
imageStreamLimits map[string][]*kapi.LimitRange
210213
// sorted queue of images to prune
@@ -292,7 +295,8 @@ func NewPruner(options PrunerOptions) (Pruner, kerrors.Aggregate) {
292295
registryClientFactory: options.RegistryClientFactory,
293296
registryURL: options.RegistryURL,
294297
processedImages: make(map[*imagegraph.ImageNode]*Job),
295-
imageStreamWatcher: options.StreamsWatcher,
298+
imageWatcher: options.ImageWatcher,
299+
imageStreamWatcher: options.StreamWatcher,
296300
imageStreamLimits: options.LimitRanges,
297301
numWorkers: options.NumWorkers,
298302
}
@@ -828,14 +832,52 @@ func (p *pruner) handleImageStreamEvent(event watch.Event) {
828832
if isNode != nil {
829833
glog.V(4).Infof("Removing updated ImageStream %s from the graph", getName(is))
830834
// first remove the current node if present
831-
p.g.RemoveNode(imagegraph.EnsureImageStreamNode(p.g, is))
835+
p.g.RemoveNode(isNode)
832836
}
833837

834838
glog.V(4).Infof("Adding updated ImageStream %s back to the graph", getName(is))
835839
p.addImageStreamsToGraph(&imageapi.ImageStreamList{Items: []imageapi.ImageStream{*is}}, p.imageStreamLimits)
836840
}
837841
}
838842

843+
func (p *pruner) handleImageEvent(event watch.Event) {
844+
getImageNode := func() (*imageapi.Image, *imagegraph.ImageNode) {
845+
img, ok := event.Object.(*imageapi.Image)
846+
if !ok {
847+
utilruntime.HandleError(fmt.Errorf("internal error: expected Image object in %s event, not %T", event.Type, event.Object))
848+
return nil, nil
849+
}
850+
n := p.g.Find(imagegraph.ImageNodeName(img))
851+
if imgNode, ok := n.(*imagegraph.ImageNode); ok {
852+
return img, imgNode
853+
}
854+
return img, nil
855+
}
856+
857+
switch event.Type {
858+
case watch.Added:
859+
img, imgNode := getImageNode()
860+
if img == nil {
861+
return
862+
}
863+
if imgNode != nil {
864+
glog.V(4).Infof("Ignoring added Image %s that is already present in the graph", img)
865+
return
866+
}
867+
glog.V(4).Infof("Adding new Image %s to the graph", img.Name)
868+
p.addImagesToGraph(&imageapi.ImageList{Items: []imageapi.Image{*img}})
869+
870+
case watch.Deleted:
871+
img, imgNode := getImageNode()
872+
if imgNode == nil {
873+
glog.V(4).Infof("Ignoring event for deleted Image %s that is not present in the graph", img.Name)
874+
return
875+
}
876+
glog.V(4).Infof("Removing deleted image %s from the graph", img.Name)
877+
p.g.RemoveNode(imgNode)
878+
}
879+
}
880+
839881
// getImageNodes returns only nodes of type ImageNode.
840882
func getImageNodes(nodes []gonum.Node) map[string]*imagegraph.ImageNode {
841883
ret := make(map[string]*imagegraph.ImageNode)
@@ -1205,6 +1247,7 @@ func (p *pruner) runLoop(
12051247
jobChan chan<- *Job,
12061248
resultChan <-chan JobResult,
12071249
) (deletions []Deletion, failures []Failure) {
1250+
imgUpdateChan := p.imageWatcher.ResultChan()
12081251
isUpdateChan := p.imageStreamWatcher.ResultChan()
12091252
for {
12101253
// make workers busy
@@ -1235,7 +1278,8 @@ func (p *pruner) runLoop(
12351278
delete(p.processedImages, res.Job.Image)
12361279
case event := <-isUpdateChan:
12371280
p.handleImageStreamEvent(event)
1238-
// TODO: handle new images - do not add them to the queue though
1281+
case event := <-imgUpdateChan:
1282+
p.handleImageEvent(event)
12391283
}
12401284
}
12411285
}

pkg/oc/admin/prune/imageprune/prune_test.go

+31-27
Original file line numberDiff line numberDiff line change
@@ -950,8 +950,9 @@ func TestImagePruning(t *testing.T) {
950950
Namespace: test.namespace,
951951
AllImages: test.allImages,
952952
Images: &test.images,
953+
ImageWatcher: watch.NewFake(),
953954
Streams: &test.streams,
954-
StreamsWatcher: watch.NewFake(),
955+
StreamWatcher: watch.NewFake(),
955956
Pods: &test.pods,
956957
RCs: &test.rcs,
957958
BCs: &test.bcs,
@@ -1318,8 +1319,9 @@ func TestRegistryPruning(t *testing.T) {
13181319
KeepTagRevisions: &keepTagRevisions,
13191320
PruneRegistry: &test.pruneRegistry,
13201321
Images: &test.images,
1322+
ImageWatcher: watch.NewFake(),
13211323
Streams: &test.streams,
1322-
StreamsWatcher: watch.NewFake(),
1324+
StreamWatcher: watch.NewFake(),
13231325
Pods: &kapi.PodList{},
13241326
RCs: &kapi.ReplicationControllerList{},
13251327
BCs: &buildapi.BuildConfigList{},
@@ -1393,17 +1395,18 @@ func TestImageWithStrongAndWeakRefsIsNotPruned(t *testing.T) {
13931395
rss := testutil.RSList()
13941396

13951397
options := PrunerOptions{
1396-
Images: &images,
1397-
Streams: &streams,
1398-
StreamsWatcher: watch.NewFake(),
1399-
Pods: &pods,
1400-
RCs: &rcs,
1401-
BCs: &bcs,
1402-
Builds: &builds,
1403-
DSs: &dss,
1404-
Deployments: &deployments,
1405-
DCs: &dcs,
1406-
RSs: &rss,
1398+
Images: &images,
1399+
ImageWatcher: watch.NewFake(),
1400+
Streams: &streams,
1401+
StreamWatcher: watch.NewFake(),
1402+
Pods: &pods,
1403+
RCs: &rcs,
1404+
BCs: &bcs,
1405+
Builds: &builds,
1406+
DSs: &dss,
1407+
Deployments: &deployments,
1408+
DCs: &dcs,
1409+
RSs: &rss,
14071410
}
14081411
keepYoungerThan := 24 * time.Hour
14091412
keepTagRevisions := 2
@@ -1662,7 +1665,7 @@ func TestChangeImageStreamsWhilePruning(t *testing.T) {
16621665
)
16631666

16641667
streams := testutil.StreamList(testutil.Stream("registry1", "foo", "bar", testutil.Tags()))
1665-
streamsWatcher := watch.NewFake()
1668+
streamWatcher := watch.NewFake()
16661669
pods := testutil.PodList()
16671670
rcs := testutil.RCList()
16681671
bcs := testutil.BCList()
@@ -1673,17 +1676,18 @@ func TestChangeImageStreamsWhilePruning(t *testing.T) {
16731676
rss := testutil.RSList()
16741677

16751678
options := PrunerOptions{
1676-
Images: &images,
1677-
Streams: &streams,
1678-
StreamsWatcher: streamsWatcher,
1679-
Pods: &pods,
1680-
RCs: &rcs,
1681-
BCs: &bcs,
1682-
Builds: &builds,
1683-
DSs: &dss,
1684-
Deployments: &deployments,
1685-
DCs: &dcs,
1686-
RSs: &rss,
1679+
Images: &images,
1680+
ImageWatcher: watch.NewFake(),
1681+
Streams: &streams,
1682+
StreamWatcher: streamWatcher,
1683+
Pods: &pods,
1684+
RCs: &rcs,
1685+
BCs: &bcs,
1686+
Builds: &builds,
1687+
DSs: &dss,
1688+
Deployments: &deployments,
1689+
DCs: &dcs,
1690+
RSs: &rss,
16871691
RegistryClientFactory: FakeRegistryClientFactory,
16881692
RegistryURL: &url.URL{Scheme: "https", Host: "registry1.io"},
16891693
NumWorkers: 1,
@@ -1731,7 +1735,7 @@ func TestChangeImageStreamsWhilePruning(t *testing.T) {
17311735
testutil.Tag("latest",
17321736
testutil.TagEvent("sha256:0000000000000000000000000000000000000000000000000000000000000002", "registry1/foo/new@sha256:0000000000000000000000000000000000000000000000000000000000000002"),
17331737
)))
1734-
streamsWatcher.Add(&stream)
1738+
streamWatcher.Add(&stream)
17351739
imageDeleter.unblock()
17361740

17371741
// the pruner shall skip the newly referenced image
@@ -1748,7 +1752,7 @@ func TestChangeImageStreamsWhilePruning(t *testing.T) {
17481752
testutil.TagEvent("sha256:0000000000000000000000000000000000000000000000000000000000000000", "registry1/foo/bar@sha256:0000000000000000000000000000000000000000000000000000000000000000"),
17491753
testutil.TagEvent("sha256:0000000000000000000000000000000000000000000000000000000000000004", "registry1/foo/bar@sha256:0000000000000000000000000000000000000000000000000000000000000004"),
17501754
)))
1751-
streamsWatcher.Modify(&stream)
1755+
streamWatcher.Modify(&stream)
17521756
imageDeleter.unblock()
17531757

17541758
// the pruner shall skip the newly referenced image

pkg/oc/admin/prune/images.go

+13-6
Original file line numberDiff line numberDiff line change
@@ -251,11 +251,6 @@ func (o PruneImagesOptions) Validate() error {
251251

252252
// Run contains all the necessary functionality for the OpenShift cli prune images command.
253253
func (o PruneImagesOptions) Run() error {
254-
allImages, err := o.ImageClient.Images().List(metav1.ListOptions{})
255-
if err != nil {
256-
return err
257-
}
258-
259254
allPods, err := o.KubeClient.Core().Pods(o.Namespace).List(metav1.ListOptions{})
260255
if err != nil {
261256
return err
@@ -327,6 +322,17 @@ func (o PruneImagesOptions) Run() error {
327322
limitRangesMap[limit.Namespace] = limits
328323
}
329324

325+
allImages, err := o.ImageClient.Images().List(metav1.ListOptions{})
326+
if err != nil {
327+
return err
328+
}
329+
imageWatcher, err := o.Imageclient.Images().Watch(metav1.ListOptions{})
330+
if err != nil {
331+
utilruntime.HandleError(fmt.Errorf("internal error: failed to watch for images: %v"+
332+
"\n - image changes will not be detected", err))
333+
imageWatcher = watch.NewFake()
334+
}
335+
330336
imageStreamWatcher, err := o.ImageClient.ImageStreams(o.Namespace).Watch(metav1.ListOptions{})
331337
if err != nil {
332338
utilruntime.HandleError(fmt.Errorf("internal error: failed to watch for image streams: %v"+
@@ -393,8 +399,9 @@ func (o PruneImagesOptions) Run() error {
393399
PruneOverSizeLimit: o.PruneOverSizeLimit,
394400
AllImages: o.AllImages,
395401
Images: allImages,
402+
ImageWatcher: imageWatcher,
396403
Streams: allStreams,
397-
StreamsWatcher: imageStreamWatcher,
404+
StreamWatcher: imageStreamWatcher,
398405
Pods: allPods,
399406
RCs: allRCs,
400407
BCs: allBCs,

0 commit comments

Comments
 (0)