github.com/emate/packer@v0.8.1-0.20150625195101-fe0fde195dc6/helper/communicator/step_connect_winrm.go (about)

     1  package communicator
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"log"
     7  	"time"
     8  
     9  	"github.com/mitchellh/multistep"
    10  	"github.com/mitchellh/packer/communicator/winrm"
    11  	"github.com/mitchellh/packer/packer"
    12  )
    13  
    14  // StepConnectWinRM is a multistep Step implementation that waits for WinRM
    15  // to become available. It gets the connection information from a single
    16  // configuration when creating the step.
    17  //
    18  // Uses:
    19  //   ui packer.Ui
    20  //
    21  // Produces:
    22  //   communicator packer.Communicator
    23  type StepConnectWinRM struct {
    24  	// All the fields below are documented on StepConnect
    25  	Config      *Config
    26  	Host        func(multistep.StateBag) (string, error)
    27  	WinRMConfig func(multistep.StateBag) (*WinRMConfig, error)
    28  }
    29  
    30  func (s *StepConnectWinRM) Run(state multistep.StateBag) multistep.StepAction {
    31  	ui := state.Get("ui").(packer.Ui)
    32  
    33  	var comm packer.Communicator
    34  	var err error
    35  
    36  	cancel := make(chan struct{})
    37  	waitDone := make(chan bool, 1)
    38  	go func() {
    39  		ui.Say("Waiting for WinRM to become available...")
    40  		comm, err = s.waitForWinRM(state, cancel)
    41  		waitDone <- true
    42  	}()
    43  
    44  	log.Printf("Waiting for WinRM, up to timeout: %s", s.Config.WinRMTimeout)
    45  	timeout := time.After(s.Config.WinRMTimeout)
    46  WaitLoop:
    47  	for {
    48  		// Wait for either WinRM to become available, a timeout to occur,
    49  		// or an interrupt to come through.
    50  		select {
    51  		case <-waitDone:
    52  			if err != nil {
    53  				ui.Error(fmt.Sprintf("Error waiting for WinRM: %s", err))
    54  				return multistep.ActionHalt
    55  			}
    56  
    57  			ui.Say("Connected to WinRM!")
    58  			state.Put("communicator", comm)
    59  			break WaitLoop
    60  		case <-timeout:
    61  			err := fmt.Errorf("Timeout waiting for WinRM.")
    62  			state.Put("error", err)
    63  			ui.Error(err.Error())
    64  			close(cancel)
    65  			return multistep.ActionHalt
    66  		case <-time.After(1 * time.Second):
    67  			if _, ok := state.GetOk(multistep.StateCancelled); ok {
    68  				// The step sequence was cancelled, so cancel waiting for WinRM
    69  				// and just start the halting process.
    70  				close(cancel)
    71  				log.Println("Interrupt detected, quitting waiting for WinRM.")
    72  				return multistep.ActionHalt
    73  			}
    74  		}
    75  	}
    76  
    77  	return multistep.ActionContinue
    78  }
    79  
    80  func (s *StepConnectWinRM) Cleanup(multistep.StateBag) {
    81  }
    82  
    83  func (s *StepConnectWinRM) waitForWinRM(state multistep.StateBag, cancel <-chan struct{}) (packer.Communicator, error) {
    84  	var comm packer.Communicator
    85  	for {
    86  		select {
    87  		case <-cancel:
    88  			log.Println("[INFO] WinRM wait cancelled. Exiting loop.")
    89  			return nil, errors.New("WinRM wait cancelled")
    90  		case <-time.After(5 * time.Second):
    91  		}
    92  
    93  		host, err := s.Host(state)
    94  		if err != nil {
    95  			log.Printf("[DEBUG] Error getting WinRM host: %s", err)
    96  			continue
    97  		}
    98  		port := s.Config.WinRMPort
    99  
   100  		user := s.Config.WinRMUser
   101  		password := s.Config.WinRMPassword
   102  		if s.WinRMConfig != nil {
   103  			config, err := s.WinRMConfig(state)
   104  			if err != nil {
   105  				log.Printf("[DEBUG] Error getting WinRM config: %s", err)
   106  				continue
   107  			}
   108  
   109  			if config.Username != "" {
   110  				user = config.Username
   111  			}
   112  			if config.Password != "" {
   113  				password = config.Password
   114  			}
   115  		}
   116  
   117  		log.Println("[INFO] Attempting WinRM connection...")
   118  		comm, err = winrm.New(&winrm.Config{
   119  			Host:     host,
   120  			Port:     port,
   121  			Username: user,
   122  			Password: password,
   123  			Timeout:  s.Config.WinRMTimeout,
   124  		})
   125  		if err != nil {
   126  			log.Printf("[ERROR] WinRM connection err: %s", err)
   127  			continue
   128  		}
   129  
   130  		break
   131  	}
   132  
   133  	return comm, nil
   134  }