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 }