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  }