-
Notifications
You must be signed in to change notification settings - Fork 61
/
tar_file.go
96 lines (82 loc) · 2.5 KB
/
tar_file.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
// Copyright 2020 VMware, Inc.
// SPDX-License-Identifier: Apache-2.0
package imagetar
import (
"archive/tar"
"fmt"
"io"
"os"
"strings"
regv1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/vmware-tanzu/carvel-imgpkg/pkg/imgpkg/imagedesc"
"github.com/vmware-tanzu/carvel-imgpkg/pkg/imgpkg/internal/util"
)
type tarFile struct {
path string
}
var _ imagedesc.LayerProvider = tarFile{}
type tarFileChunk struct {
file tarFile
chunkPath string
}
var _ imagedesc.LayerContents = tarFileChunk{}
type tarFileChunkReadCloser struct {
DebugID string
io.Reader
io.Closer
}
func (f tarFile) Chunk(path string) tarFileChunk {
return tarFileChunk{f, path}
}
func (f tarFile) FindLayer(layerTD imagedesc.ImageLayerDescriptor) (imagedesc.LayerContents, error) {
digest, err := regv1.NewHash(layerTD.Digest)
if err != nil {
return nil, err
}
return tarFileChunk{f, digest.Algorithm + "-" + digest.Hex + ".tar.gz"}, nil
}
func (f tarFileChunk) Open() (io.ReadCloser, error) {
return f.file.openChunk(f.chunkPath)
}
func (f tarFile) openChunk(path string) (io.ReadCloser, error) {
file, err := os.Open(f.path)
if err != nil {
return nil, err
}
tf := tar.NewReader(file)
for {
hdr, err := tf.Next()
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
if hdr.Name == path {
return tarFileChunkReadCloser{
DebugID: fmt.Sprintf("%s/%p", path, tf),
Reader: tf, Closer: file}, nil
}
}
return nil, util.NonRetryableError{Message: fmt.Sprintf("file %s not found in tar (hint: This may be because when copying to a tarball, the --include-non-distributable-layers flag should have been provided.)", path)}
}
func (f tarFileChunkReadCloser) Close() error {
// It seems that there is a race between go-containerregistry library
// and net/http's transport to close the request body. Specifically
// between net/http.(*transferWriter).writeBody(...) and
// gcrr.pkg/v1/remote.(*writer).streamBlob(...).
// Example trace: https://gist.github.com/cppforlife/aeeb989c83aebd061561d12524c6476b
// Following lines are used to identify locations where Close()
// method is being called during runtime.
// fmt.Printf("%s: close\n>>>%s<<<\n\n", f.DebugId, debug.Stack())
err := f.Closer.Close()
if err != nil {
// Ignore dup close error since closing file twice does
// not have any side-effects (except just being conceptually wrong)
if strings.Contains(err.Error(), "file already closed") {
// fmt.Printf("%s: dup close\n\n", f.DebugId)
return nil
}
}
return err
}