@@ -19,6 +19,132 @@ import (
1919 imageapiv1 "github.com/openshift/origin/pkg/image/apis/image/v1"
2020)
2121
22+ // Pruner defines a common set of operations for pruning
23+ type Pruner interface {
24+ DeleteRepository (ctx context.Context , reponame string ) error
25+ DeleteManifestLink (ctx context.Context , svc distribution.ManifestService , reponame string , dgst digest.Digest ) error
26+ DeleteBlob (ctx context.Context , dgst digest.Digest ) error
27+ }
28+
29+ // DryRunPruner prints information about each object that going to remove.
30+ type DryRunPruner struct {}
31+
32+ var _ Pruner = & DryRunPruner {}
33+
34+ func (p * DryRunPruner ) DeleteRepository (ctx context.Context , reponame string ) error {
35+ logger := context .GetLogger (ctx )
36+ logger .Printf ("Would delete repository: %s" , reponame )
37+ return nil
38+ }
39+
40+ func (p * DryRunPruner ) DeleteManifestLink (ctx context.Context , svc distribution.ManifestService , reponame string , dgst digest.Digest ) error {
41+ logger := context .GetLogger (ctx )
42+ logger .Printf ("Would delete manifest link: %s@%s" , reponame , dgst )
43+ return nil
44+ }
45+
46+ func (p * DryRunPruner ) DeleteBlob (ctx context.Context , dgst digest.Digest ) error {
47+ logger := context .GetLogger (ctx )
48+ logger .Printf ("Would delete blob: %s" , dgst )
49+ return nil
50+ }
51+
52+ // RegistryPruner deletes objects.
53+ type RegistryPruner struct {
54+ StorageDriver driver.StorageDriver
55+ }
56+
57+ var _ Pruner = & RegistryPruner {}
58+
59+ // DeleteRepository removes a repository directory from the storage
60+ func (p * RegistryPruner ) DeleteRepository (ctx context.Context , reponame string ) error {
61+ vacuum := storage .NewVacuum (ctx , p .StorageDriver )
62+
63+ // Log message will be generated by RemoveRepository with loglevel=info.
64+ if err := vacuum .RemoveRepository (reponame ); err != nil {
65+ return fmt .Errorf ("unable to remove the repository %s: %v" , reponame , err )
66+ }
67+
68+ return nil
69+ }
70+
71+ // DeleteManifestLink removes a manifest link from the storage
72+ func (p * RegistryPruner ) DeleteManifestLink (ctx context.Context , svc distribution.ManifestService , reponame string , dgst digest.Digest ) error {
73+ logger := context .GetLogger (ctx )
74+
75+ logger .Printf ("Deleting manifest link: %s@%s" , reponame , dgst )
76+ if err := svc .Delete (ctx , dgst ); err != nil {
77+ return fmt .Errorf ("failed to delete the manifest link %s@%s: %v" , reponame , dgst , err )
78+ }
79+
80+ return nil
81+ }
82+
83+ // DeleteBlob removes a blob from the storage
84+ func (p * RegistryPruner ) DeleteBlob (ctx context.Context , dgst digest.Digest ) error {
85+ vacuum := storage .NewVacuum (ctx , p .StorageDriver )
86+
87+ // Log message will be generated by RemoveBlob with loglevel=info.
88+ if err := vacuum .RemoveBlob (string (dgst )); err != nil {
89+ return fmt .Errorf ("failed to delete the blob %s: %v" , dgst , err )
90+ }
91+
92+ return nil
93+ }
94+
95+ // garbageCollector holds objects for later deletion. If the object is replaced,
96+ // then the previous one will be deleted.
97+ type garbageCollector struct {
98+ Pruner Pruner
99+ Ctx context.Context
100+
101+ repoName string
102+
103+ manifestService distribution.ManifestService
104+ manifestRepo string
105+ manifestLink digest.Digest
106+ }
107+
108+ func (gc * garbageCollector ) AddRepository (repoName string ) error {
109+ // If the place is occupied, then it is necessary to clean it.
110+ if err := gc .Collect (); err != nil {
111+ return err
112+ }
113+
114+ gc .repoName = repoName
115+
116+ return nil
117+ }
118+
119+ func (gc * garbageCollector ) AddManifestLink (svc distribution.ManifestService , repoName string , dgst digest.Digest ) error {
120+ // If the place is occupied, then it is necessary to clean it.
121+ if err := gc .Collect (); err != nil {
122+ return err
123+ }
124+
125+ gc .manifestService = svc
126+ gc .manifestRepo = repoName
127+ gc .manifestLink = dgst
128+
129+ return nil
130+ }
131+
132+ func (gc * garbageCollector ) Collect () error {
133+ if len (gc .manifestLink ) > 0 {
134+ if err := gc .Pruner .DeleteManifestLink (gc .Ctx , gc .manifestService , gc .manifestRepo , gc .manifestLink ); err != nil {
135+ return err
136+ }
137+ gc .manifestLink = ""
138+ }
139+ if len (gc .repoName ) > 0 {
140+ if err := gc .Pruner .DeleteRepository (gc .Ctx , gc .repoName ); err != nil {
141+ return err
142+ }
143+ gc .repoName = ""
144+ }
145+ return nil
146+ }
147+
22148func imageStreamHasManifestDigest (is * imageapiv1.ImageStream , dgst digest.Digest ) bool {
23149 for _ , tagEventList := range is .Status .Tags {
24150 for _ , tagEvent := range tagEventList .Items {
@@ -42,7 +168,7 @@ type Summary struct {
42168//
43169// TODO(dmage): remove layer links to a blob if the blob is removed or it doesn't belong to the ImageStream.
44170// TODO(dmage): keep young blobs (docker/distribution#2297).
45- func Prune (ctx context.Context , storageDriver driver. StorageDriver , registry distribution.Namespace , registryClient client.RegistryClient , dryRun bool ) (Summary , error ) {
171+ func Prune (ctx context.Context , registry distribution.Namespace , registryClient client.RegistryClient , pruner Pruner ) (Summary , error ) {
46172 logger := context .GetLogger (ctx )
47173
48174 repositoryEnumerator , ok := registry .(distribution.RepositoryEnumerator )
@@ -84,7 +210,15 @@ func Prune(ctx context.Context, storageDriver driver.StorageDriver, registry dis
84210
85211 var stats Summary
86212
87- var reposToDelete []string
213+ // The Enumerate calls a Stat() on each file or directory in the tree before call our handler.
214+ // Therefore, we can not delete subdirectories from the handler. On some types of storage (S3),
215+ // this can lead to an error in the Enumerate.
216+ // We are waiting for the completion of our handler and perform deferred deletion of objects.
217+ gc := & garbageCollector {
218+ Ctx : ctx ,
219+ Pruner : pruner ,
220+ }
221+
88222 err = repositoryEnumerator .Enumerate (ctx , func (repoName string ) error {
89223 logger .Debugln ("Processing repository" , repoName )
90224
@@ -101,11 +235,7 @@ func Prune(ctx context.Context, storageDriver driver.StorageDriver, registry dis
101235 is , err := oc .ImageStreams (ref .Namespace ).Get (ref .Name , metav1.GetOptions {})
102236 if kerrors .IsNotFound (err ) {
103237 logger .Printf ("The image stream %s/%s is not found, will remove the whole repository" , ref .Namespace , ref .Name )
104-
105- // We cannot delete the repository at this point, because it would break Enumerate.
106- reposToDelete = append (reposToDelete , repoName )
107-
108- return nil
238+ return gc .AddRepository (repoName )
109239 } else if err != nil {
110240 return fmt .Errorf ("failed to get the image stream %s: %v" , repoName , err )
111241 }
@@ -131,17 +261,7 @@ func Prune(ctx context.Context, storageDriver driver.StorageDriver, registry dis
131261 return nil
132262 }
133263
134- if dryRun {
135- logger .Printf ("Would delete manifest link: %s@%s" , repoName , dgst )
136- return nil
137- }
138-
139- logger .Printf ("Deleting manifest link: %s@%s" , repoName , dgst )
140- if err := manifestService .Delete (ctx , dgst ); err != nil {
141- return fmt .Errorf ("failed to delete the manifest link %s@%s: %v" , repoName , dgst , err )
142- }
143-
144- return nil
264+ return gc .AddManifestLink (manifestService , repoName , dgst )
145265 })
146266 if e , ok := err .(driver.PathNotFoundError ); ok {
147267 logger .Printf ("Skipped manifest link pruning for the repository %s: %v" , repoName , e )
@@ -158,18 +278,8 @@ func Prune(ctx context.Context, storageDriver driver.StorageDriver, registry dis
158278 return stats , err
159279 }
160280
161- vacuum := storage .NewVacuum (ctx , storageDriver )
162-
163- logger .Debugln ("Removing repositories" )
164- for _ , repoName := range reposToDelete {
165- if dryRun {
166- logger .Printf ("Would delete repository: %s" , repoName )
167- continue
168- }
169-
170- if err = vacuum .RemoveRepository (repoName ); err != nil {
171- return stats , fmt .Errorf ("unable to remove the repository %s: %v" , repoName , err )
172- }
281+ if err := gc .Collect (); err != nil {
282+ return stats , err
173283 }
174284
175285 logger .Debugln ("Processing blobs" )
@@ -188,16 +298,7 @@ func Prune(ctx context.Context, storageDriver driver.StorageDriver, registry dis
188298 stats .Blobs ++
189299 stats .DiskSpace += desc .Size
190300
191- if dryRun {
192- logger .Printf ("Would delete blob: %s" , dgst )
193- return nil
194- }
195-
196- if err := vacuum .RemoveBlob (string (dgst )); err != nil {
197- return fmt .Errorf ("failed to delete the blob %s: %v" , dgst , err )
198- }
199-
200- return nil
301+ return pruner .DeleteBlob (ctx , dgst )
201302 })
202303 return stats , err
203304}
0 commit comments