github.com/aspring/packer@v0.8.1-0.20150629211158-9db281ac0f89/communicator/winrm/communicator.go (about)

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