@@ -19,6 +19,132 @@ import (
19
19
imageapiv1 "github.com/openshift/origin/pkg/image/apis/image/v1"
20
20
)
21
21
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
+
22
148
func imageStreamHasManifestDigest (is * imageapiv1.ImageStream , dgst digest.Digest ) bool {
23
149
for _ , tagEventList := range is .Status .Tags {
24
150
for _ , tagEvent := range tagEventList .Items {
@@ -42,7 +168,7 @@ type Summary struct {
42
168
//
43
169
// TODO(dmage): remove layer links to a blob if the blob is removed or it doesn't belong to the ImageStream.
44
170
// 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 ) {
46
172
logger := context .GetLogger (ctx )
47
173
48
174
repositoryEnumerator , ok := registry .(distribution.RepositoryEnumerator )
@@ -84,7 +210,15 @@ func Prune(ctx context.Context, storageDriver driver.StorageDriver, registry dis
84
210
85
211
var stats Summary
86
212
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
+
88
222
err = repositoryEnumerator .Enumerate (ctx , func (repoName string ) error {
89
223
logger .Debugln ("Processing repository" , repoName )
90
224
@@ -101,11 +235,7 @@ func Prune(ctx context.Context, storageDriver driver.StorageDriver, registry dis
101
235
is , err := oc .ImageStreams (ref .Namespace ).Get (ref .Name , metav1.GetOptions {})
102
236
if kerrors .IsNotFound (err ) {
103
237
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 )
109
239
} else if err != nil {
110
240
return fmt .Errorf ("failed to get the image stream %s: %v" , repoName , err )
111
241
}
@@ -131,17 +261,7 @@ func Prune(ctx context.Context, storageDriver driver.StorageDriver, registry dis
131
261
return nil
132
262
}
133
263
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 )
145
265
})
146
266
if e , ok := err .(driver.PathNotFoundError ); ok {
147
267
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
158
278
return stats , err
159
279
}
160
280
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
173
283
}
174
284
175
285
logger .Debugln ("Processing blobs" )
@@ -188,16 +298,7 @@ func Prune(ctx context.Context, storageDriver driver.StorageDriver, registry dis
188
298
stats .Blobs ++
189
299
stats .DiskSpace += desc .Size
190
300
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 )
201
302
})
202
303
return stats , err
203
304
}
0 commit comments