@@ -19,6 +19,133 @@ 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+ logger := context .GetLogger (ctx )
62+ vacuum := storage .NewVacuum (ctx , p .StorageDriver )
63+
64+ // Log message will be generated by RemoveRepository with loglevel=info.
65+ if err := vacuum .RemoveRepository (reponame ); err != nil {
66+ return fmt .Errorf ("unable to remove the repository %s: %v" , reponame , err )
67+ }
68+
69+ return nil
70+ }
71+
72+ // DeleteManifestLink removes a manifest link from the storage
73+ func (p * RegistryPruner ) DeleteManifestLink (ctx context.Context , svc distribution.ManifestService , reponame string , dgst digest.Digest ) error {
74+ logger := context .GetLogger (ctx )
75+
76+ logger .Printf ("Deleting manifest link: %s@%s" , reponame , dgst )
77+ if err := svc .Delete (ctx , dgst ); err != nil {
78+ return fmt .Errorf ("failed to delete the manifest link %s@%s: %v" , reponame , dgst , err )
79+ }
80+
81+ return nil
82+ }
83+
84+ // DeleteBlob removes a blob from the storage
85+ func (p * RegistryPruner ) DeleteBlob (ctx context.Context , dgst digest.Digest ) error {
86+ vacuum := storage .NewVacuum (ctx , p .StorageDriver )
87+
88+ // Log message will be generated by RemoveBlob with loglevel=info.
89+ if err := vacuum .RemoveBlob (string (dgst )); err != nil {
90+ return fmt .Errorf ("failed to delete the blob %s: %v" , dgst , err )
91+ }
92+
93+ return nil
94+ }
95+
96+ // garbageCollector holds objects for later deletion. If the object is replaced,
97+ // then the previous one will be deleted.
98+ type garbageCollector struct {
99+ Pruner Pruner
100+ Ctx context.Context
101+
102+ repoName string
103+
104+ manifestService distribution.ManifestService
105+ manifestRepo string
106+ manifestLink digest.Digest
107+ }
108+
109+ func (gc * garbageCollector ) AddRepository (repoName string ) error {
110+ // If the place is occupied, then it is necessary to clean it.
111+ if err := gc .Collect (); err != nil {
112+ return err
113+ }
114+
115+ gc .repoName = repoName
116+
117+ return nil
118+ }
119+
120+ func (gc * garbageCollector ) AddManifestLink (svc distribution.ManifestService , repoName string , dgst digest.Digest ) error {
121+ // If the place is occupied, then it is necessary to clean it.
122+ if err := gc .Collect (); err != nil {
123+ return err
124+ }
125+
126+ gc .manifestService = svc
127+ gc .manifestRepo = repoName
128+ gc .manifestLink = dgst
129+
130+ return nil
131+ }
132+
133+ func (gc * garbageCollector ) Collect () error {
134+ if len (gc .manifestLink ) > 0 {
135+ if err := gc .Pruner .DeleteManifestLink (gc .Ctx , gc .manifestService , gc .manifestRepo , gc .manifestLink ); err != nil {
136+ return err
137+ }
138+ gc .manifestLink = ""
139+ }
140+ if len (gc .repoName ) > 0 {
141+ if err := gc .Pruner .DeleteRepository (gc .Ctx , gc .repoName ); err != nil {
142+ return err
143+ }
144+ gc .repoName = ""
145+ }
146+ return nil
147+ }
148+
22149func imageStreamHasManifestDigest (is * imageapiv1.ImageStream , dgst digest.Digest ) bool {
23150 for _ , tagEventList := range is .Status .Tags {
24151 for _ , tagEvent := range tagEventList .Items {
@@ -42,7 +169,7 @@ type Summary struct {
42169//
43170// TODO(dmage): remove layer links to a blob if the blob is removed or it doesn't belong to the ImageStream.
44171// 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 ) {
172+ func Prune (ctx context.Context , registry distribution.Namespace , registryClient client.RegistryClient , pruner Pruner ) (Summary , error ) {
46173 logger := context .GetLogger (ctx )
47174
48175 repositoryEnumerator , ok := registry .(distribution.RepositoryEnumerator )
@@ -84,7 +211,15 @@ func Prune(ctx context.Context, storageDriver driver.StorageDriver, registry dis
84211
85212 var stats Summary
86213
87- var reposToDelete []string
214+ // The Enumerate calls a Stat() on each file or directory in the tree before call our handler.
215+ // Therefore, we can not delete subdirectories from the handler. On some types of storage (S3),
216+ // this can lead to an error in the Enumerate.
217+ // We are waiting for the completion of our handler and perform deferred deletion of objects.
218+ gc := & garbageCollector {
219+ Ctx : ctx ,
220+ Pruner : pruner ,
221+ }
222+
88223 err = repositoryEnumerator .Enumerate (ctx , func (repoName string ) error {
89224 logger .Debugln ("Processing repository" , repoName )
90225
@@ -101,11 +236,7 @@ func Prune(ctx context.Context, storageDriver driver.StorageDriver, registry dis
101236 is , err := oc .ImageStreams (ref .Namespace ).Get (ref .Name , metav1.GetOptions {})
102237 if kerrors .IsNotFound (err ) {
103238 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
239+ return gc .AddRepository (repoName )
109240 } else if err != nil {
110241 return fmt .Errorf ("failed to get the image stream %s: %v" , repoName , err )
111242 }
@@ -131,17 +262,7 @@ func Prune(ctx context.Context, storageDriver driver.StorageDriver, registry dis
131262 return nil
132263 }
133264
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
265+ return gc .AddManifestLink (manifestService , repoName , dgst )
145266 })
146267 if e , ok := err .(driver.PathNotFoundError ); ok {
147268 logger .Printf ("Skipped manifest link pruning for the repository %s: %v" , repoName , e )
@@ -158,18 +279,8 @@ func Prune(ctx context.Context, storageDriver driver.StorageDriver, registry dis
158279 return stats , err
159280 }
160281
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- }
282+ if err := gc .Collect (); err != nil {
283+ return stats , err
173284 }
174285
175286 logger .Debugln ("Processing blobs" )
@@ -188,16 +299,7 @@ func Prune(ctx context.Context, storageDriver driver.StorageDriver, registry dis
188299 stats .Blobs ++
189300 stats .DiskSpace += desc .Size
190301
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
302+ return pruner .DeleteBlob (ctx , dgst )
201303 })
202304 return stats , err
203305}
0 commit comments