github.com/dacamp/packer@v0.10.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/mitchellh/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 remote path: %s", p.config.RestartTimeout)
    36  	}
    37  
    38  	if p.config.RestartCommand != "shutdown /r /f /t 0 /c \"packer restart\"" {
    39  		t.Errorf("unexpected remote path: %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 remote path: %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.StartExitStatus = 1
   246  	p.Prepare(config)
   247  	err := waitForCommunicator(p)
   248  
   249  	if err != nil {
   250  		t.Fatalf("should not have error, got: %s", err.Error())
   251  	}
   252  
   253  	expectedCommand := DefaultRestartCheckCommand
   254  
   255  	// Should run the command without alteration
   256  	if comm.StartCmd.Command != expectedCommand {
   257  		t.Fatalf("Expect command to be: %s, got %s", expectedCommand, comm.StartCmd.Command)
   258  	}
   259  }
   260  
   261  func TestProvision_waitForCommunicatorWithCancel(t *testing.T) {
   262  	config := testConfig()
   263  
   264  	// Defaults provided by Packer
   265  	ui := testUi()
   266  	p := new(Provisioner)
   267  
   268  	// Defaults provided by Packer
   269  	comm := new(packer.MockCommunicator)
   270  	p.comm = comm
   271  	p.ui = ui
   272  	retryableSleep = 5 * time.Second
   273  	p.cancel = make(chan struct{})
   274  	var err error
   275  
   276  	comm.StartStderr = "WinRM terminated"
   277  	comm.StartExitStatus = 1 // Always fail
   278  	p.Prepare(config)
   279  
   280  	// Run 2 goroutines;
   281  	//  1st to call waitForCommunicator (that will always fail)
   282  	//  2nd to cancel the operation
   283  	waitStart := make(chan bool)
   284  	waitDone := make(chan bool)
   285  	go func() {
   286  		waitStart <- true
   287  		err = waitForCommunicator(p)
   288  		waitDone <- true
   289  	}()
   290  
   291  	go func() {
   292  		time.Sleep(10 * time.Millisecond)
   293  		<-waitStart
   294  		p.Cancel()
   295  	}()
   296  	<-waitDone
   297  
   298  	// Expect a Cancel error
   299  	if err == nil {
   300  		t.Fatalf("Should have err")
   301  	}
   302  }
   303  
   304  func TestRetryable(t *testing.T) {
   305  	config := testConfig()
   306  
   307  	count := 0
   308  	retryMe := func() error {
   309  		t.Logf("RetryMe, attempt number %d", count)
   310  		if count == 2 {
   311  			return nil
   312  		}
   313  		count++
   314  		return errors.New(fmt.Sprintf("Still waiting %d more times...", 2-count))
   315  	}
   316  	retryableSleep = 50 * time.Millisecond
   317  	p := new(Provisioner)
   318  	p.config.RestartTimeout = 155 * time.Millisecond
   319  	err := p.Prepare(config)
   320  	err = p.retryable(retryMe)
   321  	if err != nil {
   322  		t.Fatalf("should not have error retrying funuction")
   323  	}
   324  
   325  	count = 0
   326  	p.config.RestartTimeout = 10 * time.Millisecond
   327  	err = p.Prepare(config)
   328  	err = p.retryable(retryMe)
   329  	if err == nil {
   330  		t.Fatalf("should have error retrying funuction")
   331  	}
   332  }
   333  
   334  func TestProvision_Cancel(t *testing.T) {
   335  	config := testConfig()
   336  
   337  	// Defaults provided by Packer
   338  	ui := testUi()
   339  	p := new(Provisioner)
   340  
   341  	var err error
   342  
   343  	comm := new(packer.MockCommunicator)
   344  	p.Prepare(config)
   345  	waitStart := make(chan bool)
   346  	waitDone := make(chan bool)
   347  
   348  	// Block until cancel comes through
   349  	waitForCommunicator = func(p *Provisioner) error {
   350  		waitStart <- true
   351  		for {
   352  			select {
   353  			case <-p.cancel:
   354  			}
   355  		}
   356  	}
   357  
   358  	// Create two go routines to provision and cancel in parallel
   359  	// Provision will block until cancel happens
   360  	go func() {
   361  		err = p.Provision(ui, comm)
   362  		waitDone <- true
   363  	}()
   364  
   365  	go func() {
   366  		<-waitStart
   367  		p.Cancel()
   368  	}()
   369  	<-waitDone
   370  
   371  	// Expect interupt error
   372  	if err == nil {
   373  		t.Fatal("should have error")
   374  	}
   375  }