diff --git a/slug.go b/slug.go index 3f29b7b..19ccd29 100644 --- a/slug.go +++ b/slug.go @@ -70,11 +70,23 @@ func AllowSymlinkTarget(path string) PackerOption { } } +// CompressionLevel allows callers to set the gzip compression level to use +// when packing the source directory. The default compression level (6, or +// gzip.DefaultCompression) is used if this option is not set. All available +// compression constants are defined in the gzip and flate packages. +func CompressionLevel(level int) PackerOption { + return func(p *Packer) error { + p.compressionLevel = level + return nil + } +} + // Packer holds options for the Pack function. type Packer struct { dereference bool applyTerraformIgnore bool allowSymlinkTargets []string + compressionLevel int } // NewPacker is a constructor for Packer. @@ -82,6 +94,7 @@ func NewPacker(options ...PackerOption) (*Packer, error) { p := &Packer{ dereference: false, applyTerraformIgnore: false, + compressionLevel: gzip.DefaultCompression, } for _, opt := range options { @@ -103,6 +116,7 @@ func Pack(src string, w io.Writer, dereference bool) (*Meta, error) { // This defaults to false in NewPacker, but is true here. This matches // the old behavior of Pack, which always used .terraformignore. applyTerraformIgnore: true, + compressionLevel: gzip.DefaultCompression, } return p.Pack(src, w) } @@ -116,7 +130,11 @@ func Pack(src string, w io.Writer, dereference bool) (*Meta, error) { // from the slug. func (p *Packer) Pack(src string, w io.Writer) (*Meta, error) { // Gzip compress all the output data. - gzipW := gzip.NewWriter(w) + gzipW, err := gzip.NewWriterLevel(w, p.compressionLevel) + if err != nil { + // An invalid compression level was used + return nil, fmt.Errorf("failed to initialize compressed stream: %w", err) + } // Tar the file contents. tarW := tar.NewWriter(gzipW) @@ -132,7 +150,7 @@ func (p *Packer) Pack(src string, w io.Writer) (*Meta, error) { meta := &Meta{} // Walk the tree of files. - err := filepath.Walk(src, p.packWalkFn(src, src, src, tarW, meta, ignoreRules)) + err = filepath.Walk(src, p.packWalkFn(src, src, src, tarW, meta, ignoreRules)) if err != nil { return nil, err } diff --git a/slug_test.go b/slug_test.go index 6639414..f2261e1 100644 --- a/slug_test.go +++ b/slug_test.go @@ -139,6 +139,39 @@ func TestPack(t *testing.T) { } } +func TestPackWithLevel(t *testing.T) { + uncompressedSlug := bytes.NewBuffer(nil) + compressedSlug := bytes.NewBuffer(nil) + + // Valid compression level + p, err := NewPacker(CompressionLevel(gzip.NoCompression)) + if err != nil { + t.Fatalf("err: %v", err) + } + + _, err = p.Pack("testdata/archive-dir", uncompressedSlug) + if err != nil { + t.Fatalf("err: %v", err) + } + + // Default compression + _, err = Pack("testdata/archive-dir", compressedSlug, false) + if err != nil { + t.Fatalf("err: %v", err) + } + + if compressedSlug.Len() >= uncompressedSlug.Len() { + t.Fatalf("setting the compression level didn't seem to work. compressed size (default) was %d and uncompressed size was %d", compressedSlug.Len(), uncompressedSlug.Len()) + } + + // Invalid compression level + p, _ = NewPacker(CompressionLevel(9000)) + _, err = p.Pack("testdata/archive-dir", compressedSlug) + if err == nil { + t.Fatal("expected error but got none") + } +} + func TestPackWithoutIgnoring(t *testing.T) { slug := bytes.NewBuffer(nil) @@ -1063,13 +1096,15 @@ func TestNewPacker(t *testing.T) { expect: &Packer{ dereference: false, applyTerraformIgnore: false, + compressionLevel: gzip.DefaultCompression, }, }, { desc: "enable dereferencing", options: []PackerOption{DereferenceSymlinks()}, expect: &Packer{ - dereference: true, + dereference: true, + compressionLevel: gzip.DefaultCompression, }, }, { @@ -1077,14 +1112,23 @@ func TestNewPacker(t *testing.T) { options: []PackerOption{ApplyTerraformIgnore()}, expect: &Packer{ applyTerraformIgnore: true, + compressionLevel: gzip.DefaultCompression, + }, + }, + { + desc: "compression level", + options: []PackerOption{CompressionLevel(gzip.BestSpeed)}, + expect: &Packer{ + compressionLevel: gzip.BestSpeed, }, }, { desc: "multiple options", - options: []PackerOption{ApplyTerraformIgnore(), DereferenceSymlinks()}, + options: []PackerOption{ApplyTerraformIgnore(), DereferenceSymlinks(), CompressionLevel(gzip.BestCompression)}, expect: &Packer{ dereference: true, applyTerraformIgnore: true, + compressionLevel: gzip.BestCompression, }, }, } {