vitess.io/vitess@v0.16.2/go/vt/vtorc/inst/instance_key.go (about)

     1  /*
     2     Copyright 2015 Shlomi Noach, courtesy Booking.com
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package inst
    18  
    19  import (
    20  	"fmt"
    21  	"regexp"
    22  	"strconv"
    23  	"strings"
    24  )
    25  
    26  // InstanceKey is an instance indicator, identifued by hostname and port
    27  type InstanceKey struct {
    28  	Hostname string
    29  	Port     int
    30  }
    31  
    32  var (
    33  	ipv4Regexp         = regexp.MustCompile(`^([0-9]+)[.]([0-9]+)[.]([0-9]+)[.]([0-9]+)$`)
    34  	ipv4HostPortRegexp = regexp.MustCompile(`^([^:]+):([0-9]+)$`)
    35  	ipv4HostRegexp     = regexp.MustCompile(`^([^:]+)$`)
    36  	ipv6HostPortRegexp = regexp.MustCompile(`^\[([:0-9a-fA-F]+)\]:([0-9]+)$`) // e.g. [2001:db8:1f70::999:de8:7648:6e8]:3308
    37  	ipv6HostRegexp     = regexp.MustCompile(`^([:0-9a-fA-F]+)$`)              // e.g. 2001:db8:1f70::999:de8:7648:6e8
    38  )
    39  
    40  const detachHint = "//"
    41  
    42  func newInstanceKey(hostname string, port int, resolve bool) (instanceKey *InstanceKey, err error) {
    43  	if hostname == "" {
    44  		return instanceKey, fmt.Errorf("NewResolveInstanceKey: Empty hostname")
    45  	}
    46  
    47  	instanceKey = &InstanceKey{Hostname: hostname, Port: port}
    48  	if resolve {
    49  		instanceKey, err = instanceKey.ResolveHostname()
    50  	}
    51  	return instanceKey, err
    52  }
    53  
    54  // newInstanceKeyStrings
    55  func newInstanceKeyStrings(hostname string, port string, resolve bool) (*InstanceKey, error) {
    56  	portInt, err := strconv.Atoi(port)
    57  	if err != nil {
    58  		return nil, fmt.Errorf("Invalid port: %s", port)
    59  	}
    60  	return newInstanceKey(hostname, portInt, resolve)
    61  }
    62  
    63  func parseRawInstanceKey(hostPort string, resolve bool) (instanceKey *InstanceKey, err error) {
    64  	hostname := ""
    65  	port := ""
    66  	if submatch := ipv4HostPortRegexp.FindStringSubmatch(hostPort); len(submatch) > 0 {
    67  		hostname = submatch[1]
    68  		port = submatch[2]
    69  	} else if submatch := ipv4HostRegexp.FindStringSubmatch(hostPort); len(submatch) > 0 {
    70  		hostname = submatch[1]
    71  	} else if submatch := ipv6HostPortRegexp.FindStringSubmatch(hostPort); len(submatch) > 0 {
    72  		hostname = submatch[1]
    73  		port = submatch[2]
    74  	} else if submatch := ipv6HostRegexp.FindStringSubmatch(hostPort); len(submatch) > 0 {
    75  		hostname = submatch[1]
    76  	} else {
    77  		return nil, fmt.Errorf("Cannot parse address: %s", hostPort)
    78  	}
    79  	if port == "" {
    80  		port = "3306"
    81  	}
    82  	return newInstanceKeyStrings(hostname, port, resolve)
    83  }
    84  
    85  func NewResolveInstanceKey(hostname string, port int) (instanceKey *InstanceKey, err error) {
    86  	return newInstanceKey(hostname, port, true)
    87  }
    88  
    89  // NewResolveInstanceKeyStrings creates and resolves a new instance key based on string params
    90  func NewResolveInstanceKeyStrings(hostname string, port string) (*InstanceKey, error) {
    91  	return newInstanceKeyStrings(hostname, port, true)
    92  }
    93  
    94  func ParseResolveInstanceKey(hostPort string) (instanceKey *InstanceKey, err error) {
    95  	return parseRawInstanceKey(hostPort, true)
    96  }
    97  
    98  func ParseRawInstanceKey(hostPort string) (instanceKey *InstanceKey, err error) {
    99  	return parseRawInstanceKey(hostPort, false)
   100  }
   101  
   102  // NewResolveInstanceKeyStrings creates and resolves a new instance key based on string params
   103  func NewRawInstanceKeyStrings(hostname string, port string) (*InstanceKey, error) {
   104  	return newInstanceKeyStrings(hostname, port, false)
   105  }
   106  
   107  func (instanceKey *InstanceKey) ResolveHostname() (*InstanceKey, error) {
   108  	if !instanceKey.IsValid() {
   109  		return instanceKey, nil
   110  	}
   111  
   112  	hostname, err := ResolveHostname(instanceKey.Hostname)
   113  	if err == nil {
   114  		instanceKey.Hostname = hostname
   115  	}
   116  	return instanceKey, err
   117  }
   118  
   119  // Equals tests equality between this key and another key
   120  func (instanceKey *InstanceKey) Equals(other *InstanceKey) bool {
   121  	if other == nil {
   122  		return false
   123  	}
   124  	return instanceKey.Hostname == other.Hostname && instanceKey.Port == other.Port
   125  }
   126  
   127  // SmallerThan returns true if this key is dictionary-smaller than another.
   128  // This is used for consistent sorting/ordering; there's nothing magical about it.
   129  func (instanceKey *InstanceKey) SmallerThan(other *InstanceKey) bool {
   130  	if instanceKey.Hostname < other.Hostname {
   131  		return true
   132  	}
   133  	if instanceKey.Hostname == other.Hostname && instanceKey.Port < other.Port {
   134  		return true
   135  	}
   136  	return false
   137  }
   138  
   139  // IsDetached returns 'true' when this hostname is logically "detached"
   140  func (instanceKey *InstanceKey) IsDetached() bool {
   141  	return strings.HasPrefix(instanceKey.Hostname, detachHint)
   142  }
   143  
   144  // IsValid uses simple heuristics to see whether this key represents an actual instance
   145  func (instanceKey *InstanceKey) IsValid() bool {
   146  	if instanceKey.Hostname == "_" {
   147  		return false
   148  	}
   149  	if instanceKey.IsDetached() {
   150  		return false
   151  	}
   152  	return len(instanceKey.Hostname) > 0 && instanceKey.Port > 0
   153  }
   154  
   155  // DetachedKey returns an instance key whose hostname is detahced: invalid, but recoverable
   156  func (instanceKey *InstanceKey) DetachedKey() *InstanceKey {
   157  	if instanceKey.IsDetached() {
   158  		return instanceKey
   159  	}
   160  	return &InstanceKey{Hostname: fmt.Sprintf("%s%s", detachHint, instanceKey.Hostname), Port: instanceKey.Port}
   161  }
   162  
   163  // ReattachedKey returns an instance key whose hostname is detahced: invalid, but recoverable
   164  func (instanceKey *InstanceKey) ReattachedKey() *InstanceKey {
   165  	if !instanceKey.IsDetached() {
   166  		return instanceKey
   167  	}
   168  	return &InstanceKey{Hostname: instanceKey.Hostname[len(detachHint):], Port: instanceKey.Port}
   169  }
   170  
   171  // StringCode returns an official string representation of this key
   172  func (instanceKey *InstanceKey) StringCode() string {
   173  	return fmt.Sprintf("%s:%d", instanceKey.Hostname, instanceKey.Port)
   174  }
   175  
   176  // DisplayString returns a user-friendly string representation of this key
   177  func (instanceKey *InstanceKey) DisplayString() string {
   178  	return instanceKey.StringCode()
   179  }
   180  
   181  // String returns a user-friendly string representation of this key
   182  func (instanceKey InstanceKey) String() string {
   183  	return instanceKey.StringCode()
   184  }
   185  
   186  // IsValid uses simple heuristics to see whether this key represents an actual instance
   187  func (instanceKey *InstanceKey) IsIPv4() bool {
   188  	return ipv4Regexp.MatchString(instanceKey.Hostname)
   189  }