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