github.com/hashicorp/packer@v1.14.3/post-processor/compress/post-processor_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package compress
     5  
     6  import (
     7  	"archive/tar"
     8  	"archive/zip"
     9  	"compress/gzip"
    10  	"context"
    11  	"fmt"
    12  	"io"
    13  	"os"
    14  	"strings"
    15  	"testing"
    16  
    17  	"github.com/dsnet/compress/bzip2"
    18  	packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
    19  	"github.com/hashicorp/packer-plugin-sdk/template"
    20  	"github.com/hashicorp/packer/builder/file"
    21  	"github.com/pierrec/lz4/v4"
    22  )
    23  
    24  func TestDetectFilename(t *testing.T) {
    25  	// Test default / fallback with no file extension
    26  	nakedFilename := Config{OutputPath: "test"}
    27  	nakedFilename.detectFromFilename()
    28  	if nakedFilename.Archive != "tar" {
    29  		t.Error("Expected to find tar archive setting")
    30  	}
    31  	if nakedFilename.Algorithm != "pgzip" {
    32  		t.Error("Expected to find pgzip algorithm setting")
    33  	}
    34  
    35  	// Test .archive
    36  	zipFilename := Config{OutputPath: "test.zip"}
    37  	zipFilename.detectFromFilename()
    38  	if zipFilename.Archive != "zip" {
    39  		t.Error("Expected to find zip archive setting")
    40  	}
    41  	if zipFilename.Algorithm != "" {
    42  		t.Error("Expected to find empty algorithm setting")
    43  	}
    44  
    45  	// Test .compress
    46  	lz4Filename := Config{OutputPath: "test.lz4"}
    47  	lz4Filename.detectFromFilename()
    48  	if lz4Filename.Archive != "" {
    49  		t.Error("Expected to find empty archive setting")
    50  	}
    51  	if lz4Filename.Algorithm != "lz4" {
    52  		t.Error("Expected to find lz4 algorithm setting")
    53  	}
    54  
    55  	// Test .archive.compress with some.extra.dots...
    56  	lotsOfDots := Config{OutputPath: "test.blah.bloo.blee.tar.lz4"}
    57  	lotsOfDots.detectFromFilename()
    58  	if lotsOfDots.Archive != "tar" {
    59  		t.Error("Expected to find tar archive setting")
    60  	}
    61  	if lotsOfDots.Algorithm != "lz4" {
    62  		t.Error("Expected to find lz4 algorithm setting")
    63  	}
    64  }
    65  
    66  const expectedFileContents = "Hello world!"
    67  
    68  func TestCompressOptions(t *testing.T) {
    69  	const config = `
    70  	{
    71  	    "post-processors": [
    72  	        {
    73  	            "type": "compress",
    74  	            "output": "package.gz",
    75  	            "compression_level": 9
    76  	        }
    77  	    ]
    78  	}
    79  	`
    80  
    81  	artifact := testArchive(t, config)
    82  	defer artifact.Destroy()
    83  
    84  	filename := "package.gz"
    85  	archive, _ := os.Open(filename)
    86  	gzipReader, _ := gzip.NewReader(archive)
    87  	data, _ := io.ReadAll(gzipReader)
    88  
    89  	if string(data) != expectedFileContents {
    90  		t.Errorf("Expected:\n%s\nFound:\n%s\n", expectedFileContents, data)
    91  	}
    92  }
    93  
    94  func TestCompressInterpolation(t *testing.T) {
    95  	const config = `
    96  	{
    97  	    "post-processors": [
    98  	        {
    99  	            "type": "compress",
   100  	            "output": "{{ build_name}}-{{ .BuildName }}-{{.BuilderType}}.gz"
   101  	        }
   102  	    ]
   103  	}
   104  	`
   105  
   106  	artifact := testArchive(t, config)
   107  	defer artifact.Destroy()
   108  
   109  	// You can interpolate using the .BuildName variable or build_name global
   110  	// function. We'll check both.
   111  	filename := "chocolate-vanilla-file.gz"
   112  	archive, err := os.Open(filename)
   113  	if err != nil {
   114  		t.Fatalf("Unable to read %s: %s", filename, err)
   115  	}
   116  
   117  	gzipReader, _ := gzip.NewReader(archive)
   118  	data, _ := io.ReadAll(gzipReader)
   119  
   120  	if string(data) != expectedFileContents {
   121  		t.Errorf("Expected:\n%s\nFound:\n%s\n", expectedFileContents, data)
   122  	}
   123  }
   124  
   125  // Test Helpers
   126  
   127  func setup(t *testing.T) (packersdk.Ui, packersdk.Artifact, error) {
   128  	// Create fake UI and Cache
   129  	ui := packersdk.TestUi(t)
   130  
   131  	// Create config for file builder
   132  	const fileConfig = `{"builders":[{"type":"file","target":"package.txt","content":"Hello world!"}]}`
   133  	tpl, err := template.Parse(strings.NewReader(fileConfig))
   134  	if err != nil {
   135  		return nil, nil, fmt.Errorf("Unable to parse setup configuration: %s", err)
   136  	}
   137  
   138  	// Prepare the file builder
   139  	builder := file.Builder{}
   140  	_, warnings, err := builder.Prepare(tpl.Builders["file"].Config)
   141  	if len(warnings) > 0 {
   142  		for _, warn := range warnings {
   143  			return nil, nil, fmt.Errorf("Configuration warning: %s", warn)
   144  		}
   145  	}
   146  	if err != nil {
   147  		return nil, nil, fmt.Errorf("Invalid configuration: %s", err)
   148  	}
   149  
   150  	// Run the file builder
   151  	artifact, err := builder.Run(context.Background(), ui, nil)
   152  	if err != nil {
   153  		return nil, nil, fmt.Errorf("Failed to build artifact: %s", err)
   154  	}
   155  
   156  	return ui, artifact, err
   157  }
   158  
   159  func testArchive(t *testing.T, config string) packersdk.Artifact {
   160  	ui, artifact, err := setup(t)
   161  	if err != nil {
   162  		t.Fatalf("Error bootstrapping test: %s", err)
   163  	}
   164  	if artifact != nil {
   165  		defer artifact.Destroy()
   166  	}
   167  
   168  	tpl, err := template.Parse(strings.NewReader(config))
   169  	if err != nil {
   170  		t.Fatalf("Unable to parse test config: %s", err)
   171  	}
   172  
   173  	compressor := PostProcessor{}
   174  	compressor.Configure(tpl.PostProcessors[0][0].Config)
   175  
   176  	// I get the feeling these should be automatically available somewhere, but
   177  	// some of the post-processors construct this manually.
   178  	compressor.config.ctx.BuildName = "chocolate"
   179  	compressor.config.PackerBuildName = "vanilla"
   180  	compressor.config.PackerBuilderType = "file"
   181  
   182  	artifactOut, _, _, err := compressor.PostProcess(context.Background(), ui, artifact)
   183  	if err != nil {
   184  		t.Fatalf("Failed to compress artifact: %s", err)
   185  	}
   186  
   187  	return artifactOut
   188  }
   189  
   190  func TestArchive(t *testing.T) {
   191  	tc := map[string]func(*os.File) ([]byte, error){
   192  		"bzip2": func(archive *os.File) ([]byte, error) {
   193  			bzipReader, err := bzip2.NewReader(archive, nil)
   194  			if err != nil {
   195  				return nil, err
   196  			}
   197  			return io.ReadAll(bzipReader)
   198  		},
   199  		"zip": func(archive *os.File) ([]byte, error) {
   200  			fi, _ := archive.Stat()
   201  			zipReader, err := zip.NewReader(archive, fi.Size())
   202  			if err != nil {
   203  				return nil, err
   204  			}
   205  			ctt, err := zipReader.File[0].Open()
   206  			if err != nil {
   207  				return nil, err
   208  			}
   209  			return io.ReadAll(ctt)
   210  		},
   211  		"tar": func(archive *os.File) ([]byte, error) {
   212  			tarReader := tar.NewReader(archive)
   213  			_, err := tarReader.Next()
   214  			if err != nil {
   215  				return nil, err
   216  			}
   217  			return io.ReadAll(tarReader)
   218  		},
   219  		"tar.gz": func(archive *os.File) ([]byte, error) {
   220  			gzipReader, err := gzip.NewReader(archive)
   221  			if err != nil {
   222  				return nil, err
   223  			}
   224  			tarReader := tar.NewReader(gzipReader)
   225  			_, err = tarReader.Next()
   226  			if err != nil {
   227  				return nil, err
   228  			}
   229  			return io.ReadAll(tarReader)
   230  		},
   231  		"gz": func(archive *os.File) ([]byte, error) {
   232  			gzipReader, _ := gzip.NewReader(archive)
   233  			return io.ReadAll(gzipReader)
   234  		},
   235  		"lz4": func(archive *os.File) ([]byte, error) {
   236  			lz4Reader := lz4.NewReader(archive)
   237  			return io.ReadAll(lz4Reader)
   238  		},
   239  	}
   240  
   241  	tmpArchiveFile := "temp-archive-package"
   242  	for format, unzip := range tc {
   243  		t.Run(format, func(t *testing.T) {
   244  			config := fmt.Sprintf(`
   245  		{
   246  			"post-processors": [
   247  				{
   248  					"type": "compress",
   249  					"output": "%s.%s"
   250  				}
   251  			]
   252  		}
   253  		`, tmpArchiveFile, format)
   254  
   255  			artifact := testArchive(t, config)
   256  			defer func() {
   257  				err := artifact.Destroy()
   258  				if err != nil {
   259  					t.Fatal(err)
   260  				}
   261  			}()
   262  
   263  			filename := fmt.Sprintf("%s.%s", tmpArchiveFile, format)
   264  			// Verify things look good
   265  			_, err := os.Stat(filename)
   266  			if err != nil {
   267  				t.Errorf("Unable to read archive: %s", err)
   268  			}
   269  
   270  			archive, err := os.Open(filename)
   271  			if err != nil {
   272  				t.Fatal(err)
   273  			}
   274  			defer func() {
   275  				err := archive.Close()
   276  				if err != nil {
   277  					t.Fatal(err)
   278  				}
   279  			}()
   280  
   281  			found, err := unzip(archive)
   282  			if err != nil {
   283  				t.Fatal(err)
   284  			}
   285  			if string(found) != expectedFileContents {
   286  				t.Errorf("Expected:\n%s\nFound:\n%s\n", expectedFileContents, string(found))
   287  			}
   288  		})
   289  	}
   290  
   291  }