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