go.temporal.io/server@v1.23.0/common/persistence/nosql/nosqlplugin/cassandra/translator/fixed_address_translator.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package translator 26 27 import ( 28 "errors" 29 "fmt" 30 "net" 31 32 "github.com/gocql/gocql" 33 "go.temporal.io/server/common/config" 34 ) 35 36 const ( 37 fixedTranslatorName = "fixed-address-translator" 38 advertisedHostnameKey = "advertised-hostname" 39 ) 40 41 func init() { 42 RegisterTranslator(fixedTranslatorName, NewFixedAddressTranslatorPlugin()) 43 } 44 45 type FixedAddressTranslatorPlugin struct { 46 } 47 48 func NewFixedAddressTranslatorPlugin() TranslatorPlugin { 49 return &FixedAddressTranslatorPlugin{} 50 } 51 52 // GetTranslator What gocql driver does is that it will connect to the first node in the list in configuration 53 // (if there is more than one), if it fails to connect to it, it will pick another from that list and so on so. 54 // When the connection is initialised, the driver does not know what the topology of a cluster looks like yet. 55 // It just connected to a node. In order to know, for the driver itself, internally, what the topology is like, 56 // it will query that node it just connected to, and it will read system.peers table. In that table, 57 // there are all other nodes of a cluster as that node, gocql just connected to, sees it. 58 // 59 // Every other node has the very same system.peers table where all other nodes of the cluster are. 60 // The returned rows, representing all other nodes in the cluster, contain IP addresses internal of that cluster. 61 // They are not necessarily the same hostnames as the ones you would translate with your service resolver 62 // (they are IP addresses, not hostnames, actually). 63 // 64 // For the case if the nodes are behind the proxy, service resolver would translate just the publicly 65 // visible hostname which a user put into configuration so driver can connect to it, but that is not enough, 66 // IP addresses behind a proxy are not reachable from client's perspective. These are not translatable 67 // with service resolver so client can not connect to them directly - that is what gocql address 68 // translator is exactly for. 69 // 70 // The implementation of fixed address translator plugin is fed the internal IP address from the system.peers table, 71 // and it will always return same fixed ip based on what advertised-hostname is resolved to. That IP address, 72 // from client's perspective, might be, for example, an IP address of a load balancer which is reachable from client. 73 // As the IP address of all nodes are some, the difference between the nodes can be achieved by running them on 74 // a different port for each node. 75 // see also https://github.com/gocql/gocql/pull/1635 76 func (plugin *FixedAddressTranslatorPlugin) GetTranslator(cfg *config.Cassandra) (gocql.AddressTranslator, error) { 77 if cfg.AddressTranslator == nil { 78 return nil, errors.New("there is no addressTranslator configuration in cassandra configuration") 79 } 80 81 opts := cfg.AddressTranslator.Options 82 if opts == nil { 83 return nil, errors.New("there are no options for translator plugin") 84 } 85 86 advertisedHostname, found := opts[advertisedHostnameKey] 87 88 if !found || len(advertisedHostname) == 0 { 89 return nil, errors.New("detected no advertised-hostname key or empty value for translator plugin options") 90 } 91 92 var resolvedIp net.IP = nil 93 ips, _ := net.LookupIP(advertisedHostname) 94 for _, ip := range ips { 95 if ipv4 := ip.To4(); ipv4 != nil { 96 resolvedIp = ipv4 97 break 98 } 99 } 100 101 if resolvedIp == nil { 102 return nil, fmt.Errorf("no resolved IP for advertised hostname %q", advertisedHostname) 103 } 104 105 return gocql.AddressTranslatorFunc(func(addr net.IP, port int) (net.IP, int) { 106 return resolvedIp, port 107 }), nil 108 }