github.com/amanya/packer@v0.12.1-0.20161117214323-902ac5ab2eb6/builder/openstack/ssh.go (about)

     1  package openstack
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"log"
     7  	"time"
     8  
     9  	"github.com/mitchellh/multistep"
    10  	packerssh "github.com/mitchellh/packer/communicator/ssh"
    11  	"github.com/rackspace/gophercloud"
    12  	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip"
    13  	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
    14  	"golang.org/x/crypto/ssh"
    15  )
    16  
    17  // CommHost looks up the host for the communicator.
    18  func CommHost(
    19  	client *gophercloud.ServiceClient,
    20  	sshinterface string,
    21  	sshipversion string) func(multistep.StateBag) (string, error) {
    22  	return func(state multistep.StateBag) (string, error) {
    23  		s := state.Get("server").(*servers.Server)
    24  
    25  		// If we have a specific interface, try that
    26  		if sshinterface != "" {
    27  			if addr := sshAddrFromPool(s, sshinterface, sshipversion); addr != "" {
    28  				log.Printf("[DEBUG] Using IP address %s from specified interface %s to connect", addr, sshinterface)
    29  				return addr, nil
    30  			}
    31  		}
    32  
    33  		// If we have a floating IP, use that
    34  		ip := state.Get("access_ip").(*floatingip.FloatingIP)
    35  		if ip != nil && ip.IP != "" {
    36  			log.Printf("[DEBUG] Using floating IP %s to connect", ip.IP)
    37  			return ip.IP, nil
    38  		}
    39  
    40  		if s.AccessIPv4 != "" {
    41  			log.Printf("[DEBUG] Using AccessIPv4 %s to connect", s.AccessIPv4)
    42  			return s.AccessIPv4, nil
    43  		}
    44  
    45  		// Try to get it from the requested interface
    46  		if addr := sshAddrFromPool(s, sshinterface, sshipversion); addr != "" {
    47  			log.Printf("[DEBUG] Using IP address %s to connect", addr)
    48  			return addr, nil
    49  		}
    50  
    51  		s, err := servers.Get(client, s.ID).Extract()
    52  		if err != nil {
    53  			return "", err
    54  		}
    55  
    56  		state.Put("server", s)
    57  		time.Sleep(1 * time.Second)
    58  
    59  		return "", errors.New("couldn't determine IP address for server")
    60  	}
    61  }
    62  
    63  // SSHConfig returns a function that can be used for the SSH communicator
    64  // config for connecting to the instance created over SSH using a private key
    65  // or a password.
    66  func SSHConfig(username, password string) func(multistep.StateBag) (*ssh.ClientConfig, error) {
    67  	return func(state multistep.StateBag) (*ssh.ClientConfig, error) {
    68  
    69  		privateKey, hasKey := state.GetOk("privateKey")
    70  
    71  		if hasKey {
    72  
    73  			signer, err := ssh.ParsePrivateKey([]byte(privateKey.(string)))
    74  			if err != nil {
    75  				return nil, fmt.Errorf("Error setting up SSH config: %s", err)
    76  			}
    77  
    78  			return &ssh.ClientConfig{
    79  				User: username,
    80  				Auth: []ssh.AuthMethod{
    81  					ssh.PublicKeys(signer),
    82  				},
    83  			}, nil
    84  
    85  		} else {
    86  
    87  			return &ssh.ClientConfig{
    88  				User: username,
    89  				Auth: []ssh.AuthMethod{
    90  					ssh.Password(password),
    91  					ssh.KeyboardInteractive(
    92  						packerssh.PasswordKeyboardInteractive(password)),
    93  				}}, nil
    94  		}
    95  	}
    96  }
    97  
    98  func sshAddrFromPool(s *servers.Server, desired string, sshIPVersion string) string {
    99  	// Get all the addresses associated with this server. This
   100  	// was taken directly from Terraform.
   101  	for pool, networkAddresses := range s.Addresses {
   102  		// If we have an SSH interface specified, skip it if no match
   103  		if desired != "" && pool != desired {
   104  			log.Printf(
   105  				"[INFO] Skipping pool %s, doesn't match requested %s",
   106  				pool, desired)
   107  			continue
   108  		}
   109  
   110  		elements, ok := networkAddresses.([]interface{})
   111  		if !ok {
   112  			log.Printf(
   113  				"[ERROR] Unknown return type for address field: %#v",
   114  				networkAddresses)
   115  			continue
   116  		}
   117  
   118  		for _, element := range elements {
   119  			var addr string
   120  			address := element.(map[string]interface{})
   121  			if address["OS-EXT-IPS:type"] == "floating" {
   122  				addr = address["addr"].(string)
   123  			} else if sshIPVersion == "4" {
   124  				if address["version"].(float64) == 4 {
   125  					addr = address["addr"].(string)
   126  				}
   127  			} else if sshIPVersion == "6" {
   128  				if address["version"].(float64) == 6 {
   129  					addr = fmt.Sprintf("[%s]", address["addr"].(string))
   130  				}
   131  			} else {
   132  				if address["version"].(float64) == 6 {
   133  					addr = fmt.Sprintf("[%s]", address["addr"].(string))
   134  				} else {
   135  					addr = address["addr"].(string)
   136  				}
   137  			}
   138  
   139  			if addr != "" {
   140  				log.Printf("[DEBUG] Detected address: %s", addr)
   141  				return addr
   142  			}
   143  		}
   144  	}
   145  
   146  	return ""
   147  }