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