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  }