4 | 4 |
"bytes"
|
5 | 5 |
"compress/gzip"
|
6 | 6 |
"errors"
|
|
7 |
"fmt"
|
7 | 8 |
"io"
|
8 | 9 |
"io/ioutil"
|
9 | 10 |
"os"
|
|
137 | 138 |
}
|
138 | 139 |
}
|
139 | 140 |
|
140 | |
func TestPackWithDereferencing(t *testing.T) {
|
|
141 |
func TestPackWithoutIgnoring(t *testing.T) {
|
141 | 142 |
slug := bytes.NewBuffer(nil)
|
142 | 143 |
|
143 | |
meta, err := Pack("testdata/archive-dir", slug, true)
|
|
144 |
// By default NewPacker() creates a Packer that does not use
|
|
145 |
// .terraformignore or dereference symlinks.
|
|
146 |
p, err := NewPacker()
|
|
147 |
if err != nil {
|
|
148 |
t.Fatalf("err: %v", err)
|
|
149 |
}
|
|
150 |
|
|
151 |
meta, err := p.Pack("testdata/archive-dir", slug)
|
144 | 152 |
if err != nil {
|
145 | 153 |
t.Fatalf("err: %v", err)
|
146 | 154 |
}
|
|
169 | 177 |
if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA {
|
170 | 178 |
slugSize += hdr.Size
|
171 | 179 |
}
|
172 | |
|
173 | |
if hdr.Name == "foo.txt" {
|
174 | |
if hdr.Typeflag != tar.TypeReg {
|
175 | |
t.Fatalf("expect symlink 'foo.txt' to be dereferenced")
|
176 | |
}
|
177 | |
}
|
178 | |
}
|
179 | |
|
180 | |
// Check the metadata
|
181 | |
expect := &Meta{
|
182 | |
Files: fileList,
|
183 | |
Size: slugSize,
|
184 | |
}
|
185 | |
if !reflect.DeepEqual(meta, expect) {
|
186 | |
t.Fatalf("\nexpect:\n%#v\n\nactual:\n%#v", expect, meta)
|
187 | |
}
|
188 | |
}
|
189 | |
|
190 | |
func TestPackWithoutDereferencing(t *testing.T) {
|
191 | |
slug := bytes.NewBuffer(nil)
|
192 | |
|
193 | |
meta, err := Pack("testdata/archive-dir", slug, false)
|
194 | |
if err != nil {
|
195 | |
t.Fatalf("err: %v", err)
|
196 | |
}
|
197 | |
|
198 | |
gzipR, err := gzip.NewReader(slug)
|
199 | |
if err != nil {
|
200 | |
t.Fatalf("err: %v", err)
|
201 | |
}
|
202 | |
|
203 | |
tarR := tar.NewReader(gzipR)
|
204 | |
var (
|
205 | |
fileList []string
|
206 | |
slugSize int64
|
207 | |
)
|
208 | |
|
209 | |
for {
|
210 | |
hdr, err := tarR.Next()
|
211 | |
if err == io.EOF {
|
212 | |
break
|
213 | |
}
|
214 | |
if err != nil {
|
215 | |
t.Fatalf("err: %v", err)
|
216 | |
}
|
217 | |
|
218 | |
fileList = append(fileList, hdr.Name)
|
219 | |
if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA {
|
220 | |
slugSize += hdr.Size
|
221 | |
}
|
222 | |
}
|
223 | |
|
224 | |
// Make sure the the foo.txt symlink is not dereferenced
|
225 | |
// but is indeed ignored and not added to the archive.
|
226 | |
for _, file := range fileList {
|
227 | |
if file == "foo.txt" {
|
228 | |
t.Fatalf("unexpected dereferenced symlink: %s", file)
|
229 | |
}
|
230 | |
}
|
231 | |
|
232 | |
// Check the metadata
|
233 | |
expect := &Meta{
|
234 | |
Files: fileList,
|
235 | |
Size: slugSize,
|
236 | |
}
|
237 | |
if !reflect.DeepEqual(meta, expect) {
|
238 | |
t.Fatalf("\nexpect:\n%#v\n\nactual:\n%#v", expect, meta)
|
239 | |
}
|
240 | |
}
|
241 | |
|
242 | |
func TestPackWithoutIgnoring(t *testing.T) {
|
243 | |
slug := bytes.NewBuffer(nil)
|
244 | |
|
245 | |
// By default NewPacker() creates a Packer that does not use
|
246 | |
// .terraformignore or dereference symlinks.
|
247 | |
p, err := NewPacker()
|
248 | |
if err != nil {
|
249 | |
t.Fatalf("err: %v", err)
|
250 | |
}
|
251 | |
|
252 | |
meta, err := p.Pack("testdata/archive-dir", slug)
|
253 | |
if err != nil {
|
254 | |
t.Fatalf("err: %v", err)
|
255 | |
}
|
256 | |
|
257 | |
gzipR, err := gzip.NewReader(slug)
|
258 | |
if err != nil {
|
259 | |
t.Fatalf("err: %v", err)
|
260 | |
}
|
261 | |
|
262 | |
tarR := tar.NewReader(gzipR)
|
263 | |
var (
|
264 | |
fileList []string
|
265 | |
slugSize int64
|
266 | |
)
|
267 | |
|
268 | |
for {
|
269 | |
hdr, err := tarR.Next()
|
270 | |
if err == io.EOF {
|
271 | |
break
|
272 | |
}
|
273 | |
if err != nil {
|
274 | |
t.Fatalf("err: %v", err)
|
275 | |
}
|
276 | |
|
277 | |
fileList = append(fileList, hdr.Name)
|
278 | |
if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA {
|
279 | |
slugSize += hdr.Size
|
280 | |
}
|
281 | 180 |
}
|
282 | 181 |
|
283 | 182 |
// baz.txt would normally be ignored, but should not be
|
|
312 | 211 |
}
|
313 | 212 |
}
|
314 | 213 |
|
|
214 |
func TestPack_symlinks(t *testing.T) {
|
|
215 |
type tcase struct {
|
|
216 |
absolute bool
|
|
217 |
external bool
|
|
218 |
targetExists bool
|
|
219 |
dereference bool
|
|
220 |
}
|
|
221 |
|
|
222 |
var tcases []tcase
|
|
223 |
for _, absolute := range []bool{true, false} {
|
|
224 |
for _, external := range []bool{true, false} {
|
|
225 |
for _, targetExists := range []bool{true, false} {
|
|
226 |
for _, dereference := range []bool{true, false} {
|
|
227 |
tcases = append(tcases, tcase{
|
|
228 |
absolute: absolute,
|
|
229 |
external: external,
|
|
230 |
targetExists: targetExists,
|
|
231 |
dereference: dereference,
|
|
232 |
})
|
|
233 |
}
|
|
234 |
}
|
|
235 |
}
|
|
236 |
}
|
|
237 |
|
|
238 |
for _, tc := range tcases {
|
|
239 |
desc := fmt.Sprintf(
|
|
240 |
"absolute:%v external:%v targetExists:%v dereference:%v",
|
|
241 |
tc.absolute, tc.external, tc.targetExists, tc.dereference)
|
|
242 |
|
|
243 |
t.Run(desc, func(t *testing.T) {
|
|
244 |
td, err := ioutil.TempDir("", "go-slug")
|
|
245 |
if err != nil {
|
|
246 |
t.Fatal(err)
|
|
247 |
}
|
|
248 |
defer os.RemoveAll(td)
|
|
249 |
|
|
250 |
internal := filepath.Join(td, "internal")
|
|
251 |
if err := os.MkdirAll(internal, 0700); err != nil {
|
|
252 |
t.Fatal(err)
|
|
253 |
}
|
|
254 |
|
|
255 |
external := filepath.Join(td, "external")
|
|
256 |
if err := os.MkdirAll(external, 0700); err != nil {
|
|
257 |
t.Fatal(err)
|
|
258 |
}
|
|
259 |
|
|
260 |
// Make the symlink in a subdirectory. This will help ensure that
|
|
261 |
// a proper relative link gets created, even when the relative
|
|
262 |
// path requires upward directory traversal.
|
|
263 |
symPath := filepath.Join(internal, "sub", "sym")
|
|
264 |
if err := os.MkdirAll(filepath.Join(internal, "sub"), 0700); err != nil {
|
|
265 |
t.Fatal(err)
|
|
266 |
}
|
|
267 |
|
|
268 |
// Get an absolute path within the temp dir and an absolute target.
|
|
269 |
// We place the target into a subdir to ensure the link is created
|
|
270 |
// properly within a nested structure.
|
|
271 |
var targetPath string
|
|
272 |
if tc.external {
|
|
273 |
targetPath = filepath.Join(external, "foo", "bar")
|
|
274 |
} else {
|
|
275 |
targetPath = filepath.Join(internal, "foo", "bar")
|
|
276 |
}
|
|
277 |
|
|
278 |
if tc.targetExists {
|
|
279 |
if err := os.MkdirAll(filepath.Dir(targetPath), 0700); err != nil {
|
|
280 |
t.Fatal(err)
|
|
281 |
}
|
|
282 |
if err := ioutil.WriteFile(targetPath, []byte("foo"), 0644); err != nil {
|
|
283 |
t.Fatal(err)
|
|
284 |
}
|
|
285 |
}
|
|
286 |
|
|
287 |
if !tc.absolute {
|
|
288 |
targetPath, err = filepath.Rel(filepath.Dir(symPath), targetPath)
|
|
289 |
if err != nil {
|
|
290 |
t.Fatal(err)
|
|
291 |
}
|
|
292 |
}
|
|
293 |
|
|
294 |
// Make the symlink.
|
|
295 |
if err := os.Symlink(targetPath, symPath); err != nil {
|
|
296 |
t.Fatal(err)
|
|
297 |
}
|
|
298 |
|
|
299 |
var expectErr string
|
|
300 |
if tc.external && !tc.dereference {
|
|
301 |
expectErr = "target outside"
|
|
302 |
}
|
|
303 |
if tc.external && tc.dereference && !tc.targetExists {
|
|
304 |
expectErr = "no such file or directory"
|
|
305 |
}
|
|
306 |
|
|
307 |
var expectTypeflag byte
|
|
308 |
if tc.external && tc.dereference {
|
|
309 |
expectTypeflag = tar.TypeReg
|
|
310 |
} else {
|
|
311 |
expectTypeflag = tar.TypeSymlink
|
|
312 |
}
|
|
313 |
|
|
314 |
// Pack up the temp dir.
|
|
315 |
slug := bytes.NewBuffer(nil)
|
|
316 |
_, err = Pack(internal, slug, tc.dereference)
|
|
317 |
if expectErr != "" {
|
|
318 |
if err != nil {
|
|
319 |
if strings.Contains(err.Error(), expectErr) {
|
|
320 |
return
|
|
321 |
}
|
|
322 |
t.Fatalf("expected error %q, got %v", expectErr, err)
|
|
323 |
}
|
|
324 |
t.Fatal("expected error, got nil")
|
|
325 |
} else if err != nil {
|
|
326 |
t.Fatal(err)
|
|
327 |
}
|
|
328 |
|
|
329 |
// Inspect the result.
|
|
330 |
gzipR, err := gzip.NewReader(slug)
|
|
331 |
if err != nil {
|
|
332 |
t.Fatalf("err: %v", err)
|
|
333 |
}
|
|
334 |
tarR := tar.NewReader(gzipR)
|
|
335 |
|
|
336 |
symFound := false
|
|
337 |
for {
|
|
338 |
hdr, err := tarR.Next()
|
|
339 |
if err == io.EOF {
|
|
340 |
break
|
|
341 |
}
|
|
342 |
if err != nil {
|
|
343 |
t.Fatalf("err: %v", err)
|
|
344 |
}
|
|
345 |
if hdr.Name == "sub/sym" {
|
|
346 |
symFound = true
|
|
347 |
if hdr.Typeflag != expectTypeflag {
|
|
348 |
t.Fatalf("unexpected file type in slug: %q", hdr.Typeflag)
|
|
349 |
}
|
|
350 |
if expectTypeflag == tar.TypeSymlink && hdr.Linkname != "../foo/bar" {
|
|
351 |
t.Fatalf("unexpected link target in slug: %q", hdr.Linkname)
|
|
352 |
}
|
|
353 |
}
|
|
354 |
}
|
|
355 |
|
|
356 |
if !symFound {
|
|
357 |
t.Fatal("did not find symlink in archive")
|
|
358 |
}
|
|
359 |
})
|
|
360 |
}
|
|
361 |
}
|
|
362 |
|
315 | 363 |
func TestUnpack(t *testing.T) {
|
316 | 364 |
// First create the slug file so we can try to unpack it.
|
317 | 365 |
slug := bytes.NewBuffer(nil)
|
|
333 | 381 |
}
|
334 | 382 |
|
335 | 383 |
// Verify all the files
|
336 | |
verifyFile(t, filepath.Join(dst, "foo.txt"), 0, "foo\n")
|
337 | 384 |
verifyFile(t, filepath.Join(dst, "bar.txt"), 0, "bar\n")
|
338 | 385 |
verifyFile(t, filepath.Join(dst, "sub", "bar.txt"), os.ModeSymlink, "../bar.txt")
|
339 | 386 |
verifyFile(t, filepath.Join(dst, "sub", "zip.txt"), 0, "zip\n")
|
340 | 387 |
|
341 | 388 |
// Check that we can set permissions properly
|
342 | |
verifyPerms(t, filepath.Join(dst, "foo.txt"), 0644)
|
343 | 389 |
verifyPerms(t, filepath.Join(dst, "bar.txt"), 0644)
|
344 | 390 |
verifyPerms(t, filepath.Join(dst, "sub", "zip.txt"), 0644)
|
345 | 391 |
verifyPerms(t, filepath.Join(dst, "sub", "bar.txt"), 0644)
|
|
408 | 454 |
|
409 | 455 |
// Check that we can set permissions properly
|
410 | 456 |
verifyPerms(t, filepath.Join(dst, "a"), 0400)
|
|
457 |
}
|
|
458 |
|
|
459 |
func TestUnpackPaxHeaders(t *testing.T) {
|
|
460 |
tcases := []struct {
|
|
461 |
desc string
|
|
462 |
headers []*tar.Header
|
|
463 |
}{
|
|
464 |
{
|
|
465 |
desc: "extended pax header",
|
|
466 |
headers: []*tar.Header{
|
|
467 |
&tar.Header{
|
|
468 |
Name: "h",
|
|
469 |
Typeflag: tar.TypeXHeader,
|
|
470 |
},
|
|
471 |
},
|
|
472 |
},
|
|
473 |
{
|
|
474 |
desc: "global pax header",
|
|
475 |
headers: []*tar.Header{
|
|
476 |
&tar.Header{
|
|
477 |
Name: "h",
|
|
478 |
Typeflag: tar.TypeXGlobalHeader,
|
|
479 |
},
|
|
480 |
},
|
|
481 |
},
|
|
482 |
}
|
|
483 |
|
|
484 |
for _, tc := range tcases {
|
|
485 |
t.Run(tc.desc, func(t *testing.T) {
|
|
486 |
dir, err := ioutil.TempDir("", "slug")
|
|
487 |
if err != nil {
|
|
488 |
t.Fatalf("err:%v", err)
|
|
489 |
}
|
|
490 |
defer os.RemoveAll(dir)
|
|
491 |
in := filepath.Join(dir, "slug.tar.gz")
|
|
492 |
|
|
493 |
// Create the output file
|
|
494 |
wfh, err := os.Create(in)
|
|
495 |
if err != nil {
|
|
496 |
t.Fatalf("err: %v", err)
|
|
497 |
}
|
|
498 |
|
|
499 |
// Gzip compress all the output data
|
|
500 |
gzipW := gzip.NewWriter(wfh)
|
|
501 |
|
|
502 |
// Tar the file contents
|
|
503 |
tarW := tar.NewWriter(gzipW)
|
|
504 |
|
|
505 |
for _, hdr := range tc.headers {
|
|
506 |
tarW.WriteHeader(hdr)
|
|
507 |
}
|
|
508 |
|
|
509 |
tarW.Close()
|
|
510 |
gzipW.Close()
|
|
511 |
wfh.Close()
|
|
512 |
|
|
513 |
// Open the slug file for reading.
|
|
514 |
fh, err := os.Open(in)
|
|
515 |
if err != nil {
|
|
516 |
t.Fatalf("err: %v", err)
|
|
517 |
}
|
|
518 |
|
|
519 |
// Create a dir to unpack into.
|
|
520 |
dst, err := ioutil.TempDir(dir, "")
|
|
521 |
if err != nil {
|
|
522 |
t.Fatalf("err: %v", err)
|
|
523 |
}
|
|
524 |
defer os.RemoveAll(dst)
|
|
525 |
|
|
526 |
// Now try unpacking it.
|
|
527 |
if err := Unpack(fh, dst); err != nil {
|
|
528 |
t.Fatalf("err: %v", err)
|
|
529 |
}
|
|
530 |
|
|
531 |
// Verify no file was created.
|
|
532 |
path := filepath.Join(dst, "h")
|
|
533 |
fh, err = os.Open(path)
|
|
534 |
if err == nil {
|
|
535 |
t.Fatalf("expected file not to exist: %q", path)
|
|
536 |
}
|
|
537 |
defer fh.Close()
|
|
538 |
})
|
|
539 |
}
|
411 | 540 |
}
|
412 | 541 |
|
413 | 542 |
// ensure Unpack returns an error when an unsupported file type is encountered
|
|
769 | 898 |
}
|
770 | 899 |
|
771 | 900 |
func verifyFile(t *testing.T, path string, mode os.FileMode, expect string) {
|
772 | |
fh, err := os.Open(path)
|
|
901 |
info, err := os.Lstat(path)
|
773 | 902 |
if err != nil {
|
774 | 903 |
t.Fatal(err)
|
775 | 904 |
}
|
776 | |
defer fh.Close()
|
777 | |
|
778 | |
info, err := fh.Stat()
|
779 | |
if err != nil {
|
780 | |
t.Fatal(err)
|
781 | |
}
|
|
905 |
|
|
906 |
if info.Mode()&os.ModeSymlink != 0 {
|
|
907 |
if mode == os.ModeSymlink {
|
|
908 |
if target, _ := os.Readlink(path); target != expect {
|
|
909 |
t.Fatalf("expect link target %q, got %q", expect, target)
|
|
910 |
}
|
|
911 |
return
|
|
912 |
} else {
|
|
913 |
t.Fatalf("found symlink, expected %v", mode)
|
|
914 |
}
|
|
915 |
}
|
|
916 |
|
782 | 917 |
if !((mode == 0 && info.Mode().IsRegular()) || info.Mode()&mode == 0) {
|
783 | 918 |
t.Fatalf("wrong file mode for %q", path)
|
784 | 919 |
}
|
785 | 920 |
|
786 | |
if mode == os.ModeSymlink {
|
787 | |
if target, _ := os.Readlink(path); target != expect {
|
788 | |
t.Fatalf("expect link target %q, got %q", expect, target)
|
789 | |
}
|
790 | |
return
|
791 | |
}
|
|
921 |
fh, err := os.Open(path)
|
|
922 |
if err != nil {
|
|
923 |
t.Fatal(err)
|
|
924 |
}
|
|
925 |
defer fh.Close()
|
792 | 926 |
|
793 | 927 |
raw := make([]byte, info.Size())
|
794 | 928 |
if _, err := fh.Read(raw); err != nil {
|