github.com/rothwerx/packer@v0.9.0/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  	WinRMPort   func(multistep.StateBag) (int, error)
    29  }
    30  
    31  func (s *StepConnectWinRM) Run(state multistep.StateBag) multistep.StepAction {
    32  	ui := state.Get("ui").(packer.Ui)
    33  
    34  	var comm packer.Communicator
    35  	var err error
    36  
    37  	cancel := make(chan struct{})
    38  	waitDone := make(chan bool, 1)
    39  	go func() {
    40  		ui.Say("Waiting for WinRM to become available...")
    41  		comm, err = s.waitForWinRM(state, cancel)
    42  		waitDone <- true
    43  	}()
    44  
    45  	log.Printf("Waiting for WinRM, up to timeout: %s", s.Config.WinRMTimeout)
    46  	timeout := time.After(s.Config.WinRMTimeout)
    47  WaitLoop:
    48  	for {
    49  		// Wait for either WinRM to become available, a timeout to occur,
    50  		// or an interrupt to come through.
    51  		select {
    52  		case <-waitDone:
    53  			if err != nil {
    54  				ui.Error(fmt.Sprintf("Error waiting for WinRM: %s", err))
    55  				return multistep.ActionHalt
    56  			}
    57  
    58  			ui.Say("Connected to WinRM!")
    59  			state.Put("communicator", comm)
    60  			break WaitLoop
    61  		case <-timeout:
    62  			err := fmt.Errorf("Timeout waiting for WinRM.")
    63  			state.Put("error", err)
    64  			ui.Error(err.Error())
    65  			close(cancel)
    66  			return multistep.ActionHalt
    67  		case <-time.After(1 * time.Second):
    68  			if _, ok := state.GetOk(multistep.StateCancelled); ok {
    69  				// The step sequence was cancelled, so cancel waiting for WinRM
    70  				// and just start the halting process.
    71  				close(cancel)
    72  				log.Println("Interrupt detected, quitting waiting for WinRM.")
    73  				return multistep.ActionHalt
    74  			}
    75  		}
    76  	}
    77  
    78  	return multistep.ActionContinue
    79  }
    80  
    81  func (s *StepConnectWinRM) Cleanup(multistep.StateBag) {
    82  }
    83  
    84  func (s *StepConnectWinRM) waitForWinRM(state multistep.StateBag, cancel <-chan struct{}) (packer.Communicator, error) {
    85  	var comm packer.Communicator
    86  	for {
    87  		select {
    88  		case <-cancel:
    89  			log.Println("[INFO] WinRM wait cancelled. Exiting loop.")
    90  			return nil, errors.New("WinRM wait cancelled")
    91  		case <-time.After(5 * time.Second):
    92  		}
    93  
    94  		host, err := s.Host(state)
    95  		if err != nil {
    96  			log.Printf("[DEBUG] Error getting WinRM host: %s", err)
    97  			continue
    98  		}
    99  		port := s.Config.WinRMPort
   100  		if s.WinRMPort != nil {
   101  			port, err = s.WinRMPort(state)
   102  			if err != nil {
   103  				log.Printf("[DEBUG] Error getting WinRM port: %s", err)
   104  				continue
   105  			}
   106  		}
   107  
   108  		user := s.Config.WinRMUser
   109  		password := s.Config.WinRMPassword
   110  		if s.WinRMConfig != nil {
   111  			config, err := s.WinRMConfig(state)
   112  			if err != nil {
   113  				log.Printf("[DEBUG] Error getting WinRM config: %s", err)
   114  				continue
   115  			}
   116  
   117  			if config.Username != "" {
   118  				user = config.Username
   119  			}
   120  			if config.Password != "" {
   121  				password = config.Password
   122  			}
   123  		}
   124  
   125  		log.Println("[INFO] Attempting WinRM connection...")
   126  		comm, err = winrm.New(&winrm.Config{
   127  			Host:     host,
   128  			Port:     port,
   129  			Username: user,
   130  			Password: password,
   131  			Timeout:  s.Config.WinRMTimeout,
   132  			Https:    s.Config.WinRMUseSSL,
   133  			Insecure: s.Config.WinRMInsecure,
   134  		})
   135  		if err != nil {
   136  			log.Printf("[ERROR] WinRM connection err: %s", err)
   137  			continue
   138  		}
   139  
   140  		break
   141  	}
   142  
   143  	return comm, nil
   144  }