github.com/marksheahan/packer@v0.10.2-0.20160613200515-1acb2d6645a0/communicator/winrm/communicator.go (about)

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