github.com/partitio/terraform@v0.11.12-beta1/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"
    15  	"github.com/packer-community/winrmcp/winrmcp"
    16  )
    17  
    18  // Communicator represents the WinRM communicator
    19  type Communicator struct {
    20  	connInfo *connectionInfo
    21  	client   *winrm.Client
    22  	endpoint *winrm.Endpoint
    23  	rand     *rand.Rand
    24  }
    25  
    26  // New creates a new communicator implementation over WinRM.
    27  func New(s *terraform.InstanceState) (*Communicator, error) {
    28  	connInfo, err := parseConnectionInfo(s)
    29  	if err != nil {
    30  		return nil, err
    31  	}
    32  
    33  	endpoint := &winrm.Endpoint{
    34  		Host:     connInfo.Host,
    35  		Port:     connInfo.Port,
    36  		HTTPS:    connInfo.HTTPS,
    37  		Insecure: connInfo.Insecure,
    38  	}
    39  	if len(connInfo.CACert) > 0 {
    40  		endpoint.CACert = []byte(connInfo.CACert)
    41  	}
    42  
    43  	comm := &Communicator{
    44  		connInfo: connInfo,
    45  		endpoint: endpoint,
    46  		// Seed our own rand source so that script paths are not deterministic
    47  		rand: rand.New(rand.NewSource(time.Now().UnixNano())),
    48  	}
    49  
    50  	return comm, nil
    51  }
    52  
    53  // Connect implementation of communicator.Communicator interface
    54  func (c *Communicator) Connect(o terraform.UIOutput) error {
    55  	if c.client != nil {
    56  		return nil
    57  	}
    58  
    59  	params := winrm.DefaultParameters
    60  	params.Timeout = formatDuration(c.Timeout())
    61  	if c.connInfo.NTLM == true {
    62  		params.TransportDecorator = func() winrm.Transporter { return &winrm.ClientNTLM{} }
    63  	}
    64  
    65  	client, err := winrm.NewClientWithParameters(
    66  		c.endpoint, c.connInfo.User, c.connInfo.Password, params)
    67  	if err != nil {
    68  		return err
    69  	}
    70  
    71  	if o != nil {
    72  		o.Output(fmt.Sprintf(
    73  			"Connecting to remote host via WinRM...\n"+
    74  				"  Host: %s\n"+
    75  				"  Port: %d\n"+
    76  				"  User: %s\n"+
    77  				"  Password: %t\n"+
    78  				"  HTTPS: %t\n"+
    79  				"  Insecure: %t\n"+
    80  				"  NTLM: %t\n"+
    81  				"  CACert: %t",
    82  			c.connInfo.Host,
    83  			c.connInfo.Port,
    84  			c.connInfo.User,
    85  			c.connInfo.Password != "",
    86  			c.connInfo.HTTPS,
    87  			c.connInfo.Insecure,
    88  			c.connInfo.NTLM,
    89  			c.connInfo.CACert != "",
    90  		))
    91  	}
    92  
    93  	log.Printf("[DEBUG] connecting to remote shell using WinRM")
    94  	shell, err := client.CreateShell()
    95  	if err != nil {
    96  		log.Printf("[ERROR] error creating shell: %s", err)
    97  		return err
    98  	}
    99  
   100  	err = shell.Close()
   101  	if err != nil {
   102  		log.Printf("[ERROR] error closing shell: %s", err)
   103  		return err
   104  	}
   105  
   106  	if o != nil {
   107  		o.Output("Connected!")
   108  	}
   109  
   110  	c.client = client
   111  
   112  	return nil
   113  }
   114  
   115  // Disconnect implementation of communicator.Communicator interface
   116  func (c *Communicator) Disconnect() error {
   117  	c.client = nil
   118  	return nil
   119  }
   120  
   121  // Timeout implementation of communicator.Communicator interface
   122  func (c *Communicator) Timeout() time.Duration {
   123  	return c.connInfo.TimeoutVal
   124  }
   125  
   126  // ScriptPath implementation of communicator.Communicator interface
   127  func (c *Communicator) ScriptPath() string {
   128  	return strings.Replace(
   129  		c.connInfo.ScriptPath, "%RAND%",
   130  		strconv.FormatInt(int64(c.rand.Int31()), 10), -1)
   131  }
   132  
   133  // Start implementation of communicator.Communicator interface
   134  func (c *Communicator) Start(rc *remote.Cmd) error {
   135  	rc.Init()
   136  	log.Printf("[DEBUG] starting remote command: %s", rc.Command)
   137  
   138  	// TODO: make sure communicators always connect first, so we can get output
   139  	// from the connection.
   140  	if c.client == nil {
   141  		log.Println("[WARN] winrm client not connected, attempting to connect")
   142  		if err := c.Connect(nil); err != nil {
   143  			return err
   144  		}
   145  	}
   146  
   147  	status, err := c.client.Run(rc.Command, rc.Stdout, rc.Stderr)
   148  	rc.SetExitStatus(status, err)
   149  
   150  	return nil
   151  }
   152  
   153  // Upload implementation of communicator.Communicator interface
   154  func (c *Communicator) Upload(path string, input io.Reader) error {
   155  	wcp, err := c.newCopyClient()
   156  	if err != nil {
   157  		return err
   158  	}
   159  	log.Printf("[DEBUG] Uploading file to '%s'", path)
   160  	return wcp.Write(path, input)
   161  }
   162  
   163  // UploadScript implementation of communicator.Communicator interface
   164  func (c *Communicator) UploadScript(path string, input io.Reader) error {
   165  	return c.Upload(path, input)
   166  }
   167  
   168  // UploadDir implementation of communicator.Communicator interface
   169  func (c *Communicator) UploadDir(dst string, src string) error {
   170  	log.Printf("[DEBUG] Uploading dir '%s' to '%s'", src, dst)
   171  	wcp, err := c.newCopyClient()
   172  	if err != nil {
   173  		return err
   174  	}
   175  	return wcp.Copy(src, dst)
   176  }
   177  
   178  func (c *Communicator) newCopyClient() (*winrmcp.Winrmcp, error) {
   179  	addr := fmt.Sprintf("%s:%d", c.endpoint.Host, c.endpoint.Port)
   180  
   181  	config := winrmcp.Config{
   182  		Auth: winrmcp.Auth{
   183  			User:     c.connInfo.User,
   184  			Password: c.connInfo.Password,
   185  		},
   186  		Https:                 c.connInfo.HTTPS,
   187  		Insecure:              c.connInfo.Insecure,
   188  		OperationTimeout:      c.Timeout(),
   189  		MaxOperationsPerShell: 15, // lowest common denominator
   190  	}
   191  
   192  	if c.connInfo.NTLM == true {
   193  		config.TransportDecorator = func() winrm.Transporter { return &winrm.ClientNTLM{} }
   194  	}
   195  
   196  	if c.connInfo.CACert != "" {
   197  		config.CACertBytes = []byte(c.connInfo.CACert)
   198  	}
   199  
   200  	return winrmcp.New(addr, &config)
   201  }