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