github.com/erriapo/terraform@v0.6.12-0.20160203182612-0340ea72354f/communicator/winrm/communicator.go (about)

     1  package winrm
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"log"
     7  	"math/rand"
     8  	"strconv"
     9  	"strings"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/hashicorp/terraform/communicator/remote"
    14  	"github.com/hashicorp/terraform/terraform"
    15  	"github.com/masterzen/winrm/winrm"
    16  	"github.com/packer-community/winrmcp/winrmcp"
    17  
    18  	// This import is a bit strange, but it's needed so `make updatedeps` can see and download it
    19  	_ "github.com/dylanmei/winrmtest"
    20  )
    21  
    22  // Communicator represents the WinRM communicator
    23  type Communicator struct {
    24  	connInfo *connectionInfo
    25  	client   *winrm.Client
    26  	endpoint *winrm.Endpoint
    27  }
    28  
    29  // New creates a new communicator implementation over WinRM.
    30  func New(s *terraform.InstanceState) (*Communicator, error) {
    31  	connInfo, err := parseConnectionInfo(s)
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  
    36  	endpoint := &winrm.Endpoint{
    37  		Host:     connInfo.Host,
    38  		Port:     connInfo.Port,
    39  		HTTPS:    connInfo.HTTPS,
    40  		Insecure: connInfo.Insecure,
    41  		CACert:   connInfo.CACert,
    42  	}
    43  
    44  	comm := &Communicator{
    45  		connInfo: connInfo,
    46  		endpoint: endpoint,
    47  	}
    48  
    49  	return comm, nil
    50  }
    51  
    52  // Connect implementation of communicator.Communicator interface
    53  func (c *Communicator) Connect(o terraform.UIOutput) error {
    54  	if c.client != nil {
    55  		return nil
    56  	}
    57  
    58  	params := winrm.DefaultParameters()
    59  	params.Timeout = formatDuration(c.Timeout())
    60  
    61  	client, err := winrm.NewClientWithParameters(
    62  		c.endpoint, c.connInfo.User, c.connInfo.Password, params)
    63  	if err != nil {
    64  		return err
    65  	}
    66  
    67  	if o != nil {
    68  		o.Output(fmt.Sprintf(
    69  			"Connecting to remote host via WinRM...\n"+
    70  				"  Host: %s\n"+
    71  				"  Port: %d\n"+
    72  				"  User: %s\n"+
    73  				"  Password: %t\n"+
    74  				"  HTTPS: %t\n"+
    75  				"  Insecure: %t\n"+
    76  				"  CACert: %t",
    77  			c.connInfo.Host,
    78  			c.connInfo.Port,
    79  			c.connInfo.User,
    80  			c.connInfo.Password != "",
    81  			c.connInfo.HTTPS,
    82  			c.connInfo.Insecure,
    83  			c.connInfo.CACert != nil,
    84  		))
    85  	}
    86  
    87  	log.Printf("connecting to remote shell using WinRM")
    88  	shell, err := client.CreateShell()
    89  	if err != nil {
    90  		log.Printf("connection error: %s", err)
    91  		return err
    92  	}
    93  
    94  	err = shell.Close()
    95  	if err != nil {
    96  		log.Printf("error closing connection: %s", err)
    97  		return err
    98  	}
    99  
   100  	if o != nil {
   101  		o.Output("Connected!")
   102  	}
   103  
   104  	c.client = client
   105  
   106  	return nil
   107  }
   108  
   109  // Disconnect implementation of communicator.Communicator interface
   110  func (c *Communicator) Disconnect() error {
   111  	c.client = nil
   112  	return nil
   113  }
   114  
   115  // Timeout implementation of communicator.Communicator interface
   116  func (c *Communicator) Timeout() time.Duration {
   117  	return c.connInfo.TimeoutVal
   118  }
   119  
   120  // ScriptPath implementation of communicator.Communicator interface
   121  func (c *Communicator) ScriptPath() string {
   122  	return strings.Replace(
   123  		c.connInfo.ScriptPath, "%RAND%",
   124  		strconv.FormatInt(int64(rand.Int31()), 10), -1)
   125  }
   126  
   127  // Start implementation of communicator.Communicator interface
   128  func (c *Communicator) Start(rc *remote.Cmd) error {
   129  	err := c.Connect(nil)
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	shell, err := c.client.CreateShell()
   135  	if err != nil {
   136  		return err
   137  	}
   138  
   139  	log.Printf("starting remote command: %s", rc.Command)
   140  	cmd, err := shell.Execute(rc.Command)
   141  	if err != nil {
   142  		return err
   143  	}
   144  
   145  	go runCommand(shell, cmd, rc)
   146  	return nil
   147  }
   148  
   149  func runCommand(shell *winrm.Shell, cmd *winrm.Command, rc *remote.Cmd) {
   150  	defer shell.Close()
   151  
   152  	var wg sync.WaitGroup
   153  	go func() {
   154  		wg.Add(1)
   155  		io.Copy(rc.Stdout, cmd.Stdout)
   156  		wg.Done()
   157  	}()
   158  	go func() {
   159  		wg.Add(1)
   160  		io.Copy(rc.Stderr, cmd.Stderr)
   161  		wg.Done()
   162  	}()
   163  
   164  	cmd.Wait()
   165  	wg.Wait()
   166  	rc.SetExited(cmd.ExitCode())
   167  }
   168  
   169  // Upload implementation of communicator.Communicator interface
   170  func (c *Communicator) Upload(path string, input io.Reader) error {
   171  	wcp, err := c.newCopyClient()
   172  	if err != nil {
   173  		return err
   174  	}
   175  	log.Printf("Uploading file to '%s'", path)
   176  	return wcp.Write(path, input)
   177  }
   178  
   179  // UploadScript implementation of communicator.Communicator interface
   180  func (c *Communicator) UploadScript(path string, input io.Reader) error {
   181  	return c.Upload(path, input)
   182  }
   183  
   184  // UploadDir implementation of communicator.Communicator interface
   185  func (c *Communicator) UploadDir(dst string, src string) error {
   186  	log.Printf("Uploading dir '%s' to '%s'", src, dst)
   187  	wcp, err := c.newCopyClient()
   188  	if err != nil {
   189  		return err
   190  	}
   191  	return wcp.Copy(src, dst)
   192  }
   193  
   194  func (c *Communicator) newCopyClient() (*winrmcp.Winrmcp, error) {
   195  	addr := fmt.Sprintf("%s:%d", c.endpoint.Host, c.endpoint.Port)
   196  	return winrmcp.New(addr, &winrmcp.Config{
   197  		Auth: winrmcp.Auth{
   198  			User:     c.connInfo.User,
   199  			Password: c.connInfo.Password,
   200  		},
   201  		OperationTimeout:      c.Timeout(),
   202  		MaxOperationsPerShell: 15, // lowest common denominator
   203  	})
   204  }