@@ -96,8 +96,9 @@ type FileContext struct {
96
96
type ExtractFunction func (string , * tar.Header , string , io.Reader ) error
97
97
98
98
type FSConfig struct {
99
- includeWhiteout bool
100
- extractFunc ExtractFunction
99
+ includeWhiteout bool
100
+ printExtractionProgress bool
101
+ extractFunc ExtractFunction
101
102
}
102
103
103
104
type FSOpt func (* FSConfig )
@@ -126,6 +127,12 @@ func IncludeWhiteout() FSOpt {
126
127
}
127
128
}
128
129
130
+ func PrintExtractionProgress () FSOpt {
131
+ return func (opts * FSConfig ) {
132
+ opts .printExtractionProgress = true
133
+ }
134
+ }
135
+
129
136
func ExtractFunc (extractFunc ExtractFunction ) FSOpt {
130
137
return func (opts * FSConfig ) {
131
138
opts .extractFunc = extractFunc
@@ -144,7 +151,7 @@ func GetFSFromImage(root string, img v1.Image, extract ExtractFunction) ([]strin
144
151
return nil , err
145
152
}
146
153
147
- return GetFSFromLayers (root , layers , ExtractFunc (extract ))
154
+ return GetFSFromLayers (root , layers , ExtractFunc (extract ), PrintExtractionProgress () )
148
155
}
149
156
150
157
func GetFSFromLayers (root string , layers []v1.Layer , opts ... FSOpt ) ([]string , error ) {
@@ -163,12 +170,37 @@ func GetFSFromLayers(root string, layers []v1.Layer, opts ...FSOpt) ([]string, e
163
170
return nil , errors .New ("must supply an extract function" )
164
171
}
165
172
173
+ var totalSize int64
174
+ layerSizes := make ([]int64 , 0 , len (layers ))
175
+ for i , l := range layers {
176
+ layerSize , err := l .Size ()
177
+ if err != nil {
178
+ return nil , errors .Wrap (err , fmt .Sprintf ("error checking layer size %d" , i ))
179
+ }
180
+ layerSizes = append (layerSizes , layerSize )
181
+ totalSize += layerSize
182
+ }
183
+ printExtractionProgress := cfg .printExtractionProgress
184
+ if totalSize == 0 {
185
+ printExtractionProgress = false
186
+ }
187
+
188
+ if printExtractionProgress {
189
+ logrus .Infof ("Extracting image layers to %s" , root )
190
+ }
191
+
166
192
extractedFiles := []string {}
193
+ var extractedBytes int64
167
194
for i , l := range layers {
168
195
if mediaType , err := l .MediaType (); err == nil {
169
- logrus .Tracef ("Extracting layer %d of media type %s" , i , mediaType )
196
+ logrus .Tracef ("Extracting layer %d/%d of media type %s" , i + 1 , len ( layers ) , mediaType )
170
197
} else {
171
- logrus .Tracef ("Extracting layer %d" , i )
198
+ logrus .Tracef ("Extracting layer %d/%d" , i + 1 , len (layers ))
199
+ }
200
+
201
+ progressPerc := float64 (extractedBytes ) / float64 (totalSize ) * 100
202
+ if printExtractionProgress {
203
+ logrus .Infof ("Extracting layer %d/%d (%.1f%%)" , i + 1 , len (layers ), progressPerc )
172
204
}
173
205
174
206
r , err := l .Uncompressed ()
@@ -177,6 +209,16 @@ func GetFSFromLayers(root string, layers []v1.Layer, opts ...FSOpt) ([]string, e
177
209
}
178
210
defer r .Close ()
179
211
212
+ if printExtractionProgress {
213
+ r = & printAfterReader {
214
+ ReadCloser : r ,
215
+ after : time .Second ,
216
+ print : func (n int ) {
217
+ logrus .Infof ("Extracting layer %d/%d (%.1f%%) %s" , i + 1 , len (layers ), progressPerc , strings .Repeat ("." , n ))
218
+ },
219
+ }
220
+ }
221
+
180
222
tr := tar .NewReader (r )
181
223
for {
182
224
hdr , err := tr .Next ()
@@ -225,10 +267,38 @@ func GetFSFromLayers(root string, layers []v1.Layer, opts ...FSOpt) ([]string, e
225
267
226
268
extractedFiles = append (extractedFiles , filepath .Join (root , cleanedName ))
227
269
}
270
+
271
+ extractedBytes += layerSizes [i ]
228
272
}
273
+
274
+ if printExtractionProgress {
275
+ logrus .Infof ("Extraction complete" )
276
+ }
277
+
229
278
return extractedFiles , nil
230
279
}
231
280
281
+ type printAfterReader struct {
282
+ io.ReadCloser
283
+ t time.Time
284
+ after time.Duration
285
+ count int
286
+ print func (int )
287
+ }
288
+
289
+ func (r * printAfterReader ) Read (p []byte ) (n int , err error ) {
290
+ n , err = r .ReadCloser .Read (p )
291
+ if r .t .IsZero () {
292
+ r .t = time .Now ()
293
+ }
294
+ if time .Since (r .t ) >= r .after {
295
+ r .count ++
296
+ r .print (r .count )
297
+ r .t = time .Now ()
298
+ }
299
+ return
300
+ }
301
+
232
302
// DeleteFilesystem deletes the extracted image file system
233
303
func DeleteFilesystem () error {
234
304
logrus .Info ("Deleting filesystem..." )
0 commit comments