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