github.com/hashicorp/packer@v1.14.3/provisioner/file/provisioner_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package file 5 6 import ( 7 "bytes" 8 "context" 9 "os" 10 "path/filepath" 11 "regexp" 12 "strings" 13 "testing" 14 15 packersdk "github.com/hashicorp/packer-plugin-sdk/packer" 16 ) 17 18 func testConfig() map[string]interface{} { 19 return map[string]interface{}{ 20 "destination": "something", 21 } 22 } 23 24 func TestProvisioner_Impl(t *testing.T) { 25 var raw interface{} 26 raw = &Provisioner{} 27 if _, ok := raw.(packersdk.Provisioner); !ok { 28 t.Fatalf("must be a provisioner") 29 } 30 } 31 32 func TestProvisionerPrepare_InvalidKey(t *testing.T) { 33 var p Provisioner 34 config := testConfig() 35 36 // Add a random key 37 config["i_should_not_be_valid"] = true 38 err := p.Prepare(config) 39 if err == nil { 40 t.Fatal("should have error") 41 } 42 } 43 44 func TestProvisionerPrepare_InvalidSource(t *testing.T) { 45 var p Provisioner 46 config := testConfig() 47 config["source"] = "/this/should/not/exist" 48 49 err := p.Prepare(config) 50 if err == nil { 51 t.Fatalf("should require existing file") 52 } 53 54 config["generated"] = false 55 err = p.Prepare(config) 56 if err == nil { 57 t.Fatalf("should required existing file") 58 } 59 } 60 61 func TestProvisionerPrepare_ValidSource(t *testing.T) { 62 var p Provisioner 63 64 tf, err := os.CreateTemp("", "packer") 65 if err != nil { 66 t.Fatalf("error tempfile: %s", err) 67 } 68 defer os.Remove(tf.Name()) 69 70 config := testConfig() 71 config["source"] = tf.Name() 72 err = p.Prepare(config) 73 if err != nil { 74 t.Fatalf("should allow valid file: %s", err) 75 } 76 77 config["generated"] = false 78 err = p.Prepare(config) 79 if err != nil { 80 t.Fatalf("should allow valid file: %s", err) 81 } 82 } 83 84 func TestProvisionerPrepare_GeneratedSource(t *testing.T) { 85 var p Provisioner 86 87 config := testConfig() 88 config["source"] = "/this/should/not/exist" 89 config["generated"] = true 90 err := p.Prepare(config) 91 if err != nil { 92 t.Fatalf("should allow non-existing file: %s", err) 93 } 94 } 95 96 func TestProvisionerPrepare_EmptyDestination(t *testing.T) { 97 var p Provisioner 98 99 config := testConfig() 100 delete(config, "destination") 101 err := p.Prepare(config) 102 if err == nil { 103 t.Fatalf("should require destination path") 104 } 105 } 106 107 func TestProvisionerProvision_SendsFile(t *testing.T) { 108 var p Provisioner 109 tf, err := os.CreateTemp("", "packer") 110 if err != nil { 111 t.Fatalf("error tempfile: %s", err) 112 } 113 defer os.Remove(tf.Name()) 114 115 if _, err = tf.Write([]byte("hello")); err != nil { 116 t.Fatalf("error writing tempfile: %s", err) 117 } 118 119 config := map[string]interface{}{ 120 "source": tf.Name(), 121 "destination": "something", 122 } 123 124 if err := p.Prepare(config); err != nil { 125 t.Fatalf("err: %s", err) 126 } 127 128 b := bytes.NewBuffer(nil) 129 ui := &packersdk.BasicUi{ 130 Writer: b, 131 PB: &packersdk.NoopProgressTracker{}, 132 } 133 comm := &packersdk.MockCommunicator{} 134 err = p.Provision(context.Background(), ui, comm, make(map[string]interface{})) 135 if err != nil { 136 t.Fatalf("should successfully provision: %s", err) 137 } 138 139 if !strings.Contains(b.String(), tf.Name()) { 140 t.Fatalf("should print source filename") 141 } 142 143 if !strings.Contains(b.String(), "something") { 144 t.Fatalf("should print destination filename") 145 } 146 147 if comm.UploadPath != "something" { 148 t.Fatalf("should upload to configured destination") 149 } 150 151 if comm.UploadData != "hello" { 152 t.Fatalf("should upload with source file's data") 153 } 154 } 155 156 func TestProvisionerProvision_SendsContent(t *testing.T) { 157 var p Provisioner 158 159 dst := "something.txt" 160 content := "hello" 161 config := map[string]interface{}{ 162 "content": content, 163 "destination": dst, 164 } 165 166 if err := p.Prepare(config); err != nil { 167 t.Fatalf("err: %s", err) 168 } 169 170 b := bytes.NewBuffer(nil) 171 ui := &packersdk.BasicUi{ 172 Writer: b, 173 PB: &packersdk.NoopProgressTracker{}, 174 } 175 comm := &packersdk.MockCommunicator{} 176 err := p.Provision(context.Background(), ui, comm, make(map[string]interface{})) 177 if err != nil { 178 t.Fatalf("should successfully provision: %s", err) 179 } 180 181 if !strings.Contains(b.String(), "something") { 182 t.Fatalf("should print destination filename") 183 } 184 185 if comm.UploadPath != dst { 186 t.Fatalf("should upload to configured destination") 187 } 188 189 if comm.UploadData != content { 190 t.Fatalf("should upload with source file's data") 191 } 192 193 } 194 195 func TestProvisionerProvision_SendsFileMultipleFiles(t *testing.T) { 196 var p Provisioner 197 tf1, err := os.CreateTemp("", "packer") 198 if err != nil { 199 t.Fatalf("error tempfile: %s", err) 200 } 201 defer os.Remove(tf1.Name()) 202 203 if _, err = tf1.Write([]byte("hello")); err != nil { 204 t.Fatalf("error writing tempfile: %s", err) 205 } 206 207 tf2, err := os.CreateTemp("", "packer") 208 if err != nil { 209 t.Fatalf("error tempfile: %s", err) 210 } 211 defer os.Remove(tf2.Name()) 212 213 if _, err = tf2.Write([]byte("hello")); err != nil { 214 t.Fatalf("error writing tempfile: %s", err) 215 } 216 217 config := map[string]interface{}{ 218 "sources": []string{tf1.Name(), tf2.Name()}, 219 "destination": "something", 220 } 221 222 if err := p.Prepare(config); err != nil { 223 t.Fatalf("err: %s", err) 224 } 225 226 b := bytes.NewBuffer(nil) 227 ui := &packersdk.BasicUi{ 228 Writer: b, 229 PB: &packersdk.NoopProgressTracker{}, 230 } 231 comm := &packersdk.MockCommunicator{} 232 err = p.Provision(context.Background(), ui, comm, make(map[string]interface{})) 233 if err != nil { 234 t.Fatalf("should successfully provision: %s", err) 235 } 236 237 if !strings.Contains(b.String(), tf1.Name()) { 238 t.Fatalf("should print first source filename") 239 } 240 241 if !strings.Contains(b.String(), tf2.Name()) { 242 t.Fatalf("should print second source filename") 243 } 244 } 245 246 func TestProvisionerProvision_SendsFileMultipleDirs(t *testing.T) { 247 var p Provisioner 248 249 // Prepare the first directory 250 td1, err := os.MkdirTemp("", "packerdir") 251 if err != nil { 252 t.Fatalf("error temp folder 1: %s", err) 253 } 254 defer os.Remove(td1) 255 256 tf1, err := os.CreateTemp(td1, "packer") 257 if err != nil { 258 t.Fatalf("error tempfile: %s", err) 259 } 260 261 if _, err = tf1.Write([]byte("hello")); err != nil { 262 t.Fatalf("error writing tempfile: %s", err) 263 } 264 265 // Prepare the second directory 266 td2, err := os.MkdirTemp("", "packerdir") 267 if err != nil { 268 t.Fatalf("error temp folder 1: %s", err) 269 } 270 defer os.Remove(td2) 271 272 tf2, err := os.CreateTemp(td2, "packer") 273 if err != nil { 274 t.Fatalf("error tempfile: %s", err) 275 } 276 277 if _, err = tf2.Write([]byte("hello")); err != nil { 278 t.Fatalf("error writing tempfile: %s", err) 279 } 280 281 if _, err = tf1.Write([]byte("hello")); err != nil { 282 t.Fatalf("error writing tempfile: %s", err) 283 } 284 285 // Run Provision 286 287 config := map[string]interface{}{ 288 "sources": []string{td1, td2}, 289 "destination": "something", 290 } 291 292 if err := p.Prepare(config); err != nil { 293 t.Fatalf("err: %s", err) 294 } 295 296 b := bytes.NewBuffer(nil) 297 ui := &packersdk.BasicUi{ 298 Writer: b, 299 PB: &packersdk.NoopProgressTracker{}, 300 } 301 comm := &packersdk.MockCommunicator{} 302 err = p.Provision(context.Background(), ui, comm, make(map[string]interface{})) 303 if err != nil { 304 t.Fatalf("should successfully provision: %s", err) 305 } 306 307 if !strings.Contains(b.String(), td1) { 308 t.Fatalf("should print first directory") 309 } 310 311 if !strings.Contains(b.String(), td2) { 312 t.Fatalf("should print second directory") 313 } 314 } 315 316 func TestProvisionerProvision_DownloadsMultipleFilesToFolder(t *testing.T) { 317 var p Provisioner 318 319 tf1, err := os.CreateTemp("", "packer") 320 if err != nil { 321 t.Fatalf("error tempfile: %s", err) 322 } 323 defer os.Remove(tf1.Name()) 324 325 if _, err = tf1.Write([]byte("hello")); err != nil { 326 t.Fatalf("error writing tempfile: %s", err) 327 } 328 329 tf2, err := os.CreateTemp("", "packer") 330 if err != nil { 331 t.Fatalf("error tempfile: %s", err) 332 } 333 defer os.Remove(tf2.Name()) 334 335 if _, err = tf2.Write([]byte("hello")); err != nil { 336 t.Fatalf("error writing tempfile: %s", err) 337 } 338 339 config := map[string]interface{}{ 340 "sources": []string{tf1.Name(), tf2.Name()}, 341 "destination": "something/", 342 "direction": "download", 343 } 344 345 // Cleaning up destination directory 346 cwd, err := os.Getwd() 347 if err != nil { 348 t.Fatalf("Failed getting current working directory") 349 } 350 destinationDir := filepath.Join(cwd, "something") 351 defer os.RemoveAll(destinationDir) 352 353 if err := p.Prepare(config); err != nil { 354 t.Fatalf("err: %s", err) 355 } 356 357 b := bytes.NewBuffer(nil) 358 ui := &packersdk.BasicUi{ 359 Writer: b, 360 PB: &packersdk.NoopProgressTracker{}, 361 } 362 comm := &packersdk.MockCommunicator{} 363 err = p.Provision(context.Background(), ui, comm, make(map[string]interface{})) 364 if err != nil { 365 t.Fatalf("should successfully provision: %s", err) 366 } 367 368 if !strings.Contains(b.String(), tf1.Name()) { 369 t.Errorf("should print source filenam '%s'e; output: \n%s", tf1.Name(), b.String()) 370 } 371 372 if !strings.Contains(b.String(), tf2.Name()) { 373 t.Errorf("should second source filename '%s'; output: \n%s", tf2.Name(), b.String()) 374 } 375 376 dst1 := filepath.Join("something", filepath.Base(tf1.Name())) 377 if !strings.Contains(b.String(), dst1) { 378 t.Errorf("should print destination filename '%s'; output: \n%s", dst1, b.String()) 379 } 380 381 dst2 := filepath.Join("something", filepath.Base(tf2.Name())) 382 if !strings.Contains(b.String(), dst2) { 383 t.Errorf("should print destination filename '%s'; output: \n%s", dst2, b.String()) 384 } 385 } 386 387 func TestProvisionerProvision_SendsFileMultipleFilesToFolder(t *testing.T) { 388 var p Provisioner 389 390 tf1, err := os.CreateTemp("", "packer") 391 if err != nil { 392 t.Fatalf("error tempfile: %s", err) 393 } 394 defer os.Remove(tf1.Name()) 395 396 if _, err = tf1.Write([]byte("hello")); err != nil { 397 t.Fatalf("error writing tempfile: %s", err) 398 } 399 400 tf2, err := os.CreateTemp("", "packer") 401 if err != nil { 402 t.Fatalf("error tempfile: %s", err) 403 } 404 defer os.Remove(tf2.Name()) 405 406 if _, err = tf2.Write([]byte("hello")); err != nil { 407 t.Fatalf("error writing tempfile: %s", err) 408 } 409 410 config := map[string]interface{}{ 411 "sources": []string{tf1.Name(), tf2.Name()}, 412 "destination": "something/", 413 } 414 415 if err := p.Prepare(config); err != nil { 416 t.Fatalf("err: %s", err) 417 } 418 419 b := bytes.NewBuffer(nil) 420 ui := &packersdk.BasicUi{ 421 Writer: b, 422 PB: &packersdk.NoopProgressTracker{}, 423 } 424 comm := &packersdk.MockCommunicator{} 425 err = p.Provision(context.Background(), ui, comm, make(map[string]interface{})) 426 if err != nil { 427 t.Fatalf("should successfully provision: %s", err) 428 } 429 430 if !strings.Contains(b.String(), tf1.Name()) { 431 t.Fatalf("should print first source filename") 432 } 433 434 if !strings.Contains(b.String(), tf2.Name()) { 435 t.Fatalf("should print second source filename") 436 } 437 438 dstRegex := regexp.MustCompile("something/\n") 439 allDst := dstRegex.FindAllString(b.String(), -1) 440 if len(allDst) != 2 { 441 t.Fatalf("some destinations are broken; output: \n%s", b.String()) 442 } 443 } 444 445 func TestProvisionDownloadMkdirAll(t *testing.T) { 446 tests := []struct { 447 path string 448 }{ 449 {"dir"}, 450 {"dir/"}, 451 {"dir/subdir"}, 452 {"dir/subdir/"}, 453 {"path/to/dir"}, 454 {"path/to/dir/"}, 455 } 456 tmpDir, err := os.MkdirTemp("", "packer-file") 457 if err != nil { 458 t.Fatalf("error tempdir: %s", err) 459 } 460 defer os.RemoveAll(tmpDir) 461 tf, err := os.CreateTemp(tmpDir, "packer") 462 if err != nil { 463 t.Fatalf("error tempfile: %s", err) 464 } 465 defer os.Remove(tf.Name()) 466 467 config := map[string]interface{}{ 468 "source": tf.Name(), 469 } 470 var p Provisioner 471 for _, test := range tests { 472 path := filepath.Join(tmpDir, test.path) 473 config["destination"] = filepath.Join(path, "something") 474 if err := p.Prepare(config); err != nil { 475 t.Fatalf("err: %s", err) 476 } 477 b := bytes.NewBuffer(nil) 478 ui := &packersdk.BasicUi{ 479 Writer: b, 480 PB: &packersdk.NoopProgressTracker{}, 481 } 482 comm := &packersdk.MockCommunicator{} 483 err = p.ProvisionDownload(ui, comm) 484 if err != nil { 485 t.Fatalf("should successfully provision: %s", err) 486 } 487 488 if !strings.Contains(b.String(), tf.Name()) { 489 t.Fatalf("should print source filename") 490 } 491 492 if !strings.Contains(b.String(), "something") { 493 t.Fatalf("should print destination filename") 494 } 495 496 if _, err := os.Stat(path); err != nil { 497 t.Fatalf("stat of download dir should not error: %s", err) 498 } 499 500 if _, err := os.Stat(config["destination"].(string)); err != nil { 501 t.Fatalf("stat of destination file should not error: %s", err) 502 } 503 } 504 }