github.com/tappoz/packer@v1.0.0-rc1/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 100 port := s.Config.WinRMPort 101 if s.WinRMPort != nil { 102 port, err = s.WinRMPort(state) 103 if err != nil { 104 log.Printf("[DEBUG] Error getting WinRM port: %s", err) 105 continue 106 } 107 } 108 109 user := s.Config.WinRMUser 110 password := s.Config.WinRMPassword 111 if s.WinRMConfig != nil { 112 config, err := s.WinRMConfig(state) 113 if err != nil { 114 log.Printf("[DEBUG] Error getting WinRM config: %s", err) 115 continue 116 } 117 118 if config.Username != "" { 119 user = config.Username 120 } 121 if config.Password != "" { 122 password = config.Password 123 } 124 } 125 126 log.Println("[INFO] Attempting WinRM connection...") 127 comm, err = winrm.New(&winrm.Config{ 128 Host: host, 129 Port: port, 130 Username: user, 131 Password: password, 132 Timeout: s.Config.WinRMTimeout, 133 Https: s.Config.WinRMUseSSL, 134 Insecure: s.Config.WinRMInsecure, 135 TransportDecorator: s.Config.WinRMTransportDecorator, 136 }) 137 if err != nil { 138 log.Printf("[ERROR] WinRM connection err: %s", err) 139 continue 140 } 141 142 break 143 } 144 145 return comm, nil 146 }