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