github.com/emate/packer@v0.8.1-0.20150625195101-fe0fde195dc6/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 != "powershell \"& {Restart-Computer -force }\"" {
    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  	err := p.Provision(ui, comm)
   104  	if err != nil {
   105  		t.Fatal("should not have error")
   106  	}
   107  
   108  	expectedCommand := DefaultRestartCommand
   109  
   110  	// Should run the command without alteration
   111  	if comm.StartCmd.Command != expectedCommand {
   112  		t.Fatalf("Expect command to be: %s, got %s", expectedCommand, comm.StartCmd.Command)
   113  	}
   114  	// Set this back!
   115  	waitForCommunicator = waitForCommunicatorOld
   116  }
   117  
   118  func TestProvisionerProvision_CustomCommand(t *testing.T) {
   119  	config := testConfig()
   120  
   121  	// Defaults provided by Packer
   122  	ui := testUi()
   123  	p := new(Provisioner)
   124  	expectedCommand := "specialrestart.exe -NOW"
   125  	config["restart_command"] = expectedCommand
   126  
   127  	// Defaults provided by Packer
   128  	comm := new(packer.MockCommunicator)
   129  	p.Prepare(config)
   130  	waitForCommunicatorOld := waitForCommunicator
   131  	waitForCommunicator = func(p *Provisioner) error {
   132  		return nil
   133  	}
   134  	err := p.Provision(ui, comm)
   135  	if err != nil {
   136  		t.Fatal("should not have error")
   137  	}
   138  
   139  	// Should run the command without alteration
   140  	if comm.StartCmd.Command != expectedCommand {
   141  		t.Fatalf("Expect command to be: %s, got %s", expectedCommand, comm.StartCmd.Command)
   142  	}
   143  	// Set this back!
   144  	waitForCommunicator = waitForCommunicatorOld
   145  }
   146  
   147  func TestProvisionerProvision_RestartCommandFail(t *testing.T) {
   148  	config := testConfig()
   149  	ui := testUi()
   150  	p := new(Provisioner)
   151  	comm := new(packer.MockCommunicator)
   152  	comm.StartStderr = "WinRM terminated"
   153  	comm.StartExitStatus = 1
   154  
   155  	p.Prepare(config)
   156  	err := p.Provision(ui, comm)
   157  	if err == nil {
   158  		t.Fatal("should have error")
   159  	}
   160  }
   161  func TestProvisionerProvision_WaitForRestartFail(t *testing.T) {
   162  	config := testConfig()
   163  
   164  	// Defaults provided by Packer
   165  	ui := testUi()
   166  	p := new(Provisioner)
   167  
   168  	// Defaults provided by Packer
   169  	comm := new(packer.MockCommunicator)
   170  	p.Prepare(config)
   171  	waitForCommunicatorOld := waitForCommunicator
   172  	waitForCommunicator = func(p *Provisioner) error {
   173  		return fmt.Errorf("Machine did not restart properly")
   174  	}
   175  	err := p.Provision(ui, comm)
   176  	if err == nil {
   177  		t.Fatal("should have error")
   178  	}
   179  
   180  	// Set this back!
   181  	waitForCommunicator = waitForCommunicatorOld
   182  }
   183  
   184  func TestProvision_waitForRestartTimeout(t *testing.T) {
   185  	retryableSleep = 10 * time.Millisecond
   186  	config := testConfig()
   187  	config["restart_timeout"] = "1ms"
   188  	ui := testUi()
   189  	p := new(Provisioner)
   190  	comm := new(packer.MockCommunicator)
   191  	var err error
   192  
   193  	p.Prepare(config)
   194  	waitForCommunicatorOld := waitForCommunicator
   195  	waitDone := make(chan bool)
   196  
   197  	// Block until cancel comes through
   198  	waitForCommunicator = func(p *Provisioner) error {
   199  		for {
   200  			select {
   201  			case <-waitDone:
   202  			}
   203  		}
   204  	}
   205  
   206  	go func() {
   207  		err = p.Provision(ui, comm)
   208  		waitDone <- true
   209  	}()
   210  	<-waitDone
   211  
   212  	if err == nil {
   213  		t.Fatal("should not have error")
   214  	}
   215  
   216  	// Set this back!
   217  	waitForCommunicator = waitForCommunicatorOld
   218  
   219  }
   220  
   221  func TestProvision_waitForCommunicator(t *testing.T) {
   222  	config := testConfig()
   223  
   224  	// Defaults provided by Packer
   225  	ui := testUi()
   226  	p := new(Provisioner)
   227  
   228  	// Defaults provided by Packer
   229  	comm := new(packer.MockCommunicator)
   230  	p.comm = comm
   231  	p.ui = ui
   232  	comm.StartStderr = "WinRM terminated"
   233  	comm.StartExitStatus = 1
   234  	p.Prepare(config)
   235  	err := waitForCommunicator(p)
   236  
   237  	if err != nil {
   238  		t.Fatalf("should not have error, got: %s", err.Error())
   239  	}
   240  
   241  	expectedCommand := DefaultRestartCheckCommand
   242  
   243  	// Should run the command without alteration
   244  	if comm.StartCmd.Command != expectedCommand {
   245  		t.Fatalf("Expect command to be: %s, got %s", expectedCommand, comm.StartCmd.Command)
   246  	}
   247  }
   248  
   249  func TestProvision_waitForCommunicatorWithCancel(t *testing.T) {
   250  	config := testConfig()
   251  
   252  	// Defaults provided by Packer
   253  	ui := testUi()
   254  	p := new(Provisioner)
   255  
   256  	// Defaults provided by Packer
   257  	comm := new(packer.MockCommunicator)
   258  	p.comm = comm
   259  	p.ui = ui
   260  	retryableSleep = 5 * time.Second
   261  	p.cancel = make(chan struct{})
   262  	var err error
   263  
   264  	comm.StartStderr = "WinRM terminated"
   265  	comm.StartExitStatus = 1 // Always fail
   266  	p.Prepare(config)
   267  
   268  	// Run 2 goroutines;
   269  	//  1st to call waitForCommunicator (that will always fail)
   270  	//  2nd to cancel the operation
   271  	waitDone := make(chan bool)
   272  	go func() {
   273  		err = waitForCommunicator(p)
   274  	}()
   275  
   276  	go func() {
   277  		time.Sleep(10 * time.Millisecond)
   278  		p.Cancel()
   279  		waitDone <- true
   280  	}()
   281  	<-waitDone
   282  
   283  	// Expect a Cancel error
   284  	if err == nil {
   285  		t.Fatalf("Should have err")
   286  	}
   287  }
   288  
   289  func TestRetryable(t *testing.T) {
   290  	config := testConfig()
   291  
   292  	count := 0
   293  	retryMe := func() error {
   294  		t.Logf("RetryMe, attempt number %d", count)
   295  		if count == 2 {
   296  			return nil
   297  		}
   298  		count++
   299  		return errors.New(fmt.Sprintf("Still waiting %d more times...", 2-count))
   300  	}
   301  	retryableSleep = 50 * time.Millisecond
   302  	p := new(Provisioner)
   303  	p.config.RestartTimeout = 155 * time.Millisecond
   304  	err := p.Prepare(config)
   305  	err = p.retryable(retryMe)
   306  	if err != nil {
   307  		t.Fatalf("should not have error retrying funuction")
   308  	}
   309  
   310  	count = 0
   311  	p.config.RestartTimeout = 10 * time.Millisecond
   312  	err = p.Prepare(config)
   313  	err = p.retryable(retryMe)
   314  	if err == nil {
   315  		t.Fatalf("should have error retrying funuction")
   316  	}
   317  }
   318  
   319  func TestProvision_Cancel(t *testing.T) {
   320  	config := testConfig()
   321  
   322  	// Defaults provided by Packer
   323  	ui := testUi()
   324  	p := new(Provisioner)
   325  
   326  	var err error
   327  
   328  	comm := new(packer.MockCommunicator)
   329  	p.Prepare(config)
   330  	waitDone := make(chan bool)
   331  
   332  	// Block until cancel comes through
   333  	waitForCommunicator = func(p *Provisioner) error {
   334  		for {
   335  			select {
   336  			case <-waitDone:
   337  			}
   338  		}
   339  	}
   340  
   341  	// Create two go routines to provision and cancel in parallel
   342  	// Provision will block until cancel happens
   343  	go func() {
   344  		err = p.Provision(ui, comm)
   345  		waitDone <- true
   346  	}()
   347  
   348  	go func() {
   349  		p.Cancel()
   350  	}()
   351  	<-waitDone
   352  
   353  	// Expect interupt error
   354  	if err == nil {
   355  		t.Fatal("should have error")
   356  	}
   357  }