blob: a3179729b4d377350e4ec59d252373eeb0688665 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package internal
import (
"net"
"sync"
"v.io/v23/logging"
"v.io/v23/rpc"
)
type addressChooser struct {
logger logging.Logger
gcePublicAddressOnce sync.Once
gcePublicAddress net.Addr
ipChooser IPAddressChooser
}
func (c *addressChooser) setGCEPublicAddress() {
c.gcePublicAddressOnce.Do(func() {
if ipaddr := GCEPublicAddress(c.logger); ipaddr != nil {
c.gcePublicAddress = ipaddr
}
})
}
func (c *addressChooser) ChooseAddresses(protocol string, candidates []net.Addr) ([]net.Addr, error) {
c.setGCEPublicAddress() // Blocks till the address is set
if c.gcePublicAddress == nil {
return c.ipChooser.ChooseAddresses(protocol, candidates)
}
return []net.Addr{c.gcePublicAddress}, nil
}
// NewAddressChooser will return the public IP of process if the process is
// is being hosted by a cloud service provider (e.g. Google Compute Engine,
// Amazon EC2), and if not will be the same as IPAddressChooser.
func NewAddressChooser(logger logging.Logger) rpc.AddressChooser {
if HasPublicIP(logger) {
return IPAddressChooser{}
}
// Our address is private, so we test for running on GCE and for its 1:1 NAT
// configuration. GCEPublicAddress returns a non-nil addr if we are
// running on GCE/AWS.
//
// GCEPublicAddress can unforunately take up to 1 second to determine that the
// external address (see https://github.com/vanadium/issues/issues/776).
//
// So NewAddressChooser fires it up in a goroutine and returns immediately,
// thus avoiding any blockage till the AddressChooser is actually invoked.
//
// I apologize for the existence of this code! It is ugly, so if you have any
// suggestions please do share. Ideally, the operation to "detect whether the
// process is running under an Amazon EC2 instance" wouldn't block for a
// timeout of 1 second and we can do away with this mess.
ret := &addressChooser{logger: logger}
go ret.setGCEPublicAddress()
return ret
}