github.com/hashicorp/packer@v1.14.3/provisioner/windows-shell/provisioner_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package shell
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"log"
    10  	"os"
    11  	"strings"
    12  	"testing"
    13  
    14  	"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
    15  	packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
    16  )
    17  
    18  func testConfig() map[string]interface{} {
    19  	return map[string]interface{}{
    20  		"inline": []interface{}{"foo", "bar"},
    21  	}
    22  }
    23  
    24  func TestProvisionerPrepare_extractScript(t *testing.T) {
    25  	config := testConfig()
    26  	p := new(Provisioner)
    27  	_ = p.Prepare(config)
    28  	file, err := extractScript(p)
    29  	defer os.Remove(file)
    30  	if err != nil {
    31  		t.Fatalf("Should not be error: %s", err)
    32  	}
    33  	log.Printf("File: %s", file)
    34  	if strings.Index(file, os.TempDir()) != 0 {
    35  		t.Fatalf("Temp file should reside in %s. File location: %s", os.TempDir(), file)
    36  	}
    37  
    38  	// File contents should contain 2 lines concatenated by newlines: foo\nbar
    39  	readFile, err := os.ReadFile(file)
    40  	if err != nil {
    41  		t.Fatalf("Should not be error: %s", err)
    42  	}
    43  	expectedContents := "foo\nbar\n"
    44  	s := string(readFile[:])
    45  	if s != expectedContents {
    46  		t.Fatalf("Expected generated inlineScript to equal '%s', got '%s'", expectedContents, s)
    47  	}
    48  }
    49  
    50  func TestProvisioner_Impl(t *testing.T) {
    51  	var raw interface{}
    52  	raw = &Provisioner{}
    53  	if _, ok := raw.(packersdk.Provisioner); !ok {
    54  		t.Fatalf("must be a Provisioner")
    55  	}
    56  }
    57  
    58  func TestProvisionerPrepare_Defaults(t *testing.T) {
    59  	var p Provisioner
    60  	config := testConfig()
    61  
    62  	err := p.Prepare(config)
    63  	if err != nil {
    64  		t.Fatalf("err: %s", err)
    65  	}
    66  
    67  	if p.config.RemotePath != DefaultRemotePath {
    68  		t.Errorf("unexpected remote path: %s", p.config.RemotePath)
    69  	}
    70  
    71  	if p.config.ExecuteCommand != "{{.Vars}}\"{{.Path}}\"" {
    72  		t.Fatalf("Default command should be powershell {{.Vars}}\"{{.Path}}\", but got %s", p.config.ExecuteCommand)
    73  	}
    74  }
    75  
    76  func TestProvisionerPrepare_Config(t *testing.T) {
    77  
    78  }
    79  
    80  func TestProvisionerPrepare_InvalidKey(t *testing.T) {
    81  	var p Provisioner
    82  	config := testConfig()
    83  
    84  	// Add a random key
    85  	config["i_should_not_be_valid"] = true
    86  	err := p.Prepare(config)
    87  	if err == nil {
    88  		t.Fatal("should have error")
    89  	}
    90  }
    91  
    92  func TestProvisionerPrepare_Script(t *testing.T) {
    93  	config := testConfig()
    94  	delete(config, "inline")
    95  
    96  	config["script"] = "/this/should/not/exist"
    97  	p := new(Provisioner)
    98  	err := p.Prepare(config)
    99  	if err == nil {
   100  		t.Fatal("should have error")
   101  	}
   102  
   103  	// Test with a good one
   104  	tf, err := os.CreateTemp("", "packer")
   105  	if err != nil {
   106  		t.Fatalf("error tempfile: %s", err)
   107  	}
   108  	defer os.Remove(tf.Name())
   109  	defer tf.Close()
   110  
   111  	config["script"] = tf.Name()
   112  	p = new(Provisioner)
   113  	err = p.Prepare(config)
   114  	if err != nil {
   115  		t.Fatalf("should not have error: %s", err)
   116  	}
   117  }
   118  
   119  func TestProvisionerPrepare_ScriptAndInline(t *testing.T) {
   120  	var p Provisioner
   121  	config := testConfig()
   122  
   123  	delete(config, "inline")
   124  	delete(config, "script")
   125  	err := p.Prepare(config)
   126  	if err == nil {
   127  		t.Fatal("should have error")
   128  	}
   129  
   130  	// Test with both
   131  	tf, err := os.CreateTemp("", "packer")
   132  	if err != nil {
   133  		t.Fatalf("error tempfile: %s", err)
   134  	}
   135  	defer os.Remove(tf.Name())
   136  	defer tf.Close()
   137  
   138  	config["inline"] = []interface{}{"foo"}
   139  	config["script"] = tf.Name()
   140  	err = p.Prepare(config)
   141  	if err == nil {
   142  		t.Fatal("should have error")
   143  	}
   144  }
   145  
   146  func TestProvisionerPrepare_ScriptAndScripts(t *testing.T) {
   147  	var p Provisioner
   148  	config := testConfig()
   149  
   150  	// Test with both
   151  	tf, err := os.CreateTemp("", "packer")
   152  	if err != nil {
   153  		t.Fatalf("error tempfile: %s", err)
   154  	}
   155  	defer os.Remove(tf.Name())
   156  	defer tf.Close()
   157  
   158  	config["inline"] = []interface{}{"foo"}
   159  	config["scripts"] = []string{tf.Name()}
   160  	err = p.Prepare(config)
   161  	if err == nil {
   162  		t.Fatal("should have error")
   163  	}
   164  }
   165  
   166  func TestProvisionerPrepare_Scripts(t *testing.T) {
   167  	config := testConfig()
   168  	delete(config, "inline")
   169  
   170  	config["scripts"] = []string{}
   171  	p := new(Provisioner)
   172  	err := p.Prepare(config)
   173  	if err == nil {
   174  		t.Fatal("should have error")
   175  	}
   176  
   177  	// Test with a good one
   178  	tf, err := os.CreateTemp("", "packer")
   179  	if err != nil {
   180  		t.Fatalf("error tempfile: %s", err)
   181  	}
   182  	defer os.Remove(tf.Name())
   183  	defer tf.Close()
   184  
   185  	config["scripts"] = []string{tf.Name()}
   186  	p = new(Provisioner)
   187  	err = p.Prepare(config)
   188  	if err != nil {
   189  		t.Fatalf("should not have error: %s", err)
   190  	}
   191  }
   192  
   193  func TestProvisionerPrepare_EnvironmentVars(t *testing.T) {
   194  	config := testConfig()
   195  
   196  	// Test with a bad case
   197  	config["environment_vars"] = []string{"badvar", "good=var"}
   198  	p := new(Provisioner)
   199  	err := p.Prepare(config)
   200  	if err == nil {
   201  		t.Fatal("should have error")
   202  	}
   203  
   204  	// Test with a trickier case
   205  	config["environment_vars"] = []string{"=bad"}
   206  	p = new(Provisioner)
   207  	err = p.Prepare(config)
   208  	if err == nil {
   209  		t.Fatal("should have error")
   210  	}
   211  
   212  	// Test with a good case
   213  	// Note: baz= is a real env variable, just empty
   214  	config["environment_vars"] = []string{"FOO=bar", "baz="}
   215  	p = new(Provisioner)
   216  	err = p.Prepare(config)
   217  	if err != nil {
   218  		t.Fatalf("should not have error: %s", err)
   219  	}
   220  
   221  	// Test when the env variable value contains an equals sign
   222  	config["environment_vars"] = []string{"good=withequals=true"}
   223  	p = new(Provisioner)
   224  	err = p.Prepare(config)
   225  	if err != nil {
   226  		t.Fatalf("should not have error: %s", err)
   227  	}
   228  
   229  	// Test when the env variable value starts with an equals sign
   230  	config["environment_vars"] = []string{"good==true"}
   231  	p = new(Provisioner)
   232  	err = p.Prepare(config)
   233  	if err != nil {
   234  		t.Fatalf("should not have error: %s", err)
   235  	}
   236  }
   237  
   238  func TestProvisionerQuote_EnvironmentVars(t *testing.T) {
   239  	config := testConfig()
   240  
   241  	config["environment_vars"] = []string{
   242  		"keyone=valueone",
   243  		"keytwo=value\ntwo",
   244  		"keythree='valuethree'",
   245  		"keyfour='value\nfour'",
   246  		"keyfive='value=five'",
   247  		"keysix='=six'",
   248  	}
   249  
   250  	expected := []string{
   251  		"keyone=valueone",
   252  		"keytwo=value\ntwo",
   253  		"keythree='valuethree'",
   254  		"keyfour='value\nfour'",
   255  		"keyfive='value=five'",
   256  		"keysix='=six'",
   257  	}
   258  
   259  	p := new(Provisioner)
   260  	p.Prepare(config)
   261  
   262  	for i, expectedValue := range expected {
   263  		if p.config.Vars[i] != expectedValue {
   264  			t.Fatalf("%s should be equal to %s", p.config.Vars[i], expectedValue)
   265  		}
   266  	}
   267  
   268  }
   269  
   270  func testUi() *packersdk.BasicUi {
   271  	return &packersdk.BasicUi{
   272  		Reader:      new(bytes.Buffer),
   273  		Writer:      new(bytes.Buffer),
   274  		ErrorWriter: new(bytes.Buffer),
   275  	}
   276  }
   277  
   278  func TestProvisionerProvision_Inline(t *testing.T) {
   279  	config := testConfig()
   280  	delete(config, "inline")
   281  
   282  	// Defaults provided by Packer
   283  	config["remote_path"] = "c:/Windows/Temp/inlineScript.bat"
   284  	config["inline"] = []string{"whoami"}
   285  	ui := testUi()
   286  	p := new(Provisioner)
   287  
   288  	// Defaults provided by Packer
   289  	p.config.PackerBuildName = "vmware"
   290  	p.config.PackerBuilderType = "iso"
   291  	comm := new(packersdk.MockCommunicator)
   292  	p.Prepare(config)
   293  
   294  	err := p.Provision(context.Background(), ui, comm, generatedData())
   295  	if err != nil {
   296  		t.Fatal("should not have error")
   297  	}
   298  
   299  	expectedCommand := `set "PACKER_BUILDER_TYPE=iso" && set "PACKER_BUILD_NAME=vmware" && "c:/Windows/Temp/inlineScript.bat"`
   300  
   301  	// Should run the command without alteration
   302  	if comm.StartCmd.Command != expectedCommand {
   303  		t.Fatalf("Expect command to be: %s, got %s", expectedCommand, comm.StartCmd.Command)
   304  	}
   305  
   306  	envVars := make([]string, 2)
   307  	envVars[0] = "FOO=BAR"
   308  	envVars[1] = "BAR=BAZ"
   309  	config["environment_vars"] = envVars
   310  	config["remote_path"] = "c:/Windows/Temp/inlineScript.bat"
   311  
   312  	p.Prepare(config)
   313  	err = p.Provision(context.Background(), ui, comm, generatedData())
   314  	if err != nil {
   315  		t.Fatal("should not have error")
   316  	}
   317  
   318  	expectedCommand = `set "BAR=BAZ" && set "FOO=BAR" && set "PACKER_BUILDER_TYPE=iso" && set "PACKER_BUILD_NAME=vmware" && "c:/Windows/Temp/inlineScript.bat"`
   319  
   320  	// Should run the command without alteration
   321  	if comm.StartCmd.Command != expectedCommand {
   322  		t.Fatalf("Expect command to be: %s, got: %s", expectedCommand, comm.StartCmd.Command)
   323  	}
   324  }
   325  
   326  func TestProvisionerProvision_Scripts(t *testing.T) {
   327  	tf, err := os.CreateTemp("", "packer")
   328  	if err != nil {
   329  		t.Fatalf("error tempfile: %s", err)
   330  	}
   331  	defer os.Remove(tf.Name())
   332  	defer tf.Close()
   333  
   334  	config := testConfig()
   335  	delete(config, "inline")
   336  	config["scripts"] = []string{tf.Name()}
   337  	config["packer_build_name"] = "foobuild"
   338  	config["packer_builder_type"] = "footype"
   339  	ui := testUi()
   340  
   341  	p := new(Provisioner)
   342  	comm := new(packersdk.MockCommunicator)
   343  	p.Prepare(config)
   344  	err = p.Provision(context.Background(), ui, comm, generatedData())
   345  	if err != nil {
   346  		t.Fatal("should not have error")
   347  	}
   348  
   349  	//powershell -Command "$env:PACKER_BUILDER_TYPE=''"; powershell -Command "$env:PACKER_BUILD_NAME='foobuild'";  powershell -Command c:/Windows/Temp/script.ps1
   350  	expectedCommand := `set "PACKER_BUILDER_TYPE=footype" && set "PACKER_BUILD_NAME=foobuild" && "c:/Windows/Temp/script.bat"`
   351  
   352  	// Should run the command without alteration
   353  	if comm.StartCmd.Command != expectedCommand {
   354  		t.Fatalf("Expect command to be %s NOT %s", expectedCommand, comm.StartCmd.Command)
   355  	}
   356  }
   357  
   358  func TestProvisionerProvision_ScriptsWithEnvVars(t *testing.T) {
   359  	tf, err := os.CreateTemp("", "packer")
   360  	if err != nil {
   361  		t.Fatalf("error tempfile: %s", err)
   362  	}
   363  	defer os.Remove(tf.Name())
   364  	defer tf.Close()
   365  
   366  	config := testConfig()
   367  	ui := testUi()
   368  	delete(config, "inline")
   369  
   370  	config["scripts"] = []string{tf.Name()}
   371  	config["packer_build_name"] = "foobuild"
   372  	config["packer_builder_type"] = "footype"
   373  
   374  	// Env vars - currently should not effect them
   375  	envVars := make([]string, 2)
   376  	envVars[0] = "FOO=BAR"
   377  	envVars[1] = "BAR=BAZ"
   378  	config["environment_vars"] = envVars
   379  
   380  	p := new(Provisioner)
   381  	comm := new(packersdk.MockCommunicator)
   382  	p.Prepare(config)
   383  	err = p.Provision(context.Background(), ui, comm, generatedData())
   384  	if err != nil {
   385  		t.Fatal("should not have error")
   386  	}
   387  
   388  	expectedCommand := `set "BAR=BAZ" && set "FOO=BAR" && set "PACKER_BUILDER_TYPE=footype" && set "PACKER_BUILD_NAME=foobuild" && "c:/Windows/Temp/script.bat"`
   389  
   390  	// Should run the command without alteration
   391  	if comm.StartCmd.Command != expectedCommand {
   392  		t.Fatalf("Expect command to be %s NOT %s", expectedCommand, comm.StartCmd.Command)
   393  	}
   394  }
   395  
   396  func TestProvisioner_createFlattenedEnvVars_windows(t *testing.T) {
   397  	var flattenedEnvVars string
   398  	config := testConfig()
   399  
   400  	userEnvVarTests := [][]string{
   401  		{},                     // No user env var
   402  		{"FOO=bar"},            // Single user env var
   403  		{"FOO=bar", "BAZ=qux"}, // Multiple user env vars
   404  		{"FOO=bar=baz"},        // User env var with value containing equals
   405  		{"FOO==bar"},           // User env var with value starting with equals
   406  	}
   407  	userEnvVarmapTests := []map[string]string{
   408  		{},
   409  		{
   410  			"BAR": "foo",
   411  		},
   412  		{
   413  			"BAR": "foo",
   414  			"YAR": "yaa",
   415  		},
   416  		{
   417  			"BAR": "foo=yar",
   418  		},
   419  		{
   420  			"BAR": "=foo",
   421  		},
   422  	}
   423  	expected := []string{
   424  		`set "PACKER_BUILDER_TYPE=iso" && set "PACKER_BUILD_NAME=vmware" && `,
   425  		`set "BAR=foo" && set "FOO=bar" && set "PACKER_BUILDER_TYPE=iso" && set "PACKER_BUILD_NAME=vmware" && `,
   426  		`set "BAR=foo" && set "BAZ=qux" && set "FOO=bar" && set "PACKER_BUILDER_TYPE=iso" && set "PACKER_BUILD_NAME=vmware" && set "YAR=yaa" && `,
   427  		`set "BAR=foo=yar" && set "FOO=bar=baz" && set "PACKER_BUILDER_TYPE=iso" && set "PACKER_BUILD_NAME=vmware" && `,
   428  		`set "BAR==foo" && set "FOO==bar" && set "PACKER_BUILDER_TYPE=iso" && set "PACKER_BUILD_NAME=vmware" && `,
   429  	}
   430  
   431  	p := new(Provisioner)
   432  	p.generatedData = generatedData()
   433  	p.Prepare(config)
   434  
   435  	// Defaults provided by Packer
   436  	p.config.PackerBuildName = "vmware"
   437  	p.config.PackerBuilderType = "iso"
   438  
   439  	for i, expectedValue := range expected {
   440  		p.config.Vars = userEnvVarTests[i]
   441  		p.config.Env = userEnvVarmapTests[i]
   442  		flattenedEnvVars = p.createFlattenedEnvVars()
   443  		if flattenedEnvVars != expectedValue {
   444  			t.Fatalf("expected flattened env vars to be: %s, got %s.", expectedValue, flattenedEnvVars)
   445  		}
   446  	}
   447  }
   448  
   449  func TestCancel(t *testing.T) {
   450  	// Don't actually call Cancel() as it performs an os.Exit(0)
   451  	// which kills the 'go test' tool
   452  }
   453  func generatedData() map[string]interface{} {
   454  	return map[string]interface{}{
   455  		"PackerHTTPAddr": commonsteps.HttpAddrNotImplemented,
   456  		"PackerHTTPIP":   commonsteps.HttpIPNotImplemented,
   457  		"PackerHTTPPort": commonsteps.HttpPortNotImplemented,
   458  	}
   459  }