gopkg.in/hashicorp/packer.v1@v1.3.2/provisioner/windows-restart/provisioner_test.go (about)

     1  package restart
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/hashicorp/packer/packer"
    11  )
    12  
    13  func testConfig() map[string]interface{} {
    14  	return map[string]interface{}{}
    15  }
    16  
    17  func TestProvisioner_Impl(t *testing.T) {
    18  	var raw interface{}
    19  	raw = &Provisioner{}
    20  	if _, ok := raw.(packer.Provisioner); !ok {
    21  		t.Fatalf("must be a Provisioner")
    22  	}
    23  }
    24  
    25  func TestProvisionerPrepare_Defaults(t *testing.T) {
    26  	var p Provisioner
    27  	config := testConfig()
    28  
    29  	err := p.Prepare(config)
    30  	if err != nil {
    31  		t.Fatalf("err: %s", err)
    32  	}
    33  
    34  	if p.config.RestartTimeout != 5*time.Minute {
    35  		t.Errorf("unexpected restart timeout: %s", p.config.RestartTimeout)
    36  	}
    37  
    38  	if p.config.RestartCommand != "shutdown /r /f /t 0 /c \"packer restart\"" {
    39  		t.Errorf("unexpected restart command: %s", p.config.RestartCommand)
    40  	}
    41  }
    42  
    43  func TestProvisionerPrepare_ConfigRetryTimeout(t *testing.T) {
    44  	var p Provisioner
    45  	config := testConfig()
    46  	config["restart_timeout"] = "1m"
    47  
    48  	err := p.Prepare(config)
    49  	if err != nil {
    50  		t.Fatalf("err: %s", err)
    51  	}
    52  
    53  	if p.config.RestartTimeout != 1*time.Minute {
    54  		t.Errorf("unexpected restart timeout: %s", p.config.RestartTimeout)
    55  	}
    56  }
    57  
    58  func TestProvisionerPrepare_ConfigErrors(t *testing.T) {
    59  	var p Provisioner
    60  	config := testConfig()
    61  	config["restart_timeout"] = "m"
    62  
    63  	err := p.Prepare(config)
    64  	if err == nil {
    65  		t.Fatal("Expected error parsing restart_timeout but did not receive one.")
    66  	}
    67  }
    68  
    69  func TestProvisionerPrepare_InvalidKey(t *testing.T) {
    70  	var p Provisioner
    71  	config := testConfig()
    72  
    73  	// Add a random key
    74  	config["i_should_not_be_valid"] = true
    75  	err := p.Prepare(config)
    76  	if err == nil {
    77  		t.Fatal("should have error")
    78  	}
    79  }
    80  
    81  func testUi() *packer.BasicUi {
    82  	return &packer.BasicUi{
    83  		Reader:      new(bytes.Buffer),
    84  		Writer:      new(bytes.Buffer),
    85  		ErrorWriter: new(bytes.Buffer),
    86  	}
    87  }
    88  
    89  func TestProvisionerProvision_Success(t *testing.T) {
    90  	config := testConfig()
    91  
    92  	// Defaults provided by Packer
    93  	ui := testUi()
    94  	p := new(Provisioner)
    95  
    96  	// Defaults provided by Packer
    97  	comm := new(packer.MockCommunicator)
    98  	p.Prepare(config)
    99  	waitForCommunicatorOld := waitForCommunicator
   100  	waitForCommunicator = func(p *Provisioner) error {
   101  		return nil
   102  	}
   103  	waitForRestartOld := waitForRestart
   104  	waitForRestart = func(p *Provisioner, comm packer.Communicator) error {
   105  		return nil
   106  	}
   107  	err := p.Provision(ui, comm)
   108  	if err != nil {
   109  		t.Fatal("should not have error")
   110  	}
   111  
   112  	expectedCommand := DefaultRestartCommand
   113  
   114  	// Should run the command without alteration
   115  	if comm.StartCmd.Command != expectedCommand {
   116  		t.Fatalf("Expect command to be: %s, got %s", expectedCommand, comm.StartCmd.Command)
   117  	}
   118  	// Set this back!
   119  	waitForCommunicator = waitForCommunicatorOld
   120  	waitForRestart = waitForRestartOld
   121  }
   122  
   123  func TestProvisionerProvision_CustomCommand(t *testing.T) {
   124  	config := testConfig()
   125  
   126  	// Defaults provided by Packer
   127  	ui := testUi()
   128  	p := new(Provisioner)
   129  	expectedCommand := "specialrestart.exe -NOW"
   130  	config["restart_command"] = expectedCommand
   131  
   132  	// Defaults provided by Packer
   133  	comm := new(packer.MockCommunicator)
   134  	p.Prepare(config)
   135  	waitForCommunicatorOld := waitForCommunicator
   136  	waitForCommunicator = func(p *Provisioner) error {
   137  		return nil
   138  	}
   139  	waitForRestartOld := waitForRestart
   140  	waitForRestart = func(p *Provisioner, comm packer.Communicator) error {
   141  		return nil
   142  	}
   143  	err := p.Provision(ui, comm)
   144  	if err != nil {
   145  		t.Fatal("should not have error")
   146  	}
   147  
   148  	// Should run the command without alteration
   149  	if comm.StartCmd.Command != expectedCommand {
   150  		t.Fatalf("Expect command to be: %s, got %s", expectedCommand, comm.StartCmd.Command)
   151  	}
   152  	// Set this back!
   153  	waitForCommunicator = waitForCommunicatorOld
   154  	waitForRestart = waitForRestartOld
   155  }
   156  
   157  func TestProvisionerProvision_RestartCommandFail(t *testing.T) {
   158  	config := testConfig()
   159  	ui := testUi()
   160  	p := new(Provisioner)
   161  	comm := new(packer.MockCommunicator)
   162  	comm.StartStderr = "WinRM terminated"
   163  	comm.StartExitStatus = 1
   164  
   165  	p.Prepare(config)
   166  	err := p.Provision(ui, comm)
   167  	if err == nil {
   168  		t.Fatal("should have error")
   169  	}
   170  }
   171  func TestProvisionerProvision_WaitForRestartFail(t *testing.T) {
   172  	config := testConfig()
   173  
   174  	// Defaults provided by Packer
   175  	ui := testUi()
   176  	p := new(Provisioner)
   177  
   178  	// Defaults provided by Packer
   179  	comm := new(packer.MockCommunicator)
   180  	p.Prepare(config)
   181  	waitForCommunicatorOld := waitForCommunicator
   182  	waitForCommunicator = func(p *Provisioner) error {
   183  		return fmt.Errorf("Machine did not restart properly")
   184  	}
   185  	err := p.Provision(ui, comm)
   186  	if err == nil {
   187  		t.Fatal("should have error")
   188  	}
   189  
   190  	// Set this back!
   191  	waitForCommunicator = waitForCommunicatorOld
   192  }
   193  
   194  func TestProvision_waitForRestartTimeout(t *testing.T) {
   195  	retryableSleep = 10 * time.Millisecond
   196  	config := testConfig()
   197  	config["restart_timeout"] = "1ms"
   198  	ui := testUi()
   199  	p := new(Provisioner)
   200  	comm := new(packer.MockCommunicator)
   201  	var err error
   202  
   203  	p.Prepare(config)
   204  	waitForCommunicatorOld := waitForCommunicator
   205  	waitDone := make(chan bool)
   206  	waitContinue := make(chan bool)
   207  
   208  	// Block until cancel comes through
   209  	waitForCommunicator = func(p *Provisioner) error {
   210  		for {
   211  			select {
   212  			case <-waitDone:
   213  				waitContinue <- true
   214  			}
   215  		}
   216  	}
   217  
   218  	go func() {
   219  		err = p.Provision(ui, comm)
   220  		waitDone <- true
   221  	}()
   222  	<-waitContinue
   223  
   224  	if err == nil {
   225  		t.Fatal("should not have error")
   226  	}
   227  
   228  	// Set this back!
   229  	waitForCommunicator = waitForCommunicatorOld
   230  
   231  }
   232  
   233  func TestProvision_waitForCommunicator(t *testing.T) {
   234  	config := testConfig()
   235  
   236  	// Defaults provided by Packer
   237  	ui := testUi()
   238  	p := new(Provisioner)
   239  
   240  	// Defaults provided by Packer
   241  	comm := new(packer.MockCommunicator)
   242  	p.comm = comm
   243  	p.ui = ui
   244  	comm.StartStderr = "WinRM terminated"
   245  	comm.StartStdout = "WIN-V4CEJ7MC5SN restarted."
   246  	comm.StartExitStatus = 1
   247  	p.Prepare(config)
   248  	err := waitForCommunicator(p)
   249  
   250  	if err != nil {
   251  		t.Fatalf("should not have error, got: %s", err.Error())
   252  	}
   253  
   254  	expectedCommand := DefaultRestartCheckCommand
   255  
   256  	// Should run the command without alteration
   257  	if comm.StartCmd.Command != expectedCommand {
   258  		t.Fatalf("Expect command to be: %s, got %s", expectedCommand, comm.StartCmd.Command)
   259  	}
   260  }
   261  
   262  func TestProvision_waitForCommunicatorWithCancel(t *testing.T) {
   263  	config := testConfig()
   264  
   265  	// Defaults provided by Packer
   266  	ui := testUi()
   267  	p := new(Provisioner)
   268  
   269  	// Defaults provided by Packer
   270  	comm := new(packer.MockCommunicator)
   271  	p.comm = comm
   272  	p.ui = ui
   273  	retryableSleep = 5 * time.Second
   274  	p.cancel = make(chan struct{})
   275  	var err error
   276  
   277  	comm.StartStderr = "WinRM terminated"
   278  	comm.StartExitStatus = 1 // Always fail
   279  	p.Prepare(config)
   280  
   281  	// Run 2 goroutines;
   282  	//  1st to call waitForCommunicator (that will always fail)
   283  	//  2nd to cancel the operation
   284  	waitStart := make(chan bool)
   285  	waitDone := make(chan bool)
   286  	go func() {
   287  		waitStart <- true
   288  		err = waitForCommunicator(p)
   289  		waitDone <- true
   290  	}()
   291  
   292  	go func() {
   293  		time.Sleep(10 * time.Millisecond)
   294  		<-waitStart
   295  		p.Cancel()
   296  	}()
   297  	<-waitDone
   298  
   299  	// Expect a Cancel error
   300  	if err == nil {
   301  		t.Fatalf("Should have err")
   302  	}
   303  }
   304  
   305  func TestRetryable(t *testing.T) {
   306  	config := testConfig()
   307  
   308  	count := 0
   309  	retryMe := func() error {
   310  		t.Logf("RetryMe, attempt number %d", count)
   311  		if count == 2 {
   312  			return nil
   313  		}
   314  		count++
   315  		return errors.New(fmt.Sprintf("Still waiting %d more times...", 2-count))
   316  	}
   317  	retryableSleep = 50 * time.Millisecond
   318  	p := new(Provisioner)
   319  	p.config.RestartTimeout = 155 * time.Millisecond
   320  	err := p.Prepare(config)
   321  	err = p.retryable(retryMe)
   322  	if err != nil {
   323  		t.Fatalf("should not have error retrying function")
   324  	}
   325  
   326  	count = 0
   327  	p.config.RestartTimeout = 10 * time.Millisecond
   328  	err = p.Prepare(config)
   329  	err = p.retryable(retryMe)
   330  	if err == nil {
   331  		t.Fatalf("should have error retrying function")
   332  	}
   333  }
   334  
   335  func TestProvision_Cancel(t *testing.T) {
   336  	config := testConfig()
   337  
   338  	// Defaults provided by Packer
   339  	ui := testUi()
   340  	p := new(Provisioner)
   341  
   342  	var err error
   343  
   344  	comm := new(packer.MockCommunicator)
   345  	p.Prepare(config)
   346  	waitStart := make(chan bool)
   347  	waitDone := make(chan bool)
   348  
   349  	// Block until cancel comes through
   350  	waitForCommunicator = func(p *Provisioner) error {
   351  		waitStart <- true
   352  		for {
   353  			select {
   354  			case <-p.cancel:
   355  			}
   356  		}
   357  	}
   358  
   359  	// Create two go routines to provision and cancel in parallel
   360  	// Provision will block until cancel happens
   361  	go func() {
   362  		err = p.Provision(ui, comm)
   363  		waitDone <- true
   364  	}()
   365  
   366  	go func() {
   367  		<-waitStart
   368  		p.Cancel()
   369  	}()
   370  	<-waitDone
   371  
   372  	// Expect interrupt error
   373  	if err == nil {
   374  		t.Fatal("should have error")
   375  	}
   376  }