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 }