github.com/cloudfoundry-incubator/stembuild@v0.0.0-20211223202937-5b61d62226c6/remotemanager/winrm_remotemanager.go (about) 1 package remotemanager 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "net" 8 "os" 9 "time" 10 11 "github.com/masterzen/winrm" 12 13 "github.com/packer-community/winrmcp/winrmcp" 14 ) 15 16 const WinRmPort = 5985 17 const WinRmTimeout = 120 * time.Second 18 19 type WinRM struct { 20 host string 21 username string 22 password string 23 clientFactory WinRMClientFactoryI 24 } 25 26 //go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . WinRMClient 27 type WinRMClient interface { 28 Run(command string, stdout io.Writer, stderr io.Writer) (int, error) 29 CreateShell() (*winrm.Shell, error) 30 } 31 32 //go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . WinRMClientFactoryI 33 type WinRMClientFactoryI interface { 34 Build(timeout time.Duration) (WinRMClient, error) 35 } 36 37 func NewWinRM(host string, username string, password string, clientFactory WinRMClientFactoryI) RemoteManager { 38 return &WinRM{host, username, password, clientFactory} 39 } 40 41 func (w *WinRM) CanReachVM() error { 42 conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", w.host, WinRmPort), time.Duration(time.Second*60)) 43 if err != nil { 44 return fmt.Errorf("host %s is unreachable. Please ensure WinRM is enabled and the IP is correct: %s", w.host, err) 45 } 46 conn.Close() 47 return nil 48 } 49 50 func (w *WinRM) CanLoginVM() error { 51 winrmClient, err := w.clientFactory.Build(WinRmTimeout) 52 53 if err != nil { 54 return fmt.Errorf("failed to create winrm client: %s", err) 55 } 56 57 s, err := winrmClient.CreateShell() 58 if err != nil { 59 return fmt.Errorf("failed to create winrm shell: %s", err) 60 } 61 s.Close() 62 63 return nil 64 } 65 66 func (w *WinRM) UploadArtifact(sourceFilePath, destinationFilePath string) error { 67 client, err := winrmcp.New(w.host, &winrmcp.Config{ 68 Auth: winrmcp.Auth{User: w.username, Password: w.password}, 69 Https: false, 70 Insecure: true, 71 ConnectTimeout: WinRmTimeout, 72 OperationTimeout: WinRmTimeout, 73 MaxOperationsPerShell: 15, 74 }) 75 76 if err != nil { 77 return err 78 } 79 80 // We override Stderr because WinRM Copy output a lot of XML status messages to StdErr 81 // even though they are not errors. In addition, these status messages are difficult to read 82 // and add little customer value. WinRM does not have an output override for Copy yet 83 reader, tmpStdOut, _ := os.Pipe() 84 oldStdErr := os.Stderr 85 os.Stderr = tmpStdOut 86 87 defer func() { 88 os.Stderr = oldStdErr 89 _ = tmpStdOut.Close() 90 _ = reader.Close() 91 }() 92 93 return client.Copy(sourceFilePath, destinationFilePath) 94 } 95 96 func (w *WinRM) ExtractArchive(source, destination string) error { 97 command := fmt.Sprintf("powershell.exe Expand-Archive %s %s -Force", source, destination) 98 _, err := w.ExecuteCommand(command) 99 return err 100 } 101 102 func (w *WinRM) ExecuteCommandWithTimeout(command string, timeout time.Duration) (int, error) { 103 client, err := w.clientFactory.Build(timeout) 104 if err != nil { 105 return -1, err 106 } 107 errBuffer := new(bytes.Buffer) 108 exitCode, err := client.Run(command, os.Stdout, io.MultiWriter(errBuffer, os.Stderr)) 109 if err == nil && exitCode != 0 { 110 err = fmt.Errorf("%s: %s", PowershellExecutionErrorMessage, errBuffer.String()) 111 } 112 return exitCode, err 113 } 114 115 func (w *WinRM) ExecuteCommand(command string) (int, error) { 116 //fmt.Printf("Executing %s\n", command) 117 exitCode, err := w.ExecuteCommandWithTimeout(command, WinRmTimeout) 118 //fmt.Printf("Exectued. exit code %d\n", exitCode) 119 return exitCode, err 120 }