@@ -4,30 +4,46 @@ import (
4
4
"archive/tar"
5
5
"fmt"
6
6
"io"
7
- "log "
7
+ "os "
8
8
"strings"
9
9
10
10
zglob "github.com/mattn/go-zglob"
11
11
"github.com/mholt/archiver"
12
+ "github.com/pkg/errors"
12
13
)
13
14
14
15
// Converts container image layer archive (tar) to Lambda layer archive (zip).
15
16
// Filters files from the source and only writes a new archive if at least
16
17
// one file in the source matches the filter (i.e. does not create empty archives).
17
- func RepackLayer (outputFilename string , layerContents io.Reader , filterPattern string ) (created bool , err error ) {
18
+ func RepackLayer (outputFilename string , layerContents io.Reader , filterPattern string ) (created bool , retError error ) {
18
19
// TODO: support image types other than local Docker images (docker-daemon transport),
19
20
// where the layer format is tar. For example, layers directly from a Docker registry
20
21
// will be .tar.gz-formatted. OCI images can be either tar or tar.gz, based on the
21
22
// layer's media type.
22
23
t := archiver .NewTar ()
23
24
24
- err = t .Open (layerContents , 0 )
25
+ err : = t .Open (layerContents , 0 )
25
26
if err != nil {
26
27
return false , fmt .Errorf ("opening layer tar: %v" , err )
27
28
}
28
29
defer t .Close ()
29
30
30
31
// Walk the files in the tar
32
+ z := (* archiver .Zip )(nil )
33
+ out := (* os .File )(nil )
34
+ defer func () {
35
+ if z != nil {
36
+ if err := z .Close (); err != nil {
37
+ retError = errors .Wrapf (err , " (zip close error: %v)" , err )
38
+ }
39
+ }
40
+ if out != nil {
41
+ if err := out .Close (); err != nil {
42
+ retError = errors .Wrapf (err , " (file close error: %v)" , err )
43
+ }
44
+ }
45
+ }()
46
+
31
47
for {
32
48
// Get next file in tar
33
49
f , err := t .Read ()
@@ -45,15 +61,38 @@ func RepackLayer(outputFilename string, layerContents io.Reader, filterPattern s
45
61
return false , fmt .Errorf ("filtering file in layer tar: %v" , err )
46
62
}
47
63
if repack {
48
- err = repackLayerFile (f )
64
+ if z == nil {
65
+ z , out , err = startZipFile (outputFilename )
66
+ if err != nil {
67
+ return false , fmt .Errorf ("starting zip file: %v" , err )
68
+ }
69
+ }
70
+
71
+ err = repackLayerFile (f , z )
49
72
}
50
73
51
74
if err != nil {
52
75
return false , fmt .Errorf ("walking %s in layer tar: %v" , f .Name (), err )
53
76
}
54
77
}
55
78
56
- return false , nil
79
+ return (z != nil ), nil
80
+ }
81
+
82
+ func startZipFile (destination string ) (zip * archiver.Zip , zipFile * os.File , err error ) {
83
+ z := archiver .NewZip ()
84
+
85
+ out , err := os .Create (destination )
86
+ if err != nil {
87
+ return nil , nil , fmt .Errorf ("creating %s: %v" , destination , err )
88
+ }
89
+
90
+ err = z .Create (out )
91
+ if err != nil {
92
+ return nil , nil , fmt .Errorf ("creating zip: %v" , err )
93
+ }
94
+
95
+ return z , out , nil
57
96
}
58
97
59
98
func shouldRepackLayerFile (f archiver.File , matchPattern string ) (should bool , err error ) {
@@ -62,19 +101,36 @@ func shouldRepackLayerFile(f archiver.File, matchPattern string) (should bool, e
62
101
return false , fmt .Errorf ("expected header to be *tar.Header but was %T" , f .Header )
63
102
}
64
103
65
- if f .IsDir () {
104
+ if f .IsDir () || header . Typeflag == tar . TypeDir {
66
105
return false , nil
67
106
}
68
107
108
+ // Ignore whiteout files
69
109
if strings .HasPrefix (f .Name (), ".wh." ) {
70
110
return false , nil
71
111
}
72
112
73
113
return zglob .Match (matchPattern , header .Name )
74
114
}
75
115
76
- func repackLayerFile (f archiver.File ) error {
77
- log .Printf (f .Header .(* tar.Header ).Name )
116
+ func repackLayerFile (f archiver.File , z * archiver.Zip ) error {
117
+ hdr , ok := f .Header .(* tar.Header )
118
+ if ! ok {
119
+ return fmt .Errorf ("expected header to be *tar.Header but was %T" , f .Header )
120
+ }
78
121
79
- return nil
122
+ switch hdr .Typeflag {
123
+ case tar .TypeReg , tar .TypeRegA , tar .TypeChar , tar .TypeBlock , tar .TypeFifo , tar .TypeSymlink , tar .TypeLink :
124
+ return z .Write (archiver.File {
125
+ FileInfo : archiver.FileInfo {
126
+ FileInfo : f .FileInfo ,
127
+ CustomName : hdr .Name ,
128
+ },
129
+ ReadCloser : f ,
130
+ })
131
+ case tar .TypeXGlobalHeader :
132
+ return nil // ignore
133
+ default :
134
+ return fmt .Errorf ("%s: unknown type flag: %c" , hdr .Name , hdr .Typeflag )
135
+ }
80
136
}
0 commit comments