github.com/kimor79/packer@v0.8.7-0.20151221212622-d507b18eb4cf/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  
    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  	params.Timeout = formatDuration(config.Timeout)
    43  	client, err := winrm.NewClientWithParameters(
    44  		endpoint, config.Username, config.Password, params)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  
    49  	// Create the shell to verify the connection
    50  	log.Printf("[DEBUG] connecting to remote shell using WinRM")
    51  	shell, err := client.CreateShell()
    52  	if err != nil {
    53  		log.Printf("[ERROR] connection error: %s", err)
    54  		return nil, err
    55  	}
    56  
    57  	if err := shell.Close(); err != nil {
    58  		log.Printf("[ERROR] error closing connection: %s", err)
    59  		return nil, err
    60  	}
    61  
    62  	return &Communicator{
    63  		config:   config,
    64  		client:   client,
    65  		endpoint: endpoint,
    66  	}, nil
    67  }
    68  
    69  // Start implementation of communicator.Communicator interface
    70  func (c *Communicator) Start(rc *packer.RemoteCmd) error {
    71  	shell, err := c.client.CreateShell()
    72  	if err != nil {
    73  		return err
    74  	}
    75  
    76  	log.Printf("[INFO] starting remote command: %s", rc.Command)
    77  	cmd, err := shell.Execute(rc.Command)
    78  	if err != nil {
    79  		return err
    80  	}
    81  
    82  	go runCommand(shell, cmd, rc)
    83  	return nil
    84  }
    85  
    86  func runCommand(shell *winrm.Shell, cmd *winrm.Command, rc *packer.RemoteCmd) {
    87  	defer shell.Close()
    88  	var wg sync.WaitGroup
    89  
    90  	copyFunc := func(w io.Writer, r io.Reader) {
    91  		defer wg.Done()
    92  		io.Copy(w, r)
    93  	}
    94  
    95  	if rc.Stdout != nil && cmd.Stdout != nil {
    96  		wg.Add(1)
    97  		go copyFunc(rc.Stdout, cmd.Stdout)
    98  	} else {
    99  		log.Printf("[WARN] Failed to read stdout for command '%s'", rc.Command)
   100  	}
   101  
   102  	if rc.Stderr != nil && cmd.Stderr != nil {
   103  		wg.Add(1)
   104  		go copyFunc(rc.Stderr, cmd.Stderr)
   105  	} else {
   106  		log.Printf("[WARN] Failed to read stderr for command '%s'", rc.Command)
   107  	}
   108  
   109  	cmd.Wait()
   110  	wg.Wait()
   111  
   112  	code := cmd.ExitCode()
   113  	log.Printf("[INFO] command '%s' exited with code: %d", rc.Command, code)
   114  	rc.SetExited(code)
   115  }
   116  
   117  // Upload implementation of communicator.Communicator interface
   118  func (c *Communicator) Upload(path string, input io.Reader, _ *os.FileInfo) error {
   119  	wcp, err := c.newCopyClient()
   120  	if err != nil {
   121  		return err
   122  	}
   123  	log.Printf("Uploading file to '%s'", path)
   124  	return wcp.Write(path, input)
   125  }
   126  
   127  // UploadDir implementation of communicator.Communicator interface
   128  func (c *Communicator) UploadDir(dst string, src string, exclude []string) error {
   129  	log.Printf("Uploading dir '%s' to '%s'", src, dst)
   130  	wcp, err := c.newCopyClient()
   131  	if err != nil {
   132  		return err
   133  	}
   134  	return wcp.Copy(src, dst)
   135  }
   136  
   137  func (c *Communicator) Download(src string, dst io.Writer) error {
   138  	return fmt.Errorf("WinRM doesn't support download.")
   139  }
   140  
   141  func (c *Communicator) newCopyClient() (*winrmcp.Winrmcp, error) {
   142  	addr := fmt.Sprintf("%s:%d", c.endpoint.Host, c.endpoint.Port)
   143  	return winrmcp.New(addr, &winrmcp.Config{
   144  		Auth: winrmcp.Auth{
   145  			User:     c.config.Username,
   146  			Password: c.config.Password,
   147  		},
   148  		OperationTimeout:      c.config.Timeout,
   149  		MaxOperationsPerShell: 15, // lowest common denominator
   150  	})
   151  }