github.com/kevinklinger/open_terraform@v1.3.6/noninternal/communicator/winrm/provisioner.go (about)

     1  package winrm
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"path/filepath"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/kevinklinger/open_terraform/noninternal/communicator/shared"
    11  	"github.com/zclconf/go-cty/cty"
    12  	"github.com/zclconf/go-cty/cty/gocty"
    13  )
    14  
    15  const (
    16  	// DefaultUser is used if there is no user given
    17  	DefaultUser = "Administrator"
    18  
    19  	// DefaultPort is used if there is no port given
    20  	DefaultPort = 5985
    21  
    22  	// DefaultHTTPSPort is used if there is no port given and HTTPS is true
    23  	DefaultHTTPSPort = 5986
    24  
    25  	// DefaultScriptPath is used as the path to copy the file to
    26  	// for remote execution if not provided otherwise.
    27  	DefaultScriptPath = "C:/Temp/terraform_%RAND%.cmd"
    28  
    29  	// DefaultTimeout is used if there is no timeout given
    30  	DefaultTimeout = 5 * time.Minute
    31  )
    32  
    33  // connectionInfo is decoded from the ConnInfo of the resource. These are the
    34  // only keys we look at. If a KeyFile is given, that is used instead
    35  // of a password.
    36  type connectionInfo struct {
    37  	User       string
    38  	Password   string
    39  	Host       string
    40  	Port       uint16
    41  	HTTPS      bool
    42  	Insecure   bool
    43  	NTLM       bool   `mapstructure:"use_ntlm"`
    44  	CACert     string `mapstructure:"cacert"`
    45  	Timeout    string
    46  	ScriptPath string        `mapstructure:"script_path"`
    47  	TimeoutVal time.Duration `mapstructure:"-"`
    48  }
    49  
    50  // decodeConnInfo decodes the given cty.Value using the same behavior as the
    51  // lgeacy mapstructure decoder in order to preserve as much of the existing
    52  // logic as possible for compatibility.
    53  func decodeConnInfo(v cty.Value) (*connectionInfo, error) {
    54  	connInfo := &connectionInfo{}
    55  	if v.IsNull() {
    56  		return connInfo, nil
    57  	}
    58  
    59  	for k, v := range v.AsValueMap() {
    60  		if v.IsNull() {
    61  			continue
    62  		}
    63  
    64  		switch k {
    65  		case "user":
    66  			connInfo.User = v.AsString()
    67  		case "password":
    68  			connInfo.Password = v.AsString()
    69  		case "host":
    70  			connInfo.Host = v.AsString()
    71  		case "port":
    72  			if err := gocty.FromCtyValue(v, &connInfo.Port); err != nil {
    73  				return nil, err
    74  			}
    75  		case "https":
    76  			connInfo.HTTPS = v.True()
    77  		case "insecure":
    78  			connInfo.Insecure = v.True()
    79  		case "use_ntlm":
    80  			connInfo.NTLM = v.True()
    81  		case "cacert":
    82  			connInfo.CACert = v.AsString()
    83  		case "script_path":
    84  			connInfo.ScriptPath = v.AsString()
    85  		case "timeout":
    86  			connInfo.Timeout = v.AsString()
    87  		}
    88  	}
    89  	return connInfo, nil
    90  }
    91  
    92  // parseConnectionInfo is used to convert the ConnInfo of the InstanceState into
    93  // a ConnectionInfo struct
    94  func parseConnectionInfo(v cty.Value) (*connectionInfo, error) {
    95  	v, err := shared.ConnectionBlockSupersetSchema.CoerceValue(v)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  
   100  	connInfo, err := decodeConnInfo(v)
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  	// Check on script paths which point to the default Windows TEMP folder because files
   105  	// which are put in there very early in the boot process could get cleaned/deleted
   106  	// before you had the change to execute them.
   107  	//
   108  	// TODO (SvH) Needs some more debugging to fully understand the exact sequence of events
   109  	// causing this...
   110  	if strings.HasPrefix(filepath.ToSlash(connInfo.ScriptPath), "C:/Windows/Temp") {
   111  		return nil, fmt.Errorf(
   112  			`Using the C:\Windows\Temp folder is not supported. Please use a different 'script_path'.`)
   113  	}
   114  
   115  	if connInfo.User == "" {
   116  		connInfo.User = DefaultUser
   117  	}
   118  
   119  	// Format the host if needed.
   120  	// Needed for IPv6 support.
   121  	connInfo.Host = shared.IpFormat(connInfo.Host)
   122  
   123  	if connInfo.Port == 0 {
   124  		if connInfo.HTTPS {
   125  			connInfo.Port = DefaultHTTPSPort
   126  		} else {
   127  			connInfo.Port = DefaultPort
   128  		}
   129  	}
   130  	if connInfo.ScriptPath == "" {
   131  		connInfo.ScriptPath = DefaultScriptPath
   132  	}
   133  	if connInfo.Timeout != "" {
   134  		connInfo.TimeoutVal = safeDuration(connInfo.Timeout, DefaultTimeout)
   135  	} else {
   136  		connInfo.TimeoutVal = DefaultTimeout
   137  	}
   138  
   139  	return connInfo, nil
   140  }
   141  
   142  // safeDuration returns either the parsed duration or a default value
   143  func safeDuration(dur string, defaultDur time.Duration) time.Duration {
   144  	d, err := time.ParseDuration(dur)
   145  	if err != nil {
   146  		log.Printf("Invalid duration '%s', using default of %s", dur, defaultDur)
   147  		return defaultDur
   148  	}
   149  	return d
   150  }
   151  
   152  func formatDuration(duration time.Duration) string {
   153  	h := int(duration.Hours())
   154  	m := int(duration.Minutes()) - h*60
   155  	s := int(duration.Seconds()) - (h*3600 + m*60)
   156  
   157  	res := "PT"
   158  	if h > 0 {
   159  		res = fmt.Sprintf("%s%dH", res, h)
   160  	}
   161  	if m > 0 {
   162  		res = fmt.Sprintf("%s%dM", res, m)
   163  	}
   164  	if s > 0 {
   165  		res = fmt.Sprintf("%s%dS", res, s)
   166  	}
   167  
   168  	return res
   169  }