github.com/ezbercih/terraform@v0.1.1-0.20140729011846-3c33865e0839/helper/ssh/provisioner.go (about)

     1  package ssh
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"log"
     7  	"time"
     8  
     9  	"code.google.com/p/go.crypto/ssh"
    10  	"github.com/hashicorp/terraform/terraform"
    11  	"github.com/mitchellh/mapstructure"
    12  )
    13  
    14  const (
    15  	// DefaultUser is used if there is no default user given
    16  	DefaultUser = "root"
    17  
    18  	// DefaultPort is used if there is no port given
    19  	DefaultPort = 22
    20  
    21  	// DefaultScriptPath is used as the path to copy the file to
    22  	// for remote execution if not provided otherwise.
    23  	DefaultScriptPath = "/tmp/script.sh"
    24  
    25  	// DefaultTimeout is used if there is no timeout given
    26  	DefaultTimeout = 5 * time.Minute
    27  )
    28  
    29  // SSHConfig is decoded from the ConnInfo of the resource. These
    30  // are the only keys we look at. If a KeyFile is given, that is used
    31  // instead of a password.
    32  type SSHConfig struct {
    33  	User       string
    34  	Password   string
    35  	KeyFile    string `mapstructure:"key_file"`
    36  	Host       string
    37  	Port       int
    38  	Timeout    string
    39  	ScriptPath string        `mapstructure:"script_path"`
    40  	TimeoutVal time.Duration `mapstructure:"-"`
    41  }
    42  
    43  // verifySSH is used to verify the ConnInfo is usable by remote-exec
    44  func VerifySSH(s *terraform.ResourceState) error {
    45  	connType := s.ConnInfo["type"]
    46  	switch connType {
    47  	case "":
    48  	case "ssh":
    49  	default:
    50  		return fmt.Errorf("Connection type '%s' not supported", connType)
    51  	}
    52  	return nil
    53  }
    54  
    55  // ParseSSHConfig is used to convert the ConnInfo of the ResourceState into
    56  // a SSHConfig struct
    57  func ParseSSHConfig(s *terraform.ResourceState) (*SSHConfig, error) {
    58  	sshConf := &SSHConfig{}
    59  	decConf := &mapstructure.DecoderConfig{
    60  		WeaklyTypedInput: true,
    61  		Result:           sshConf,
    62  	}
    63  	dec, err := mapstructure.NewDecoder(decConf)
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  	if err := dec.Decode(s.ConnInfo); err != nil {
    68  		return nil, err
    69  	}
    70  	if sshConf.User == "" {
    71  		sshConf.User = DefaultUser
    72  	}
    73  	if sshConf.Port == 0 {
    74  		sshConf.Port = DefaultPort
    75  	}
    76  	if sshConf.ScriptPath == "" {
    77  		sshConf.ScriptPath = DefaultScriptPath
    78  	}
    79  	if sshConf.Timeout != "" {
    80  		sshConf.TimeoutVal = safeDuration(sshConf.Timeout, DefaultTimeout)
    81  	} else {
    82  		sshConf.TimeoutVal = DefaultTimeout
    83  	}
    84  	return sshConf, nil
    85  }
    86  
    87  // safeDuration returns either the parsed duration or a default value
    88  func safeDuration(dur string, defaultDur time.Duration) time.Duration {
    89  	d, err := time.ParseDuration(dur)
    90  	if err != nil {
    91  		log.Printf("Invalid duration '%s', using default of %s", dur, defaultDur)
    92  		return defaultDur
    93  	}
    94  	return d
    95  }
    96  
    97  // PrepareConfig is used to turn the *SSHConfig provided into a
    98  // usable *Config for client initialization.
    99  func PrepareConfig(conf *SSHConfig) (*Config, error) {
   100  	sshConf := &ssh.ClientConfig{
   101  		User: conf.User,
   102  	}
   103  	if conf.KeyFile != "" {
   104  		key, err := ioutil.ReadFile(conf.KeyFile)
   105  		if err != nil {
   106  			return nil, fmt.Errorf("Failed to read key file '%s': %v", conf.KeyFile, err)
   107  		}
   108  		signer, err := ssh.ParsePrivateKey(key)
   109  		if err != nil {
   110  			return nil, fmt.Errorf("Failed to parse key file '%s': %v", conf.KeyFile, err)
   111  		}
   112  		sshConf.Auth = append(sshConf.Auth, ssh.PublicKeys(signer))
   113  	}
   114  	if conf.Password != "" {
   115  		sshConf.Auth = append(sshConf.Auth,
   116  			ssh.Password(conf.Password))
   117  		sshConf.Auth = append(sshConf.Auth,
   118  			ssh.KeyboardInteractive(PasswordKeyboardInteractive(conf.Password)))
   119  	}
   120  	host := fmt.Sprintf("%s:%d", conf.Host, conf.Port)
   121  	config := &Config{
   122  		SSHConfig:  sshConf,
   123  		Connection: ConnectFunc("tcp", host),
   124  	}
   125  	return config, nil
   126  }