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 }