Skip to content

Commit 9d0d559

Browse files
authored
Add extraction progress output to GetFSFromImage/GetFSFromLayers (#11)
1 parent e4a3f0b commit 9d0d559

File tree

2 files changed

+83
-5
lines changed

2 files changed

+83
-5
lines changed

pkg/util/fs_util.go

+75-5
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,9 @@ type FileContext struct {
9696
type ExtractFunction func(string, *tar.Header, string, io.Reader) error
9797

9898
type FSConfig struct {
99-
includeWhiteout bool
100-
extractFunc ExtractFunction
99+
includeWhiteout bool
100+
printExtractionProgress bool
101+
extractFunc ExtractFunction
101102
}
102103

103104
type FSOpt func(*FSConfig)
@@ -126,6 +127,12 @@ func IncludeWhiteout() FSOpt {
126127
}
127128
}
128129

130+
func PrintExtractionProgress() FSOpt {
131+
return func(opts *FSConfig) {
132+
opts.printExtractionProgress = true
133+
}
134+
}
135+
129136
func ExtractFunc(extractFunc ExtractFunction) FSOpt {
130137
return func(opts *FSConfig) {
131138
opts.extractFunc = extractFunc
@@ -144,7 +151,7 @@ func GetFSFromImage(root string, img v1.Image, extract ExtractFunction) ([]strin
144151
return nil, err
145152
}
146153

147-
return GetFSFromLayers(root, layers, ExtractFunc(extract))
154+
return GetFSFromLayers(root, layers, ExtractFunc(extract), PrintExtractionProgress())
148155
}
149156

150157
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
163170
return nil, errors.New("must supply an extract function")
164171
}
165172

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+
166192
extractedFiles := []string{}
193+
var extractedBytes int64
167194
for i, l := range layers {
168195
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)
170197
} 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)
172204
}
173205

174206
r, err := l.Uncompressed()
@@ -177,6 +209,16 @@ func GetFSFromLayers(root string, layers []v1.Layer, opts ...FSOpt) ([]string, e
177209
}
178210
defer r.Close()
179211

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+
180222
tr := tar.NewReader(r)
181223
for {
182224
hdr, err := tr.Next()
@@ -225,10 +267,38 @@ func GetFSFromLayers(root string, layers []v1.Layer, opts ...FSOpt) ([]string, e
225267

226268
extractedFiles = append(extractedFiles, filepath.Join(root, cleanedName))
227269
}
270+
271+
extractedBytes += layerSizes[i]
228272
}
273+
274+
if printExtractionProgress {
275+
logrus.Infof("Extraction complete")
276+
}
277+
229278
return extractedFiles, nil
230279
}
231280

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+
232302
// DeleteFilesystem deletes the extracted image file system
233303
func DeleteFilesystem() error {
234304
logrus.Info("Deleting filesystem...")

pkg/util/fs_util_test.go

+8
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,7 @@ func Test_GetFSFromLayers_with_whiteouts_include_whiteout_enabled(t *testing.T)
10671067
f(expectedFiles, tw)
10681068

10691069
mockLayer := mockv1.NewMockLayer(ctrl)
1070+
mockLayer.EXPECT().Size().Return(int64(buf.Len()), nil)
10701071
mockLayer.EXPECT().MediaType().Return(types.OCILayer, nil)
10711072

10721073
rc := io.NopCloser(buf)
@@ -1082,6 +1083,7 @@ func Test_GetFSFromLayers_with_whiteouts_include_whiteout_enabled(t *testing.T)
10821083
f(secondLayerFiles, tw)
10831084

10841085
mockLayer2 := mockv1.NewMockLayer(ctrl)
1086+
mockLayer2.EXPECT().Size().Return(int64(buf.Len()), nil)
10851087
mockLayer2.EXPECT().MediaType().Return(types.OCILayer, nil)
10861088

10871089
rc = io.NopCloser(buf)
@@ -1175,6 +1177,7 @@ func Test_GetFSFromLayers_with_whiteouts_include_whiteout_disabled(t *testing.T)
11751177
f(expectedFiles, tw)
11761178

11771179
mockLayer := mockv1.NewMockLayer(ctrl)
1180+
mockLayer.EXPECT().Size().Return(int64(buf.Len()), nil)
11781181
mockLayer.EXPECT().MediaType().Return(types.OCILayer, nil)
11791182
layerFiles := []string{
11801183
filepath.Join(root, "foobar"),
@@ -1197,6 +1200,7 @@ func Test_GetFSFromLayers_with_whiteouts_include_whiteout_disabled(t *testing.T)
11971200
f(secondLayerFiles, tw)
11981201

11991202
mockLayer2 := mockv1.NewMockLayer(ctrl)
1203+
mockLayer2.EXPECT().Size().Return(int64(buf.Len()), nil)
12001204
mockLayer2.EXPECT().MediaType().Return(types.OCILayer, nil)
12011205

12021206
rc = io.NopCloser(buf)
@@ -1280,6 +1284,7 @@ func Test_GetFSFromLayers_ignorelist(t *testing.T) {
12801284
f(expectedFiles, tw)
12811285

12821286
mockLayer := mockv1.NewMockLayer(ctrl)
1287+
mockLayer.EXPECT().Size().Return(int64(buf.Len()), nil)
12831288
mockLayer.EXPECT().MediaType().Return(types.OCILayer, nil)
12841289
layerFiles := []string{
12851290
filepath.Join(root, ".wh.testdir"),
@@ -1345,6 +1350,8 @@ func Test_GetFSFromLayers_ignorelist(t *testing.T) {
13451350

13461351
f(layerFiles, tw)
13471352

1353+
mockLayer.EXPECT().Size().Return(int64(buf.Len()), nil)
1354+
13481355
rc = io.NopCloser(buf)
13491356
mockLayer.EXPECT().Uncompressed().Return(rc, nil)
13501357

@@ -1410,6 +1417,7 @@ func Test_GetFSFromLayers(t *testing.T) {
14101417
}
14111418

14121419
mockLayer := mockv1.NewMockLayer(ctrl)
1420+
mockLayer.EXPECT().Size().Return(int64(buf.Len()), nil)
14131421
mockLayer.EXPECT().MediaType().Return(types.OCILayer, nil)
14141422

14151423
rc := io.NopCloser(buf)

0 commit comments

Comments
 (0)