From 49b80df27b668512914799be756fdfdf6957e8cd Mon Sep 17 00:00:00 2001 From: Shlomi Noach Date: Sun, 16 Sep 2018 11:44:52 +0300 Subject: [PATCH 1/3] Parsing ipv6 addresses --- go/logic/migrator.go | 2 +- go/mysql/instance_key.go | 39 +++++++++++++++++++++++++----------- go/mysql/instance_key_map.go | 2 +- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/go/logic/migrator.go b/go/logic/migrator.go index f52e2b8..eaddbbe 100644 --- a/go/logic/migrator.go +++ b/go/logic/migrator.go @@ -739,7 +739,7 @@ func (this *Migrator) initiateInspector() (err error) { log.Infof("Master found to be %+v", *this.migrationContext.ApplierConnectionConfig.ImpliedKey) } else { // Forced master host. - key, err := mysql.ParseRawInstanceKeyLoose(this.migrationContext.AssumeMasterHostname) + key, err := mysql.ParseInstanceKey(this.migrationContext.AssumeMasterHostname) if err != nil { return err } diff --git a/go/mysql/instance_key.go b/go/mysql/instance_key.go index 67284d9..56fe415 100644 --- a/go/mysql/instance_key.go +++ b/go/mysql/instance_key.go @@ -7,6 +7,7 @@ package mysql import ( "fmt" + "regexp" "strconv" "strings" ) @@ -15,6 +16,12 @@ const ( DefaultInstancePort = 3306 ) +var ( + ipv4HostPortRegexp = regexp.MustCompile("^([^:]+):([0-9]+)$") + ipv4HostRegexp = regexp.MustCompile("^([^:]+)$") + ipv6HostPortRegexp = regexp.MustCompile("^\\[(.+)\\]:([0-9]+)$") // e.g. [2001:db8:1f70::999:de8:7648:6e8]:3308 +) + // InstanceKey is an instance indicator, identified by hostname and port type InstanceKey struct { Hostname string @@ -25,25 +32,33 @@ const detachHint = "//" // ParseInstanceKey will parse an InstanceKey from a string representation such as 127.0.0.1:3306 func NewRawInstanceKey(hostPort string) (*InstanceKey, error) { - tokens := strings.SplitN(hostPort, ":", 2) - if len(tokens) != 2 { - return nil, fmt.Errorf("Cannot parse InstanceKey from %s. Expected format is host:port", hostPort) + hostname := "" + port := "" + if submatch := ipv4HostPortRegexp.FindStringSubmatch(hostPort); len(submatch) > 0 { + hostname = submatch[1] + port = submatch[2] + } else if submatch := ipv4HostRegexp.FindStringSubmatch(hostPort); len(submatch) > 0 { + hostname = submatch[1] + } else if submatch := ipv6HostPortRegexp.FindStringSubmatch(hostPort); len(submatch) > 0 { + hostname = submatch[1] + port = submatch[2] + } else { + return nil, fmt.Errorf("Cannot parse address: %s", hostPort) } - instanceKey := &InstanceKey{Hostname: tokens[0]} - var err error - if instanceKey.Port, err = strconv.Atoi(tokens[1]); err != nil { - return instanceKey, fmt.Errorf("Invalid port: %s", tokens[1]) + instanceKey := &InstanceKey{Hostname: hostname, Port: DefaultInstancePort} + if port != "" { + var err error + if instanceKey.Port, err = strconv.Atoi(port); err != nil { + return instanceKey, fmt.Errorf("Invalid port: %s", port) + } } return instanceKey, nil } -// ParseRawInstanceKeyLoose will parse an InstanceKey from a string representation such as 127.0.0.1:3306. +// ParseInstanceKey will parse an InstanceKey from a string representation such as 127.0.0.1:3306. // The port part is optional; there will be no name resolve -func ParseRawInstanceKeyLoose(hostPort string) (*InstanceKey, error) { - if !strings.Contains(hostPort, ":") { - return &InstanceKey{Hostname: hostPort, Port: DefaultInstancePort}, nil - } +func ParseInstanceKey(hostPort string) (*InstanceKey, error) { return NewRawInstanceKey(hostPort) } diff --git a/go/mysql/instance_key_map.go b/go/mysql/instance_key_map.go index d0900ef..1065fb9 100644 --- a/go/mysql/instance_key_map.go +++ b/go/mysql/instance_key_map.go @@ -92,7 +92,7 @@ func (this *InstanceKeyMap) ReadCommaDelimitedList(list string) error { } tokens := strings.Split(list, ",") for _, token := range tokens { - key, err := ParseRawInstanceKeyLoose(token) + key, err := ParseInstanceKey(token) if err != nil { return err } From a7cfaa4d336c5e1d0084c1120537bbdad3ad239c Mon Sep 17 00:00:00 2001 From: Shlomi Noach Date: Sun, 16 Sep 2018 11:48:15 +0300 Subject: [PATCH 2/3] added testing --- go/mysql/instance_key_test.go | 58 +++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 go/mysql/instance_key_test.go diff --git a/go/mysql/instance_key_test.go b/go/mysql/instance_key_test.go new file mode 100644 index 0000000..73e22f3 --- /dev/null +++ b/go/mysql/instance_key_test.go @@ -0,0 +1,58 @@ +/* + Copyright 2016 GitHub Inc. + See https://github.com/github/gh-ost/blob/master/LICENSE +*/ + +package mysql + +import ( + "testing" + + "github.com/outbrain/golib/log" + test "github.com/outbrain/golib/tests" +) + +func init() { + log.SetLevel(log.ERROR) +} + +func TestParseInstanceKey(t *testing.T) { + { + key, err := ParseInstanceKey("myhost:1234") + test.S(t).ExpectNil(err) + test.S(t).ExpectEquals(key.Hostname, "myhost") + test.S(t).ExpectEquals(key.Port, 1234) + } + { + key, err := ParseInstanceKey("myhost") + test.S(t).ExpectNil(err) + test.S(t).ExpectEquals(key.Hostname, "myhost") + test.S(t).ExpectEquals(key.Port, 3306) + } + { + key, err := ParseInstanceKey("10.0.0.3:3307") + test.S(t).ExpectNil(err) + test.S(t).ExpectEquals(key.Hostname, "10.0.0.3") + test.S(t).ExpectEquals(key.Port, 3307) + } + { + key, err := ParseInstanceKey("10.0.0.3") + test.S(t).ExpectNil(err) + test.S(t).ExpectEquals(key.Hostname, "10.0.0.3") + test.S(t).ExpectEquals(key.Port, 3306) + } + { + key, err := ParseInstanceKey("[2001:db8:1f70::999:de8:7648:6e8]:3308") + test.S(t).ExpectNil(err) + test.S(t).ExpectEquals(key.Hostname, "2001:db8:1f70::999:de8:7648:6e8") + test.S(t).ExpectEquals(key.Port, 3308) + } + { + _, err := ParseInstanceKey("10.0.0.4:") + test.S(t).ExpectNotNil(err) + } + { + _, err := ParseInstanceKey("10.0.0.4:5.6.7") + test.S(t).ExpectNotNil(err) + } +} From 959d1af211547f428a7d324cc91e0b3a8b6b4bf8 Mon Sep 17 00:00:00 2001 From: Shlomi Noach Date: Sun, 16 Sep 2018 11:52:59 +0300 Subject: [PATCH 3/3] support ipv6 without port --- go/mysql/instance_key.go | 5 ++++- go/mysql/instance_key_test.go | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/go/mysql/instance_key.go b/go/mysql/instance_key.go index 56fe415..eb108d8 100644 --- a/go/mysql/instance_key.go +++ b/go/mysql/instance_key.go @@ -19,7 +19,8 @@ const ( var ( ipv4HostPortRegexp = regexp.MustCompile("^([^:]+):([0-9]+)$") ipv4HostRegexp = regexp.MustCompile("^([^:]+)$") - ipv6HostPortRegexp = regexp.MustCompile("^\\[(.+)\\]:([0-9]+)$") // e.g. [2001:db8:1f70::999:de8:7648:6e8]:3308 + ipv6HostPortRegexp = regexp.MustCompile("^\\[([:0-9a-fA-F]+)\\]:([0-9]+)$") // e.g. [2001:db8:1f70::999:de8:7648:6e8]:3308 + ipv6HostRegexp = regexp.MustCompile("^([:0-9a-fA-F]+)$") // e.g. 2001:db8:1f70::999:de8:7648:6e8 ) // InstanceKey is an instance indicator, identified by hostname and port @@ -42,6 +43,8 @@ func NewRawInstanceKey(hostPort string) (*InstanceKey, error) { } else if submatch := ipv6HostPortRegexp.FindStringSubmatch(hostPort); len(submatch) > 0 { hostname = submatch[1] port = submatch[2] + } else if submatch := ipv6HostRegexp.FindStringSubmatch(hostPort); len(submatch) > 0 { + hostname = submatch[1] } else { return nil, fmt.Errorf("Cannot parse address: %s", hostPort) } diff --git a/go/mysql/instance_key_test.go b/go/mysql/instance_key_test.go index 73e22f3..778a5b3 100644 --- a/go/mysql/instance_key_test.go +++ b/go/mysql/instance_key_test.go @@ -47,6 +47,22 @@ func TestParseInstanceKey(t *testing.T) { test.S(t).ExpectEquals(key.Hostname, "2001:db8:1f70::999:de8:7648:6e8") test.S(t).ExpectEquals(key.Port, 3308) } + { + key, err := ParseInstanceKey("::1") + test.S(t).ExpectNil(err) + test.S(t).ExpectEquals(key.Hostname, "::1") + test.S(t).ExpectEquals(key.Port, 3306) + } + { + key, err := ParseInstanceKey("0:0:0:0:0:0:0:0") + test.S(t).ExpectNil(err) + test.S(t).ExpectEquals(key.Hostname, "0:0:0:0:0:0:0:0") + test.S(t).ExpectEquals(key.Port, 3306) + } + { + _, err := ParseInstanceKey("[2001:xxxx:1f70::999:de8:7648:6e8]:3308") + test.S(t).ExpectNotNil(err) + } { _, err := ParseInstanceKey("10.0.0.4:") test.S(t).ExpectNotNil(err)