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  }