github.com/benphegan/packer@v1.0.0-rc1/communicator/winrm/communicator.go (about)

     1  package winrm
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"log"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  	"sync"
    11  
    12  	"github.com/masterzen/winrm"
    13  	"github.com/mitchellh/packer/packer"
    14  	"github.com/packer-community/winrmcp/winrmcp"
    15  )
    16  
    17  // Communicator represents the WinRM communicator
    18  type Communicator struct {
    19  	config   *Config
    20  	client   *winrm.Client
    21  	endpoint *winrm.Endpoint
    22  }
    23  
    24  // New creates a new communicator implementation over WinRM.
    25  func New(config *Config) (*Communicator, error) {
    26  	endpoint := &winrm.Endpoint{
    27  		Host:     config.Host,
    28  		Port:     config.Port,
    29  		HTTPS:    config.Https,
    30  		Insecure: config.Insecure,
    31  
    32  		/*
    33  			TODO
    34  			HTTPS:    connInfo.HTTPS,
    35  			Insecure: connInfo.Insecure,
    36  			CACert:   connInfo.CACert,
    37  		*/
    38  	}
    39  
    40  	// Create the client
    41  	params := *winrm.DefaultParameters
    42  
    43  	if config.TransportDecorator != nil {
    44  		params.TransportDecorator = config.TransportDecorator
    45  	}
    46  
    47  	params.Timeout = formatDuration(config.Timeout)
    48  	client, err := winrm.NewClientWithParameters(
    49  		endpoint, config.Username, config.Password, &params)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	// Create the shell to verify the connection
    55  	log.Printf("[DEBUG] connecting to remote shell using WinRM")
    56  	shell, err := client.CreateShell()
    57  	if err != nil {
    58  		log.Printf("[ERROR] connection error: %s", err)
    59  		return nil, err
    60  	}
    61  
    62  	if err := shell.Close(); err != nil {
    63  		log.Printf("[ERROR] error closing connection: %s", err)
    64  		return nil, err
    65  	}
    66  
    67  	return &Communicator{
    68  		config:   config,
    69  		client:   client,
    70  		endpoint: endpoint,
    71  	}, nil
    72  }
    73  
    74  // Start implementation of communicator.Communicator interface
    75  func (c *Communicator) Start(rc *packer.RemoteCmd) error {
    76  	shell, err := c.client.CreateShell()
    77  	if err != nil {
    78  		return err
    79  	}
    80  
    81  	log.Printf("[INFO] starting remote command: %s", rc.Command)
    82  	cmd, err := shell.Execute(rc.Command)
    83  	if err != nil {
    84  		return err
    85  	}
    86  
    87  	go runCommand(shell, cmd, rc)
    88  	return nil
    89  }
    90  
    91  func runCommand(shell *winrm.Shell, cmd *winrm.Command, rc *packer.RemoteCmd) {
    92  	defer shell.Close()
    93  	var wg sync.WaitGroup
    94  
    95  	copyFunc := func(w io.Writer, r io.Reader) {
    96  		defer wg.Done()
    97  		io.Copy(w, r)
    98  	}
    99  
   100  	if rc.Stdout != nil && cmd.Stdout != nil {
   101  		wg.Add(1)
   102  		go copyFunc(rc.Stdout, cmd.Stdout)
   103  	} else {
   104  		log.Printf("[WARN] Failed to read stdout for command '%s'", rc.Command)
   105  	}
   106  
   107  	if rc.Stderr != nil && cmd.Stderr != nil {
   108  		wg.Add(1)
   109  		go copyFunc(rc.Stderr, cmd.Stderr)
   110  	} else {
   111  		log.Printf("[WARN] Failed to read stderr for command '%s'", rc.Command)
   112  	}
   113  
   114  	cmd.Wait()
   115  	wg.Wait()
   116  
   117  	code := cmd.ExitCode()
   118  	log.Printf("[INFO] command '%s' exited with code: %d", rc.Command, code)
   119  	rc.SetExited(code)
   120  }
   121  
   122  // Upload implementation of communicator.Communicator interface
   123  func (c *Communicator) Upload(path string, input io.Reader, _ *os.FileInfo) error {
   124  	wcp, err := c.newCopyClient()
   125  	if err != nil {
   126  		return err
   127  	}
   128  	log.Printf("Uploading file to '%s'", path)
   129  	return wcp.Write(path, input)
   130  }
   131  
   132  // UploadDir implementation of communicator.Communicator interface
   133  func (c *Communicator) UploadDir(dst string, src string, exclude []string) error {
   134  	if !strings.HasSuffix(src, "/") {
   135  		dst = fmt.Sprintf("%s\\%s", dst, filepath.Base(src))
   136  	}
   137  	log.Printf("Uploading dir '%s' to '%s'", src, dst)
   138  	wcp, err := c.newCopyClient()
   139  	if err != nil {
   140  		return err
   141  	}
   142  	return wcp.Copy(src, dst)
   143  }
   144  
   145  func (c *Communicator) Download(src string, dst io.Writer) error {
   146  	return fmt.Errorf("WinRM doesn't support download.")
   147  }
   148  
   149  func (c *Communicator) DownloadDir(src string, dst string, exclude []string) error {
   150  	return fmt.Errorf("WinRM doesn't support download dir.")
   151  }
   152  
   153  func (c *Communicator) newCopyClient() (*winrmcp.Winrmcp, error) {
   154  	addr := fmt.Sprintf("%s:%d", c.endpoint.Host, c.endpoint.Port)
   155  	return winrmcp.New(addr, &winrmcp.Config{
   156  		Auth: winrmcp.Auth{
   157  			User:     c.config.Username,
   158  			Password: c.config.Password,
   159  		},
   160  		Https:                 c.config.Https,
   161  		Insecure:              c.config.Insecure,
   162  		OperationTimeout:      c.config.Timeout,
   163  		MaxOperationsPerShell: 15, // lowest common denominator
   164  		TransportDecorator:    c.config.TransportDecorator,
   165  	})
   166  }